jaspex.transactifier.Transactifier.java Source code

Java tutorial

Introduction

Here is the source code for jaspex.transactifier.Transactifier.java

Source

/*
 * jaspex-mls: a Java Software Speculative Parallelization Framework
 * Copyright (C) 2015 Ivo Anjo <ivo.anjo@ist.utl.pt>
 *
 * This file is part of jaspex-mls.
 *
 * jaspex-mls is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * jaspex-mls is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with jaspex-mls.  If not, see <http://www.gnu.org/licenses/>.
 */

package jaspex.transactifier;

import asmlib.*;
import asmlib.Type;

import java.io.*;

import org.objectweb.asm.*;
import org.objectweb.asm.commons.JSRInlinerAdapter;
import org.objectweb.asm.util.CheckClassAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jaspex.Options;

public class Transactifier {

    private static final Logger Log = LoggerFactory.getLogger(Transactifier.class);

    private final ClassReader cr;
    private final InfoClass currentClass;
    private final boolean _JDKClass;

    /** Constructor utilizado pelo JDKTransactifier **/
    public Transactifier(byte[] classBytes) throws IOException {
        this(new ClassReader(classBytes), true);
    }

    /** Constructor normal **/
    public Transactifier(Type type) throws IOException {
        this(new ClassReader(type.commonName()), false);
    }

    private Transactifier(ClassReader classReader, boolean JDKClass) throws IOException {
        // Forar a gerao de FRAMES para todas as classes, logo no inicio de toda a cadeia,
        // para evitar possveis problemas em tudo o que se segue e que vai processar esta classe
        ClassWriter cw = new jaspex.util.ClassWriter(ClassWriter.COMPUTE_FRAMES);
        try {
            classReader.accept(cw, ClassReader.EXPAND_FRAMES);
        } catch (RuntimeException e) {
            if (!e.getMessage().equals("JSR/RET are not supported with computeFrames option"))
                throw e;

            Log.debug("Class " + classReader.getClassName() + " uses JSR/RET. Inlining...");
            // Repetir processo, fazendo inline de JSR/RET primeiro
            cw = new jaspex.util.ClassWriter(ClassWriter.COMPUTE_FRAMES);

            classReader.accept(new ClassVisitor(Opcodes.ASM4, cw) {
                @Override
                public MethodVisitor visitMethod(int access, String name, String desc, String signature,
                        String[] exceptions) {
                    return new JSRInlinerAdapter(cv.visitMethod(access, name, desc, signature, exceptions), access,
                            name, desc, signature, exceptions);
                }
            }, ClassReader.EXPAND_FRAMES);
        }

        cr = new ClassReader(cw.toByteArray());
        currentClass = InfoClass.fromType(Type.fromAsm(cr.getClassName()));
        _JDKClass = JDKClass;
    }

    public byte[] transform() throws IOException {
        byte[] output = transactify();

        // Passar verificador do ASM pelo output
        //checkBytecode(output);

        return output;
    }

    public static void checkBytecode(byte[] output) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        ClassReader cr = new ClassReader(output);

        try {
            if (!Options.FASTMODE)
                CheckClassAdapter.verify(cr, jaspex.speculation.SpeculativeClassLoader.INSTANCE, false, pw);
        } catch (ClassCircularityError e) {
        }

        try {
            DuplicateMethodChecker.verify(cr, pw);
            UninitializedCallChecker.verify(cr, pw);
        } catch (RuntimeException e) {
            // Ignorar erro
            if (!e.toString().contains("jaspex.MARKER.Transactional"))
                throw e;
        } catch (AssertionError e) {
            if (!e.toString().contains("Unexpected type found on stack"))
                throw e;
            e.printStackTrace();
        } catch (LinkageError e) {
            // Causado pelo ClassWriter.getCommonSuperClass tentar carregar classes na VM
        }

        String verifierOutput = sw.toString();
        int length = verifierOutput.length();
        if (length > 0) {
            if (verifierOutput.charAt(length - 1) == '\n')
                verifierOutput = verifierOutput.substring(0, length - 1);
            Log.warn("Error(s) were detected on the output bytecode for class {}",
                    cr.getClassName().replace('/', '.'));
            Log.debug(verifierOutput);
        }
    }

    private byte[] transactify() throws IOException {
        final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);

        // Copiar mtodos originais inalterados
        cr.accept(new ClassVisitor(Opcodes.ASM4) {
            @Override
            public void visit(int version, int access, String name, String signature, String superName,
                    String[] interfaces) {
                cw.visit(version, access, name, signature, superName, interfaces);
            }

            @Override
            public MethodVisitor visitMethod(int access, String name, String desc, String signature,
                    String[] exceptions) {
                if (name.equals("<clinit>"))
                    return new EmptyMethodVisitor();
                return cw.visitMethod(access, name, desc, signature, exceptions);
            }

            @Override
            public void visitEnd() {
                cw.visitEnd();
            }
        }, 0);

        // Varivel que contm o ltimo ClassVisitor da "chain" de classvisitors que servem de filtros ao
        // ficheiro original
        ClassVisitor cv = cw;

        // Mtodos alterados so marcados com $transactional no nome
        // Este marcador  temporrio, e ir ser substituido por $speculative mais  frente
        // Nota: Apenas os mtodos so renomeados, os seus INVOKE* ainda no apontam para os
        // $transactional; essa alterao  feita no SpeculativeTransformer
        cv = new ClassVisitor(Opcodes.ASM4, cv) {
            @Override
            public MethodVisitor visitMethod(int access, String name, String desc, String signature,
                    String[] exceptions) {
                if (name.equals("<init>")) {
                    desc = desc.replace(")", "Ljaspex/MARKER/Transactional;)");
                } else if (!name.equals("<clinit>")) {
                    name += "$transactional";
                }
                return cv.visitMethod(access, name, desc, signature, exceptions);
            }
        };

        // Remover ACC_SYNCHRONIZED dos mtodos
        if (Options.REMOVESYNC || _JDKClass)
            cv = new RemoveSyncClassVisitor(cv);

        // Remover MONITORENTER/MONITOREXIT dos mtodos
        if (Options.REMOVEMONITORS)
            cv = new GenericMethodVisitorAdapter(cv, RemoveMonitorsClassVisitor.class);

        // Verificar se existem mtodos com ACC_SYNCHRONIZED ou opcodes MONITORENTER/MONITOREXIT
        if (!_JDKClass)
            cv = new CheckMonitorUsage(cv);

        // Corrigir chamadas a alguns mtodos de java.lang.Object
        cv = new GenericMethodVisitorAdapter(cv, ChangeObjectMethodsMethodVisitor.class, currentClass);

        // Adicionar overrides a alguns mtodos de java.lang.Object
        cv = new AddObjectMethodsClassVisitor(cv, currentClass);

        // Suporte para Arrays
        cv = new GenericMethodVisitorAdapter(cv, ChangeArrayAccessMethodVisitor.class, currentClass, _JDKClass);

        // Adicionar inicializao de offsets usados pela unsafetrans ao clinit da classe
        // Nota: Visitor tem que estar *depois* do FieldTransactifierClassVisitor
        if (!_JDKClass)
            cv = new GenericMethodVisitorAdapter(cv, ChangeClinitMethodVisitor.class, currentClass);

        // Visitor que cria os fields offset e o staticfieldbase
        if (!_JDKClass)
            cv = new FieldTransactifierClassVisitor(cv);

        // Alterar acessos a fields para passarem pela STM
        cv = new GenericMethodVisitorAdapter(cv, ChangeFieldAccessMethodVisitor.class, currentClass, _JDKClass);

        // Modificar string com filename da classe que aparece em excepes
        if (!_JDKClass)
            cv = new MarkAsTransactifiedClassVisitor(cv);

        // Verificar e fazer upgrade da verso da classe, se necessrio
        if (!_JDKClass)
            cv = new ClassVisitor(Opcodes.ASM4, cv) {
                @Override
                public void visit(int version, int access, String name, String signature, String superName,
                        String[] interfaces) {
                    // Transactificao precisa de version >= 49, porque usa o MethodVisitor.visitLdcInsn(Type)
                    // MethodVisitor.visitLdcInsn(Type), que s funciona a partir dessa verso dos classfiles.
                    // http://asm.ow2.org/asm40/javadoc/user/org/objectweb/asm/MethodVisitor.html#visitLdcInsn(java.lang.Object)
                    // http://stackoverflow.com/questions/2784791/
                    // Caso especial: V1_1  196653, por alguma razo...
                    if (version < Opcodes.V1_5 || version == Opcodes.V1_1) {
                        //Log.debug("Class " + name + " has version " + version + ", upgrading it to Java 5");
                        version = Opcodes.V1_5;
                    }

                    if (version > Opcodes.V1_6 && !name.startsWith("java/")) {
                        Log.warn("Class " + name + " is compiled for Java 7 or newer");
                    }

                    super.visit(version, access, name, signature, superName, interfaces);
                }
            };

        cr.accept(cv, ClassReader.EXPAND_FRAMES);

        return cw.toByteArray();
    }

}