官术网_书友最值得收藏!

解析class文件的屬性表

字段結構和方法結構也都有屬性表,所以要注意不要將這些屬性表混在一起理解。但所有屬性都有一個通用的結構,這在解析字段那部分已經介紹。因此,解析class文件結構的屬性表我們也可以使用通用的屬性結構來解析。

解析步驟是先從class文件字節緩存中讀取兩個字節,如果前面的解析工作都正常,那么現在讀取到的這兩個字節就是該class文件屬性表的長度。接著根據長度創建屬性表,使用通用屬性結構循環解析出每個屬性,循環次數為屬性的總數。class文件結構的屬性表解析器AttributesHandler的實現如代碼清單2-54所示。

代碼清單2-54 class文件結構屬性表解析器

public class AttributesHandler implements BaseByteCodeHandler {

    @Override
    public int order() {
        return 8;
    }

    @Override
    public void read(ByteBuffer codeBuf, ClassFile classFile) throws Exception {
       classFile.setAttributes_count(new U2(codeBuf.get(), codeBuf.get()));
        // 獲取屬性總數
        int len = classFile.getAttributes_count().toInt();
        if (len == 0) {
            return;
        }
        // 創建屬性表
        AttributeInfo[] attributeInfos = new AttributeInfo[len];
        classFile.setAttributes(attributeInfos);
        for (int i = 0; i < len; i++) {
            // 創建屬性
            AttributeInfo attributeInfo = new AttributeInfo();
            attributeInfos[i] = attributeInfo;
            // 解析屬性
            attributeInfo.setAttribute_name_index(new U2(codeBuf.get(), codeBuf.get()));
            attributeInfo.setAttribute_length(new U4(codeBuf.get(), codeBuf.get(), codeBuf.get(), codeBuf.get()));
            int attr_len = attributeInfo.getAttribute_length().toInt();
            if (attr_len == 0) {
                continue;
            }
            // 解析屬性的info項
            byte[] bytes = new byte[attr_len];
codeBuf.get(bytes, 0, bytes.length);
            attributeInfo.setInfo(bytes);
        }
    }

}

將class文件結構的屬性表解析器AttributesHandler注冊到ClassFileAnalysiser。

現在我們已經編寫完成class文件結構各項的解析器,并且都已經注冊到ClassFileAnalysiser,現在ClassFileAnalysiser類持有的解析器有MagicHandler、VersionHandler、ConstantPoolHandler、AccessFlagsHandler、ThisAndSuperClassHandler、InterfacesHandler、FieldHandler、MethodHandler、AttributesHandler,如代碼清單2-55所示。

代碼清單2-55 ClassFileAnalysiser類

public class ClassFileAnalysiser {

    private final static List<BaseByteCodeHandler> handlers = new ArrayList<>();

    static {
        handlers.add(new MagicHandler());
        handlers.add(new VersionHandler());
        handlers.add(new ConstantPoolHandler());
        handlers.add(new AccessFlagsHandler());
        handlers.add(new ThisAndSuperClassHandler());
        handlers.add(new InterfacesHandler());
        handlers.add(new FieldHandler());
        handlers.add(new MethodHandler());
        handlers.add(new AttributesHandler());
// 如果解析器是按順序注冊的,那么排序可以忽略
      handlers.sort((Comparator.comparingInt(BaseByteCodeHandler::order)));
    }

    public static ClassFile analysis(ByteBuffer codeBuf) throws Exception {
        ClassFile classFile = new ClassFile();
        codeBuf.position(0);
        for (BaseByteCodeHandler handler : handlers) {
            handler.read(codeBuf, classFile);
        }
        System.out.println("class文件結構解析完成,解析是否正常(剩余未解析的字節數):" + codeBuf.remaining());
        return classFile;
    }

}

最后我們還需要對所有解析器進行單元測試,此處單元測試重點關注解析完成后,class文件字節緩存中是否還有未讀取的字節,如果有說明某個解析器的某個解析步驟出錯了,如果沒有,則所有解析器都正常工作。

重點關注ClassFileAnalysiser的analysis方法中的打印語句輸出的結果。單元測試如代碼清單2-56所示。

代碼清單2-56 測試整個框架的解析是否正常

public class AllHandlerTest {

    @Test
    public void test() throws Exception {
        ByteBuffer codeBuf = ClassFileAnalysisMain.readFile("RecursionAlgorithmMain.class");
        ClassFile classFile = ClassFileAnalysiser.analysis(codeBuf);
    }

}

單元測試結果輸出如圖2.11所示。

圖2.11 解析框架單元測試結果

從結果輸出中可以看出,所有解析器解析完成后,class文件字節緩存ByteBuffer對象剩余可讀的字節數為0,即ByteBuffer對象的讀指針與limit指針重合,表示讀完,因此解析正常。

至此,我們對整個class文件結構的解析工作就已經基本完成了。而對于屬性的解析,我們都只是使用通用的解析器解析。在《Java虛擬機規范》Java SE 8版本中,預定義屬性就有23個,但本書不會對每個屬性都進行詳細介紹。

如果想要深入理解某個屬性,我們可再對其進行二次解析。如何使用我們編寫的項目對class文件結構、字段結構、方法結構的屬性表中的屬性進行二次解析呢?我們以字段的ConstantValue屬性為例。

主站蜘蛛池模板: 扎囊县| 红河县| 安吉县| 常州市| 新兴县| 宣化县| 建阳市| 南乐县| 昭平县| 尼玛县| 巴青县| 红安县| 日照市| 桃源县| 冕宁县| 唐河县| 阿城市| 洛南县| 平陆县| 微博| 安多县| 镇平县| 镇沅| 祥云县| 大邑县| 临沂市| 山西省| 寻甸| 黄龙县| 亳州市| 柳林县| 沙洋县| 酒泉市| 兴文县| 喀喇沁旗| 临西县| 舞阳县| 盐源县| 察隅县| 北京市| 柳河县|