ASM4.0源码走读之三 readCode方法分析方法代码
程序员文章站
2022-07-07 17:55:54
...
继第一篇,我们来看看readCode的代码:
private void readCode(final MethodVisitor mv, final Context context, int u) { // reads the header byte[] b = this.b; char[] c = context.buffer;//这个buffer长度为maxStringLength int maxStack = readUnsignedShort(u);//maxStack值 int maxLocals = readUnsignedShort(u + 2);//max locals 值 int codeLength = readInt(u + 4);//代码长度 u += 8; // reads the bytecode to find the labels int codeStart = u; int codeEnd = u + codeLength; Label[] labels = context.labels = new Label[codeLength + 2];//为啥+2? readLabel(codeLength + 1, labels); while (u < codeEnd) { int offset = u - codeStart; int opcode = b[u] & 0xFF; switch (ClassWriter.TYPE[opcode]) { case ClassWriter.NOARG_INSN: case ClassWriter.IMPLVAR_INSN: u += 1; break; case ClassWriter.LABEL_INSN: readLabel(offset + readShort(u + 1), labels); u += 3; break; case ClassWriter.LABELW_INSN: readLabel(offset + readInt(u + 1), labels); u += 5; break; case ClassWriter.WIDE_INSN: opcode = b[u + 1] & 0xFF; if (opcode == Opcodes.IINC) { u += 6; } else { u += 4; } break; case ClassWriter.TABL_INSN: // skips 0 to 3 padding bytes u = u + 4 - (offset & 3); // reads instruction readLabel(offset + readInt(u), labels); for (int i = readInt(u + 8) - readInt(u + 4) + 1; i > 0; --i) { readLabel(offset + readInt(u + 12), labels); u += 4; } u += 12; break; case ClassWriter.LOOK_INSN: // skips 0 to 3 padding bytes u = u + 4 - (offset & 3); // reads instruction readLabel(offset + readInt(u), labels); for (int i = readInt(u + 4); i > 0; --i) { readLabel(offset + readInt(u + 12), labels); u += 8; } u += 8; break; case ClassWriter.VAR_INSN: case ClassWriter.SBYTE_INSN: case ClassWriter.LDC_INSN: u += 2; break; case ClassWriter.SHORT_INSN: case ClassWriter.LDCW_INSN: case ClassWriter.FIELDORMETH_INSN: case ClassWriter.TYPE_INSN: case ClassWriter.IINC_INSN: u += 3; break; case ClassWriter.ITFMETH_INSN: case ClassWriter.INDYMETH_INSN: u += 5; break; // case MANA_INSN: default: u += 4; break; } } // reads the try catch entries to find the labels, and also visits them for (int i = readUnsignedShort(u); i > 0; --i) { Label start = readLabel(readUnsignedShort(u + 2), labels); Label end = readLabel(readUnsignedShort(u + 4), labels); Label handler = readLabel(readUnsignedShort(u + 6), labels); String type = readUTF8(items[readUnsignedShort(u + 8)], c); mv.visitTryCatchBlock(start, end, handler, type); u += 8; } u += 2; // reads the code attributes int[] tanns = null; // start index of each visible type annotation int[] itanns = null; // start index of each invisible type annotation int tann = 0; // current index in tanns array int itann = 0; // current index in itanns array int ntoff = -1; // next visible type annotation code offset int nitoff = -1; // next invisible type annotation code offset int varTable = 0; int varTypeTable = 0; boolean zip = true; boolean unzip = (context.flags & EXPAND_FRAMES) != 0; int stackMap = 0; int stackMapSize = 0; int frameCount = 0; Context frame = null; Attribute attributes = null; for (int i = readUnsignedShort(u); i > 0; --i) { String attrName = readUTF8(u + 2, c); if ("LocalVariableTable".equals(attrName)) { if ((context.flags & SKIP_DEBUG) == 0) { varTable = u + 8; for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) { int label = readUnsignedShort(v + 10); if (labels[label] == null) { readLabel(label, labels).status |= Label.DEBUG; } label += readUnsignedShort(v + 12); if (labels[label] == null) { readLabel(label, labels).status |= Label.DEBUG; } v += 10; } } } else if ("LocalVariableTypeTable".equals(attrName)) { varTypeTable = u + 8; } else if ("LineNumberTable".equals(attrName)) { if ((context.flags & SKIP_DEBUG) == 0) { for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) { int label = readUnsignedShort(v + 10); if (labels[label] == null) { readLabel(label, labels).status |= Label.DEBUG; } labels[label].line = readUnsignedShort(v + 12); v += 4; } } } else if (ANNOTATIONS && "RuntimeVisibleTypeAnnotations".equals(attrName)) { tanns = readTypeAnnotations(mv, context, u + 8, true); ntoff = tann >= tanns.length || readUnsignedShort(tanns[tann]) < 0x86 ? -1 : readUnsignedShort(tanns[tann] + 2); } else if (ANNOTATIONS && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { itanns = readTypeAnnotations(mv, context, u + 8, false); nitoff = itann >= itanns.length || readUnsignedShort(itanns[itann]) < 0x86 ? -1 : readUnsignedShort(itanns[itann] + 2); } else if (FRAMES && "StackMapTable".equals(attrName)) { if ((context.flags & SKIP_FRAMES) == 0) { stackMap = u + 10;//stackMap位置后边是stack_map_frame类型的entries,10 = attribute_count(u2)、attribute_name_index(u2)、attribute_length(u4)、number_of_entries(u2) stackMapSize = readInt(u + 4);//stackMapSize是attribute_length frameCount = readUnsignedShort(u + 8);// } /* * here we do not extract the labels corresponding to the * attribute content. This would require a full parsing of the * attribute, which would need to be repeated in the second * phase (see below). Instead the content of the attribute is * read one frame at a time (i.e. after a frame has been * visited, the next frame is read), and the labels it contains * are also extracted one frame at a time. Thanks to the * ordering of frames, having only a "one frame lookahead" is * not a problem, i.e. it is not possible to see an offset * smaller than the offset of the current insn and for which no * Label exist. */ /* * This is not true for UNINITIALIZED type offsets. We solve * this by parsing the stack map table without a full decoding * (see below). */ } else if (FRAMES && "StackMap".equals(attrName)) { if ((context.flags & SKIP_FRAMES) == 0) { zip = false; stackMap = u + 10; stackMapSize = readInt(u + 4); frameCount = readUnsignedShort(u + 8); } /* * IMPORTANT! here we assume that the frames are ordered, as in * the StackMapTable attribute, although this is not guaranteed * by the attribute format. */ } else { for (int j = 0; j < context.attrs.length; ++j) { if (context.attrs[j].type.equals(attrName)) { Attribute attr = context.attrs[j].read(this, u + 8, readInt(u + 4), c, codeStart - 8, labels); if (attr != null) { attr.next = attributes; attributes = attr; } } } } u += 6 + readInt(u + 4); } u += 2; // generates the first (implicit) stack map frame if (FRAMES && (stackMap != 0 || unzip)) { /* * for the first explicit frame the offset is not offset_delta + 1 * but only offset_delta; setting the implicit frame offset to -1 * allow the use of the "offset_delta + 1" rule in all cases */ frame = context; frame.offset = -1; frame.mode = 0; frame.localCount = 0; frame.localDiff = 0; frame.stackCount = 0; frame.local = new Object[maxLocals]; frame.stack = new Object[maxStack]; if (unzip) { getImplicitFrame(context); } } if (FRAMES && stackMap != 0) { /* * Finds labels for UNINITIALIZED frame types. Instead of decoding * each element of the stack map table, we look for 3 consecutive * bytes that "look like" an UNINITIALIZED type (tag 8, offset * within code bounds, NEW instruction at this offset). We may find * false positives (i.e. not real UNINITIALIZED types), but this * should be rare, and the only consequence will be the creation of * an unneeded label. This is better than creating a label for each * NEW instruction, and faster than fully decoding the whole stack * map table. */ for (int i = stackMap; i < stackMap + stackMapSize - 2; ++i) { if (b[i] == 8) { // UNINITIALIZED FRAME TYPE int v = readUnsignedShort(i + 1); if (v >= 0 && v < codeLength) { if ((b[codeStart + v] & 0xFF) == Opcodes.NEW) { readLabel(v, labels); } } } } } // visits the instructions u = codeStart; while (u < codeEnd) { int offset = u - codeStart; // visits the label and line number for this offset, if any Label l = labels[offset]; if (l != null) { mv.visitLabel(l); if ((context.flags & SKIP_DEBUG) == 0 && l.line > 0) { mv.visitLineNumber(l.line, l); } } // visits the frame(s) for this offset, if any while (FRAMES && frame != null && (frame.offset == offset || frame.offset == -1)) { // if there is a frame for this offset, makes the visitor visit // it, and reads the next frame if there is one. if (!zip || unzip) { mv.visitFrame(Opcodes.F_NEW, frame.localCount, frame.local, frame.stackCount, frame.stack); } else if (frame.offset != -1) { mv.visitFrame(frame.mode, frame.localDiff, frame.local, frame.stackCount, frame.stack); } if (frameCount > 0) { stackMap = readFrame(stackMap, zip, unzip, frame); --frameCount; } else { frame = null; } } // visits the instruction at this offset int opcode = b[u] & 0xFF; switch (ClassWriter.TYPE[opcode]) { case ClassWriter.NOARG_INSN: mv.visitInsn(opcode); u += 1; break; case ClassWriter.IMPLVAR_INSN: if (opcode > Opcodes.ISTORE) { opcode -= 59; // ISTORE_0 mv.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), opcode & 0x3); } else { opcode -= 26; // ILOAD_0 mv.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), opcode & 0x3); } u += 1; break; case ClassWriter.LABEL_INSN: mv.visitJumpInsn(opcode, labels[offset + readShort(u + 1)]); u += 3; break; case ClassWriter.LABELW_INSN: mv.visitJumpInsn(opcode - 33, labels[offset + readInt(u + 1)]); u += 5; break; case ClassWriter.WIDE_INSN: opcode = b[u + 1] & 0xFF; if (opcode == Opcodes.IINC) { mv.visitIincInsn(readUnsignedShort(u + 2), readShort(u + 4)); u += 6; } else { mv.visitVarInsn(opcode, readUnsignedShort(u + 2)); u += 4; } break; case ClassWriter.TABL_INSN: { // skips 0 to 3 padding bytes u = u + 4 - (offset & 3); // reads instruction int label = offset + readInt(u); int min = readInt(u + 4); int max = readInt(u + 8); Label[] table = new Label[max - min + 1]; u += 12; for (int i = 0; i < table.length; ++i) { table[i] = labels[offset + readInt(u)]; u += 4; } mv.visitTableSwitchInsn(min, max, labels[label], table); break; } case ClassWriter.LOOK_INSN: { // skips 0 to 3 padding bytes u = u + 4 - (offset & 3); // reads instruction int label = offset + readInt(u); int len = readInt(u + 4); int[] keys = new int[len]; Label[] values = new Label[len]; u += 8; for (int i = 0; i < len; ++i) { keys[i] = readInt(u); values[i] = labels[offset + readInt(u + 4)]; u += 8; } mv.visitLookupSwitchInsn(labels[label], keys, values); break; } case ClassWriter.VAR_INSN: mv.visitVarInsn(opcode, b[u + 1] & 0xFF); u += 2; break; case ClassWriter.SBYTE_INSN: mv.visitIntInsn(opcode, b[u + 1]); u += 2; break; case ClassWriter.SHORT_INSN: mv.visitIntInsn(opcode, readShort(u + 1)); u += 3; break; case ClassWriter.LDC_INSN: mv.visitLdcInsn(readConst(b[u + 1] & 0xFF, c)); u += 2; break; case ClassWriter.LDCW_INSN: mv.visitLdcInsn(readConst(readUnsignedShort(u + 1), c)); u += 3; break; case ClassWriter.FIELDORMETH_INSN: case ClassWriter.ITFMETH_INSN: { int cpIndex = items[readUnsignedShort(u + 1)]; String iowner = readClass(cpIndex, c); cpIndex = items[readUnsignedShort(cpIndex + 2)]; String iname = readUTF8(cpIndex, c); String idesc = readUTF8(cpIndex + 2, c); if (opcode < Opcodes.INVOKEVIRTUAL) { mv.visitFieldInsn(opcode, iowner, iname, idesc); } else { mv.visitMethodInsn(opcode, iowner, iname, idesc); } if (opcode == Opcodes.INVOKEINTERFACE) { u += 5; } else { u += 3; } break; } case ClassWriter.INDYMETH_INSN: { int cpIndex = items[readUnsignedShort(u + 1)]; int bsmIndex = context.bootstrapMethods[readUnsignedShort(cpIndex)]; Handle bsm = (Handle) readConst(readUnsignedShort(bsmIndex), c); int bsmArgCount = readUnsignedShort(bsmIndex + 2); Object[] bsmArgs = new Object[bsmArgCount]; bsmIndex += 4; for (int i = 0; i < bsmArgCount; i++) { bsmArgs[i] = readConst(readUnsignedShort(bsmIndex), c); bsmIndex += 2; } cpIndex = items[readUnsignedShort(cpIndex + 2)]; String iname = readUTF8(cpIndex, c); String idesc = readUTF8(cpIndex + 2, c); mv.visitInvokeDynamicInsn(iname, idesc, bsm, bsmArgs); u += 5; break; } case ClassWriter.TYPE_INSN: mv.visitTypeInsn(opcode, readClass(u + 1, c)); u += 3; break; case ClassWriter.IINC_INSN: mv.visitIincInsn(b[u + 1] & 0xFF, b[u + 2]); u += 3; break; // case MANA_INSN: default: mv.visitMultiANewArrayInsn(readClass(u + 1, c), b[u + 3] & 0xFF); u += 4; break; } // visit the instruction annotations, if any while (tanns != null && tann < tanns.length && ntoff <= offset) { if (ntoff == offset) { int v = readAnnotationTarget(context, tanns[tann]); readAnnotationValues(v + 2, c, true, mv.visitInsnAnnotation(context.target, context.path, readUTF8(v, c), true)); } ntoff = ++tann >= tanns.length || readUnsignedShort(tanns[tann]) < 0x86 ? -1 : readUnsignedShort(tanns[tann] + 2); } while (itanns != null && itann < itanns.length && nitoff <= offset) { if (nitoff == offset) { int v = readAnnotationTarget(context, itanns[itann]); readAnnotationValues(v + 2, c, true, mv.visitInsnAnnotation(context.target, context.path, readUTF8(v, c), false)); } nitoff = ++itann >= itanns.length || readUnsignedShort(itanns[itann]) < 0x86 ? -1 : readUnsignedShort(itanns[itann] + 2); } } if (labels[codeLength] != null) { mv.visitLabel(labels[codeLength]); } // visits the local variable tables if ((context.flags & SKIP_DEBUG) == 0 && varTable != 0) { int[] typeTable = null; if (varTypeTable != 0) { u = varTypeTable + 2; typeTable = new int[readUnsignedShort(varTypeTable) * 3]; for (int i = typeTable.length; i > 0;) { typeTable[--i] = u + 6; // signature typeTable[--i] = readUnsignedShort(u + 8); // index typeTable[--i] = readUnsignedShort(u); // start u += 10; } } u = varTable + 2; for (int i = readUnsignedShort(varTable); i > 0; --i) { int start = readUnsignedShort(u); int length = readUnsignedShort(u + 2); int index = readUnsignedShort(u + 8); String vsignature = null; if (typeTable != null) { for (int j = 0; j < typeTable.length; j += 3) { if (typeTable[j] == start && typeTable[j + 1] == index) { vsignature = readUTF8(typeTable[j + 2], c); break; } } } mv.visitLocalVariable(readUTF8(u + 4, c), readUTF8(u + 6, c), vsignature, labels[start], labels[start + length], index); u += 10; } } // visits the local variables type annotations if (tanns != null) { for (int i = 0; i < tanns.length; ++i) { if ((readUnsignedShort(tanns[i]) & 0xFC) == 0x80) { int v = readAnnotationTarget(context, tanns[i]); v = readAnnotationValues(v + 2, c, true, mv.visitLocalVariableAnnotation(context.target, context.path, context.start, context.end, context.index, readUTF8(v, c), true)); } } } if (itanns != null) { for (int i = 0; i < itanns.length; ++i) { if ((readUnsignedShort(itanns[i]) & 0xFC) == 0x80) { int v = readAnnotationTarget(context, itanns[i]); v = readAnnotationValues(v + 2, c, true, mv.visitLocalVariableAnnotation(context.target, context.path, context.start, context.end, context.index, readUTF8(v, c), false)); } } } // visits the code attributes while (attributes != null) { Attribute attr = attributes.next; attributes.next = null; mv.visitAttribute(attributes); attributes = attr; } // visits the max stack and max locals values mv.visitMaxs(maxStack, maxLocals); }
ASM4.0在readCode里有个局部变量Label[] labels,同时也指向org.objectweb.asm.Context.labels;是通过调用org.objectweb.asm.ClassReader .readLabel(int, Label[]) 来操作的
ASM2.2.3里边是access方法里边有一个操作Label[] labels局部变量,直接在access内读写的
4.0中的labels数组,是在访问了某些指令的时候,会构造一个label:
1、代码指令类:
if类、goto、jsr的目标地址; goto_w、jsr_w的目标地址; tableswitch、lookupswitch目标地址、默认目标地址;
2、异常表类
start_pc、end_pc、handler_pc
3、code属性的属性
LocalVariableTable,每个变量的start_pc和start_pc + length LineNumberTable,指令索引、行号 未知属性表 StackMapTable,初始帧、栈帧表每一帧,帧类型(2个) RuntimeVisibleTypeAnnotations RuntimeInvisibleTypeAnnotations
接下来看看asm是如何读取常量池信息的,asm根据常量池项对应的常量池索引,根据常量池索引得到其值在class文件type数组的偏移量,然后从class文件的byte数组中读取到相应长度的值,要特别注意的是float和double值的存储是按照ieee的格式存储的,所以这里在解析的时候会进行一次转换:
/** * Reads a numeric or string constant pool item in {@link #b b}. <i>This * method is intended for {@link Attribute} sub classes, and is normally not * needed by class generators or adapters.</i> * * @param item * the index of a constant pool item. * @param buf * buffer to be used to read the item. This buffer must be * sufficiently large. It is not automatically resized. * @return the {@link Integer}, {@link Float}, {@link Long}, {@link Double}, * {@link String}, {@link Type} or {@link Handle} corresponding to * the given constant pool item. */ public Object readConst(final int item, final char[] buf) { int index = items[item]; switch (b[index - 1]) { case ClassWriter.INT://基本类型获取的都是值本身 return new Integer(readInt(index)); case ClassWriter.FLOAT: return new Float(Float.intBitsToFloat(readInt(index))); case ClassWriter.LONG: return new Long(readLong(index)); case ClassWriter.DOUBLE: return new Double(Double.longBitsToDouble(readLong(index))); case ClassWriter.CLASS://class里边存储的是Type类型 return Type.getObjectType(readUTF8(index, buf)); case ClassWriter.STR: return readUTF8(index, buf); case ClassWriter.MTYPE: return Type.getMethodType(readUTF8(index, buf)); default: // case ClassWriter.HANDLE_BASE + [1..9]: int tag = readByte(index); int[] items = this.items; int cpIndex = items[readUnsignedShort(index + 1)]; String owner = readClass(cpIndex, buf); cpIndex = items[readUnsignedShort(cpIndex + 2)]; String name = readUTF8(cpIndex, buf); String desc = readUTF8(cpIndex + 2, buf); return new Handle(tag, owner, name, desc); } }
在MethodNode中,常量值会被转换成一个LdcInsnNode节点存储,节点的cst值就是常量值。
《未完待续》