Java tutorial
/* * 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"); } }