Search in sources :

Example 1 with ChangedClassImpl

use of org.fakereplace.replacement.notification.ChangedClassImpl 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)

Aggregations

ByteArrayInputStream (java.io.ByteArrayInputStream)1 ByteArrayOutputStream (java.io.ByteArrayOutputStream)1 DataInputStream (java.io.DataInputStream)1 DataOutputStream (java.io.DataOutputStream)1 File (java.io.File)1 FileOutputStream (java.io.FileOutputStream)1 IOException (java.io.IOException)1 IllegalClassFormatException (java.lang.instrument.IllegalClassFormatException)1 UnmodifiableClassException (java.lang.instrument.UnmodifiableClassException)1 HashSet (java.util.HashSet)1 ClassPool (javassist.ClassPool)1 LoaderClassPath (javassist.LoaderClassPath)1 NotFoundException (javassist.NotFoundException)1 BadBytecode (javassist.bytecode.BadBytecode)1 Bytecode (javassist.bytecode.Bytecode)1 ClassFile (javassist.bytecode.ClassFile)1 MethodInfo (javassist.bytecode.MethodInfo)1 ChangedClass (org.fakereplace.api.ChangedClass)1 ChangedClassImpl (org.fakereplace.replacement.notification.ChangedClassImpl)1