/src/main/java/com/alibaba/fastjson/asm/MethodWriter.java
Java | 393 lines | 164 code | 48 blank | 181 comment | 43 complexity | 8ba7af5218d1718bfe5e0ea8156bf9a1 MD5 | raw file
- /***
- * ASM: a very small and fast Java bytecode manipulation framework
- * Copyright (c) 2000-2007 INRIA, France Telecom
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holders nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
- * THE POSSIBILITY OF SUCH DAMAGE.
- */
- package com.alibaba.fastjson.asm;
- /**
- * A {@link MethodVisitor} that generates methods in bytecode form. Each visit method of this class appends the bytecode
- * corresponding to the visited instruction to a byte vector, in the order these methods are called.
- *
- * @author Eric Bruneton
- * @author Eugene Kuleshov
- */
- class MethodWriter implements MethodVisitor {
- /**
- * Pseudo access flag used to denote constructors.
- */
- static final int ACC_CONSTRUCTOR = 262144;
- /**
- * Frame has exactly the same locals as the previous stack map frame and number of stack items is zero.
- */
- static final int SAME_FRAME = 0; // to 63 (0-3f)
- /**
- * Frame has exactly the same locals as the previous stack map frame and number of stack items is 1
- */
- static final int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f)
- /**
- * Reserved for future use
- */
- static final int RESERVED = 128;
- /**
- * Frame has exactly the same locals as the previous stack map frame and number of stack items is 1. Offset is
- * bigger then 63;
- */
- static final int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f7
- /**
- * Frame where current locals are the same as the locals in the previous frame, except that the k last locals are
- * absent. The value of k is given by the formula 251-frame_type.
- */
- static final int CHOP_FRAME = 248; // to 250 (f8-fA)
- /**
- * Frame has exactly the same locals as the previous stack map frame and number of stack items is zero. Offset is
- * bigger then 63;
- */
- static final int SAME_FRAME_EXTENDED = 251; // fb
- /**
- * Frame where current locals are the same as the locals in the previous frame, except that k additional locals are
- * defined. The value of k is given by the formula frame_type-251.
- */
- static final int APPEND_FRAME = 252; // to 254 // fc-fe
- /**
- * Full frame
- */
- static final int FULL_FRAME = 255; // ff
- /**
- * Next method writer (see {@link ClassWriter#firstMethod firstMethod}).
- */
- MethodWriter next;
- /**
- * The class writer to which this method must be added.
- */
- final ClassWriter cw;
- /**
- * Access flags of this method.
- */
- private int access;
- /**
- * The index of the constant pool item that contains the name of this method.
- */
- private final int name;
- /**
- * The index of the constant pool item that contains the descriptor of this method.
- */
- private final int desc;
- /**
- * If not zero, indicates that the code of this method must be copied from the ClassReader associated to this writer
- * in <code>cw.cr</code>. More precisely, this field gives the number of bytes to copied from <code>cw.cr.b</code>.
- */
- int classReaderLength;
- /**
- * Number of exceptions that can be thrown by this method.
- */
- int exceptionCount;
- /**
- * The exceptions that can be thrown by this method. More precisely, this array contains the indexes of the constant
- * pool items that contain the internal names of these exception classes.
- */
- int[] exceptions;
- /**
- * The bytecode of this method.
- */
- private ByteVector code = new ByteVector();
- /**
- * Maximum stack size of this method.
- */
- private int maxStack;
- /**
- * Maximum number of local variables for this method.
- */
- private int maxLocals;
- /**
- * Indicates if some jump instructions are too small and need to be resized.
- */
- private boolean resize;
- // ------------------------------------------------------------------------
- /*
- * Fields for the control flow graph analysis algorithm (used to compute the maximum stack size). A control flow
- * graph contains one node per "basic block", and one edge per "jump" from one basic block to another. Each node
- * (i.e., each basic block) is represented by the Label object that corresponds to the first instruction of this
- * basic block. Each node also stores the list of its successors in the graph, as a linked list of Edge objects.
- */
- // ------------------------------------------------------------------------
- // Constructor
- // ------------------------------------------------------------------------
- /**
- * Constructs a new {@link MethodWriter}.
- *
- * @param cw the class writer in which the method must be added.
- * @param access the method's access flags (see {@link Opcodes}).
- * @param name the method's name.
- * @param desc the method's descriptor (see {@link Type}).
- * @param signature the method's signature. May be <tt>null</tt>.
- * @param exceptions the internal names of the method's exceptions. May be <tt>null</tt>.
- * @param computeMaxs <tt>true</tt> if the maximum stack size and number of local variables must be automatically
- * computed.
- * @param computeFrames <tt>true</tt> if the stack map tables must be recomputed from scratch.
- */
- MethodWriter(final ClassWriter cw, final int access, final String name, final String desc, final String signature, final String[] exceptions){
- if (cw.firstMethod == null) {
- cw.firstMethod = this;
- } else {
- cw.lastMethod.next = this;
- }
- cw.lastMethod = this;
- this.cw = cw;
- this.access = access;
- this.name = cw.newUTF8(name);
- this.desc = cw.newUTF8(desc);
- if (exceptions != null && exceptions.length > 0) {
- exceptionCount = exceptions.length;
- this.exceptions = new int[exceptionCount];
- for (int i = 0; i < exceptionCount; ++i) {
- this.exceptions[i] = cw.newClass(exceptions[i]);
- }
- }
- }
- // ------------------------------------------------------------------------
- // Implementation of the MethodVisitor interface
- // ------------------------------------------------------------------------
- public void visitInsn(final int opcode) {
- // adds the instruction to the bytecode of the method
- code.putByte(opcode);
- // update currentBlock
- // Label currentBlock = this.currentBlock;
- }
- public void visitIntInsn(final int opcode, final int operand) {
- // Label currentBlock = this.currentBlock;
- // adds the instruction to the bytecode of the method
- // if (opcode == Opcodes.SIPUSH) {
- // code.put12(opcode, operand);
- // } else { // BIPUSH or NEWARRAY
- code.put11(opcode, operand);
- // }
- }
- public void visitVarInsn(final int opcode, final int var) {
- // Label currentBlock = this.currentBlock;
- // adds the instruction to the bytecode of the method
- if (var < 4 && opcode != Opcodes.RET) {
- int opt;
- if (opcode < Opcodes.ISTORE) {
- /* ILOAD_0 */
- opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var;
- } else {
- /* ISTORE_0 */
- opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var;
- }
- code.putByte(opt);
- } else if (var >= 256) {
- code.putByte(196 /* WIDE */).put12(opcode, var);
- } else {
- code.put11(opcode, var);
- }
- }
- public void visitTypeInsn(final int opcode, final String type) {
- Item i = cw.newClassItem(type);
- // Label currentBlock = this.currentBlock;
- // adds the instruction to the bytecode of the method
- code.put12(opcode, i.index);
- }
- public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) {
- Item i = cw.newFieldItem(owner, name, desc);
- // Label currentBlock = this.currentBlock;
- // adds the instruction to the bytecode of the method
- code.put12(opcode, i.index);
- }
- public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) {
- boolean itf = opcode == Opcodes.INVOKEINTERFACE;
- Item i = cw.newMethodItem(owner, name, desc, itf);
- int argSize = i.intVal;
- // Label currentBlock = this.currentBlock;
- // adds the instruction to the bytecode of the method
- if (itf) {
- if (argSize == 0) {
- argSize = Type.getArgumentsAndReturnSizes(desc);
- i.intVal = argSize;
- }
- code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0);
- } else {
- code.put12(opcode, i.index);
- }
- }
- public void visitJumpInsn(final int opcode, final Label label) {
- // Label currentBlock = this.currentBlock;
- // adds the instruction to the bytecode of the method
- if ((label.status & Label.RESOLVED) != 0 && label.position - code.length < Short.MIN_VALUE) {
- throw new UnsupportedOperationException();
- } else {
- /*
- * case of a backward jump with an offset >= -32768, or of a forward jump with, of course, an unknown
- * offset. In these cases we store the offset in 2 bytes (which will be increased in resizeInstructions, if
- * needed).
- */
- code.putByte(opcode);
- label.put(this, code, code.length - 1);
- }
- }
- public void visitLabel(final Label label) {
- // resolves previous forward references to label, if any
- resize |= label.resolve(this, code.length, code.data);
- }
- public void visitLdcInsn(final Object cst) {
- Item i = cw.newConstItem(cst);
- // Label currentBlock = this.currentBlock;
- // adds the instruction to the bytecode of the method
- int index = i.index;
- if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) {
- code.put12(20 /* LDC2_W */, index);
- } else if (index >= 256) {
- code.put12(19 /* LDC_W */, index);
- } else {
- code.put11(Opcodes.LDC, index);
- }
- }
- public void visitIincInsn(final int var, final int increment) {
- // adds the instruction to the bytecode of the method
- if ((var > 255) || (increment > 127) || (increment < -128)) {
- code.putByte(196 /* WIDE */).put12(Opcodes.IINC, var).putShort(increment);
- } else {
- code.putByte(Opcodes.IINC).put11(var, increment);
- }
- }
- public void visitMaxs(final int maxStack, final int maxLocals) {
- this.maxStack = maxStack;
- this.maxLocals = maxLocals;
- }
- public void visitEnd() {
- }
- // ------------------------------------------------------------------------
- // Utility methods: control flow analysis algorithm
- // ------------------------------------------------------------------------
- // ------------------------------------------------------------------------
- // Utility methods: stack map frames
- // ------------------------------------------------------------------------
- // ------------------------------------------------------------------------
- // Utility methods: dump bytecode array
- // ------------------------------------------------------------------------
- /**
- * Returns the size of the bytecode of this method.
- *
- * @return the size of the bytecode of this method.
- */
- final int getSize() {
- if (resize) {
- // replaces the temporary jump opcodes introduced by Label.resolve.
- throw new UnsupportedOperationException();
- }
- int size = 8;
- if (code.length > 0) {
- cw.newUTF8("Code");
- size += 18 + code.length + 8 * 0;
- }
- if (exceptionCount > 0) {
- cw.newUTF8("Exceptions");
- size += 8 + 2 * exceptionCount;
- }
- return size;
- }
- /**
- * Puts the bytecode of this method in the given byte vector.
- *
- * @param out the byte vector into which the bytecode of this method must be copied.
- */
- final void put(final ByteVector out) {
- int mask = Opcodes.ACC_DEPRECATED | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE | ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / (ClassWriter.ACC_SYNTHETIC_ATTRIBUTE / Opcodes.ACC_SYNTHETIC));
- out.putShort(access & ~mask).putShort(name).putShort(desc);
- int attributeCount = 0;
- if (code.length > 0) {
- ++attributeCount;
- }
- if (exceptionCount > 0) {
- ++attributeCount;
- }
- out.putShort(attributeCount);
- if (code.length > 0) {
- int size = 12 + code.length + 8 * 0; // handlerCount
- out.putShort(cw.newUTF8("Code")).putInt(size);
- out.putShort(maxStack).putShort(maxLocals);
- out.putInt(code.length).putByteArray(code.data, 0, code.length);
- out.putShort(0); // handlerCount
- attributeCount = 0;
- out.putShort(attributeCount);
- }
- if (exceptionCount > 0) {
- out.putShort(cw.newUTF8("Exceptions")).putInt(2 * exceptionCount + 2);
- out.putShort(exceptionCount);
- for (int i = 0; i < exceptionCount; ++i) {
- out.putShort(exceptions[i]);
- }
- }
- }
- }