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

解析字段表

同一個(gè)Class文件中,不會(huì)存在兩個(gè)相同的字段。相同指定的是字段名與類(lèi)型描述符都相同。字段結(jié)構(gòu)與Class文件結(jié)構(gòu)一樣,都有訪問(wèn)標(biāo)志項(xiàng),但兩者的訪問(wèn)標(biāo)志項(xiàng),在訪問(wèn)權(quán)限和屬性上有些區(qū)別。參照《Java虛擬機(jī)規(guī)范》,字段中的訪問(wèn)權(quán)限和屬性標(biāo)志如表2-41所示。

表2-41 字段訪問(wèn)權(quán)限與屬性標(biāo)志

字段的結(jié)構(gòu)如表2-42所示。

表2-42 字段的結(jié)構(gòu)

其中,access_flags是字段的訪問(wèn)標(biāo)志,name_index是字段名稱(chēng),descriptor_index是字段的類(lèi)型描述符。字段結(jié)構(gòu)與方法結(jié)構(gòu)、Class文件結(jié)構(gòu)都有屬性表attributes,屬性表的屬性個(gè)數(shù)可以是0個(gè)或多個(gè)。屬性的通用結(jié)構(gòu)如表2-43所示。

表2-43 屬性的通用結(jié)構(gòu)

關(guān)于屬性,我們先了解屬性的通用結(jié)構(gòu),實(shí)現(xiàn)屬性的初步解析,讓字段解析器能夠完成字段的解析工作,至于屬性info是什么暫時(shí)先不關(guān)心。

創(chuàng)建字段結(jié)構(gòu)對(duì)應(yīng)的Java類(lèi)FieldInfo,如代碼清單2-44所示。

代碼清單2-44 FieldInfo類(lèi)

public class FieldInfo {

    private U2 access_flags;
    private U2 name_index;
    private U2 descriptor_index;
    private U2 attributes_count;
    private AttributeInfo[] attributes;

}

創(chuàng)建屬性結(jié)構(gòu)對(duì)應(yīng)的Java類(lèi)AttributeInfo,如代碼清單2-45所示。

代碼清單2-45 AttributeInfo類(lèi)

public class AttributeInfo {
    private U2 attribute_name_index;
    private U4 attribute_length;
    private byte[] info;
}

創(chuàng)建字段表解析器FieldHandler,實(shí)現(xiàn)字段表的解析。字段結(jié)構(gòu)的屬性表的解析工作也由字段表解析器完成。解析流程如下:

1、先從class文件字節(jié)緩存中讀取到字段總數(shù),根據(jù)字段總數(shù)創(chuàng)建字段表;

2、循環(huán)解析出每個(gè)字段;

3、解析字段的屬性表時(shí),先解析獲取到屬性總數(shù),再根據(jù)屬性總數(shù)創(chuàng)建屬性表;

4、使用通用屬性結(jié)構(gòu)循環(huán)解析出字段的每個(gè)屬性;

5、解析屬性時(shí),先解析出attribute_name_index,再解析attribute_length獲取屬性info的長(zhǎng)度,根據(jù)長(zhǎng)度讀取指定長(zhǎng)度的字節(jié)數(shù)據(jù)存放到屬性的info字段。

字段表解析器的實(shí)現(xiàn),如代碼清單2-46所示。

代碼清單2-46 FieldHandler類(lèi)

public class FieldHandler implements BaseByteCodeHandler {

    @Override
    public int order() {
        // 排在接口解析器的后面
        return 6;
    }

    @Override
public void read(ByteBuffer codeBuf, ClassFile classFile) throws Exception {
  // 讀取字段總數(shù)
        classFile.setFields_count(new U2(codeBuf.get(), codeBuf.get()));
        int len = classFile.getFields_count().toInt();
        if (len == 0) {
            return;
        }
        // 創(chuàng)建字段表
        FieldInfo[] fieldInfos = new FieldInfo[len];
        classFile.setFields(fieldInfos);
    // 循環(huán)解析出每個(gè)字段
        for (int i = 0; i < fieldInfos.length; i++) {
            // 解析字段
            fieldInfos[i] = new FieldInfo();
           // 讀取字段的訪問(wèn)標(biāo)志
fieldInfos[i].setAccess_flags(new U2(codeBuf.get(), codeBuf.get()));
// 讀取字段名稱(chēng)
            fieldInfos[i].setName_index(new U2(codeBuf.get(), codeBuf.get()));
      // 讀取字段類(lèi)型描述符索引
            fieldInfos[i].setDescriptor_index(new U2(codeBuf.get(), codeBuf.get()));
      // 讀取屬性總數(shù)
            fieldInfos[i].setAttributes_count(new U2(codeBuf.get(), codeBuf.get()));
            // 獲取字段的屬性總數(shù)
            int attr_len = fieldInfos[i].getAttributes_count().toInt();
            if (attr_len == 0) {
                continue;
            }
            // 創(chuàng)建字段的屬性表
            fieldInfos[i].setAttributes(new AttributeInfo[attr_len]);
      // 循環(huán)解析出每個(gè)屬性,先使用通用屬性結(jié)構(gòu)解析每個(gè)屬性
            for (int j = 0; j < attr_len; j++) {
                // 解析字段的屬性
                fieldInfos[i].getAttributes()[j]
                        .setAttribute_name_index(new U2(codeBuf.get(),codeBuf.get()));
                //  獲取屬性info的長(zhǎng)度
                U4 attr_info_len = new U4(codeBuf.get(), codeBuf.get(),  codeBuf.get(), codeBuf.get());
                fieldInfos[i].getAttributes()[j].setAttribute_length(attr_info_len);
                //  解析info
                byte[] info = new byte[attr_info_len.toInt()];
                codeBuf.get(info, 0, attr_info_len.toInt());
                fieldInfos[i].getAttributes()[j].setInfo(info);
            }
        }
    }

}

編寫(xiě)將字段的訪問(wèn)標(biāo)志access_flags轉(zhuǎn)為字符串輸出的工具類(lèi)FieldAccessFlagUtils,如代碼清單2-47所示。

代碼清單2-47 access_flags轉(zhuǎn)字符串工具類(lèi)

public class FieldAccessFlagUtils {
    private static final Map<Integer, String> fieldAccessFlagMap = new HashMap<>();

    static {
        fieldAccessFlagMap.put(0x0001, "public");
        fieldAccessFlagMap.put(0x0002, "private");
        fieldAccessFlagMap.put(0x0004, "protected");
        fieldAccessFlagMap.put(0x0008, "static");
        fieldAccessFlagMap.put(0x0010, "final");
        fieldAccessFlagMap.put(0x0040, "volatile");
        fieldAccessFlagMap.put(0x0080, "transient");
        fieldAccessFlagMap.put(0x1000, "synthtic");
        fieldAccessFlagMap.put(0x4000, "enum");
    }

    /**
     * 獲取16進(jìn)制對(duì)應(yīng)的訪問(wèn)標(biāo)志和屬性字符串表示 (僅用于類(lèi)的訪問(wèn)標(biāo)志)
     *
     * @param flag字段的訪問(wèn)標(biāo)志
     * @return
     */
    public static String toFieldAccessFlagsString(U2 flag) {
        final int flagVlaue = flag.toInt();
        StringBuilder flagBuild = new StringBuilder();
        fieldAccessFlagMap.keySet()
                .forEach(key -> {
                    if ((flagVlaue & key) == key) {
                        flagBuild.append(fieldAccessFlagMap.get(key)).append(",");
                    }
                });
        return flagBuild.length() > 0 && flagBuild.charAt(flagBuild.length() - 1) == ',' ?
                     flagBuild.substring(0, flagBuild.length() - 1)  : flagBuild.toString();
    }

}

字段表解析器編寫(xiě)完成,我們先將字段解析器注冊(cè)到ClassFileAnalysiser,再編寫(xiě)單元測(cè)試。編寫(xiě)單元測(cè)試用于驗(yàn)證字段解析器是否能夠正確完成字段表地解析。編寫(xiě)單元測(cè)試要求將解析后的所有字段的名稱(chēng)、類(lèi)型、以及字段的訪問(wèn)標(biāo)志轉(zhuǎn)為字符串打印出來(lái),以驗(yàn)證結(jié)果是否正確。字段表解析器單元測(cè)試如代碼清單2-48所示。

代碼清單2-48 字段表解析器單元測(cè)試

public class FieldHandlerTest {

    private static String getName(U2 name_index, ClassFile classFile) {
        CONSTANT_Utf8_info name_info = (CONSTANT_Utf8_info)
                     classFile.getConstant_pool()[name_index.toInt() - 1];
        return name_info.toString();
    }

    @Test
    public void testFieldHandlerHandler() throws Exception {
        ByteBuffer codeBuf = ClassFileAnalysisMain.readFile("Builder.class");
        ClassFile classFile = ClassFileAnalysiser.analysis(codeBuf);
        System.out.println("字段總數(shù):" + classFile.getFields_count().toInt());
        System.out.println();
        FieldInfo[] fieldInfos = classFile.getFields();
    // 遍歷字段表
        for (FieldInfo fieldInfo : fieldInfos) {
            System.out.println("訪問(wèn)標(biāo)志和屬性:" + FieldAccessFlagUtils
                .toFieldAccessFlagsString(fieldInfo.getAccess_flags()));
            System.out.println("字段名:"
                + getName(fieldInfo.getName_index(), classFile));
            System.out.println("字段的類(lèi)型描述符:"
                + getName(fieldInfo.getDescriptor_index(), classFile));
            System.out.println("屬性總數(shù):"
                + fieldInfo.getAttributes_count().toInt());
            System.out.println();
        }
    }

}

單元測(cè)試結(jié)果輸出如圖2.9所示。

圖2.9 字段表解析器單元測(cè)試

從結(jié)果輸出可以看出,該class有三個(gè)字段,訪問(wèn)權(quán)限都是private的,字段名分別是a、b、c,并且類(lèi)型描述符都是“I”,即字段的類(lèi)型都是整型。

主站蜘蛛池模板: 青阳县| 紫金县| 滦南县| 嘉荫县| 旬阳县| 武陟县| 吉林省| 伊宁县| 黄浦区| 正阳县| 和田市| 赤壁市| 永登县| 闵行区| 蓬安县| 环江| 南皮县| 平南县| 红原县| 镇江市| 韶关市| 安丘市| 永兴县| 临桂县| 平远县| 炎陵县| 民县| 宁波市| 泰顺县| 手游| 大石桥市| 赞皇县| 和林格尔县| 萍乡市| 雅江县| 泸溪县| 浙江省| 福泉市| 平和县| 堆龙德庆县| 阳新县|