jaspex.speculation.CreateSpeculativeMethodVisitor.java Source code

Java tutorial

Introduction

Here is the source code for jaspex.speculation.CreateSpeculativeMethodVisitor.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.speculation;

import org.objectweb.asm.*;
import static org.objectweb.asm.Opcodes.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jaspex.ClassFilter;
import jaspex.Options;
import jaspex.stm.NonTransactionalStateTracker;
import jaspex.stm.NonTransactionalStateTracker.SafeOperation;

import asmlib.*;
import asmlib.Type;

/** Cria verses especulativas dos mtodos, que quando executam uma operao "forbidden" (que no pode ser
  * executada especulativamente), chamam um mtodo do runtime.SpeculationControl.
  **/
public class CreateSpeculativeMethodVisitor extends MethodVisitor {

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

    private final String _methodName;
    private final InfoClass _currentClass;
    private final boolean _active;

    private static MethodVisitor createNextMethodVisitor(int access, String name, String desc, String signature,
            String[] exceptions, ClassVisitor cv) {

        if (name.endsWith("$transactional")) {
            name = name.replace("$transactional", "$speculative");

            // Mtodo original podia ser nativo, mas o $speculative no 
            access &= ~Opcodes.ACC_NATIVE;

            // Todos os mtodos $speculative devem ser public (seno o codegen falha com java.lang.IllegalAccessError)
            access = access & ~Opcodes.ACC_PRIVATE & ~Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC;
        }

        return cv.visitMethod(access, name, desc, signature, exceptions);
    }

    public CreateSpeculativeMethodVisitor(int access, String name, String desc, String signature,
            String[] exceptions, ClassVisitor cv, InfoClass currentClass, Boolean JDKClass) {
        super(Opcodes.ASM4, createNextMethodVisitor(access, name, desc, signature, exceptions, cv));

        _active = name.endsWith("$transactional") || (name.equals("<clinit>") && !JDKClass) // No modificar <clinit> quando usado com o JDKTransactifier
                || (name.equals("<init>") && desc.contains(CommonTypes.SPECULATIVECTORMARKER.bytecodeName()));
        _currentClass = currentClass;
        name = name.replace("$transactional", "");
        _methodName = name;

        InfoMethod im = new InfoMethod(access, name, desc, signature, exceptions, null);

        if (_active && im.isSynchronized()) {
            insertBlacklistedActionAttemptedCall("INSIDE SYNCHRONIZED METHOD, USE -REMOVESYNC");
        }

        // Tratamento especial para mtodos nativos
        // Ver tambm no wiki em "Transformaes Especulao"
        if (_active && im.isNative()) {
            if (im.isPrivate()) {
                // Se este  o overload de um mtodo nativo privado, temos que corrigir aqui o
                // nome para obter o mtodo original
                name = FixPrivateMethodAccessMethodVisitor.stripPrivate(name);
            }

            mv.visitCode();
            // Contador de posies de argumentos
            int pos = 0;
            if (!im.isStatic())
                mv.visitVarInsn(ALOAD, pos++);
            for (Type argType : im.argumentTypes()) {
                mv.visitVarInsn(argType.getLoadInsn(), pos);
                pos += argType.getNumberSlots();
            }
            insertSpeculationControlCall("INVOKE NATIVE METHOD (" + name + desc + ")", "");
            mv.visitMethodInsn(im.isStatic() ? INVOKESTATIC : INVOKESPECIAL, _currentClass.type().asmName(), name,
                    desc);
            mv.visitInsn(im.returnType().getReturnInsn());
            mv.visitMaxs(0, 0);
            mv.visitEnd();
        }
    }

    @Override
    public void visitMethodInsn(int opcode, String owner, String name, String desc) {
        Type ownerType = Type.fromAsm(owner);
        String methodFullname = ownerType.commonName() + "." + name /*+ desc*/;

        if (_active && ClassFilter.isMethodBlacklisted(ownerType, name, desc)) {
            Log.debug("Code references blacklisted method " + methodFullname);
            insertBlacklistedActionAttemptedCall("INVOKE BLACKLISTED METHOD (" + methodFullname + ")");
        }

        /* Suporte para -nttracker, baseado no cdigo do instanceof Transactional
         * Altera cdigo para chamar uma verso especial do nonTransactionalActionAttempted se
         * a classe ou o mtodo estiverem listados no NonTransactionalStateTracker.SAFE_OPERATIONS
         *
         * Como o nonTransactionalActionAttempted passa a ter que receber a instncia, temos que manipular
         * a stack para colocar a instncia no topo (tal como o instanceof Transactional precisava)
         */
        if (_active && Options.NTTRACKER && !ClassFilter.isTransactifiable(ownerType)
                && !ClassFilter.isMethodWhitelisted(ownerType, name, desc)) {
            SafeOperation so = NonTransactionalStateTracker.getSafeOperation(ownerType, name);
            if (so != null) {
                boolean safeMethod = so._safeMethods.contains(name);

                // Por agora suporte apenas para 3 slots de stack usados pelos argumentos;
                // talvez no futuro se possa expandir para qualquer nmero usando a FakeLocalsStack
                InvokedMethod m = new InvokedMethod(opcode, ownerType, name, desc);
                util.UtilList<Type> args = m.argumentTypes();
                int neededSlots = m.argumentSlotsCount();
                if ((opcode == INVOKEVIRTUAL || opcode == INVOKEINTERFACE) && (neededSlots < 4)) {
                    switch (neededSlots) {
                    case 0:
                        mv.visitInsn(DUP);
                        break;
                    case 1:
                        mv.visitInsn(SWAP);
                        mv.visitInsn(DUP_X1);
                        break;
                    case 2:
                        Util.swap(mv, 2, 1);
                        mv.visitInsn(DUP_X2);
                        break;
                    case 3:
                        if (args.first().getNumberSlots() == 2) {
                            // caso especial, precisa de alguma manipulao extra
                            Util.swap(mv, 1, 2);
                        }
                        Util.swap(mv, 2, 2);
                        mv.visitInsn(DUP2_X2);
                        mv.visitInsn(POP);
                        break;
                    default:
                        throw new AssertionError();
                    }
                    // Instncia est no topo

                    // mutation?
                    mv.visitInsn(safeMethod ? ICONST_0 : ICONST_1);

                    insertSpeculationControlCall("INVOKE " + methodFullname,
                            Type.OBJECT.bytecodeName() + Type.PRIM_BOOLEAN.bytecodeName());

                    if (neededSlots == 3 && args.first().getNumberSlots() == 2) {
                        // o que faltava do caso especial acima
                        Util.swap(mv, 2, 1);
                    }
                    mv.visitMethodInsn(opcode, owner, name, desc);
                    return;
                }
            }
        }

        if (_active && !ClassFilter.isTransactifiable(ownerType)
                && !ClassFilter.isMethodWhitelisted(ownerType, name, desc)) {
            /* Suporte para instanceof Transactional desactivado, por agora, devido a problema:
             * Quando se detecta que uma instncia  Transactional, e no se faz a chamada ao
             * nonTransactionalActionAttempted,  executada na mesma a verso "normal" de um mtodo,
             * no a verso $speculative ou $non_speculative.
             *
             * A verso normal de um mtodo  apenas para ser executada (como documentado no wiki,
             * em "Transformaes Especulao") "...numa stack onde j tenha ocorrido um
             * nonTransactionalActionAttempted, e portanto todas as operaes no protegidas neste
             * mtodo so seguras."
             * Isto no acontece neste momento.
             * (De notar que no  s alterar a chamada abaixo, j que o tipo que vemos actualmente
             * no foi transactificado, e portanto no podemos simplesmente chamar o m$speculative em
             * vez de m, j que o tipo actual no tem mtodos $speculative.
             *
             * Existem 3 possveis solues, a meu ver:
             * - Acabar com mtodos $non_speculative, e fazer os normais passarem a ser $non_speculative
             * - Expandir o conceito de instanceof Transactional para instanceof Transactional$ownerType,
             *   em que para cada tipo non-transactional passa a existir uma interface que contm
             *   mtodos $speculative ou $non_speculative; depois " s" fazer cast para este tipo
             *   e chamar o mtodo correcto
             * - Com uma JDK transactificada, a existncia do instanceof Transactional j no faz
             *   sentido, portanto isto no  uma soluo, mas uma nota de que se a JDK passar a ser
             *   transaccional, j no necessitamos disto.
                
            // Suporte simples para instanceof Transactional
            // Classes tagged com esta interface foram transactificadas, e portanto podemos
            // invocar o mtodo na mesma (por exemplo, uma classe transactificada que
            // implementa java.util.List  segura)
            // Por agora suporte apenas para 3 slots de stack usados pelos argumentos;
            // talvez no futuro se possa expandir para qualquer nmero usando a FakeLocalsStack
            InvokedMethod m = new InvokedMethod(opcode, ownerType, name, desc);
            util.UtilList<Type> args = m.argumentTypes();
            int neededSlots = m.argumentSlotsCount();
            boolean isFinal = false;
            try {
               // FIXME: No futuro, poder-se-ia tambm testar se o mtodo em si  final,
               // j que nesse caso tambm de certeza que no poderia ter sido substituido
               // com uma verso transaccional
               isFinal = ((Class.forName(ownerType.commonName()).getModifiers() & ACC_FINAL) != 0);
            } catch (ClassNotFoundException e) { throw new Error(e); }
            if (!isFinal && (opcode == INVOKEVIRTUAL || opcode == INVOKEINTERFACE) && (neededSlots < 4)) {
               switch (neededSlots) {
                  case 0:
              mv.visitInsn(DUP);
              break;
                  case 1:
              mv.visitInsn(SWAP);
              mv.visitInsn(DUP_X1);
              break;
                  case 2:
              Util.swap(mv, 2, 1);
              mv.visitInsn(DUP_X2);
              break;
                  case 3:
              if (args.first().getNumberSlots() == 2) {
                 // caso especial, precisa de alguma manipulao extra
                 Util.swap(mv, 1, 2);
              }
              Util.swap(mv, 2, 2);
              mv.visitInsn(DUP2_X2);
              mv.visitInsn(POP);
              break;
                  default:
              throw new AssertionError();
               }
               mv.visitTypeInsn(INSTANCEOF, CommonTypes.TRANSACTIONAL.asmName());
               Label l = new Label();
               mv.visitJumpInsn(IFNE, l);
               insertSpeculationControlCall("INVOKE METHOD (" + methodFullname + ")");
               mv.visitLabel(l);
               if (neededSlots == 3 && args.first().getNumberSlots() == 2) {
                  // o que faltava do caso especial acima
                  Util.swap(mv, 2, 1);
               }
               mv.visitMethodInsn(opcode, owner, name, desc);
               return;
            }*/

            insertSpeculationControlCall("INVOKE " + methodFullname, "");
            mv.visitMethodInsn(opcode, owner, name, desc);
            return;
        }

        if (_active && !name.equals("<init>") && !ClassFilter.isMethodWhitelisted(ownerType, name, desc)
                && !ownerType.isArray()) {
            // Caso especial para o <clinit>: todos os <clinit> devem comportar-se como $non_speculative,
            // e portanto fazemos j aqui a alterao
            // Nota quanto ao teste do isArray: O array ainda tem os mtodos normais herdados
            // de um Object, e so todos inofensivos (portanto no precisam de ser protegidos
            // por uma chamada ao nonTransactionalActionAttempted), mas no  possvel fazer
            // o rename deles para $speculative/$non_speculative, portanto tambm no lhes
            // alteramos o nome
            if (_methodName.equals("<clinit>")) {
                name += "$non_speculative";
            } else {
                name += "$speculative";
            }
        }

        mv.visitMethodInsn(opcode, owner, name, desc);
    }

    @Override
    public void visitInsn(int opcode) {
        if (_active && (opcode == MONITORENTER || opcode == MONITOREXIT)) {
            insertBlacklistedActionAttemptedCall("INVOKE BLACKLISTED OPERATION (MONITORENTER/MONITOREXIT)");
        }

        mv.visitInsn(opcode);
    }

    private void insertSpeculationControlCall(String reason, String extraArgs) {
        if (!Options.FASTMODE) {
            mv.visitLdcInsn("(" + _currentClass.type().commonName() + "." + _methodName + "): " + reason);
            mv.visitMethodInsn(INVOKESTATIC, CommonTypes.SPECULATIONCONTROL.asmName(),
                    "nonTransactionalActionAttempted", "(" + extraArgs + Type.STRING.bytecodeName() + ")V");
        } else {
            mv.visitMethodInsn(INVOKESTATIC, CommonTypes.SPECULATIONCONTROL.asmName(),
                    "nonTransactionalActionAttempted", "(" + extraArgs + ")V");
        }
    }

    private void insertBlacklistedActionAttemptedCall(String reason) {
        mv.visitLdcInsn("(" + _currentClass.type().commonName() + "." + _methodName + "$speculative): " + reason);
        mv.visitMethodInsn(INVOKESTATIC, CommonTypes.SPECULATIONCONTROL.asmName(), "blacklistedActionAttempted",
                "(" + Type.STRING.bytecodeName() + ")V");
    }

}