日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

FindBugs檢測器實(shí)現(xiàn)(1)

系統(tǒng) 2076 0

FindBugs的檢測器大多以下面五種方式來實(shí)現(xiàn),且這五種實(shí)現(xiàn)方式findbugs都提供了接口:

  1. 檢查類、方法、字段結(jié)構(gòu)
  2. 微模式,簡單的字節(jié)碼模式
  3. 基于棧的模式
  4. 數(shù)據(jù)流分析
  5. 內(nèi)部過程的分析

本文將介紹findbugs中stack-based pattern的實(shí)現(xiàn)過程和需要用到的接口。在這之前,我們必須要有一些必備的java知識(shí),如JVM棧內(nèi)存、JVM字節(jié)碼指令、 class文件結(jié)構(gòu)

?

字節(jié)碼指令

JVM為每一個(gè)線程都分配一個(gè)java棧,且棧以棧幀的形式進(jìn)行管理,每調(diào)用一個(gè)方法都向棧中添加一個(gè)棧幀,棧幀由局部變量區(qū)、操作數(shù)區(qū)、和幀數(shù)據(jù)區(qū)組成。看一個(gè)簡單的字節(jié)碼指令示例:

              
                Code:

       
              
              0: 
              
                new
              
                         #28    
              
                //
              
              
                 class java/math/BigDecimal
              
              

       3
              
                : dup

       
              
              4: ldc2_w        #30    
              
                //
              
              
                 double 0.11d
              
              

       7: invokespecial #32    
              
                //
              
              
                 Method java/math/BigDecimal."<init>":(D)V
              
              

      10
              
                : astore_1

      
              
              11: 
              
                return
              
            
              
                public
              
              
                static
              
              
                void
              
              
                 main(String[] args) {

    BigDecimal a 
              
              = 
              
                new
              
               BigDecimal(0.11
              
                );

}
              
            

main方法中僅有一條語句,它創(chuàng)建了一個(gè)BigDecimal類實(shí)例,并把它賦值給本地變量a。左邊方框是main函數(shù)的字節(jié)碼指令,我們來看看這些指令對(duì)main方法棧幀的操作。(new)創(chuàng)建BigDecimal對(duì)象,并向棧頂壓入引用值;(dup)復(fù)制棧頂引用,壓棧;(ldc2_w)從常量池中將0.11d推送到棧頂;(invokespecial)調(diào)用構(gòu)造函數(shù),并彈出對(duì)象引用和參數(shù),調(diào)用結(jié)束將對(duì)象引用壓棧;(astore_1)彈出對(duì)象引用,并存儲(chǔ)在main函數(shù)的局部變量1位置(0位置為main方法參數(shù));(return)返回。了解JVM的棧結(jié)構(gòu)和字節(jié)碼指令對(duì)棧的操作,這樣我們才能使用stack來分析java代碼中一些不好的模式。

?

OpcodeStackDetector

使用Stack-based來實(shí)現(xiàn)findbugs的檢測器都要繼承OpcodeStackDetector這個(gè)類,并且實(shí)現(xiàn)sawOpcode(int)方法。這個(gè)方法傳入的操作碼的值,根據(jù)這個(gè)值我們可以得到操作數(shù)的信息,如操作碼是函數(shù)調(diào)用,則能獲取到函數(shù)的名稱、描述符等信息。另外我們還能獲取到方法棧數(shù)據(jù),程序計(jì)數(shù)器等數(shù)據(jù),使用這些數(shù)據(jù)便能實(shí)現(xiàn)想要檢測的代碼模式。

先來看下OpcodeStackDetector的繼承結(jié)構(gòu):

QQ截圖20150522220320

這些類的主要作用是:

BetterVisitor 定義了許多visit方法,這些方法用來實(shí)現(xiàn)對(duì)class文件對(duì)象的訪問(JavaClass,Method等)。

DismantleBytecode 用來分析字節(jié)碼,提取操作碼、操作數(shù)、計(jì)數(shù)器等數(shù)據(jù)。這個(gè)類實(shí)現(xiàn)了visit(Code)方法,并為每一個(gè)操作碼調(diào)用sawOpcode(int)方法。

OpcodeStackDetector 類有操作數(shù)stack數(shù)據(jù),對(duì)于stack based模式檢測是必不可少的。另外還定義了sawOpcode(int)方法,我們的檢測代碼在該方法中實(shí)現(xiàn)。

?

檢測器例子

仍以 從定義最簡單Findbugs Detector做起 提到的檢測器做為例子,用來檢測BigDecimal實(shí)例使用Double進(jìn)行構(gòu)造,另外每次調(diào)用sawOpcode函數(shù),都打印出方法棧信息:

      
        public
      
      
        void
      
       sawOpcode(
      
        int
      
      
         seen) {

    
      
      
        //
      
      
        System.out.println("visit seen:" + seen);

    
      
      
        //
      
      
         TODO Auto-generated method stub
      
      
        if
      
       (seen == INVOKESPECIAL && getClassConstantOperand().equals("java/math/BigDecimal"
      
        )

            
      
      && getNameConstantOperand().equals("<init>") && getSigConstantOperand().equals("(D)V"
      
        )) {

        OpcodeStack.Item top 
      
      = stack.getStackItem(0
      
        );

        Object value 
      
      =
      
         top.getConstant();

        System.out.println(
      
      "stack num local values:" + stack.getNumLocalValues() + 

                "  stack depth"+
      
         stack.getStackDepth());

        
      
      
        if
      
       (value 
      
        instanceof
      
      
         Double) {

            
      
      
        double
      
       arg =
      
         ((Double) value).doubleValue();

            String dblString 
      
      =
      
         Double.toString(arg);

            String bigDecimalString 
      
      = 
      
        new
      
      
         BigDecimal(arg).toString();

            
      
      
        boolean
      
       ok = dblString.equals(bigDecimalString) || dblString.equals(bigDecimalString + ".0"
      
        );



            
      
      
        if
      
       (!
      
        ok) {

                
      
      
        boolean
      
       scary = dblString.length() <= 8 && dblString.toUpperCase().indexOf("E") == -1
      
        ;

                bugReporter.reportBug(
      
      
        new
      
       BugInstance(
      
        this
      
      , "TUTORIAL_BUG", scary ?
      
         NORMAL_PRIORITY : LOW_PRIORITY)

                        .addClassAndMethod(
      
      
        this
      
      ).addString(dblString).addSourceLine(
      
        this
      
      
        ));

            }

        }

    }

    System.out.println(
      
      "stack num local values in return:" + stack.getNumLocalValues() + 

                "  stack depth:"+
      
         stack.getStackDepth());

    
      
      
        for
      
      (
      
        int
      
       i=0; i<stack.getStackDepth(); ++
      
        i)

        System.out.println(
      
      "stack item "+i+":" +
      
         stack.getStackItem(i));

}
      
    

首先if語句判斷操作碼是一個(gè)構(gòu)造函數(shù)調(diào)用,且方法名、類名、方法描述符都符合,以驗(yàn)證這是一個(gè)BigDecimal初始化;再取出執(zhí)行此字節(jié)命令時(shí)的棧頂操作數(shù)stack.getStackItem(0),上面的代碼用該double構(gòu)造BigDecimal實(shí)例,判斷dblString.equals(bigDecimalString)來確定是否報(bào)告這個(gè)警告。

將該檢測器放到findbugs中,檢測第一部分提到的字節(jié)碼,棧信息輸出如下:

QQ截圖20150523011014

前面提到了visit方法,這些方法在findbugs分析到相應(yīng)的class文件部分時(shí)會(huì)被調(diào)用,如visit(JavaClass obj)方法在分析class文件時(shí)調(diào)用;visit(Method me)方法在findbugs分析方法時(shí)調(diào)用;visit(Code code)在findbugs分析字節(jié)碼指令是調(diào)用。需要注意的是,調(diào)用visit(Code code)時(shí),一定要調(diào)用super.visit(code)方法,否則我們實(shí)現(xiàn)的sawOpcode方法將不會(huì)調(diào)用,因?yàn)閟awOpcode是在super.visit(code)方法中調(diào)用的。

      
        public
      
      
        void
      
      
         visit(Code code){

    System.out.println(
      
      "visit code!!!!!!!"
      
        );

    
      
      
        //
      
      
        System.out.println("code:" + code.toString());

        

    
      
      
        //
      
      
        yi ding yao fang wen zhe ge
      
      
        super
      
      
        .visit(code);

}
      
    

使用visit方法可以實(shí)現(xiàn)必要的初始化操作,如某些變量在對(duì)字節(jié)碼檢測之前設(shè)置出事狀態(tài),那么可以把這些操作放在visit(Code)中,這樣在每一次分析方法字節(jié)碼時(shí),狀態(tài)都將被重置。

FindBugs檢測器實(shí)現(xiàn)(1)


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 琼中| 桐柏县| 丰顺县| 大竹县| 普宁市| 西吉县| 宜黄县| 吉林市| 寻乌县| 襄汾县| 兴安盟| 华池县| 汝阳县| 利辛县| 彰化市| 平顶山市| 商洛市| 崇左市| 长春市| 永州市| 博野县| 正安县| 娱乐| 亳州市| 汕尾市| 集安市| 鹿邑县| 漳州市| 武胜县| 名山县| 仲巴县| 大名县| 同德县| 张北县| 惠安县| 靖江市| 延川县| 西安市| 喜德县| 通化县| 尼玛县|