Search in sources :

Example 6 with MethodData

use of org.fakereplace.data.MethodData in project fakereplace by fakereplace.

the class ConstructorReflection method getDeclaredConstructor.

public static Constructor<?> getDeclaredConstructor(Class<?> clazz, Class<?>... parameters) throws NoSuchMethodException {
    ClassData cd = ClassDataStore.instance().getModifiedClassData(clazz.getClassLoader(), Descriptor.toJvmName(clazz.getName()));
    if (cd == null || !cd.isReplaceable()) {
        return clazz.getDeclaredConstructor(parameters);
    }
    String args = '(' + DescriptorUtils.classArrayToDescriptorString(parameters) + ')';
    MethodData md = cd.getMethodData("<init>", args);
    if (md == null) {
        return clazz.getDeclaredConstructor(parameters);
    }
    switch(md.getType()) {
        case NORMAL:
            return clazz.getDeclaredConstructor(parameters);
        case FAKE_CONSTRUCTOR:
            try {
                Class<?> c = clazz.getClassLoader().loadClass(md.getClassName());
                return c.getDeclaredConstructor(parameters);
            } catch (NoSuchMethodException e) {
                throw e;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
    }
    throw new NoSuchMethodException();
}
Also used : ClassData(org.fakereplace.data.ClassData) MethodData(org.fakereplace.data.MethodData) InvocationTargetException(java.lang.reflect.InvocationTargetException)

Example 7 with MethodData

use of org.fakereplace.data.MethodData in project fakereplace by fakereplace.

the class ConstructorReflection method getDeclaredConstructors.

public static Constructor<?>[] getDeclaredConstructors(Class<?> clazz) {
    try {
        ClassData cd = ClassDataStore.instance().getModifiedClassData(clazz.getClassLoader(), Descriptor.toJvmName(clazz.getName()));
        if (cd == null || !cd.isReplaceable()) {
            return clazz.getDeclaredConstructors();
        }
        Constructor<?>[] meth = clazz.getDeclaredConstructors();
        List<Constructor<?>> visible = new ArrayList<>(meth.length);
        for (int i = 0; i < meth.length; ++i) {
            if (meth[i].getParameterTypes().length != 3 || !meth[i].getParameterTypes()[2].equals(ConstructorArgument.class)) {
                visible.add(meth[i]);
            }
        }
        for (MethodData i : cd.getMethods()) {
            if (i.getType() == MemberType.FAKE_CONSTRUCTOR) {
                Class<?> c = clazz.getClassLoader().loadClass(i.getClassName());
                visible.add(i.getConstructor(c));
            } else if (i.getType() == MemberType.REMOVED && i.getMethodName().equals("<init>")) {
                Class<?> c = clazz.getClassLoader().loadClass(i.getClassName());
                visible.remove(i.getConstructor(c));
            }
        }
        Constructor<?>[] ret = new Constructor[visible.size()];
        for (int i = 0; i < visible.size(); ++i) {
            ret[i] = visible.get(i);
        }
        return ret;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
Also used : ClassData(org.fakereplace.data.ClassData) Constructor(java.lang.reflect.Constructor) ArrayList(java.util.ArrayList) MethodData(org.fakereplace.data.MethodData) InvocationTargetException(java.lang.reflect.InvocationTargetException)

Example 8 with MethodData

use of org.fakereplace.data.MethodData in project fakereplace by fakereplace.

the class FakeMethodCallManipulator method transformClass.

public boolean transformClass(ClassFile file, ClassLoader loader, boolean modifiableClass, final Set<MethodInfo> modifiedMethods) {
    if (!Fakereplace.isRetransformationStarted()) {
        return false;
    }
    final Map<String, Set<Data>> knownFakeMethods = data.getManipulationData(loader);
    // methods that are known to need a rewrite to a generated static method
    final Map<Integer, Data> knownFakeMethodCallLocations = new HashMap<>();
    // methods that may need a rewrite to a generated static method
    final Map<Integer, AddedMethodInfo> potentialFakeMethodCallLocations = new HashMap<>();
    // 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, methodDesc, methodName;
            if (pool.getTag(i) == ConstPool.CONST_Methodref) {
                className = pool.getMethodrefClassName(i);
                methodDesc = pool.getMethodrefType(i);
                methodName = pool.getMethodrefName(i);
            } else {
                className = pool.getInterfaceMethodrefClassName(i);
                methodDesc = pool.getInterfaceMethodrefType(i);
                methodName = pool.getInterfaceMethodrefName(i);
            }
            if (methodName.equals("<clinit>") || methodName.equals("<init>")) {
                continue;
            }
            boolean handled = false;
            if (knownFakeMethods.containsKey(className)) {
                for (Data data : knownFakeMethods.get(className)) {
                    if (methodName.equals(data.getMethodName()) && methodDesc.equals(data.getMethodDesc())) {
                        // store the location in the const pool of the method ref
                        knownFakeMethodCallLocations.put(i, data);
                        // we have found a method call
                        // now lets replace it
                        handled = true;
                        break;
                    }
                }
            }
            if (loader != null && !handled && !className.equals(file.getName()) && Fakereplace.isClassReplaceable(className, loader)) {
                // may be an added method
                // if the field does not actually exist yet we just assume it is about to come into existence
                // and rewrite it anyway
                BaseClassData data = ClassDataStore.instance().getBaseClassData(loader, className);
                if (data != null) {
                    boolean noClassData = false;
                    MethodData method = null;
                    try {
                        Class<?> mainClass = loader.loadClass(className);
                        Set<Class> allClasses = new HashSet<>();
                        addToAllClasses(mainClass, allClasses);
                        for (Class clazz : allClasses) {
                            data = ClassDataStore.instance().getBaseClassData(clazz.getClassLoader(), clazz.getName());
                            if (data == null) {
                                noClassData = true;
                                break;
                            }
                            method = data.getMethodOrConstructor(methodName, methodDesc);
                            if (method != null) {
                                break;
                            }
                        }
                    } catch (ClassNotFoundException e) {
                        noClassData = true;
                    }
                    if (!noClassData) {
                        if (method == null) {
                            // this is a new method
                            // lets deal with it
                            int methodNo = MethodIdentifierStore.instance().getMethodNumber(methodName, methodDesc);
                            potentialFakeMethodCallLocations.put(i, new AddedMethodInfo(methodNo, className, methodName, methodDesc));
                        } else if (!Modifier.isPublic(method.getAccessFlags())) {
                            boolean requiresVisibilityUpgrade = false;
                            if (Modifier.isPrivate(method.getAccessFlags())) {
                                requiresVisibilityUpgrade = true;
                            } else if (!Modifier.isProtected(method.getAccessFlags())) {
                                // we can't handle protected properly, because we need to know the class heirachy
                                // this is package local, so we check the package names
                                boolean thisDefault = !file.getName().contains(".");
                                boolean thatDefault = !className.contains(".");
                                if (thisDefault && !thatDefault) {
                                    requiresVisibilityUpgrade = true;
                                } else if (thatDefault && !thisDefault) {
                                    requiresVisibilityUpgrade = true;
                                } else if (!thatDefault) {
                                    String thatPackage = className.substring(0, className.lastIndexOf("."));
                                    String thisPackage = file.getName().substring(0, file.getName().lastIndexOf("."));
                                    if (!thisPackage.equals(thatPackage)) {
                                        requiresVisibilityUpgrade = true;
                                    }
                                }
                            }
                            if (requiresVisibilityUpgrade) {
                                int methodNo = MethodIdentifierStore.instance().getMethodNumber(methodName, methodDesc);
                                potentialFakeMethodCallLocations.put(i, new AddedMethodInfo(methodNo, className, methodName, methodDesc));
                            }
                        }
                    }
                }
            }
        }
    }
    // through the methods and replace instances of the call
    if (!knownFakeMethodCallLocations.isEmpty() || !potentialFakeMethodCallLocations.isEmpty()) {
        handleLambdas(file, knownFakeMethodCallLocations, pool);
        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 || op == CodeIterator.INVOKESPECIAL) {
                        int val = it.s16bitAt(index + 1);
                        // replacing
                        if (potentialFakeMethodCallLocations.containsKey(val)) {
                            AddedMethodInfo methodInfo = potentialFakeMethodCallLocations.get(val);
                            Data data = new Data(methodInfo.className, methodInfo.name, methodInfo.desc, op == Opcode.INVOKESTATIC ? Type.STATIC : op == Opcode.INVOKEINTERFACE ? Type.INTERFACE : Type.VIRTUAL, loader, methodInfo.number, null);
                            handleFakeMethodCall(file, modifiedMethods, m, it, index, op, data);
                        } else if (knownFakeMethodCallLocations.containsKey(val)) {
                            Data data = knownFakeMethodCallLocations.get(val);
                            handleFakeMethodCall(file, modifiedMethods, m, it, index, op, data);
                        }
                    }
                }
                modifiedMethods.add(m);
                m.getCodeAttribute().computeMaxStack();
            } catch (Exception e) {
                log.error("Bad byte code transforming " + file.getName(), e);
                e.printStackTrace();
            }
        }
        return true;
    } else {
        return false;
    }
}
Also used : ConstPool(javassist.bytecode.ConstPool) HashSet(java.util.HashSet) Set(java.util.Set) HashMap(java.util.HashMap) MethodData(org.fakereplace.data.MethodData) BaseClassData(org.fakereplace.data.BaseClassData) BaseClassData(org.fakereplace.data.BaseClassData) CodeIterator(javassist.bytecode.CodeIterator) MethodData(org.fakereplace.data.MethodData) MethodInfo(javassist.bytecode.MethodInfo) HashSet(java.util.HashSet)

Example 9 with MethodData

use of org.fakereplace.data.MethodData in project fakereplace by fakereplace.

the class ConstructorReflection method newInstance.

@SuppressWarnings("restriction")
public static Object newInstance(Constructor<?> method, Object... args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException {
    final MethodData data = ClassDataStore.instance().getMethodInformation(method.getDeclaringClass().getName());
    final Class<?> info = ClassDataStore.instance().getRealClassFromProxyName(method.getDeclaringClass().getName());
    try {
        final Constructor<?> invoke = info.getConstructor(int.class, Object[].class, ConstructorArgument.class);
        Object ar = args;
        if (ar == null) {
            ar = new Object[0];
        }
        if (!Modifier.isPublic(method.getModifiers()) && !method.isAccessible()) {
            Class<?> caller = AccessVerification.getCallerClass(2);
            AccessVerification.ensureMemberAccess(caller, method.getDeclaringClass(), method.getModifiers());
        }
        return invoke.newInstance(data.getMethodNo(), ar, null);
    } catch (NoSuchMethodException | SecurityException e) {
        throw new RuntimeException(e);
    }
}
Also used : MethodData(org.fakereplace.data.MethodData)

Example 10 with MethodData

use of org.fakereplace.data.MethodData in project fakereplace by fakereplace.

the class ConstructorInvocationManipulator method transformClass.

public boolean transformClass(ClassFile file, ClassLoader loader, boolean modifiableClass, final Set<MethodInfo> modifiedMethods) {
    Map<String, Set<ConstructorRewriteData>> constructorRewrites = new HashMap<>(data.getManipulationData(loader));
    Map<Integer, ConstructorRewriteData> methodCallLocations = new HashMap<>();
    // 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) {
            boolean handled = false;
            String className = pool.getMethodrefClassName(i);
            String methodDesc = pool.getMethodrefType(i);
            String methodName = pool.getMethodrefName(i);
            if (methodName.equals("<init>")) {
                if (constructorRewrites.containsKey(className)) {
                    for (ConstructorRewriteData data : constructorRewrites.get(className)) {
                        if (methodDesc.equals(data.getMethodDesc())) {
                            // store the location in the const pool of the method ref
                            methodCallLocations.put(i, data);
                            // we have found a method call
                            // now lets replace it
                            handled = true;
                            break;
                        }
                    }
                }
                if (!handled && Fakereplace.isClassReplaceable(className, loader)) {
                    // may be an added field
                    // if the field does not actually exist yet we just assume it is about to come into existence
                    // and rewrite it anyway
                    BaseClassData data = ClassDataStore.instance().getBaseClassData(loader, className);
                    if (data != null) {
                        MethodData method = data.getMethodOrConstructor("<init>", methodDesc);
                        if (method == null) {
                            // this is a new method
                            // lets deal with it
                            int methodNo = MethodIdentifierStore.instance().getMethodNumber("<init>", methodDesc);
                            methodCallLocations.put(i, new ConstructorRewriteData(className, methodDesc, methodNo, loader));
                        }
                    }
                }
            }
        }
    }
    // through the methods and replace instances of the call
    if (!methodCallLocations.isEmpty()) {
        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.INVOKESPECIAL) {
                        int val = it.s16bitAt(index + 1);
                        // replacing
                        if (methodCallLocations.containsKey(val)) {
                            ConstructorRewriteData data = methodCallLocations.get(val);
                            // so we currently have all the arguments sitting on the
                            // stack, and we need to jigger them into
                            // an array and then call our method. First thing to do
                            // is scribble over the existing
                            // instructions:
                            it.writeByte(CodeIterator.NOP, index);
                            it.writeByte(CodeIterator.NOP, index + 1);
                            it.writeByte(CodeIterator.NOP, index + 2);
                            Bytecode bc = new Bytecode(file.getConstPool());
                            ManipulationUtils.pushParametersIntoArray(bc, data.getMethodDesc());
                            // so now our stack looks like unconstructed instance : array
                            // we need unconstructed instance : int : array : null
                            bc.addIconst(data.getMethodNo());
                            bc.add(Opcode.SWAP);
                            bc.add(Opcode.ACONST_NULL);
                            bc.addInvokespecial(data.getClazz(), "<init>", Constants.ADDED_CONSTRUCTOR_DESCRIPTOR);
                            // and we have our bytecode
                            it.insert(bc.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) Set(java.util.Set) HashMap(java.util.HashMap) BaseClassData(org.fakereplace.data.BaseClassData) CodeIterator(javassist.bytecode.CodeIterator) MethodData(org.fakereplace.data.MethodData) MethodInfo(javassist.bytecode.MethodInfo) Bytecode(javassist.bytecode.Bytecode)

Aggregations

MethodData (org.fakereplace.data.MethodData)13 InvocationTargetException (java.lang.reflect.InvocationTargetException)8 ClassData (org.fakereplace.data.ClassData)8 Method (java.lang.reflect.Method)4 ArrayList (java.util.ArrayList)4 MethodInfo (javassist.bytecode.MethodInfo)3 BaseClassData (org.fakereplace.data.BaseClassData)3 ModifiedMethod (org.fakereplace.data.ModifiedMethod)3 Constructor (java.lang.reflect.Constructor)2 HashMap (java.util.HashMap)2 HashSet (java.util.HashSet)2 Set (java.util.Set)2 Bytecode (javassist.bytecode.Bytecode)2 CodeIterator (javassist.bytecode.CodeIterator)2 ConstPool (javassist.bytecode.ConstPool)2 IOException (java.io.IOException)1 IllegalClassFormatException (java.lang.instrument.IllegalClassFormatException)1 AnnotationsAttribute (javassist.bytecode.AnnotationsAttribute)1 BadBytecode (javassist.bytecode.BadBytecode)1 CodeAttribute (javassist.bytecode.CodeAttribute)1