java 查看类常量池_我将如何解析Java类文件常量池?

到目前为止,我已经能够在此之前解析所有内容(魔术来检查它是否是类文件,主要/次要版本,常量池大小)但我仍然不明白如何解析常量池.比如,是否有用于指定方法引用和其他东西的操作码?

有没有什么办法可以在以十六进制表示文本之前引用每个十六进制值,以找出以下值是什么?

我应该通过NOP(0x00)分割每组条目然后解析不是文本值的每个字节吗?

例如,我如何准确计算出每个值代表的含义?

9c8f5c17435739d00189338d7fb07219.png

解决方法:

常量池由可变长度项组成,其第一个字节确定其类型,而后者又决定了大小.大多数项目由一个或两个指向其他项目的索引组成.一个不需要任何第三方库的简单解析代码可能如下所示:

public static final int HEAD=0xcafebabe;

// Constant pool types

public static final byte CONSTANT_Utf8 = 1;

public static final byte CONSTANT_Integer = 3;

public static final byte CONSTANT_Float = 4;

public static final byte CONSTANT_Long = 5;

public static final byte CONSTANT_Double = 6;

public static final byte CONSTANT_Class = 7;

public static final byte CONSTANT_String = 8;

public static final byte CONSTANT_FieldRef = 9;

public static final byte CONSTANT_MethodRef =10;

public static final byte CONSTANT_InterfaceMethodRef =11;

public static final byte CONSTANT_NameAndType =12;

public static final byte CONSTANT_MethodHandle =15;

public static final byte CONSTANT_MethodType =16;

public static final byte CONSTANT_InvokeDynamic =18;

static void parseRtClass(Class> clazz) throws IOException, URISyntaxException {

URL url = clazz.getResource(clazz.getSimpleName()+".class");

if(url==null) throw new IOException("can't access bytecode of "+clazz);

parse(ByteBuffer.wrap(Files.readAllBytes(Paths.get(url.toURI()))));

}

static void parseClassFile(Path path) throws IOException {

ByteBuffer bb;

try(FileChannel ch=FileChannel.open(path, StandardOpenOption.READ)) {

bb=ch.map(FileChannel.MapMode.READ_ONLY, 0, ch.size());

}

parse(bb);

}

static void parse(ByteBuffer buf) {

if(buf.order(ByteOrder.BIG_ENDIAN).getInt()!=HEAD) {

System.out.println("not a valid class file");

return;

}

int minor=buf.getChar(), ver=buf.getChar();

System.out.println("version "+ver+'.'+minor);

for(int ix=1, num=buf.getChar(); ix

String s; int index1=-1, index2=-1;

byte tag = buf.get();

switch(tag) {

default:

System.out.println("unknown pool item type "+buf.get(buf.position()-1));

return;

case CONSTANT_Utf8: decodeString(ix, buf); continue;

case CONSTANT_Class: case CONSTANT_String: case CONSTANT_MethodType:

s="%d:\t%s ref=%d%n"; index1=buf.getChar();

break;

case CONSTANT_FieldRef: case CONSTANT_MethodRef:

case CONSTANT_InterfaceMethodRef: case CONSTANT_NameAndType:

s="%d:\t%s ref1=%d, ref2=%d%n";

index1=buf.getChar(); index2=buf.getChar();

break;

case CONSTANT_Integer: s="%d:\t%s value="+buf.getInt()+"%n"; break;

case CONSTANT_Float: s="%d:\t%s value="+buf.getFloat()+"%n"; break;

case CONSTANT_Double: s="%d:\t%s value="+buf.getDouble()+"%n"; ix++; break;

case CONSTANT_Long: s="%d:\t%s value="+buf.getLong()+"%n"; ix++; break;

case CONSTANT_MethodHandle:

s="%d:\t%s kind=%d, ref=%d%n"; index1=buf.get(); index2=buf.getChar();

break;

case CONSTANT_InvokeDynamic:

s="%d:\t%s bootstrap_method_attr_index=%d, ref=%d%n";

index1=buf.getChar(); index2=buf.getChar();

break;

}

System.out.printf(s, ix, FMT[tag], index1, index2);

}

}

private static String[] FMT= {

null, "Utf8", null, "Integer", "Float", "Long", "Double", "Class",

"String", "Field", "Method", "Interface Method", "Name and Type",

null, null, "MethodHandle", "MethodType", null, "InvokeDynamic"

};

private static void decodeString(int poolIndex, ByteBuffer buf) {

int size=buf.getChar(), oldLimit=buf.limit();

buf.limit(buf.position()+size);

StringBuilder sb=new StringBuilder(size+(size>>1)+16)

.append(poolIndex).append(":\tUtf8 ");

while(buf.hasRemaining()) {

byte b=buf.get();

if(b>0) sb.append((char)b);

else

{

int b2 = buf.get();

if((b&0xf0)!=0xe0)

sb.append((char)((b&0x1F)<<6 | b2&0x3F));

else

{

int b3 = buf.get();

sb.append((char)((b&0x0F)<<12 | (b2&0x3F)<<6 | b3&0x3F));

}

}

}

buf.limit(oldLimit);

System.out.println(sb);

}

不要被getChar()调用弄糊涂,我用它们作为获取无符号short的方便方法,而不是getShort()& 0xffff.

上面的代码只是打印对其他池项的引用索引.为了解码项目,您可以首先将所有项目的数据存储到随机访问数据结构中,即阵列或列表,因为项目可以指代具有更高索引号的项目.并注意从索引1开始……

标签:pool,java,bytecode

来源: https://codeday.me/bug/20191002/1841880.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值