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 asmlib.*; import asmlib.Type; import org.objectweb.asm.*; import static org.objectweb.asm.Opcodes.*; /** MethodVisitor com soluo parcial ao problema do acesso aos mtodos privados. * Devido utilizao dos callables gerados para aceder a mtodos, todos os mtodos precisam de passar * a ser pblicos. * Infelizmente, no caso de mtodos privados isso pode causar erros na semntica do programa: * (verso mais simples do NewSpecExample56) * * class A { * private void m() { * System.out.println("A.m()"); * } * * public void runM() { * m(); // isto TEM DE SER um invokespecial * } * } * * class B extends A { * public void m() { * System.out.println("B.m()"); * } * * public static void main(String[] args) { * new B().m(); * new B().runM(); * } * } * * Neste caso, se A.m() passar a pblico, a semntica do programa alterada. * (De notar que infelizmente no possvel usar um invokespecial no callable para executar a verso * correcta de m()). * * Para resolver isto, esta classe comea com duas tranformaes: * * Transformar mtodos privados para ter $private no nome * * Alterar chamadas com invokespecial a estes mtodos para usarem o novo nome * * O mtodo tambm convertido para final, j que equivalente semanticamente, e em caso de bug e existir * um duplicado na hierarquia, causado um erro no verificador da VM ao carregar as classes. **/ public class FixPrivateMethodAccessMethodVisitor extends MethodVisitor { private final InfoClass _currentClass; private final boolean _active; private static String privateName(InfoClass infoClass) { return "$private$" + infoClass.type().commonName().replace('.', '_'); } private static boolean test(int access, int flag) { return (access & flag) != 0; } private static boolean testAccess(int access) { // Testar se privado e no static return test(access, ACC_PRIVATE) && !test(access, ACC_STATIC); } private static int convertAccess(int access, String name) { if (name.endsWith("$transactional")) { if (testAccess(access)) { // Vamos renomear o mtodo, e j agora marcamos como final return access | ACC_FINAL; } else if (test(access, ACC_PRIVATE) && test(access, ACC_STATIC)) { // Caso ainda mais estranho (NewSpecExample58): Mtodo private static final // Neste caso no precisamos de renomear os mtodos, j que o INVOKESTATIC // funciona como o INVOKESPECIAL para mtodos no-de-instncia. // MAS, se o mtodo for final isto causa problemas, logo basta retirar o final return access & ~ACC_FINAL; } } return access; } private static String convertName(int access, String name, InfoClass infoClass) { if (name.endsWith("$transactional") && testAccess(access)) { name = name.replace("$transactional", privateName(infoClass) + "$transactional"); } return name; } public FixPrivateMethodAccessMethodVisitor(int access, String name, String desc, String signature, String[] exceptions, ClassVisitor cv, InfoClass currentClass, Boolean JDKClass) { super(Opcodes.ASM4, cv.visitMethod(convertAccess(access, name), convertName(access, name, currentClass), desc, signature, exceptions)); _currentClass = currentClass; _active = name.endsWith("$transactional") || (name.equals("<clinit>") && !JDKClass) // No modificar <clinit> quando usado com o JDKTransactifier || (name.equals("<init>") && desc.contains("jaspex/MARKER/Transactional")); } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc) { if (_active && opcode == INVOKESPECIAL && !name.equals("<init>") && Type.fromAsm(owner).equals(_currentClass.type())) { assert (testAccess(_currentClass.getMethod(name, desc).access())); // Nesta altura do processamento os INVOKE* ainda no foram modificados, logo // os nomes dos mtodos nos INVOKE* no acabam com $transactional name += privateName(_currentClass); } mv.visitMethodInsn(opcode, owner, name, desc); } /** Retira parte $private$ do nome do mtodo **/ public static String stripPrivate(String methodName) { int pos = methodName.indexOf("$private$"); if (pos < 0) return methodName; int end = methodName.indexOf('$', pos + 1 + "$private$".length()); return methodName.substring(0, pos) + (end < 0 ? "" : methodName.substring(end)); } }