上善若水
          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二進(jìn)制指令代碼以以下格式緊湊排列(opcode占一個(gè)字節(jié)):

          opcode operand*

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

          通過(guò)對(duì)上面Java指令集的分析可以知道,Java指令集中很大一部分沒(méi)有操作數(shù),因而對(duì)這部分指令,只需要讀取一個(gè)字節(jié)的操作碼,將操作碼映射成助記符即可。 

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

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

          對(duì)這兩條指令,首先都要去掉填充字符以使defaultbyte1索引號(hào)是字對(duì)齊的。

              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();

                 }

          }

          對(duì)tableswitch指令,讀取defaultoffset值,最小項(xiàng)的值,最大項(xiàng)的值以及在最小項(xiàng)和最大項(xiàng)之間每一項(xiàng)的offset值。并且將讀取到的offset值和當(dāng)前指令的基地址相加:

                     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));

                  }

           

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

                     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.       所有條件跳轉(zhuǎn)指令都有兩個(gè)字節(jié)的偏移量操作數(shù)(if<cond>, if_icmp<cond>, ifnull, ifnonnull, if_acmp<cond>)。無(wú)條件跳轉(zhuǎn)指令goto和子例程跳轉(zhuǎn)指令jsr也都是兩個(gè)字節(jié)的偏移量作為操作數(shù)。

          offset = baseOffset + codes.readShort();

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

           

          3.       對(duì)寬偏移量的跳轉(zhuǎn)指令goto_w和子例程跳轉(zhuǎn)指令jsr_w的操作數(shù)是四個(gè)字節(jié)的偏移量。

          offset = baseOffset + codes.readInt();

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

           

          4.       wide指令,則繼續(xù)讀取下一條指令,并將wide參數(shù)設(shè)置為true

          byteCodeToString(codes, pool, verbose, true);

           

          5.       還有一些指令值以一個(gè)字節(jié)的局部變量索引號(hào)作為操作數(shù)的,如果有wide修飾,則用兩個(gè)字節(jié)作為操作數(shù),代表局部變量索引號(hào)。這樣的指令有: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指令,以一個(gè)字節(jié)的局部變量索引號(hào)和一個(gè)自己的常量作為參數(shù);如果以wide修飾,則該指令的局部變量索引號(hào)和常量都占兩個(gè)字節(jié)。

              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.       對(duì)象操作指令,它們的操作數(shù)都是常量池中的索引,長(zhǎng)度為兩個(gè)字節(jié)。指向CONSTANT_Class_info類型的結(jié)構(gòu),這些指令有new, checkcast, instanceof, anewarray

          index = codes.readUnsignedShort();

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

           

          8.       所有字段操作指令,它們的操作數(shù)都是常量池中的索引,長(zhǎng)度為兩個(gè)字節(jié)。指向CONSTANT_Fieldref_info類型結(jié)構(gòu),這些指令有getfield, putfield, getstatic, putstatic

          index = codes.readUnsignedShort();

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

           

          9.       非接口方法調(diào)用指令,也都是以兩個(gè)字節(jié)的索引號(hào)作為操作數(shù),指向常量池中的CONSTANT_Methodref_info類型結(jié)構(gòu),這些指令有invokespecial, invokevirtual, invokestatic

          index = codes.readUnsignedShort();

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

           

          10.   接口方法調(diào)用指令invokeinterface,它有四個(gè)字節(jié)的操作數(shù),前兩個(gè)字節(jié)為常量池的索引號(hào),指向CONSTANT_InterfaceMethodref_info類型,第三個(gè)字節(jié)為count,表示參數(shù)的字節(jié)數(shù),最后一個(gè)字節(jié)為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.   基本類型的數(shù)組創(chuàng)建指令newarray,它的操作數(shù)為一個(gè)字節(jié)的類型標(biāo)識(shí)。

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

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

           

          12.   多維數(shù)組的創(chuàng)建指令multianewarray,它有三個(gè)字節(jié)的操作數(shù),前兩個(gè)字節(jié)為索引號(hào),指向CONSTANT_Class_info類型,表示數(shù)組的類型,最后一個(gè)字節(jié)指定數(shù)組的維度。

          index = codes.readUnsignedShort();

          int dimensions = codes.readUnsignedByte();

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

           

          13.   常量入棧指令ldc,以一個(gè)字節(jié)的索引號(hào)作為參數(shù),指向CONSTANT_Integer_infoCONSTANT_Float_infoCONSTANT_String_infoCONSTANT_Class_info類型,表示要入棧的常量值(int類型值、float類型值、String引用類型值或?qū)ο笠妙愋椭担?/span>

          index = codes.readUnsignedByte();

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

           

          14.   寬索引的常量入棧指令ldc_w,以兩個(gè)字節(jié)的索引號(hào)作為參數(shù),指向CONSTANT_Integer_infoCONSTANT_Float_infoCONSTANT_String_infoCONSTANT_Class_info類型,表示要入棧的常量值(int類型值、float類型值、String引用類型值或?qū)ο笠妙愋椭担?/span>

          index = codes.readUnsignedShort();

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

           

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

          index = codes.readUnsignedShort();

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

           

          16.   bipush指令,以一個(gè)字節(jié)的常量作為操作數(shù)。

          byte constByte = codes.readByte();

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

           

          17.   sipush指令,以兩個(gè)字節(jié)的常量作為操作數(shù)。

          short constShort = codes.readShort();

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

           

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

          參見bcel項(xiàng)目的org.apache.bcel.classfile.Utility類.

          2010年10月6日

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

          FeedBack:
          # re: Java字節(jié)碼(.class文件)的代碼解析
          2011-09-13 09:04 | tb
          不錯(cuò) 收藏了  回復(fù)  更多評(píng)論
            
          主站蜘蛛池模板: 泰安市| 平谷区| 威海市| 司法| 射洪县| 新干县| 江山市| 南川市| 六枝特区| 广饶县| 寿宁县| 吉首市| 柳州市| 贵阳市| 叙永县| 旬阳县| 霞浦县| 通州市| 开原市| 红桥区| 西安市| 勐海县| 武穴市| 黄骅市| 安龙县| 富源县| 庆元县| 曲水县| 长岛县| 清徐县| 宜州市| 道真| 德安县| 留坝县| 长治县| 三门峡市| 于田县| 女性| 民丰县| 高阳县| 巨鹿县|