通过使用 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;
    }
}