深入源码:解析SpotBugs (4)如何自定义一个 SpotBugs plugin

自定义一个 spotbugs 的插件,官方有比较详细的说明:
https://spotbugs.readthedocs.io/en/stable/implement-plugin.html
本篇是跟随官网demo的足迹,略显无聊,可跳过。

创建工程

执行maven 命令

mvn archetype:generate -DarchetypeArtifactId=spotbugs-archetype -DarchetypeGroupId=com.github.spotbugs -DarchetypeVersion=0.2.3

项目给出了很清晰的代码结构和单元测试用例:
在这里插入图片描述
pom的依赖引用,除了 spotbugs 之外,还有字节码工具 asm 等,也就是说依赖字节码的解析,需要定义插件的童鞋有一定的字节码功底。
在这里插入图片描述
实例
在这里插入图片描述
bug 判断区:
在这里插入图片描述
基本框架就已经搭好,如果有自定义需求,就可以根据demo改写自己的业务逻辑。

下面我们来看看,sawOpcode 是从什么地方调用,需要我们注意什么,能拿到什么信息:
这段代码非常长,看这段代码需要点耐心。
这个方法是CodeVisitor类的一个实现,用于遍历Java字节码并处理各种操作。

  • 首先,代码定义了一些变量,如sizePrevOpcodeBuffer、currentPosInPrevOpcodeBuffer等,用于处理字节码的读取和操作。
  • 然后,代码尝试读取一个字节,并将其存储在变量opcode中。这个字节表示当前操作码,用于后续的处理。
  • 接下来,代码根据opcode的值进行不同的处理。如果opcode表示的是一个wide操作码(例如,Const.WIDE),那么需要读取下一个字节,并将两个字节组合成一个整数。
  • 代码处理各种操作码,如加载(ILOAD)、存储(ISTORE)等。这些操作通常涉及到寄存器的操作,因此需要处理registerOperand变量。
  • 如果opcode表示的是一个跳转操作(如Const.GOTO、Const.IF_A等),那么需要处理跳转目标。
  • 如果opcode表示的是一个表跳转操作(如Const.TABLESWITCH、Const.LOOKUPSWITCH),那么需要处理表跳转的偏移量、标签等。
  • 最后,代码处理完当前操作码后,继续处理下一个操作码。

需要注意的是,这段代码仅处理了Java字节码的读取和简单的操作,而没有处理字节码中的各种变量、方法、类等符号。这些符号需要通过其他方式(如Const.getOpcodeName(opcode))进行处理。

    public void visit(Code obj) {
        //        if (getXMethod().usesInvokeDynamic()) {
        //            AnalysisContext.currentAnalysisContext().analysisSkippedDueToInvokeDynamic(getXMethod());
        //            return;
        //        }
        sizePrevOpcodeBuffer = 0;
        currentPosInPrevOpcodeBuffer = prevOpcode.length - 1;

        int switchLow = 1000000;
        int switchHigh = -1000000;
        codeBytes = obj.getCode();
        DataInputStream byteStream = new DataInputStream(new ByteArrayInputStream(codeBytes));

        lineNumberTable = obj.getLineNumberTable();

        try {
            for (int i = 0; i < codeBytes.length;) {
                resetState();
                PC = i;
                opcodeIsWide = false;
                opcode = byteStream.readUnsignedByte();

                sizePrevOpcodeBuffer++;
                currentPosInPrevOpcodeBuffer++;
                if (currentPosInPrevOpcodeBuffer >= prevOpcode.length) {
                    currentPosInPrevOpcodeBuffer = 0;
                }
                prevOpcode[currentPosInPrevOpcodeBuffer] = opcode;
                i++;
                // System.out.println(Const.getOpcodeName(opCode));
                int byteStreamArgCount = Const.getNoOfOperands(opcode);
                if (byteStreamArgCount == Const.UNPREDICTABLE) {

                    if (opcode == Const.LOOKUPSWITCH) {
                        int pad = 4 - (i & 3);
                        if (pad == 4) {
                            pad = 0;
                        }
                        int count = pad;
                        while (count > 0) {
                            count -= byteStream.skipBytes(count);
                        }
                        i += pad;
                        defaultSwitchOffset = byteStream.readInt();
                        branchOffset = defaultSwitchOffset;
                        branchTarget = branchOffset + PC;
                        i += 4;
                        int npairs = byteStream.readInt();
                        i += 4;
                        switchOffsets = new int[npairs];
                        switchLabels = new int[npairs];
                        for (int o = 0; o < npairs; o++) {
                            switchLabels[o] = byteStream.readInt();
                            switchOffsets[o] = byteStream.readInt();
                            i += 8;
                        }
                        sortByOffset(switchOffsets, switchLabels);
                    } else if (opcode == Const.TABLESWITCH) {
                        int pad = 4 - (i & 3);
                        if (pad == 4) {
                            pad = 0;
                        }
                        int count = pad;
                        while (count > 0) {
                            count -= byteStream.skipBytes(count);
                        }
                        i += pad;
                        defaultSwitchOffset = byteStream.readInt();
                        branchOffset = defaultSwitchOffset;
                        branchTarget = branchOffset + PC;
                        i += 4;
                        switchLow = byteStream.readInt();
                        i += 4;
                        switchHigh = byteStream.readInt();
                        i += 4;
                        int npairs = switchHigh - switchLow + 1;
                        switchOffsets = new int[npairs];
                        switchLabels = new int[npairs];
                        for (int o = 0; o < npairs; o++) {
                            switchLabels[o] = o + switchLow;
                            switchOffsets[o] = byteStream.readInt();
                            i += 4;
                        }
                        sortByOffset(switchOffsets, switchLabels);
                    } else if (opcode == Const.WIDE) {
                        opcodeIsWide = true;
                        opcode = byteStream.readUnsignedByte();
                        i++;
                        switch (opcode) {
                        case Const.ILOAD:
                        case Const.FLOAD:
                        case Const.ALOAD:
                        case Const.LLOAD:
                        case Const.DLOAD:
                        case Const.ISTORE:
                        case Const.FSTORE:
                        case Const.ASTORE:
                        case Const.LSTORE:
                        case Const.DSTORE:
                        case Const.RET:
                            registerOperand = byteStream.readUnsignedShort();
                            i += 2;
                            break;
                        case Const.IINC:
                            registerOperand = byteStream.readUnsignedShort();
                            i += 2;
                            intConstant = byteStream.readShort();
                            i += 2;
                            break;
                        default:
                            throw new IllegalStateException(String.format("bad wide bytecode %d: %s", opcode, Const.getOpcodeName(opcode)));
                        }
                    } else {
                        throw new IllegalStateException(String.format("bad unpredicatable bytecode %d: %s", opcode, Const.getOpcodeName(opcode)));
                    }
                } else {
                    if (byteStreamArgCount < 0) {
                        throw new IllegalStateException(String.format("bad length for bytecode %d: %s", opcode, Const.getOpcodeName(opcode)));
                    }
                    for (int k = 0; k < Const.getOperandTypeCount(opcode); k++) {

                        int v;
                        int t = Const.getOperandType(opcode, k);
                        int m = MEANING_OF_OPERANDS[opcode][k];
                        boolean unsigned = (m == M_CP || m == M_R || m == M_UINT);
                        switch (t) {
                        case Const.T_BYTE:
                            if (unsigned) {
                                v = byteStream.readUnsignedByte();
                            } else {
                                v = byteStream.readByte();
                            }
                            /*
                             * System.out.print("Read byte " + v);
                             * System.out.println(" with meaning" + m);
                             */
                            i++;
                            break;
                        case Const.T_SHORT:
                            if (unsigned) {
                                v = byteStream.readUnsignedShort();
                            } else {
                                v = byteStream.readShort();
                            }
                            i += 2;
                            break;
                        case Const.T_INT:
                            v = byteStream.readInt();
                            i += 4;
                            break;
                        default:
                            throw new IllegalStateException();
                        }
                        switch (m) {
                        case M_BR:
                            branchOffset = v;
                            branchTarget = v + PC;
                            branchFallThrough = i;
                            break;
                        case M_CP:
                            constantRefOperand = getConstantPool().getConstant(v);
                            if (constantRefOperand instanceof ConstantClass) {
                                ConstantClass clazz = (ConstantClass) constantRefOperand;
                                classConstantOperand = getStringFromIndex(clazz.getNameIndex());
                                referencedClass = DescriptorFactory.createClassDescriptor(classConstantOperand);

                            } else if (constantRefOperand instanceof ConstantInteger) {
                                intConstant = ((ConstantInteger) constantRefOperand).getBytes();
                            } else if (constantRefOperand instanceof ConstantLong) {
                                longConstant = ((ConstantLong) constantRefOperand).getBytes();
                            } else if (constantRefOperand instanceof ConstantFloat) {
                                floatConstant = ((ConstantFloat) constantRefOperand).getBytes();
                            } else if (constantRefOperand instanceof ConstantDouble) {
                                doubleConstant = ((ConstantDouble) constantRefOperand).getBytes();
                            } else if (constantRefOperand instanceof ConstantString) {
                                int s = ((ConstantString) constantRefOperand).getStringIndex();

                                stringConstantOperand = getStringFromIndex(s);
                            } else if (constantRefOperand instanceof ConstantInvokeDynamic) {
                                ConstantInvokeDynamic id = (ConstantInvokeDynamic) constantRefOperand;
                                ConstantNameAndType sig = (ConstantNameAndType) getConstantPool().getConstant(
                                        id.getNameAndTypeIndex());
                                nameConstantOperand = getStringFromIndex(sig.getNameIndex());
                                sigConstantOperand = getStringFromIndex(sig.getSignatureIndex());
                            } else if (constantRefOperand instanceof ConstantCP) {
                                ConstantCP cp = (ConstantCP) constantRefOperand;
                                ConstantClass clazz = (ConstantClass) getConstantPool().getConstant(cp.getClassIndex());
                                classConstantOperand = getStringFromIndex(clazz.getNameIndex());
                                referencedClass = DescriptorFactory.createClassDescriptor(classConstantOperand);
                                referencedXClass = null;
                                ConstantNameAndType sig = (ConstantNameAndType) getConstantPool().getConstant(
                                        cp.getNameAndTypeIndex());
                                nameConstantOperand = getStringFromIndex(sig.getNameIndex());
                                sigConstantOperand = getStringFromIndex(sig.getSignatureIndex());
                                refConstantOperand = null;
                            }
                            break;
                        case M_R:
                            registerOperand = v;
                            break;
                        case M_UINT:
                        case M_INT:
                            intConstant = v;
                            break;
                        case M_PAD:
                            break;
                        default:
                            throw new IllegalStateException("Unexpecting meaning " + m);
                        }
                    }

                }
                switch (opcode) {
                case Const.IINC:
                    isRegisterLoad = true;
                    isRegisterStore = true;
                    break;
                case Const.ILOAD_0:
                case Const.ILOAD_1:
                case Const.ILOAD_2:
                case Const.ILOAD_3:
                    registerOperand = opcode - Const.ILOAD_0;
                    isRegisterLoad = true;
                    break;

                case Const.ALOAD_0:
                case Const.ALOAD_1:
                case Const.ALOAD_2:
                case Const.ALOAD_3:
                    registerOperand = opcode - Const.ALOAD_0;
                    isRegisterLoad = true;
                    break;

                case Const.FLOAD_0:
                case Const.FLOAD_1:
                case Const.FLOAD_2:
                case Const.FLOAD_3:
                    registerOperand = opcode - Const.FLOAD_0;
                    isRegisterLoad = true;
                    break;

                case Const.DLOAD_0:
                case Const.DLOAD_1:
                case Const.DLOAD_2:
                case Const.DLOAD_3:
                    registerOperand = opcode - Const.DLOAD_0;
                    isRegisterLoad = true;
                    break;

                case Const.LLOAD_0:
                case Const.LLOAD_1:
                case Const.LLOAD_2:
                case Const.LLOAD_3:
                    registerOperand = opcode - Const.LLOAD_0;
                    isRegisterLoad = true;
                    break;
                case Const.ILOAD:
                case Const.FLOAD:
                case Const.ALOAD:
                case Const.LLOAD:
                case Const.DLOAD:
                    isRegisterLoad = true;
                    break;

                case Const.ISTORE_0:
                case Const.ISTORE_1:
                case Const.ISTORE_2:
                case Const.ISTORE_3:
                    registerOperand = opcode - Const.ISTORE_0;
                    isRegisterStore = true;
                    break;

                case Const.ASTORE_0:
                case Const.ASTORE_1:
                case Const.ASTORE_2:
                case Const.ASTORE_3:
                    registerOperand = opcode - Const.ASTORE_0;
                    isRegisterStore = true;
                    break;

                case Const.FSTORE_0:
                case Const.FSTORE_1:
                case Const.FSTORE_2:
                case Const.FSTORE_3:
                    registerOperand = opcode - Const.FSTORE_0;
                    isRegisterStore = true;
                    break;

                case Const.DSTORE_0:
                case Const.DSTORE_1:
                case Const.DSTORE_2:
                case Const.DSTORE_3:
                    registerOperand = opcode - Const.DSTORE_0;
                    isRegisterStore = true;
                    break;

                case Const.LSTORE_0:
                case Const.LSTORE_1:
                case Const.LSTORE_2:
                case Const.LSTORE_3:
                    registerOperand = opcode - Const.LSTORE_0;
                    isRegisterStore = true;
                    break;
                case Const.ISTORE:
                case Const.FSTORE:
                case Const.ASTORE:
                case Const.LSTORE:
                case Const.DSTORE:
                    isRegisterStore = true;
                    break;
                default:
                    break;
                }

                switch (opcode) {
                case Const.ILOAD:
                case Const.FLOAD:
                case Const.ALOAD:
                case Const.LLOAD:
                case Const.DLOAD:
                    // registerKind = opcode - ILOAD;
                    break;
                case Const.ISTORE:
                case Const.FSTORE:
                case Const.ASTORE:
                case Const.LSTORE:
                case Const.DSTORE:
                    // registerKind = opcode - ISTORE;
                    break;
                case Const.RET:
                    // registerKind = R_REF;
                    break;
                case Const.GETSTATIC:
                case Const.PUTSTATIC:
                    refFieldIsStatic = true;
                    break;
                case Const.GETFIELD:
                case Const.PUTFIELD:
                    refFieldIsStatic = false;
                    break;
                default:
                    break;
                }

                nextPC = i;
                if (beforeOpcode(opcode)) {
                    sawOpcode(opcode);
                }
                afterOpcode(opcode);

                if (opcode == Const.TABLESWITCH) {
                    sawInt(switchLow);
                    sawInt(switchHigh);
                    //                    int prevOffset = i - PC;
                    for (int o = 0; o <= switchHigh - switchLow; o++) {
                        sawBranchTo(switchOffsets[o] + PC);
                        //                        prevOffset = switchOffsets[o];
                    }
                    sawBranchTo(defaultSwitchOffset + PC);
                } else if (opcode == Const.LOOKUPSWITCH) {
                    sawInt(switchOffsets.length);
                    //                    int prevOffset = i - PC;
                    for (int o = 0; o < switchOffsets.length; o++) {
                        sawBranchTo(switchOffsets[o] + PC);
                        //                        prevOffset = switchOffsets[o];
                        sawInt(switchLabels[o]);
                    }
                    sawBranchTo(defaultSwitchOffset + PC);
                } else {
                    for (int k = 0; k < Const.getOperandTypeCount(opcode); k++) {
                        int m = MEANING_OF_OPERANDS[opcode][k];
                        switch (m) {
                        case M_BR:
                            sawBranchTo(branchOffset + PC);
                            break;
                        case M_CP:
                            if (constantRefOperand instanceof ConstantInteger) {
                                sawInt(intConstant);
                            } else if (constantRefOperand instanceof ConstantLong) {
                                sawLong(longConstant);
                            } else if (constantRefOperand instanceof ConstantFloat) {
                                sawFloat(floatConstant);
                            } else if (constantRefOperand instanceof ConstantDouble) {
                                sawDouble(doubleConstant);
                            } else if (constantRefOperand instanceof ConstantString) {
                                sawString(stringConstantOperand);
                            } else if (constantRefOperand instanceof ConstantFieldref) {
                                sawField();
                            } else if (constantRefOperand instanceof ConstantMethodref) {
                                sawMethod();
                            } else if (constantRefOperand instanceof ConstantInterfaceMethodref) {
                                sawIMethod();
                            } else if (constantRefOperand instanceof ConstantClass) {
                                sawClass();
                            }
                            break;
                        case M_R:
                            sawRegister(registerOperand);
                            break;
                        case M_INT:
                            sawInt(intConstant);
                            break;
                        default:
                            break;
                        }
                    }
                }
            }
        } catch (IOException e) {
            AnalysisContext.logError("Error while dismantling bytecode", e);
            assert false;
        }

        try {
            byteStream.close();
        } catch (IOException e) {
            assert false;
        }
    }

调用点就在这里,visit 方法是遍历codeBytes.length
在这里插入图片描述

  • 12
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值