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 jaspex.Options; import jaspex.speculation.newspec.*; import jaspex.util.ShellColor; import java.io.PrintWriter; import java.util.*; import asmlib.*; import asmlib.extra.*; import util.UtilList; import org.objectweb.asm.*; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.util.Textifier; import org.objectweb.asm.util.TraceClassVisitor; /** Classe que efectua as transformaes para um programa poder ser executado usado especulao **/ public class SpeculativeTransformer { private InfoClass currentClass; private boolean _JDKClass; public SpeculativeTransformer(boolean JDKClass) { _JDKClass = JDKClass; } private void harvestInfoClass(ClassReader cr) { // Popular informao da classe currentClass = new InfoClass(cr.getClassName(), cr.getSuperName()); cr.accept(new InfoClassAdapter(currentClass), 0); } public byte[] transform(byte[] classBytes) { ClassReader cr = new ClassReader(classBytes); harvestInfoClass(cr); byte[] output; // Criar mtodos para execuo especulativa output = createSpeculativeMethods(cr); // Remover LINENUMBER if (Options.NOLINENUMBER) output = removeLineNumber(output); if (!_JDKClass) { // Inserir cdigo para especulao cr = new ClassReader(output); harvestInfoClass(cr); output = insertSpeculationCode(cr); } if (Options.PRINTCLASS) { Textifier t = new Textifier() { @Override public Textifier visitMethod(int access, String name, String desc, String signature, String[] exceptions) { String color; if (name.endsWith("$speculative") || (name.equals("<init>") && desc.contains(CommonTypes.SPECULATIVECTORMARKER.bytecodeName()))) { color = "48;5;69;38;5;15"; } else if (name.endsWith("$non_speculative") || (name.equals("<init>") && desc.contains(CommonTypes.NONSPECULATIVECTORMARKER.bytecodeName()))) { color = "48;5;70;38;5;15"; } else { color = "48;5;90;38;5;15"; } text.add(ShellColor.startColor(color)); Textifier ret = super.visitMethod(access, name, desc, signature, exceptions); text.add(text.size() - 1, ShellColor.endColor() + '\n'); return ret; } }; new ClassReader(output).accept(new TraceClassVisitor(null, t, new PrintWriter(System.out)), 0); } if (Options.WRITECLASS) { try { new java.io.File("output" + java.io.File.separatorChar).mkdir(); java.io.FileOutputStream fos = new java.io.FileOutputStream( "output" + java.io.File.separatorChar + currentClass.type().commonName() + ".class"); fos.write(output); fos.close(); } catch (java.io.IOException e) { throw new Error(e); } } // Passar verificador do ASM pelo output jaspex.transactifier.Transactifier.checkBytecode(output); return output; } public static byte[] removeLineNumber(byte[] output) { ClassWriter cw = new ClassWriter(0); new ClassReader(output).accept(new ClassVisitor(Opcodes.ASM4, cw) { @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { return new MethodVisitor(Opcodes.ASM4, cv.visitMethod(access, name, desc, signature, exceptions)) { @Override public void visitLineNumber(int line, Label start) { } }; } }, 0); return cw.toByteArray(); } public byte[] createSpeculativeMethods(ClassReader cr) { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); ClassVisitor cv = cw; // Tornar classe public: para o codegen funcionar, todas as classes tm que ser pblicas // (todos os mtodos $speculative tambm j so public) if (!_JDKClass) cv = new ClassVisitor(Opcodes.ASM4, cv) { @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { access = access & ~Opcodes.ACC_PRIVATE & ~Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC; cv.visit(version, access, name, signature, superName, interfaces); } }; // Visitor que adiciona fields e mtodos para o -detectlocal cv = new InjectDetectLocalClassVisitor(cv); // Criar verses $speculative de mtodos cv = new GenericMethodVisitorAdapter(cv, CreateSpeculativeMethodVisitor.class, currentClass, _JDKClass); // Marcar constructores com SpeculativeCtorMarker cv = new GenericMethodVisitorAdapter(cv, SpeculativeCtorMethodVisitor.class); // Potencialmente substituir alguns dos mtodos com verses internas do JaSPEx cv = new GenericMethodVisitorAdapter(cv, MethodReplacerMethodVisitor.class, currentClass, _JDKClass); // Resolver problema com o acesso a mtodos privados cv = new GenericMethodVisitorAdapter(cv, FixPrivateMethodAccessMethodVisitor.class, currentClass, _JDKClass); // Injectar overloads para a todos os mtodos que so herdados de superclasses no-transactificveis // Nota: Deve estar na pipeline *antes* do CreateSpeculativeMethodVisitor if (!_JDKClass) cv = new InjectOverloadsClassVisitor(cv, currentClass); cr.accept(cv, 0); // Criar verses non-speculative dos mtodos, que funcionam transaccionalmente, mas no // fazem spawn de especulaes new ClassReader(cw.toByteArray()).accept(new CreateNonSpeculativeMethodsClassVisitor(cw), 0); return cw.toByteArray(); } public byte[] insertSpeculationCode(final ClassReader cr) { ClassVisitor cv; // 1 Pass: Fazer tentativamente modificaes para especulao, concretizar todos os Futures, // e guardar o resultado num ClassNode ClassNode firstPassNode = new ClassNode(); cv = firstPassNode; // Colocao das chamadas ao get do future cv = new GenericMethodVisitorAdapter(cv, DelayGetFutureMethodVisitor.class, currentClass); // Modificaes para chamar o spawnSpeculation cv = new GenericMethodVisitorAdapter(cv, InsertContinuationSpeculationMethodVisitor.class, currentClass); cr.accept(cv, ClassReader.EXPAND_FRAMES); // 1.5 Pass: Detectar overspeculation -- casos onde a distncia entre a especulao e a sua // concretizao demasiado pequena Map<InfoMethod, UtilList<Integer>> rejectedSpecIdsMap = new HashMap<InfoMethod, UtilList<Integer>>(); RemoveOverspeculation.scanOverspeculation(currentClass, firstPassNode, rejectedSpecIdsMap); ClassNode removeOverspecNode; ClassReader finalPassReader; // Nota: Originalmente no era efectuado um loop aqui. O loop feito porque o // RemoveOverspeculation que executado depois do FixFutureMultipleControlFlows pode // detectar casos adicionais de overspeculation, e portanto temos que repetir o processo // para os excluir. // Alm disso, o processo pode demorar vrias iteraes a convergir, como no caso do // NewSpecExample72.test17() em que na primeira iterao detectada overspeculation num // dos casos, o cdigo re-gerado sem essa especulao, e s no final da segunda // iterao que detectado que os restantes casos tambm so overspeculation // (e portanto feita uma terceira iterao que ser a final, naquele caso). // Finalmente, de notar que o RemoveOverspeculation opcional, ou seja o ciclo at pode // ser comentado, voltando verso anterior em que o cdigo s fazia uma iterao. do { // 2 Pass: Repetir modificaes, tomando em conta os resultados do RemoveOverspeculation ClassNode secondPassNode = new ClassNode(); cv = secondPassNode; // Adicionar NOPs antes de todas as Labels, para facilitar o trabalho do FixFutureMultipleControlFlows cv = new GenericMethodVisitorAdapter(cv, NopAddBeforeLabelMethodVisitor.class); // Modificaes para chamar o spawnSpeculation cv = new GenericMethodVisitorAdapter(cv, InsertContinuationSpeculationMethodVisitor.class, currentClass, rejectedSpecIdsMap); cr.accept(cv, ClassReader.EXPAND_FRAMES); // 3 Pass: Deteco e resoluo de problemas devido a Futures e mltiplos fluxos de controlo // e gerao de lista de frames do mtodo a usar no DelayGetFutureMethodVisitor // Obter verso original dos mtodos antes das alteraes para especulao, para que o // computeControlFlowGraph possa fazer revert caso no consiga corrigi-los ClassNode origMethods = new ClassNode(); cr.accept(origMethods, ClassReader.EXPAND_FRAMES); FixFutureMultipleControlFlows.computeControlFlowGraph(secondPassNode, origMethods); // 4 Pass: Gerar classe final, limpar metadados extra ClassWriter cw = new jaspex.util.ClassWriter(ClassWriter.COMPUTE_FRAMES); cv = cw; // Remover NOPs de novo cv = new GenericMethodVisitorAdapter(cv, NopRemoveMethodVisitor.class); // Colocao das chamadas ao get do future cv = new GenericMethodVisitorAdapter(cv, DelayGetFutureMethodVisitor.class, currentClass); // Gerar classe a partir do ClassNode secondPassNode.accept(cv); finalPassReader = new ClassReader(cw.toByteArray()); // 4.5 Pass: Re-executar RemoveOverspeculation. Isto pode originar resultados diferentes do // pass original porque esse executado sem frames correctas e sem o // FixFutureMultipleControlFlows (ver comentrio antes do ciclo acima) removeOverspecNode = new ClassNode(); finalPassReader.accept(removeOverspecNode, 0); } while (RemoveOverspeculation.scanOverspeculation(currentClass, removeOverspecNode, rejectedSpecIdsMap)); // 5 Pass: Hack: Se houverem labels diferentes mas que esto seguidas (como no NewSpecExample8), // o RemoveUnusedTryCatchBlockMethodVisitor no funciona correctamente. Uma nova passagem // por um ClassWriter elimina labels repetidas. // A alternativa a fazer esta passagem seria criar um MethodVisitor que removesse labels // repetidas. ClassWriter cw = new jaspex.util.ClassWriter(ClassWriter.COMPUTE_FRAMES); cv = cw; // Retirar informao extra que passada dentro do tipo dos futures cv = new GenericMethodVisitorAdapter(cv, CleanupFutureTypeInfoMethodVisitor.class); // Remover marcadores de inlining da transactificao cv = new GenericMethodVisitorAdapter(cv, RemoveInlineMarkerMethodVisitor.class); // Remover entradas na exception table no usadas cv = new GenericMethodVisitorAdapter(cv, RemoveUnusedTryCatchBlockMethodVisitor.class); // (Potencialmente) corrigir problemas com blocos try/catch vazios (BUG/LIMITAO ASM) cv = new GenericMethodVisitorAdapter(cv, FixDeadTryCatchBlockMethodVisitor.class); finalPassReader.accept(cv, 0); return cw.toByteArray(); } }