通过使用 BCEL 解析 Java 字节码来确定 LCOM4(方法缺乏凝聚力)
Determining the LCOM4 (Lack of Cohesion in Methods) by parsing the Java Bytecode using BCEL
我已经构建了一个程序,它接受提供的“.class”文件并使用 BCEL 对其进行解析,但是在使用结果对象来确定LCOM4 值。我已经搜索了整个网络,试图找到一个合适的教程,但到目前为止我一直做不到(我也阅读了关于 BCEL 的整个 javadoc)。所以我想在这个问题上得到一些帮助,比如一些详细的教程或代码片段,它们可以帮助我理解如何去做。
OK,我们定义一个class来表示一组字段和方法:
public class Group {
private final Set<String> fields = new HashSet<>();
private final Set<String> methods = new HashSet<>();
public Group addFields(String...fields) {
for (String field: fields) {
this.fields.add(field);
}
return this;
}
public Group addMethods(String... methods) {
for (String method: methods) {
this.methods.add(method);
}
return this;
}
public int fields() {
return fields.size();
}
public int methods() {
return methods.size();
}
public boolean intersects(Group other) {
for (String field: other.fields) {
if (fields.contains(field)) {
return true;
}
}
for (String method: other.methods) {
if (methods.contains(method)) {
return true;
}
}
return false;
}
public void merge(Group other) {
fields.addAll(other.fields);
methods.addAll(other.methods);
}
@Override
public String toString() {
return "Group{" + "fields=" + fields + ", methods=" + methods + '}';
}
}
我们通过为 class 中定义的每个字段填充一个组来开始该过程,然后,对于每个方法,我们使用代码中引用的字段和方法构建一个组,然后通过合并和删除与方法组相交的每个组来减少组列表。
这里是 java 代码,用于加载 class 的组。 LCOM4 是 groups.size():
private List<Group> loadGroups(File file) throws IOException {
try (InputStream in = new FileInputStream(file)) {
ClassParser parser = new ClassParser(in, file.getName());
JavaClass clazz = parser.parse();
String className = clazz.getClassName();
ConstantPoolGen cp = new ConstantPoolGen(clazz.getConstantPool());
List<Group> groups = new ArrayList<Group>();
for (Field field: clazz.getFields()) {
groups.add(new Group().addFields(field.getName()));
}
for (Method method: clazz.getMethods()) {
Group group = new Group().addMethods(method.getName());
Code code = method.getCode();
InstructionList instrs = new InstructionList(code.getCode());
for (InstructionHandle ih: instrs) {
Instruction instr = ih.getInstruction();
if (instr instanceof FieldInstruction) {
FieldInstruction fld = (FieldInstruction)instr;
if (fld.getClassName(cp).equals(className)) {
group.addFields(fld.getFieldName(cp));
}
} else if (instr instanceof InvokeInstruction) {
InvokeInstruction inv = (InvokeInstruction)instr;
if (inv.getClassName(cp).equals(className)) {
group.addMethods(inv.getMethodName(cp));
}
}
}
if (group.fields() > 0 || group.methods() > 1) {
int i = groups.size();
while (i > 0) {
--i;
Group g = groups.get(i);
if (g.intersects(group)) {
group.merge(g);
groups.remove(i);
}
}
groups.add(group);
}
}
return groups;
}
}
我已经构建了一个程序,它接受提供的“.class”文件并使用 BCEL 对其进行解析,但是在使用结果对象来确定LCOM4 值。我已经搜索了整个网络,试图找到一个合适的教程,但到目前为止我一直做不到(我也阅读了关于 BCEL 的整个 javadoc)。所以我想在这个问题上得到一些帮助,比如一些详细的教程或代码片段,它们可以帮助我理解如何去做。
OK,我们定义一个class来表示一组字段和方法:
public class Group {
private final Set<String> fields = new HashSet<>();
private final Set<String> methods = new HashSet<>();
public Group addFields(String...fields) {
for (String field: fields) {
this.fields.add(field);
}
return this;
}
public Group addMethods(String... methods) {
for (String method: methods) {
this.methods.add(method);
}
return this;
}
public int fields() {
return fields.size();
}
public int methods() {
return methods.size();
}
public boolean intersects(Group other) {
for (String field: other.fields) {
if (fields.contains(field)) {
return true;
}
}
for (String method: other.methods) {
if (methods.contains(method)) {
return true;
}
}
return false;
}
public void merge(Group other) {
fields.addAll(other.fields);
methods.addAll(other.methods);
}
@Override
public String toString() {
return "Group{" + "fields=" + fields + ", methods=" + methods + '}';
}
}
我们通过为 class 中定义的每个字段填充一个组来开始该过程,然后,对于每个方法,我们使用代码中引用的字段和方法构建一个组,然后通过合并和删除与方法组相交的每个组来减少组列表。
这里是 java 代码,用于加载 class 的组。 LCOM4 是 groups.size():
private List<Group> loadGroups(File file) throws IOException {
try (InputStream in = new FileInputStream(file)) {
ClassParser parser = new ClassParser(in, file.getName());
JavaClass clazz = parser.parse();
String className = clazz.getClassName();
ConstantPoolGen cp = new ConstantPoolGen(clazz.getConstantPool());
List<Group> groups = new ArrayList<Group>();
for (Field field: clazz.getFields()) {
groups.add(new Group().addFields(field.getName()));
}
for (Method method: clazz.getMethods()) {
Group group = new Group().addMethods(method.getName());
Code code = method.getCode();
InstructionList instrs = new InstructionList(code.getCode());
for (InstructionHandle ih: instrs) {
Instruction instr = ih.getInstruction();
if (instr instanceof FieldInstruction) {
FieldInstruction fld = (FieldInstruction)instr;
if (fld.getClassName(cp).equals(className)) {
group.addFields(fld.getFieldName(cp));
}
} else if (instr instanceof InvokeInstruction) {
InvokeInstruction inv = (InvokeInstruction)instr;
if (inv.getClassName(cp).equals(className)) {
group.addMethods(inv.getMethodName(cp));
}
}
}
if (group.fields() > 0 || group.methods() > 1) {
int i = groups.size();
while (i > 0) {
--i;
Group g = groups.get(i);
if (g.intersects(group)) {
group.merge(g);
groups.remove(i);
}
}
groups.add(group);
}
}
return groups;
}
}