通过使用 BCEL 解析 Java 字节码来确定 LCOM4(方法中缺乏内聚)

Posted

技术标签:

【中文标题】通过使用 BCEL 解析 Java 字节码来确定 LCOM4(方法中缺乏内聚)【英文标题】:Determining the LCOM4 (Lack of Cohesion in Methods) by parsing the Java Bytecode using BCEL 【发布时间】:2017-05-18 07:09:06 【问题描述】:

我已经构建了一个程序,它接收提供的“.class”文件并使用 BCEL 对其进行解析,但是在使用生成的对象来确定 LCOM4 值时我有点迷茫。我已经搜索了整个网络,试图找到一个合适的教程,但到目前为止我一直无法(我也阅读了关于 BCEL 的整个 javadoc)。所以我想在这个问题上得到一些帮助,比如一些详细的教程或代码 sn-ps 可以帮助我理解如何去做。

【问题讨论】:

LCOM4是方法调用的函数吗?您绝对需要 bcel 还是可以使用 ASM? 感谢您的回复,不,这不是一个功能。 BCEL 不是绝对必要的,但如果可能的话,我想尝试使用 BCEL。 我的意思是le LCOM4是根据哪个方法调用哪个方法计算出来的? 那么你已经知道如何识别一个方法和另一个方法之间或者一个方法和一个字段之间的依赖关系了? 抱歉,我误会了。井 LCOM4 的计算是基于分组的。就像一个类中的所有方法必须相互连接一样,它们应该访问同一个变量或相互调用。如果每个班级都由上述任何一个条件连接,那么它被声明为一个组(对不起,我的英语不好) 【参考方案1】:

好的,让我们定义一个类来表示一组字段和方法:

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 + '';
    

我们通过为类中定义的每个字段填充组列表来开始该过程,然后,对于每个方法,我们使用代码中引用的字段和方法构建一个组,然后减少列表通过合并和删除与方法的组相交的每个组来分组。

这是加载类组的 java 代码。 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;
    

【讨论】:

这是你想要做的吗? 非常感谢您花时间构建此代码!它完美地工作并且对你的解释很有意义。如果可能的话,再做一件事。是否可以查看每个组中的方法名称? 如果你打印一个组,你会得到这样的东西:Groupfields=[methods, fields], methods=[addMethods, intersects, methods, merge, addFields, toString, fields, &lt;init&gt;]。当然,您可以添加 getter 方法来访问字段名称和方法名称。 比你!如果问的不是太多,您也可以看看这个问题吗?它基本相同,但要计算的指标不同。 ***.com/questions/44061843/…

以上是关于通过使用 BCEL 解析 Java 字节码来确定 LCOM4(方法中缺乏内聚)的主要内容,如果未能解决你的问题,请参考以下文章

通过反编译字节码来理解 Java 枚举

通过反编译字节码来理解 Java 枚举

org.apache.tomcat.util.bcel.classfile.ClassFormatException:常量池中的无效字节标记:15

从jvm角度来解析java语法糖

JAVAssist字节码操作

java----字节码操作