📄 methodwriter.java
字号:
0,
-1,
-1 );
}
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.putShort( this.access ).putShort( this.name ).putShort( this.desc );
if ( this.classReaderOffset != 0 ) {
out.putByteArray( this.cw.cr.b,
this.classReaderOffset,
this.classReaderLength );
return;
}
int attributeCount = 0;
if ( this.code.length > 0 ) {
++attributeCount;
}
if ( this.exceptionCount > 0 ) {
++attributeCount;
}
if ( (this.access & Opcodes.ACC_SYNTHETIC) != 0 && (this.cw.version & 0xffff) < Opcodes.V1_5 ) {
++attributeCount;
}
if ( (this.access & Opcodes.ACC_DEPRECATED) != 0 ) {
++attributeCount;
}
if ( this.cw.version == Opcodes.V1_4 ) {
if ( (this.access & Opcodes.ACC_VARARGS) != 0 ) {
++attributeCount;
}
if ( (this.access & Opcodes.ACC_BRIDGE) != 0 ) {
++attributeCount;
}
}
if ( this.signature != null ) {
++attributeCount;
}
if ( this.annd != null ) {
++attributeCount;
}
if ( this.anns != null ) {
++attributeCount;
}
if ( this.ianns != null ) {
++attributeCount;
}
if ( this.panns != null ) {
++attributeCount;
}
if ( this.ipanns != null ) {
++attributeCount;
}
if ( this.attrs != null ) {
attributeCount += this.attrs.getCount();
}
out.putShort( attributeCount );
if ( this.code.length > 0 ) {
int size = 12 + this.code.length + 8 * this.catchCount;
if ( this.localVar != null ) {
size += 8 + this.localVar.length;
}
if ( this.localVarType != null ) {
size += 8 + this.localVarType.length;
}
if ( this.lineNumber != null ) {
size += 8 + this.lineNumber.length;
}
if ( this.cattrs != null ) {
size += this.cattrs.getSize( this.cw,
this.code.data,
this.code.length,
this.maxStack,
this.maxLocals );
}
out.putShort( this.cw.newUTF8( "Code" ) ).putInt( size );
out.putShort( this.maxStack ).putShort( this.maxLocals );
out.putInt( this.code.length ).putByteArray( this.code.data,
0,
this.code.length );
out.putShort( this.catchCount );
if ( this.catchCount > 0 ) {
Handler h = this.catchTable;
while ( h != null ) {
out.putShort( h.start.position ).putShort( h.end.position ).putShort( h.handler.position ).putShort( h.type );
h = h.next;
}
}
attributeCount = 0;
if ( this.localVar != null ) {
++attributeCount;
}
if ( this.localVarType != null ) {
++attributeCount;
}
if ( this.lineNumber != null ) {
++attributeCount;
}
if ( this.cattrs != null ) {
attributeCount += this.cattrs.getCount();
}
out.putShort( attributeCount );
if ( this.localVar != null ) {
out.putShort( this.cw.newUTF8( "LocalVariableTable" ) );
out.putInt( this.localVar.length + 2 ).putShort( this.localVarCount );
out.putByteArray( this.localVar.data,
0,
this.localVar.length );
}
if ( this.localVarType != null ) {
out.putShort( this.cw.newUTF8( "LocalVariableTypeTable" ) );
out.putInt( this.localVarType.length + 2 ).putShort( this.localVarTypeCount );
out.putByteArray( this.localVarType.data,
0,
this.localVarType.length );
}
if ( this.lineNumber != null ) {
out.putShort( this.cw.newUTF8( "LineNumberTable" ) );
out.putInt( this.lineNumber.length + 2 ).putShort( this.lineNumberCount );
out.putByteArray( this.lineNumber.data,
0,
this.lineNumber.length );
}
if ( this.cattrs != null ) {
this.cattrs.put( this.cw,
this.code.data,
this.code.length,
this.maxLocals,
this.maxStack,
out );
}
}
if ( this.exceptionCount > 0 ) {
out.putShort( this.cw.newUTF8( "Exceptions" ) ).putInt( 2 * this.exceptionCount + 2 );
out.putShort( this.exceptionCount );
for ( int i = 0; i < this.exceptionCount; ++i ) {
out.putShort( this.exceptions[i] );
}
}
if ( (this.access & Opcodes.ACC_SYNTHETIC) != 0 && (this.cw.version & 0xffff) < Opcodes.V1_5 ) {
out.putShort( this.cw.newUTF8( "Synthetic" ) ).putInt( 0 );
}
if ( (this.access & Opcodes.ACC_DEPRECATED) != 0 ) {
out.putShort( this.cw.newUTF8( "Deprecated" ) ).putInt( 0 );
}
if ( this.cw.version == Opcodes.V1_4 ) {
if ( (this.access & Opcodes.ACC_VARARGS) != 0 ) {
out.putShort( this.cw.newUTF8( "Varargs" ) ).putInt( 0 );
}
if ( (this.access & Opcodes.ACC_BRIDGE) != 0 ) {
out.putShort( this.cw.newUTF8( "Bridge" ) ).putInt( 0 );
}
}
if ( this.signature != null ) {
out.putShort( this.cw.newUTF8( "Signature" ) ).putInt( 2 ).putShort( this.cw.newUTF8( this.signature ) );
}
if ( this.annd != null ) {
out.putShort( this.cw.newUTF8( "AnnotationDefault" ) );
out.putInt( this.annd.length );
out.putByteArray( this.annd.data,
0,
this.annd.length );
}
if ( this.anns != null ) {
out.putShort( this.cw.newUTF8( "RuntimeVisibleAnnotations" ) );
this.anns.put( out );
}
if ( this.ianns != null ) {
out.putShort( this.cw.newUTF8( "RuntimeInvisibleAnnotations" ) );
this.ianns.put( out );
}
if ( this.panns != null ) {
out.putShort( this.cw.newUTF8( "RuntimeVisibleParameterAnnotations" ) );
AnnotationWriter.put( this.panns,
out );
}
if ( this.ipanns != null ) {
out.putShort( this.cw.newUTF8( "RuntimeInvisibleParameterAnnotations" ) );
AnnotationWriter.put( this.ipanns,
out );
}
if ( this.attrs != null ) {
this.attrs.put( this.cw,
null,
0,
-1,
-1,
out );
}
}
// ------------------------------------------------------------------------
// 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 {@link #code code}
* 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).
*/
private int[] resizeInstructions(final int[] indexes,
final int[] sizes,
final int len) {
byte[] b = this.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[this.code.length];
// 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done
int state = 3;
do {
if ( state == 3 ) {
state = 2;
}
u = 0;
while ( u < b.length ) {
int opcode = b[u] & 0xFF; // opcode of current instruction
int insert = 0; // bytes to be added after this instruction
switch ( ClassWriter.TYPE[opcode] ) {
case ClassWriter.NOARG_INSN :
case ClassWriter.IMPLVAR_INSN :
u += 1;
break;
case ClassWriter.LABEL_INSN :
if ( opcode > 201 ) {
// converts temporary opcodes 202 to 217, 218 and
// 219 to IFEQ ... JSR (inclusive), IFNULL and
// IFNONNULL
opcode = opcode < 218 ? opcode - 49 : opcode - 20;
label = u + readUnsignedShort( b,
u + 1 );
} else {
label = u + readShort( b,
u + 1 );
}
newOffset = getNewOffset( allIndexes,
allSizes,
u,
label );
if ( newOffset < Short.MIN_VALUE || newOffset > Short.MAX_VALUE ) {
if ( !resize[u] ) {
if ( opcode == Opcodes.GOTO || opcode == Opcodes.JSR ) {
// two additional bytes will be required to
// replace this GOTO or JSR instruction with
// a GOTO_W or a JSR_W
insert = 2;
} else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -