欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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值就是常量值。

 《未完待续》

相关标签: readLabel label