package jetbrains.buildServer.tools.checkers;
import jetbrains.buildServer.tools.CheckAction;
import jetbrains.buildServer.tools.ErrorKind;
import jetbrains.buildServer.tools.ErrorReporting;
import jetbrains.buildServer.tools.ScanFile;
import jetbrains.buildServer.tools.rules.StaticRuleSettings;
import org.jetbrains.annotations.NotNull;
import org.objectweb.asm.*;
import java.io.IOException;
import java.io.InputStream;
/**
* Created 15.05.13 13:28
*
* @author Eugene Petrenko (eugene.petrenko@jetbrains.com)
*/
public class StaticFieldsChecker implements CheckAction {
private final StaticRuleSettings mySettings;
public StaticFieldsChecker(@NotNull final StaticRuleSettings settings) {
mySettings = settings;
}
public void process(@NotNull final ScanFile file, @NotNull final ErrorReporting reporting) throws IOException {
if (!file.getName().endsWith(".class")) return;
final ClassReader reader = createReader(file);
reader.accept(new ClassVisitor(Opcodes.ASM4) {
private String myClassName = "<none>";
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
myClassName = name.replace("/", ".");
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
checkFieldModifiers(access, name, desc, signature, value);
return super.visitField(access, name, desc, signature, value);
}
private void checkFieldModifiers(int access, String name, String desc, String signature, Object value) {
if ((Opcodes.ACC_NATIVE & access) != 0) return;
if ((Opcodes.ACC_ENUM & access) != 0) return;
if ((Opcodes.ACC_STATIC & access) == 0) return;
final Type type = Type.getType(desc);
if ((Opcodes.ACC_FINAL & access) == 0) {
reporting.postCheckError(file, ErrorKind.STATIC, "Static usage of '" + fieldType(desc) + "'", "Class '" + myClassName + "' contains non-final static field '" + name + "'");
return;
}
final int sort = type.getSort();
//allow primitive type constants
if (sort != Type.OBJECT && sort != Type.METHOD) return;
if (sort == Type.METHOD) return;
if (type.getClassName().equals(String.class.getName())) return;
if (mySettings.isClassAllowed(type.getClassName())) return;
reporting.postCheckError(file, ErrorKind.STATIC, "Static usage of '" + fieldType(desc) + "'", "Class '" + myClassName + "' contains final static field '" + name + "' of type '" + fieldType(desc) + "'");
}
}, 0);
}
@NotNull
private static String fieldType(@NotNull String desc) {
final Type type = Type.getType(desc);
return type.getClassName();
}
@NotNull
private ClassReader createReader(@NotNull final ScanFile file) throws IOException {
final InputStream inputStream = file.openStream();
try {
return new ClassReader(inputStream);
} finally {
inputStream.close();
}
}
}