📄 codewriter.java
字号:
labels[i].put(this, code, source, true); } } public void visitLookupSwitchInsn ( final Label dflt, final int keys[], final Label labels[]) { if (computeMaxs) { // updates current stack size (max stack size unchanged) --stackSize; // ends current block (with many new successors) if (currentBlock != null) { currentBlock.maxStackSize = maxStackSize; addSuccessor(stackSize, dflt); for (int i = 0; i < labels.length; ++i) { addSuccessor(stackSize, labels[i]); } currentBlock = null; } } // adds the instruction to the bytecode of the method int source = code.length; code.put1(Constants.LOOKUPSWITCH); while (code.length % 4 != 0) { code.put1(0); } dflt.put(this, code, source, true); code.put4(labels.length); for (int i = 0; i < labels.length; ++i) { code.put4(keys[i]); labels[i].put(this, code, source, true); } } public void visitMultiANewArrayInsn (final String desc, final int dims) { if (computeMaxs) { // updates current stack size (max stack size unchanged because stack // size variation always negative or null) stackSize += 1 - dims; } // adds the instruction to the bytecode of the method Item classItem = cw.newClass(desc); code.put12(Constants.MULTIANEWARRAY, classItem.index).put1(dims); } public void visitTryCatchBlock ( final Label start, final Label end, final Label handler, final String type) { if (CHECK) { if (start.owner != this || end.owner != this || handler.owner != this) { throw new IllegalArgumentException(); } if (!start.resolved || !end.resolved || !handler.resolved) { throw new IllegalArgumentException(); } } if (computeMaxs) { // pushes handler block onto the stack of blocks to be visited if (!handler.pushed) { handler.beginStackSize = 1; handler.pushed = true; handler.next = blockStack; blockStack = handler; } } ++catchCount; if (catchTable == null) { catchTable = new ByteVector(); } catchTable.put2(start.position); catchTable.put2(end.position); catchTable.put2(handler.position); catchTable.put2(type != null ? cw.newClass(type).index : 0); } public void visitMaxs (final int maxStack, final int maxLocals) { if (computeMaxs) { // true (non relative) max stack size int max = 0; // control flow analysis algorithm: while the block stack is not empty, // pop a block from this stack, update the max stack size, compute // the true (non relative) begin stack size of the successors of this // block, and push these successors onto the stack (unless they have // already been pushed onto the stack). Note: by hypothesis, the {@link // Label#beginStackSize} of the blocks in the block stack are the true // (non relative) beginning stack sizes of these blocks. Label stack = blockStack; while (stack != null) { // pops a block from the stack Label l = stack; stack = stack.next; // computes the true (non relative) max stack size of this block int start = l.beginStackSize; int blockMax = start + l.maxStackSize; // updates the global max stack size if (blockMax > max) { max = blockMax; } // analyses the successors of the block Edge b = l.successors; while (b != null) { l = b.successor; // if this successor has not already been pushed onto the stack... if (!l.pushed) { // computes the true beginning stack size of this successor block l.beginStackSize = start + b.stackSize; // pushes this successor onto the stack l.pushed = true; l.next = stack; stack = l; } b = b.next; } } this.maxStack = max; // releases all the Edge objects used by this CodeWriter synchronized (SIZE) { // appends the [head ... tail] list at the beginning of the pool list if (tail != null) { tail.poolNext = pool; pool = head; } } } else { this.maxStack = maxStack; this.maxLocals = maxLocals; } } public void visitLocalVariable ( final String name, final String desc, final Label start, final Label end, final int index) { if (CHECK) { if (start.owner != this || !start.resolved) { throw new IllegalArgumentException(); } if (end.owner != this || !end.resolved) { throw new IllegalArgumentException(); } } if (localVar == null) { cw.newUTF8("LocalVariableTable"); localVar = new ByteVector(); } ++localVarCount; localVar.put2(start.position); localVar.put2(end.position - start.position); localVar.put2(cw.newUTF8(name).index); localVar.put2(cw.newUTF8(desc).index); localVar.put2(index); } public void visitLineNumber (final int line, final Label start) { if (CHECK) { if (start.owner != this || !start.resolved) { throw new IllegalArgumentException(); } } if (lineNumber == null) { cw.newUTF8("LineNumberTable"); lineNumber = new ByteVector(); } ++lineNumberCount; lineNumber.put2(start.position); lineNumber.put2(line); } // -------------------------------------------------------------------------- // Utility methods: control flow analysis algorithm // -------------------------------------------------------------------------- /** * Computes the size of the arguments and of the return value of a method. * * @param desc the descriptor of a method. * @return the size of the arguments of the method (plus one for the implicit * this argument), argSize, and the size of its return value, retSize, * packed into a single int i = <tt>(argSize << 2) | retSize</tt> * (argSize is therefore equal to <tt>i >> 2</tt>, and retSize to * <tt>i & 0x03</tt>). */ private static int getArgumentsAndReturnSizes (final String desc) { int n = 1; int c = 1; while (true) { char car = desc.charAt(c++); if (car == ')') { car = desc.charAt(c); return n << 2 | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1)); } else if (car == 'L') { while (desc.charAt(c++) != ';') { } n += 1; } else if (car == '[') { while ((car = desc.charAt(c)) == '[') { ++c; } if (car == 'D' || car == 'J') { n -= 1; } } else if (car == 'D' || car == 'J') { n += 2; } else { n += 1; } } } /** * Adds a successor to the {@link #currentBlock currentBlock} block. * * @param stackSize the current (relative) stack size in the current block. * @param successor the successor block to be added to the current block. */ private void addSuccessor (final int stackSize, final Label successor) { Edge b; // creates a new Edge object or reuses one from the shared pool synchronized (SIZE) { if (pool == null) { b = new Edge(); } else { b = pool; // removes b from the pool pool = pool.poolNext; } } // adds the previous Edge to the list of Edges used by this CodeWriter if (tail == null) { tail = b; } b.poolNext = head; head = b; // initializes the previous Edge object... b.stackSize = stackSize; b.successor = successor; // ...and adds it to the successor list of the currentBlock block b.next = currentBlock.successors; currentBlock.successors = b; } // -------------------------------------------------------------------------- // 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. resizeInstructions(new int[0], new int[0], 0); } int size = 8; if (code.length > 0) { cw.newUTF8("Code"); size += 18 + code.length + 8 * catchCount; if (localVar != null) { size += 8 + localVar.length; } if (lineNumber != null) { size += 8 + lineNumber.length; } } if (exceptionCount > 0) { cw.newUTF8("Exceptions"); size += 8 + 2 * exceptionCount; } if ((access & Constants.ACC_SYNTHETIC) != 0) { cw.newUTF8("Synthetic"); size += 6; } if ((access & Constants.ACC_DEPRECATED) != 0) { cw.newUTF8("Deprecated"); size += 6; } 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) { out.put2(access).put2(name.index).put2(desc.index); int attributeCount = 0; if (code.length > 0) { ++attributeCount; } if (exceptionCount > 0) { ++attributeCount; } if ((access & Constants.ACC_SYNTHETIC) != 0) { ++attributeCount; } if ((access & Constants.ACC_DEPRECATED) != 0) { ++attributeCount; } out.put2(attributeCount); if (code.length > 0) { int size = 12 + code.length + 8 * catchCount; if (localVar != null) { size += 8 + localVar.length; } if (lineNumber != null) { size += 8 + lineNumber.length; } out.put2(cw.newUTF8("Code").index).put4(size); out.put2(maxStack).put2(maxLocals); out.put4(code.length).putByteArray(code.data, 0, code.length); out.put2(catchCount); if (catchCount > 0) { out.putByteArray(catchTable.data, 0, catchTable.length); } attributeCount = 0; if (localVar != null) { ++attributeCount; } if (lineNumber != null) { ++attributeCount; } out.put2(attributeCount); if (localVar != null) { out.put2(cw.newUTF8("LocalVariableTable").index); out.put4(localVar.length + 2).put2(localVarCount); out.putByteArray(localVar.data, 0, localVar.length); } if (lineNumber != null) { out.put2(cw.newUTF8("LineNumberTable").index); out.put4(lineNumber.length + 2).put2(lineNumberCount); out.putByteArray(lineNumber.data, 0, lineNumber.length); } } if (exceptionCount > 0) { out.put2(cw.newUTF8("Exceptions").index).put4(2 * exceptionCount + 2); out.put2(exceptionCount); for (int i = 0; i < exceptionCount; ++i) { out.put2(exceptions[i]); } } if ((access & Constants.ACC_SYNTHETIC) != 0) { out.put2(cw.newUTF8("Synthetic").index).put4(0); } if ((access & Constants.ACC_DEPRECATED) != 0) { out.put2(cw.newUTF8("Deprecated").index).put4(0); } } // -------------------------------------------------------------------------- // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W) // -------------------------------------------------------------------------- /** * Resizes the designated instructions, while keeping jump offsets and * instruction addresses consistent. This may require to resize other existing * instructions, or even to introduce new instructions: for example, * increasing the size of an instruction by 2 at the middle of a method can * increases the offset of an IFEQ instruction from 32766 to 32768, in which * case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W 32765. This, in turn, * may require to increase the size of another jump instruction, and so on... * All these operations are handled automatically by this method. * <p> * <i>This method must be called after all the method that is being built has * been visited</i>. In particular, the {@link Label Label} objects used to * construct the method are no longer valid after this method has been called. * * @param indexes current positions of the instructions to be resized. Each * instruction must be designated by the index of its <i>last</i> byte, * plus one (or, in other words, by the index of the <i>first</i> byte of * the <i>next</i> instruction). * @param sizes the number of bytes to be <i>added</i> to the above * instructions. More precisely, for each i < <tt>len</tt>, * <tt>sizes</tt>[i] bytes will be added at the end of the instruction * designated by <tt>indexes</tt>[i] or, if <tt>sizes</tt>[i] is * negative, the <i>last</i> |<tt>sizes[i]</tt>| bytes of the instruction * will be removed (the instruction size <i>must not</i> become negative * or null). The gaps introduced by this method must be filled in * "manually" in the array returned by the {@link #getCode getCode} * method. * @param len the number of instruction to be resized. Must be smaller than or * equal to <tt>indexes</tt>.length and <tt>sizes</tt>.length. * @return the <tt>indexes</tt> array, which now contains the new positions of * the resized instructions (designated as above). */ protected int[] resizeInstructions ( final int[] indexes, final int[] sizes, final int len) { byte[] b = code.data; // bytecode of the method int u, v, label; // indexes in b int i, j; // loop indexes // 1st step: // As explained above, resizing an instruction may require to resize another // one, which may require to resize yet another one, and so on. The first // step of the algorithm consists in finding all the instructions that // need to be resized, without modifying the code. This is done by the // following "fix point" algorithm: // - parse the code to find the jump instructions whose offset will need // more than 2 bytes to be stored (the future offset is computed from the // current offset and from the number of bytes that will be inserted or // removed between the source and target instructions). For each such // instruction, adds an entry in (a copy of) the indexes and sizes arrays // (if this has not already been done in a previous iteration!) // - if at least one entry has been added during the previous step, go back // to the beginning, otherwise stop. // In fact the real algorithm is complicated by the fact that the size of // TABLESWITCH and LOOKUPSWITCH instructions depends on their position in // the bytecode (because of padding). In order to ensure the convergence of // the algorithm, the number of bytes to be added or removed from these // instructions is over estimated during the previous loop, and computed // exactly only after the loop is finished (this requires another pass to // parse the bytecode of the method). int[] allIndexes = new int[len]; // copy of indexes int[] allSizes = new int[len]; // copy of sizes boolean[] resize; // instructions to be resized int newOffset; // future offset of a jump instruction System.arraycopy(indexes, 0, allIndexes, 0, len); System.arraycopy(sizes, 0, allSizes, 0, len); resize = new boolean[code.length]; int state = 3; // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -