/*
* Copyright (c) 2013-2014, Pierre Laporte
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 only, as
* published by the Free Software Foundation.
*
* This code 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 this work; if not, see <http://www.gnu.org/licenses/>.
*/
package fr.pingtimeout.tyrion.transformation;
import java.util.HashMap;
import java.util.Map;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.util.CheckClassAdapter;
import fr.pingtimeout.tyrion.util.SimpleLogger;
/**
* This class instruments synchronized methods by removing the {@code synchronized} keyword, replacing it by a
* synchronized block on the current lock ({@code this} for instance methods, the current class for static methods).
*/
class SynchronizedMethodTransformer extends ClassVisitor {
private final String className;
public SynchronizedMethodTransformer(int api, ClassVisitor cv, String className) {
super(api, cv);
this.className = className;
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
final MethodVisitor result;
if (isSynchronized(access)) {
SimpleLogger.debug("Found synchronized method : %s %s::%s %s", accessToString(access), className, name, desc);
int accessWithoutSynchronized = access & (~Opcodes.ACC_SYNCHRONIZED);
MethodVisitor previousVisitor = super.visitMethod(accessWithoutSynchronized, name, desc, signature, exceptions);
result = new SynchronizedMethodProbesInjector(api, previousVisitor, access, name, desc, className);
} else {
result = super.visitMethod(access, name, desc, signature, exceptions);
}
return result;
}
public static boolean isSynchronized(int access) {
return (access & Opcodes.ACC_SYNCHRONIZED) != 0;
}
public static boolean isStatic(int access) {
return (access & Opcodes.ACC_STATIC) != 0;
}
public static String accessToString(int access) {
Map<Integer, String> opCodes = opCodeToString();
StringBuilder result = new StringBuilder();
for (Map.Entry<Integer, String> opCodeEntry : opCodes.entrySet()) {
int opCode = opCodeEntry.getKey();
String opCodeToString = opCodeEntry.getValue();
if ((access & opCode) != 0) {
result.append(opCodeToString);
}
}
return result.toString();
}
private static Map<Integer, String> opCodeToString() {
Map<Integer, String> opCodes = new HashMap<>();
opCodes.put(Opcodes.ACC_ABSTRACT, " abstract");
opCodes.put(Opcodes.ACC_BRIDGE, " bridge");
opCodes.put(Opcodes.ACC_DEPRECATED, " deprecated");
opCodes.put(Opcodes.ACC_FINAL, " final");
opCodes.put(Opcodes.ACC_NATIVE, " native");
opCodes.put(Opcodes.ACC_PRIVATE, " private");
opCodes.put(Opcodes.ACC_PROTECTED, " protected");
opCodes.put(Opcodes.ACC_PUBLIC, " public");
opCodes.put(Opcodes.ACC_STATIC, " static");
opCodes.put(Opcodes.ACC_STRICT, " strict");
opCodes.put(Opcodes.ACC_SYNCHRONIZED, " synchronized");
opCodes.put(Opcodes.ACC_SYNTHETIC, " synthetic");
opCodes.put(Opcodes.ACC_VARARGS, " varargs");
return opCodes;
}
}