<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    上善若水
    In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
    posts - 146,comments - 147,trackbacks - 0

    Java二進制指令代碼以以下格式緊湊排列(opcode占一個字節):

    opcode operand*

    除了tableswitchlookupswitch兩條指令中間存在填充字節以外,其他指令都沒有填充字節,即使在兩條指令之間也沒有。因而在讀取指令的時候,要根據指令的定義讀取。 

    通過對上面Java指令集的分析可以知道,Java指令集中很大一部分沒有操作數,因而對這部分指令,只需要讀取一個字節的操作碼,將操作碼映射成助記符即可。 

    而對其他帶操作數的指令,則需要根據不同類型分析(由于apache中的bcelBinary Code Engineering Library)對字節碼的支持,操作碼和助記符的映射可以用com.sun.org.apache.bcel.internal.Constats中提供的映射表數組來完成)。

    1.       處理兩條特殊的指令tableswitchlookupswitch指令。

    對這兩條指令,首先都要去掉填充字符以使defaultbyte1索引號是字對齊的。

        private static void make4ByteAlignment(ByteSequence codes) {

           int usedBytes = codes.getIndex() % 4;

           int paddingBytes = (usedBytes == 0) ? 0 : 4 - usedBytes;

           for(int i = 0;i < paddingBytes;i++) {

               codes.readByte();

           }

    }

    tableswitch指令,讀取defaultoffset值,最小項的值,最大項的值以及在最小項和最大項之間每一項的offset值。并且將讀取到的offset值和當前指令的基地址相加:

               int defaultOffset1 = baseOffset + codes.readInt();

               builder.append("\tdefault = #" + defaultOffset1);

               int low = codes.readInt();

               int high = codes.readInt();

               int npair1 = high - low + 1;

               builder.append(", npairs = " + npair1 + "\n");

               for(int i = low;i <= high;i++) {

                  int match = i;

                  offset = baseOffset + codes.readInt();

                  builder.append(String.format("\tcase %d : #%d\n", match, offset));

            }

     

    lookupswitch指令,讀取defaultoffset值,鍵值對數值(npairs),以及npairs對的鍵值對,將得到的offset值和當前指令的基地址相加:

               int defaultOffset2 = baseOffset + codes.readInt();

               builder.append("\tdefault = #" + defaultOffset2);

               int npairs2 = codes.readInt();

               builder.append(", npairs = " + npairs2 + "\n");

               for(int i = 0;i < npairs2;i++) {

                  int match = codes.readInt();

                  offset = baseOffset + codes.readInt();

                  builder.append(String.format("\tcase %d : #%d\n", match, offset));

            }

     

    2.       所有條件跳轉指令都有兩個字節的偏移量操作數(if<cond>, if_icmp<cond>, ifnull, ifnonnull, if_acmp<cond>)。無條件跳轉指令goto和子例程跳轉指令jsr也都是兩個字節的偏移量作為操作數。

    offset = baseOffset + codes.readShort();

    builder.append(String.format("\t\t#%d\n", offset));

     

    3.       對寬偏移量的跳轉指令goto_w和子例程跳轉指令jsr_w的操作數是四個字節的偏移量。

    offset = baseOffset + codes.readInt();

    builder.append(String.format("\t\t#%d\n", offset));

     

    4.       wide指令,則繼續讀取下一條指令,并將wide參數設置為true

    byteCodeToString(codes, pool, verbose, true);

     

    5.       還有一些指令值以一個字節的局部變量索引號作為操作數的,如果有wide修飾,則用兩個字節作為操作數,代表局部變量索引號。這樣的指令有:aload, iload, fload, lload, dload, astore, istore, fstore, lstore, dstore, ret

    if(wide) {

        index = codes.readUnsignedShort();

    } else {

        index = codes.readUnsignedByte();

    }

    builder.append(String.format("\t\t%%%d\n", index));

    6.       iinc指令,以一個字節的局部變量索引號和一個自己的常量作為參數;如果以wide修飾,則該指令的局部變量索引號和常量都占兩個字節。

        if(wide) {

           index = codes.readUnsignedShort();

           constValue = codes.readShort();

        } else {

           index = codes.readUnsignedByte();

           constValue = codes.readByte();

        }

    builder.append(String.format("\t\t%d %d\n", index, constValue));

     

    7.       對象操作指令,它們的操作數都是常量池中的索引,長度為兩個字節。指向CONSTANT_Class_info類型的結構,這些指令有new, checkcast, instanceof, anewarray

    index = codes.readUnsignedShort();

    builder.append("\t\t" + pool.getClassInfo(index).toInstructionString(verbose) + "\n");

     

    8.       所有字段操作指令,它們的操作數都是常量池中的索引,長度為兩個字節。指向CONSTANT_Fieldref_info類型結構,這些指令有getfield, putfield, getstatic, putstatic

    index = codes.readUnsignedShort();

    builder.append("\t\t" + pool.getFieldRefInfo(index).toInstructionString(verbose) + "\n");

     

    9.       非接口方法調用指令,也都是以兩個字節的索引號作為操作數,指向常量池中的CONSTANT_Methodref_info類型結構,這些指令有invokespecial, invokevirtual, invokestatic

    index = codes.readUnsignedShort();

    builder.append("\t\t" + pool.getMethodRefInfo(index).toInstructionString(verbose) + "\n");

     

    10.   接口方法調用指令invokeinterface,它有四個字節的操作數,前兩個字節為常量池的索引號,指向CONSTANT_InterfaceMethodref_info類型,第三個字節為count,表示參數的字節數,最后一個字節為0值。

    index = codes.readUnsignedShort();

    int nargs = codes.readUnsignedByte(); //Historical, redundant

    builder.append("\t\t" + pool.getInterfaceMethodRefInfo(index).toInstructionString(verbose));

    builder.append(" : " + nargs + "\n");

    codes.readUnsignedByte(); //reserved should be zero

     

    11.   基本類型的數組創建指令newarray,它的操作數為一個字節的類型標識。

    String type = Constants.TYPE_NAMES[codes.readByte()];

    builder.append(String.format("\t\t(%s)\n", type));

     

    12.   多維數組的創建指令multianewarray,它有三個字節的操作數,前兩個字節為索引號,指向CONSTANT_Class_info類型,表示數組的類型,最后一個字節指定數組的維度。

    index = codes.readUnsignedShort();

    int dimensions = codes.readUnsignedByte();

    builder.append(String.format("\t\t%s (%d)\n", pool.getClassInfo(index).getName(), dimensions));

     

    13.   常量入棧指令ldc,以一個字節的索引號作為參數,指向CONSTANT_Integer_infoCONSTANT_Float_infoCONSTANT_String_infoCONSTANT_Class_info類型,表示要入棧的常量值(int類型值、float類型值、String引用類型值或對象引用類型值)。

    index = codes.readUnsignedByte();

    builder.append("\t\t" + pool.getPoolItem(index).toInstructionString(verbose) + "\n");

     

    14.   寬索引的常量入棧指令ldc_w,以兩個字節的索引號作為參數,指向CONSTANT_Integer_infoCONSTANT_Float_infoCONSTANT_String_infoCONSTANT_Class_info類型,表示要入棧的常量值(int類型值、float類型值、String引用類型值或對象引用類型值)。

    index = codes.readUnsignedShort();

    builder.append("\t\t" + pool.getPoolItem(index).toInstructionString(verbose) + "\n");

     

    15.   寬索引的常量入棧指令ldc2_w,以兩個字節的索引號作為參數,指向CONSTANT_Long_infoCONSTANT_Double_info類型,表示要入棧的常量值(long類型值、double類型值)。

    index = codes.readUnsignedShort();

    builder.append("\t\t" + pool.getPoolItem(index).toInstructionString(verbose) + "\n");

     

    16.   bipush指令,以一個字節的常量作為操作數。

    byte constByte = codes.readByte();

    builder.append(“\t” + constByte);

     

    17.   sipush指令,以兩個字節的常量作為操作數。

    short constShort = codes.readShort();

    builder.append(“\t” + constShort);

     

    以上還有一些沒有完成的代碼,包括字段(方法)的簽名和描述符沒有解析,有一些解析的格式還需要調整等。不管怎么樣,總體的結構就是這樣了,其它的都是細節問題,這里不討論了。 

    參見bcel項目的org.apache.bcel.classfile.Utility類.

    2010年10月6日

    posted on 2011-09-13 00:05 DLevin 閱讀(3828) 評論(1)  編輯  收藏 所屬分類: 深入JVM

    FeedBack:
    # re: Java字節碼(.class文件)的代碼解析
    2011-09-13 09:04 | tb
    不錯 收藏了  回復  更多評論
      
    主站蜘蛛池模板: 亚洲人JIZZ日本人| 日本二区免费一片黄2019| 亚洲av无码成人精品区| 亚洲AV日韩综合一区| 永久免费看bbb| 看亚洲a级一级毛片| 免费乱码中文字幕网站| 九九免费精品视频在这里| 久久亚洲国产成人影院网站| 国产黄色免费观看| 亚洲午夜福利717| 男人进去女人爽免费视频国产| 亚洲av无码潮喷在线观看| 永久在线免费观看| 亚洲啪AV永久无码精品放毛片| 拨牐拨牐x8免费| 视频免费1区二区三区| 亚洲va久久久噜噜噜久久男同| 亚洲一级毛片免费在线观看| 亚洲色中文字幕在线播放| 亚洲?V无码成人精品区日韩| 久久av免费天堂小草播放| 亚洲成人午夜在线| 人妻视频一区二区三区免费| 免费高清A级毛片在线播放| 亚洲欧洲精品无码AV| 午夜免费1000部| 亚洲AV无码一区二区三区久久精品| 国产极品美女高潮抽搐免费网站| 精品国产污污免费网站入口在线| 亚洲精品在线观看视频| 在线免费视频一区| 叮咚影视在线观看免费完整版| 亚洲a级片在线观看| 亚洲五月午夜免费在线视频| 最近中文字幕免费mv在线视频| 亚洲AV成人无码久久WWW| 亚洲av无码专区国产乱码在线观看| 91在线视频免费91| 中文字幕无码毛片免费看| 亚洲一区二区三区乱码在线欧洲|