Search in sources :

Example 21 with Bytecode

use of javassist.bytecode.Bytecode in project fakereplace by fakereplace.

the class FieldManipulator method transformClass.

public boolean transformClass(ClassFile file, ClassLoader loader, boolean modifiableClass, final Set<MethodInfo> modifiedMethods) {
    Map<String, Set<Data>> addedFieldData = data.getManipulationData(loader);
    if (addedFieldData.isEmpty()) {
        return false;
    }
    Map<Integer, Data> fieldAccessLocations = new HashMap<>();
    // first we need to scan the constant pool looking for
    // CONST_Fieldref structures
    ConstPool pool = file.getConstPool();
    for (int i = 1; i < pool.getSize(); ++i) {
        // we have a field reference
        if (pool.getTag(i) == ConstPool.CONST_Fieldref) {
            String className = pool.getFieldrefClassName(i);
            String fieldName = pool.getFieldrefName(i);
            String descriptor = pool.getFieldrefType(i);
            boolean handled = false;
            if (addedFieldData.containsKey(className)) {
                for (Data data : addedFieldData.get(className)) {
                    if (fieldName.equals(data.getName())) {
                        // store the location in the const pool of the method ref
                        fieldAccessLocations.put(i, data);
                        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) {
                    FieldData field = data.getField(fieldName);
                    if (field == null) {
                        // this is a new field
                        // lets deal with it
                        int fieldNo = FieldReferenceDataStore.instance().getFieldNo(fieldName, descriptor);
                        Data fieldData = new Data(fieldNo, fieldName, descriptor, className, loader);
                        fieldAccessLocations.put(i, fieldData);
                        Transformer.getManipulator().rewriteInstanceFieldAccess(fieldNo, fieldName, descriptor, className, loader);
                        addedFieldData = this.data.getManipulationData(loader);
                    }
                }
            }
        }
    }
    // through the methods and replace instances of the call
    if (!fieldAccessLocations.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 field access
                    if (op == Opcode.PUTFIELD || op == Opcode.GETFIELD || op == Opcode.GETSTATIC || op == Opcode.PUTSTATIC) {
                        int val = it.s16bitAt(index + 1);
                        // if the field access is for an added field
                        if (fieldAccessLocations.containsKey(val)) {
                            Data data = fieldAccessLocations.get(val);
                            int arrayPos = file.getConstPool().addIntegerInfo(data.getArrayIndex());
                            // write over the field access with nop
                            it.writeByte(Opcode.NOP, index);
                            it.writeByte(Opcode.NOP, index + 1);
                            it.writeByte(Opcode.NOP, index + 2);
                            if (op == Opcode.PUTFIELD) {
                                Bytecode b = new Bytecode(file.getConstPool());
                                if (data.getDescriptor().charAt(0) != 'L' && data.getDescriptor().charAt(0) != '[') {
                                    Boxing.box(b, data.getDescriptor().charAt(0));
                                }
                                b.addLdc(arrayPos);
                                b.addInvokestatic(FIELD_DATA_STORE_CLASS, "setValue", "(Ljava/lang/Object;Ljava/lang/Object;I)V");
                                it.insertEx(b.get());
                            } else if (op == Opcode.GETFIELD) {
                                Bytecode b = new Bytecode(file.getConstPool());
                                b.addLdc(arrayPos);
                                b.addInvokestatic(FIELD_DATA_STORE_CLASS, "getValue", "(Ljava/lang/Object;I)Ljava/lang/Object;");
                                if (DescriptorUtils.isPrimitive(data.getDescriptor())) {
                                    Boxing.unbox(b, data.getDescriptor().charAt(0));
                                } else {
                                    b.addCheckcast(DescriptorUtils.getTypeStringFromDescriptorFormat(data.getDescriptor()));
                                }
                                it.insertEx(b.get());
                            } else if (op == Opcode.PUTSTATIC) {
                                Bytecode b = new Bytecode(file.getConstPool());
                                if (data.getDescriptor().charAt(0) != 'L' && data.getDescriptor().charAt(0) != '[') {
                                    Boxing.box(b, data.getDescriptor().charAt(0));
                                }
                                b.addLdc(file.getConstPool().addClassInfo(data.getClassName()));
                                b.add(Opcode.SWAP);
                                b.addLdc(arrayPos);
                                b.addInvokestatic(FIELD_DATA_STORE_CLASS, "setValue", "(Ljava/lang/Object;Ljava/lang/Object;I)V");
                                it.insertEx(b.get());
                            } else if (op == Opcode.GETSTATIC) {
                                Bytecode b = new Bytecode(file.getConstPool());
                                b.addLdc(file.getConstPool().addClassInfo(data.getClassName()));
                                b.addLdc(arrayPos);
                                b.addInvokestatic(FIELD_DATA_STORE_CLASS, "getValue", "(Ljava/lang/Object;I)Ljava/lang/Object;");
                                if (DescriptorUtils.isPrimitive(data.getDescriptor())) {
                                    Boxing.unbox(b, data.getDescriptor().charAt(0));
                                } else {
                                    b.addCheckcast(DescriptorUtils.getTypeStringFromDescriptorFormat(data.getDescriptor()));
                                }
                                it.insertEx(b.get());
                            }
                            modifiedMethods.add(m);
                        }
                    }
                }
            } 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) Set(java.util.Set) HashMap(java.util.HashMap) BaseClassData(org.fakereplace.data.BaseClassData) FieldData(org.fakereplace.data.FieldData) FieldData(org.fakereplace.data.FieldData) BaseClassData(org.fakereplace.data.BaseClassData) CodeIterator(javassist.bytecode.CodeIterator) MethodInfo(javassist.bytecode.MethodInfo) Bytecode(javassist.bytecode.Bytecode)

Example 22 with Bytecode

use of javassist.bytecode.Bytecode 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 23 with Bytecode

use of javassist.bytecode.Bytecode 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)

Example 24 with Bytecode

use of javassist.bytecode.Bytecode in project fakereplace by fakereplace.

the class MainTransformer method transform.

@Override
public byte[] transform(final ClassLoader loader, final String className, final Class<?> classBeingRedefined, final ProtectionDomain protectionDomain, final byte[] classfileBuffer) throws IllegalClassFormatException {
    if (className == null) {
        // TODO: deal with lambdas
        return classfileBuffer;
    }
    boolean replaceable = Fakereplace.isClassReplaceable(className, loader);
    if (classBeingRedefined != null) {
        retransformationStarted = true;
        if (logClassRetransformation && replaceable) {
            log.info("Fakereplace is replacing class " + className);
        }
    }
    ChangedClassImpl changedClass = null;
    if (classBeingRedefined != null) {
        changedClass = new ChangedClassImpl(classBeingRedefined);
    }
    boolean changed = false;
    if (!replaceable && UnmodifiedFileIndex.isClassUnmodified(className)) {
        return null;
    }
    Set<Class<?>> classesToRetransform = new HashSet<>();
    final ClassFile file;
    try {
        Set<MethodInfo> modifiedMethods = new HashSet<>();
        file = new ClassFile(new DataInputStream(new ByteArrayInputStream(classfileBuffer)));
        for (final FakereplaceTransformer transformer : transformers) {
            if (transformer.transform(loader, className, classBeingRedefined, protectionDomain, file, classesToRetransform, changedClass, modifiedMethods)) {
                changed = true;
            }
        }
        if (!changed) {
            UnmodifiedFileIndex.markClassUnmodified(className);
            return null;
        } else {
            try {
                if (!modifiedMethods.isEmpty()) {
                    ClassPool classPool = new ClassPool();
                    if (loader == null) {
                        classPool.appendClassPath(new LoaderClassPath(ClassLoader.getSystemClassLoader()));
                    } else {
                        classPool.appendClassPath(new LoaderClassPath(loader));
                    }
                    classPool.appendSystemPath();
                    for (MethodInfo method : modifiedMethods) {
                        if (method.getCodeAttribute() != null) {
                            method.getCodeAttribute().computeMaxStack();
                            try {
                                method.rebuildStackMap(classPool);
                            } catch (BadBytecode e) {
                                Throwable root = e;
                                while (!(root instanceof NotFoundException) && root != null && root.getCause() != root) {
                                    root = root.getCause();
                                }
                                if (root instanceof NotFoundException) {
                                    NotFoundException cause = (NotFoundException) root;
                                    Bytecode bytecode = new Bytecode(file.getConstPool());
                                    bytecode.addNew(NoClassDefFoundError.class.getName());
                                    bytecode.add(Opcode.DUP);
                                    bytecode.addLdc(cause.getMessage());
                                    bytecode.addInvokespecial(NoClassDefFoundError.class.getName(), "<init>", "(Ljava/lang/String;)V");
                                    bytecode.add(Opcode.ATHROW);
                                    method.setCodeAttribute(bytecode.toCodeAttribute());
                                    method.getCodeAttribute().computeMaxStack();
                                    method.getCodeAttribute().setMaxLocals(DescriptorUtils.maxLocalsFromParameters(method.getDescriptor()) + 1);
                                    method.rebuildStackMap(classPool);
                                } else {
                                    throw e;
                                }
                            }
                        }
                    }
                }
            } catch (BadBytecode e) {
                throw new RuntimeException(e);
            }
            ByteArrayOutputStream bs = new ByteArrayOutputStream();
            file.write(new DataOutputStream(bs));
            // dump the class for debugging purposes
            final String dumpDir = AgentOptions.getOption(AgentOption.DUMP_DIR);
            if (dumpDir != null) {
                try {
                    File dump = new File(dumpDir + '/' + file.getName() + ".class");
                    dump.getParentFile().mkdirs();
                    FileOutputStream s = new FileOutputStream(dump);
                    DataOutputStream dos = new DataOutputStream(s);
                    file.write(dos);
                    s.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if (!classesToRetransform.isEmpty()) {
                synchronized (this) {
                    retransformationOutstandingCount++;
                }
                Thread t = new Thread(() -> {
                    try {
                        Fakereplace.getInstrumentation().retransformClasses(classesToRetransform.toArray(new Class[classesToRetransform.size()]));
                    } catch (UnmodifiableClassException e) {
                        log.error("Failed to retransform classes", e);
                    } finally {
                        synchronized (MainTransformer.this) {
                            retransformationOutstandingCount--;
                            notifyAll();
                        }
                    }
                });
                t.setDaemon(true);
                t.start();
            }
            if (classBeingRedefined != null) {
                changedClasses.add(changedClass);
                queueIntegration();
            }
            return bs.toByteArray();
        }
    } catch (IOException e) {
        e.printStackTrace();
        throw new IllegalClassFormatException(e.getMessage());
    } catch (Throwable e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    }
}
Also used : DataOutputStream(java.io.DataOutputStream) ClassPool(javassist.ClassPool) NotFoundException(javassist.NotFoundException) IllegalClassFormatException(java.lang.instrument.IllegalClassFormatException) BadBytecode(javassist.bytecode.BadBytecode) Bytecode(javassist.bytecode.Bytecode) HashSet(java.util.HashSet) ClassFile(javassist.bytecode.ClassFile) UnmodifiableClassException(java.lang.instrument.UnmodifiableClassException) ChangedClassImpl(org.fakereplace.replacement.notification.ChangedClassImpl) LoaderClassPath(javassist.LoaderClassPath) ByteArrayOutputStream(java.io.ByteArrayOutputStream) IOException(java.io.IOException) DataInputStream(java.io.DataInputStream) BadBytecode(javassist.bytecode.BadBytecode) NotFoundException(javassist.NotFoundException) IllegalClassFormatException(java.lang.instrument.IllegalClassFormatException) IOException(java.io.IOException) UnmodifiableClassException(java.lang.instrument.UnmodifiableClassException) ByteArrayInputStream(java.io.ByteArrayInputStream) FileOutputStream(java.io.FileOutputStream) ChangedClass(org.fakereplace.api.ChangedClass) MethodInfo(javassist.bytecode.MethodInfo) ClassFile(javassist.bytecode.ClassFile) File(java.io.File)

Example 25 with Bytecode

use of javassist.bytecode.Bytecode in project fakereplace by fakereplace.

the class ClassInfoTransformer method transform.

@Override
public boolean transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, ClassFile file, Set<Class<?>> classesToRetransform, ChangedClassImpl changedClass, Set<MethodInfo> modifiedMethods) throws IllegalClassFormatException, BadBytecode, DuplicateMemberException {
    if (className.equals("com/sun/beans/introspect/ClassInfo")) {
        file.addInterface(Runnable.class.getName());
        MethodInfo run = new MethodInfo(file.getConstPool(), "run", "()V");
        run.setAccessFlags(AccessFlag.PUBLIC);
        Bytecode b = new Bytecode(file.getConstPool(), 1, 1);
        b.addGetstatic(file.getName(), "CACHE", "Lcom/sun/beans/util/Cache;");
        b.addInvokevirtual("com/sun/beans/util/Cache", "clear", "()V");
        b.add(Opcode.RETURN);
        run.setCodeAttribute(b.toCodeAttribute());
        file.addMethod(run);
        MethodInfo m = file.getMethod("<init>");
        CodeIterator iterator = m.getCodeAttribute().iterator();
        int pos = 0;
        while (iterator.hasNext()) {
            pos = iterator.next();
        }
        b = new Bytecode(file.getConstPool(), 1, 0);
        b.add(Bytecode.ALOAD_0);
        b.addPutstatic(ClassInfoTransformer.class.getName(), "clearAction", "Ljava/lang/Runnable;");
        iterator.insert(pos, b.get());
        m.getCodeAttribute().computeMaxStack();
        return true;
    }
    return false;
}
Also used : CodeIterator(javassist.bytecode.CodeIterator) MethodInfo(javassist.bytecode.MethodInfo) BadBytecode(javassist.bytecode.BadBytecode) Bytecode(javassist.bytecode.Bytecode)

Aggregations

Bytecode (javassist.bytecode.Bytecode)36 MethodInfo (javassist.bytecode.MethodInfo)28 BadBytecode (javassist.bytecode.BadBytecode)24 CodeIterator (javassist.bytecode.CodeIterator)15 CodeAttribute (javassist.bytecode.CodeAttribute)11 ConstPool (javassist.bytecode.ConstPool)11 DuplicateMemberException (javassist.bytecode.DuplicateMemberException)8 IOException (java.io.IOException)6 List (java.util.List)6 ClassFile (javassist.bytecode.ClassFile)5 ByteArrayOutputStream (java.io.ByteArrayOutputStream)4 DataOutputStream (java.io.DataOutputStream)4 IllegalClassFormatException (java.lang.instrument.IllegalClassFormatException)4 Method (java.lang.reflect.Method)4 HashSet (java.util.HashSet)4 NotFoundException (javassist.NotFoundException)4 JumpMarker (org.fakereplace.util.JumpMarker)4 HashMap (java.util.HashMap)3 Set (java.util.Set)3 CannotCompileException (javassist.CannotCompileException)3