Search in sources :

Example 1 with MethodData

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

the class MethodReflection method getMethod.

public static Method getMethod(Class<?> clazz, String name, Class<?>... parameters) throws NoSuchMethodException {
    ClassData cd = ClassDataStore.instance().getModifiedClassData(clazz.getClassLoader(), Descriptor.toJvmName(clazz.getName()));
    if (cd == null) {
        return clazz.getMethod(name, parameters);
    }
    String args = '(' + DescriptorUtils.classArrayToDescriptorString(parameters) + ')';
    MethodData md = cd.getMethodData(name, args);
    Class<?> superClass = clazz;
    while (superClass.getSuperclass() != null && md == null && superClass != Object.class) {
        superClass = superClass.getSuperclass();
        cd = ClassDataStore.instance().getModifiedClassData(superClass.getClassLoader(), Descriptor.toJvmName(superClass.getName()));
        if (cd != null) {
            md = cd.getMethodData(name, args);
        }
    }
    if (md == null) {
        return clazz.getMethod(name, parameters);
    }
    switch(md.getType()) {
        case NORMAL:
            return superClass.getMethod(name, parameters);
        case FAKE:
            try {
                if (!AccessFlag.isPublic(md.getAccessFlags())) {
                    throw new NoSuchMethodException(clazz.getName() + "." + name);
                }
                Class<?> c = superClass.getClassLoader().loadClass(md.getClassName());
                return c.getMethod(name, 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 2 with MethodData

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

the class MethodReflection method getDeclaredMethods.

public static Method[] getDeclaredMethods(Class<?> clazz) {
    try {
        ClassData cd = ClassDataStore.instance().getModifiedClassData(clazz.getClassLoader(), Descriptor.toJvmName(clazz.getName()));
        if (cd == null || !cd.isReplaceable()) {
            return clazz.getDeclaredMethods();
        }
        Method[] meth = clazz.getDeclaredMethods();
        List<Method> visible = new ArrayList<>(meth.length);
        for (int i = 0; i < meth.length; ++i) {
            MethodData mData = cd.getData(meth[i]);
            if (mData == null || mData.getType() == MemberType.NORMAL) {
                visible.add(meth[i]);
            }
        }
        for (MethodData i : cd.getMethods()) {
            if (i.getType() == MemberType.FAKE) {
                Class<?> c = clazz.getClassLoader().loadClass(i.getClassName());
                visible.add(i.getMethod(c));
            }
        }
        Method[] ret = new Method[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) ArrayList(java.util.ArrayList) MethodData(org.fakereplace.data.MethodData) ModifiedMethod(org.fakereplace.data.ModifiedMethod) Method(java.lang.reflect.Method) InvocationTargetException(java.lang.reflect.InvocationTargetException)

Example 3 with MethodData

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

the class MethodReplacementTransformer method transform.

@Override
public boolean transform(ClassLoader loader, String className, Class<?> oldClass, ProtectionDomain protectionDomain, ClassFile file, Set<Class<?>> classesToRetransform, ChangedClassImpl changedClass, Set<MethodInfo> modifiedMethods) throws IllegalClassFormatException, BadBytecode, DuplicateMemberException {
    if (oldClass == null || className == null) {
        return false;
    }
    final Set<MethodData> methodsToRemove = new HashSet<>();
    final Set<FakeMethod> methodsToAdd = new HashSet<>();
    final Set<FakeMethod> constructorsToAdd = new HashSet<>();
    BaseClassData data = ClassDataStore.instance().getBaseClassData(loader, className);
    // state for added static methods
    CodeAttribute staticCodeAttribute = null, virtualCodeAttribute = null, constructorCodeAttribute = null;
    try {
        // stick our added methods into the class file
        // we can't finalise the code yet because we will probably need
        // the add stuff to them
        MethodInfo virtMethod = new MethodInfo(file.getConstPool(), Constants.ADDED_METHOD_NAME, Constants.ADDED_METHOD_DESCRIPTOR);
        modifiedMethods.add(virtMethod);
        virtMethod.setAccessFlags(AccessFlag.PUBLIC);
        if (file.isInterface()) {
            virtMethod.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.ABSTRACT | AccessFlag.SYNTHETIC);
        } else {
            virtMethod.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.SYNTHETIC);
            Bytecode b = new Bytecode(file.getConstPool(), 0, 3);
            if (BuiltinClassData.skipInstrumentation(file.getSuperclass())) {
                b.addNew(NoSuchMethodError.class.getName());
                b.add(Opcode.DUP);
                b.addInvokespecial(NoSuchMethodError.class.getName(), "<init>", "()V");
                b.add(Opcode.ATHROW);
            } else {
                b.add(Bytecode.ALOAD_0);
                b.add(Bytecode.ILOAD_1);
                b.add(Bytecode.ALOAD_2);
                b.addInvokespecial(file.getSuperclass(), Constants.ADDED_METHOD_NAME, Constants.ADDED_METHOD_DESCRIPTOR);
                b.add(Bytecode.ARETURN);
            }
            virtualCodeAttribute = b.toCodeAttribute();
            virtMethod.setCodeAttribute(virtualCodeAttribute);
            MethodInfo m = new MethodInfo(file.getConstPool(), Constants.ADDED_STATIC_METHOD_NAME, Constants.ADDED_METHOD_DESCRIPTOR);
            modifiedMethods.add(m);
            m.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.STATIC | AccessFlag.SYNTHETIC);
            b = new Bytecode(file.getConstPool(), 0, 3);
            b.addNew(NoSuchMethodError.class.getName());
            b.add(Opcode.DUP);
            b.addInvokespecial(NoSuchMethodError.class.getName(), "<init>", "()V");
            b.add(Opcode.ATHROW);
            staticCodeAttribute = b.toCodeAttribute();
            m.setCodeAttribute(staticCodeAttribute);
            file.addMethod(m);
            m = new MethodInfo(file.getConstPool(), "<init>", Constants.ADDED_CONSTRUCTOR_DESCRIPTOR);
            modifiedMethods.add(m);
            m.setAccessFlags(AccessFlag.PUBLIC | AccessFlag.SYNTHETIC);
            b = new Bytecode(file.getConstPool(), 0, 4);
            if (ManipulationUtils.addBogusConstructorCall(file, b)) {
                constructorCodeAttribute = b.toCodeAttribute();
                m.setCodeAttribute(constructorCodeAttribute);
                constructorCodeAttribute.setMaxLocals(6);
                file.addMethod(m);
            }
        }
        file.addMethod(virtMethod);
    } catch (DuplicateMemberException e) {
        e.printStackTrace();
    }
    Set<MethodData> methods = new HashSet<>();
    methods.addAll(data.getMethods());
    ListIterator<?> it = file.getMethods().listIterator();
    // IncompatibleClassChange exception will be thrown
    while (it.hasNext()) {
        MethodInfo m = (MethodInfo) it.next();
        MethodData md = null;
        boolean upgradedVisibility = false;
        for (MethodData i : methods) {
            if (i.getMethodName().equals(m.getName()) && i.getDescriptor().equals(m.getDescriptor())) {
                // depends on what has changed
                if (i.getAccessFlags() != m.getAccessFlags()) {
                    if (AccessFlagUtils.upgradeVisibility(m.getAccessFlags(), i.getAccessFlags())) {
                        upgradedVisibility = true;
                    } else if (AccessFlagUtils.downgradeVisibility(m.getAccessFlags(), i.getAccessFlags())) {
                    // ignore this, we don't need to do anything
                    } else {
                        // we can't handle this yet
                        continue;
                    }
                }
                m.setAccessFlags(i.getAccessFlags());
                // if it is the constructor
                if (m.getName().equals("<init>")) {
                    try {
                        Constructor<?> meth = i.getConstructor(oldClass);
                        AnnotationDataStore.recordConstructorAnnotations(meth, (AnnotationsAttribute) m.getAttribute(AnnotationsAttribute.visibleTag));
                        AnnotationDataStore.recordConstructorParameterAnnotations(meth, (ParameterAnnotationsAttribute) m.getAttribute(ParameterAnnotationsAttribute.visibleTag));
                        // now revert the annotations:
                        m.addAttribute(AnnotationReplacer.duplicateAnnotationsAttribute(file.getConstPool(), meth));
                        m.addAttribute(AnnotationReplacer.duplicateParameterAnnotationsAttribute(file.getConstPool(), meth));
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                } else if (!m.getName().equals("<clinit>")) {
                    // we do not have to worry about them
                    try {
                        Method meth = i.getMethod(oldClass);
                        AnnotationDataStore.recordMethodAnnotations(meth, (AnnotationsAttribute) m.getAttribute(AnnotationsAttribute.visibleTag));
                        AnnotationDataStore.recordMethodParameterAnnotations(meth, (ParameterAnnotationsAttribute) m.getAttribute(ParameterAnnotationsAttribute.visibleTag));
                        // now revert the annotations:
                        m.addAttribute(AnnotationReplacer.duplicateAnnotationsAttribute(file.getConstPool(), meth));
                        m.addAttribute(AnnotationReplacer.duplicateParameterAnnotationsAttribute(file.getConstPool(), meth));
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
                md = i;
                break;
            }
        }
        // we do not need to deal with these
        if (m.getName().equals(Constants.ADDED_METHOD_NAME) || m.getName().equals(Constants.ADDED_STATIC_METHOD_NAME)) {
            break;
        }
        // so it is still in the original
        if (md == null || upgradedVisibility) {
            if ((m.getAccessFlags() & AccessFlag.STATIC) != 0) {
                Class<?> c = addMethod(file, loader, m, methodsToAdd, staticCodeAttribute, true, oldClass);
                if (c != null) {
                    classesToRetransform.add(c);
                }
            } else if ((m.getName().equals("<init>"))) {
                addConstructor(file, loader, m, constructorsToAdd, constructorCodeAttribute, oldClass);
            } else if (m.getName().equals("<clinit>")) {
            // nop, we can't change this, just ignore it
            } else {
                Class<?> c = addMethod(file, loader, m, methodsToAdd, virtualCodeAttribute, false, oldClass);
                if (c != null) {
                    classesToRetransform.add(c);
                }
            }
            if (!upgradedVisibility) {
                it.remove();
            }
        } else {
            methods.remove(md);
        }
        if (upgradedVisibility) {
            methods.remove(md);
        }
    }
    for (MethodData md : methods) {
        if (md.getType() == MemberType.NORMAL) {
            MethodInfo removedMethod = createRemovedMethod(file, md, oldClass, methodsToRemove);
            if (removedMethod != null) {
                modifiedMethods.add(removedMethod);
            }
        }
    }
    ClassDataStore.instance().modifyCurrentData(loader, className, (builder) -> {
        for (MethodData method : methodsToRemove) {
            builder.removeMethod(method);
        }
        for (FakeMethod fake : methodsToAdd) {
            ClassDataStore.instance().registerReplacedMethod(fake.proxyName, builder.addFakeMethod(fake.name, fake.descriptor, fake.proxyName, fake.accessFlags));
        }
        for (FakeMethod fake : constructorsToAdd) {
            ClassDataStore.instance().registerReplacedMethod(fake.proxyName, builder.addFakeConstructor(fake.name, fake.descriptor, fake.proxyName, fake.accessFlags, fake.methodCount));
        }
    });
    // the method declaration to propagate the call to the parent
    if (!file.isInterface()) {
        try {
            staticCodeAttribute.computeMaxStack();
            virtualCodeAttribute.computeMaxStack();
            if (constructorCodeAttribute != null) {
                constructorCodeAttribute.computeMaxStack();
            }
        } catch (BadBytecode e) {
            e.printStackTrace();
        }
    }
    return true;
}
Also used : DuplicateMemberException(javassist.bytecode.DuplicateMemberException) ParameterAnnotationsAttribute(javassist.bytecode.ParameterAnnotationsAttribute) ParameterAnnotationsAttribute(javassist.bytecode.ParameterAnnotationsAttribute) AnnotationsAttribute(javassist.bytecode.AnnotationsAttribute) Method(java.lang.reflect.Method) DuplicateMemberException(javassist.bytecode.DuplicateMemberException) IllegalClassFormatException(java.lang.instrument.IllegalClassFormatException) IOException(java.io.IOException) BadBytecode(javassist.bytecode.BadBytecode) BaseClassData(org.fakereplace.data.BaseClassData) CodeAttribute(javassist.bytecode.CodeAttribute) MethodData(org.fakereplace.data.MethodData) MethodInfo(javassist.bytecode.MethodInfo) BadBytecode(javassist.bytecode.BadBytecode) Bytecode(javassist.bytecode.Bytecode) HashSet(java.util.HashSet)

Example 4 with MethodData

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

the class ConstructorReflection method getConstructor.

public static Constructor<?> getConstructor(Class<?> clazz, Class<?>... parameters) throws NoSuchMethodException {
    ClassData cd = ClassDataStore.instance().getModifiedClassData(clazz.getClassLoader(), Descriptor.toJvmName(clazz.getName()));
    if (cd == null || !cd.isReplaceable()) {
        return clazz.getConstructor(parameters);
    }
    String args = '(' + DescriptorUtils.classArrayToDescriptorString(parameters) + ')';
    MethodData md = cd.getMethodData("<init>", args);
    if (md == null) {
        return clazz.getConstructor(parameters);
    }
    switch(md.getType()) {
        case NORMAL:
            return clazz.getConstructor(parameters);
        case FAKE_CONSTRUCTOR:
            try {
                Class<?> c = clazz.getClassLoader().loadClass(md.getClassName());
                return c.getConstructor(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 5 with MethodData

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

the class ConstructorReflection method getConstructors.

public static Constructor<?>[] getConstructors(Class<?> clazz) {
    try {
        ClassData cd = ClassDataStore.instance().getModifiedClassData(clazz.getClassLoader(), Descriptor.toJvmName(clazz.getName()));
        if (cd == null || !cd.isReplaceable()) {
            return clazz.getConstructors();
        }
        Constructor<?>[] meth = clazz.getConstructors();
        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]);
            }
        }
        ClassData cta = cd;
        while (cta != null) {
            for (MethodData i : cta.getMethods()) {
                if (i.isConstructor()) {
                    if (i.getType() == MemberType.FAKE_CONSTRUCTOR && AccessFlag.isPublic(i.getAccessFlags())) {
                        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));
                    }
                }
            }
            cta = cta.getSuperClassInformation();
        }
        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)

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