|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
package jdk.internal.org.objectweb.asm; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
final class MethodWriter extends MethodVisitor { |
|
|
|
|
|
static final int COMPUTE_NOTHING = 0; |
|
|
|
|
|
|
|
|
|
*/ |
|
static final int COMPUTE_MAX_STACK_AND_LOCAL = 1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static final int COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES = 2; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static final int COMPUTE_INSERTED_FRAMES = 3; |
|
|
|
|
|
|
|
|
|
*/ |
|
static final int COMPUTE_ALL_FRAMES = 4; |
|
|
|
|
|
private static final int NA = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static final int[] STACK_SIZE_DELTA = { |
|
0, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
1, |
|
2, |
|
2, |
|
1, |
|
1, |
|
1, |
|
2, |
|
2, |
|
1, |
|
1, |
|
1, |
|
NA, |
|
NA, |
|
1, |
|
2, |
|
1, |
|
2, |
|
1, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
-1, |
|
0, |
|
-1, |
|
0, |
|
-1, |
|
-1, |
|
-1, |
|
-1, |
|
-1, |
|
-2, |
|
-1, |
|
-2, |
|
-1, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
-3, |
|
-4, |
|
-3, |
|
-4, |
|
-3, |
|
-3, |
|
-3, |
|
-3, |
|
-1, |
|
-2, |
|
1, |
|
1, |
|
1, |
|
2, |
|
2, |
|
2, |
|
0, |
|
-1, |
|
-2, |
|
-1, |
|
-2, |
|
-1, |
|
-2, |
|
-1, |
|
-2, |
|
-1, |
|
-2, |
|
-1, |
|
-2, |
|
-1, |
|
-2, |
|
-1, |
|
-2, |
|
-1, |
|
-2, |
|
-1, |
|
-2, |
|
0, |
|
0, |
|
0, |
|
0, |
|
-1, |
|
-1, |
|
-1, |
|
-1, |
|
-1, |
|
-1, |
|
-1, |
|
-2, |
|
-1, |
|
-2, |
|
-1, |
|
-2, |
|
0, |
|
1, |
|
0, |
|
1, |
|
-1, |
|
-1, |
|
0, |
|
0, |
|
1, |
|
1, |
|
-1, |
|
0, |
|
-1, |
|
0, |
|
0, |
|
0, |
|
-3, |
|
-1, |
|
-1, |
|
-3, |
|
-3, |
|
-1, |
|
-1, |
|
-1, |
|
-1, |
|
-1, |
|
-1, |
|
-2, |
|
-2, |
|
-2, |
|
-2, |
|
-2, |
|
-2, |
|
-2, |
|
-2, |
|
0, |
|
1, |
|
0, |
|
-1, |
|
-1, |
|
-1, |
|
-2, |
|
-1, |
|
-2, |
|
-1, |
|
0, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
NA, |
|
1, |
|
0, |
|
0, |
|
0, |
|
NA, |
|
0, |
|
0, |
|
-1, |
|
-1, |
|
NA, |
|
NA, |
|
-1, |
|
-1, |
|
NA, |
|
NA |
|
}; |
|
|
|
|
|
private final SymbolTable symbolTable; |
|
|
|
// Note: fields are ordered as in the method_info structure, and those related to attributes are |
|
// ordered as in Section 4.7 of the JVMS. |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private final int accessFlags; |
|
|
|
|
|
private final int nameIndex; |
|
|
|
|
|
private final String name; |
|
|
|
|
|
private final int descriptorIndex; |
|
|
|
|
|
private final String descriptor; |
|
|
|
// Code attribute fields and sub attributes: |
|
|
|
|
|
private int maxStack; |
|
|
|
|
|
private int maxLocals; |
|
|
|
|
|
private final ByteVector code = new ByteVector(); |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Handler firstHandler; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Handler lastHandler; |
|
|
|
|
|
private int lineNumberTableLength; |
|
|
|
|
|
private ByteVector lineNumberTable; |
|
|
|
|
|
private int localVariableTableLength; |
|
|
|
|
|
|
|
*/ |
|
private ByteVector localVariableTable; |
|
|
|
|
|
private int localVariableTypeTableLength; |
|
|
|
|
|
|
|
|
|
*/ |
|
private ByteVector localVariableTypeTable; |
|
|
|
|
|
private int stackMapTableNumberOfEntries; |
|
|
|
|
|
private ByteVector stackMapTableEntries; |
|
|
|
|
|
|
|
|
|
*/ |
|
private AnnotationWriter lastCodeRuntimeVisibleTypeAnnotation; |
|
|
|
|
|
|
|
|
|
*/ |
|
private AnnotationWriter lastCodeRuntimeInvisibleTypeAnnotation; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Attribute firstCodeAttribute; |
|
|
|
// Other method_info attributes: |
|
|
|
|
|
private final int numberOfExceptions; |
|
|
|
|
|
private final int[] exceptionIndexTable; |
|
|
|
|
|
private final int signatureIndex; |
|
|
|
|
|
|
|
|
|
*/ |
|
private AnnotationWriter lastRuntimeVisibleAnnotation; |
|
|
|
|
|
|
|
|
|
*/ |
|
private AnnotationWriter lastRuntimeInvisibleAnnotation; |
|
|
|
|
|
private int visibleAnnotableParameterCount; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private AnnotationWriter[] lastRuntimeVisibleParameterAnnotations; |
|
|
|
|
|
private int invisibleAnnotableParameterCount; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private AnnotationWriter[] lastRuntimeInvisibleParameterAnnotations; |
|
|
|
|
|
|
|
|
|
*/ |
|
private AnnotationWriter lastRuntimeVisibleTypeAnnotation; |
|
|
|
|
|
|
|
|
|
*/ |
|
private AnnotationWriter lastRuntimeInvisibleTypeAnnotation; |
|
|
|
|
|
private ByteVector defaultValue; |
|
|
|
|
|
private int parametersCount; |
|
|
|
|
|
private ByteVector parameters; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Attribute firstAttribute; |
|
|
|
// ----------------------------------------------------------------------------------------------- |
|
// Fields used to compute the maximum stack size and number of locals, and the stack map frames |
|
// ----------------------------------------------------------------------------------------------- |
|
|
|
|
|
|
|
|
|
*/ |
|
private final int compute; |
|
|
|
|
|
|
|
|
|
*/ |
|
private Label firstBasicBlock; |
|
|
|
|
|
|
|
|
|
*/ |
|
private Label lastBasicBlock; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Label currentBasicBlock; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private int relativeStackSize; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private int maxRelativeStackSize; |
|
|
|
|
|
private int currentLocals; |
|
|
|
|
|
private int previousFrameOffset; |
|
|
|
|
|
|
|
|
|
*/ |
|
private int[] previousFrame; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private int[] currentFrame; |
|
|
|
|
|
private boolean hasSubroutines; |
|
|
|
// ----------------------------------------------------------------------------------------------- |
|
// Other miscellaneous status fields |
|
// ----------------------------------------------------------------------------------------------- |
|
|
|
|
|
private boolean hasAsmInstructions; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private int lastBytecodeOffset; |
|
|
|
|
|
|
|
|
|
*/ |
|
private int sourceOffset; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private int sourceLength; |
|
|
|
// ----------------------------------------------------------------------------------------------- |
|
// Constructor and accessors |
|
// ----------------------------------------------------------------------------------------------- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
MethodWriter( |
|
final SymbolTable symbolTable, |
|
final int access, |
|
final String name, |
|
final String descriptor, |
|
final String signature, |
|
final String[] exceptions, |
|
final int compute) { |
|
super( Opcodes.ASM8); |
|
this.symbolTable = symbolTable; |
|
this.accessFlags = "<init>".equals(name) ? access | Constants.ACC_CONSTRUCTOR : access; |
|
this.nameIndex = symbolTable.addConstantUtf8(name); |
|
this.name = name; |
|
this.descriptorIndex = symbolTable.addConstantUtf8(descriptor); |
|
this.descriptor = descriptor; |
|
this.signatureIndex = signature == null ? 0 : symbolTable.addConstantUtf8(signature); |
|
if (exceptions != null && exceptions.length > 0) { |
|
numberOfExceptions = exceptions.length; |
|
this.exceptionIndexTable = new int[numberOfExceptions]; |
|
for (int i = 0; i < numberOfExceptions; ++i) { |
|
this.exceptionIndexTable[i] = symbolTable.addConstantClass(exceptions[i]).index; |
|
} |
|
} else { |
|
numberOfExceptions = 0; |
|
this.exceptionIndexTable = null; |
|
} |
|
this.compute = compute; |
|
if (compute != COMPUTE_NOTHING) { |
|
|
|
int argumentsSize = Type.getArgumentsAndReturnSizes(descriptor) >> 2; |
|
if ((access & Opcodes.ACC_STATIC) != 0) { |
|
--argumentsSize; |
|
} |
|
maxLocals = argumentsSize; |
|
currentLocals = argumentsSize; |
|
|
|
firstBasicBlock = new Label(); |
|
visitLabel(firstBasicBlock); |
|
} |
|
} |
|
|
|
boolean hasFrames() { |
|
return stackMapTableNumberOfEntries > 0; |
|
} |
|
|
|
boolean hasAsmInstructions() { |
|
return hasAsmInstructions; |
|
} |
|
|
|
// ----------------------------------------------------------------------------------------------- |
|
// Implementation of the MethodVisitor abstract class |
|
// ----------------------------------------------------------------------------------------------- |
|
|
|
@Override |
|
public void visitParameter(final String name, final int access) { |
|
if (parameters == null) { |
|
parameters = new ByteVector(); |
|
} |
|
++parametersCount; |
|
parameters.putShort((name == null) ? 0 : symbolTable.addConstantUtf8(name)).putShort(access); |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitAnnotationDefault() { |
|
defaultValue = new ByteVector(); |
|
return new AnnotationWriter(symbolTable, false, defaultValue, null); |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) { |
|
if (visible) { |
|
return lastRuntimeVisibleAnnotation = |
|
AnnotationWriter.create(symbolTable, descriptor, lastRuntimeVisibleAnnotation); |
|
} else { |
|
return lastRuntimeInvisibleAnnotation = |
|
AnnotationWriter.create(symbolTable, descriptor, lastRuntimeInvisibleAnnotation); |
|
} |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitTypeAnnotation( |
|
final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { |
|
if (visible) { |
|
return lastRuntimeVisibleTypeAnnotation = |
|
AnnotationWriter.create( |
|
symbolTable, typeRef, typePath, descriptor, lastRuntimeVisibleTypeAnnotation); |
|
} else { |
|
return lastRuntimeInvisibleTypeAnnotation = |
|
AnnotationWriter.create( |
|
symbolTable, typeRef, typePath, descriptor, lastRuntimeInvisibleTypeAnnotation); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitAnnotableParameterCount(final int parameterCount, final boolean visible) { |
|
if (visible) { |
|
visibleAnnotableParameterCount = parameterCount; |
|
} else { |
|
invisibleAnnotableParameterCount = parameterCount; |
|
} |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitParameterAnnotation( |
|
final int parameter, final String annotationDescriptor, final boolean visible) { |
|
if (visible) { |
|
if (lastRuntimeVisibleParameterAnnotations == null) { |
|
lastRuntimeVisibleParameterAnnotations = |
|
new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; |
|
} |
|
return lastRuntimeVisibleParameterAnnotations[parameter] = |
|
AnnotationWriter.create( |
|
symbolTable, annotationDescriptor, lastRuntimeVisibleParameterAnnotations[parameter]); |
|
} else { |
|
if (lastRuntimeInvisibleParameterAnnotations == null) { |
|
lastRuntimeInvisibleParameterAnnotations = |
|
new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; |
|
} |
|
return lastRuntimeInvisibleParameterAnnotations[parameter] = |
|
AnnotationWriter.create( |
|
symbolTable, |
|
annotationDescriptor, |
|
lastRuntimeInvisibleParameterAnnotations[parameter]); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitAttribute(final Attribute attribute) { |
|
|
|
if (attribute.isCodeAttribute()) { |
|
attribute.nextAttribute = firstCodeAttribute; |
|
firstCodeAttribute = attribute; |
|
} else { |
|
attribute.nextAttribute = firstAttribute; |
|
firstAttribute = attribute; |
|
} |
|
} |
|
|
|
@Override |
|
public void visitCode() { |
|
// Nothing to do. |
|
} |
|
|
|
@Override |
|
public void visitFrame( |
|
final int type, |
|
final int numLocal, |
|
final Object[] local, |
|
final int numStack, |
|
final Object[] stack) { |
|
if (compute == COMPUTE_ALL_FRAMES) { |
|
return; |
|
} |
|
|
|
if (compute == COMPUTE_INSERTED_FRAMES) { |
|
if (currentBasicBlock.frame == null) { |
|
// This should happen only once, for the implicit first frame (which is explicitly visited |
|
// in ClassReader if the EXPAND_ASM_INSNS option is used - and COMPUTE_INSERTED_FRAMES |
|
|
|
currentBasicBlock.frame = new CurrentFrame(currentBasicBlock); |
|
currentBasicBlock.frame.setInputFrameFromDescriptor( |
|
symbolTable, accessFlags, descriptor, numLocal); |
|
currentBasicBlock.frame.accept(this); |
|
} else { |
|
if (type == Opcodes.F_NEW) { |
|
currentBasicBlock.frame.setInputFrameFromApiFormat( |
|
symbolTable, numLocal, local, numStack, stack); |
|
} |
|
// If type is not F_NEW then it is F_INSERT by hypothesis, and currentBlock.frame contains |
|
// the stack map frame at the current instruction, computed from the last F_NEW frame and |
|
|
|
currentBasicBlock.frame.accept(this); |
|
} |
|
} else if (type == Opcodes.F_NEW) { |
|
if (previousFrame == null) { |
|
int argumentsSize = Type.getArgumentsAndReturnSizes(descriptor) >> 2; |
|
Frame implicitFirstFrame = new Frame(new Label()); |
|
implicitFirstFrame.setInputFrameFromDescriptor( |
|
symbolTable, accessFlags, descriptor, argumentsSize); |
|
implicitFirstFrame.accept(this); |
|
} |
|
currentLocals = numLocal; |
|
int frameIndex = visitFrameStart(code.length, numLocal, numStack); |
|
for (int i = 0; i < numLocal; ++i) { |
|
currentFrame[frameIndex++] = Frame.getAbstractTypeFromApiFormat(symbolTable, local[i]); |
|
} |
|
for (int i = 0; i < numStack; ++i) { |
|
currentFrame[frameIndex++] = Frame.getAbstractTypeFromApiFormat(symbolTable, stack[i]); |
|
} |
|
visitFrameEnd(); |
|
} else { |
|
if (symbolTable.getMajorVersion() < Opcodes.V1_6) { |
|
throw new IllegalArgumentException("Class versions V1_5 or less must use F_NEW frames."); |
|
} |
|
int offsetDelta; |
|
if (stackMapTableEntries == null) { |
|
stackMapTableEntries = new ByteVector(); |
|
offsetDelta = code.length; |
|
} else { |
|
offsetDelta = code.length - previousFrameOffset - 1; |
|
if (offsetDelta < 0) { |
|
if (type == Opcodes.F_SAME) { |
|
return; |
|
} else { |
|
throw new IllegalStateException(); |
|
} |
|
} |
|
} |
|
|
|
switch (type) { |
|
case Opcodes.F_FULL: |
|
currentLocals = numLocal; |
|
stackMapTableEntries.putByte(Frame.FULL_FRAME).putShort(offsetDelta).putShort(numLocal); |
|
for (int i = 0; i < numLocal; ++i) { |
|
putFrameType(local[i]); |
|
} |
|
stackMapTableEntries.putShort(numStack); |
|
for (int i = 0; i < numStack; ++i) { |
|
putFrameType(stack[i]); |
|
} |
|
break; |
|
case Opcodes.F_APPEND: |
|
currentLocals += numLocal; |
|
stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED + numLocal).putShort(offsetDelta); |
|
for (int i = 0; i < numLocal; ++i) { |
|
putFrameType(local[i]); |
|
} |
|
break; |
|
case Opcodes.F_CHOP: |
|
currentLocals -= numLocal; |
|
stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED - numLocal).putShort(offsetDelta); |
|
break; |
|
case Opcodes.F_SAME: |
|
if (offsetDelta < 64) { |
|
stackMapTableEntries.putByte(offsetDelta); |
|
} else { |
|
stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED).putShort(offsetDelta); |
|
} |
|
break; |
|
case Opcodes.F_SAME1: |
|
if (offsetDelta < 64) { |
|
stackMapTableEntries.putByte(Frame.SAME_LOCALS_1_STACK_ITEM_FRAME + offsetDelta); |
|
} else { |
|
stackMapTableEntries |
|
.putByte(Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) |
|
.putShort(offsetDelta); |
|
} |
|
putFrameType(stack[0]); |
|
break; |
|
default: |
|
throw new IllegalArgumentException(); |
|
} |
|
|
|
previousFrameOffset = code.length; |
|
++stackMapTableNumberOfEntries; |
|
} |
|
|
|
if (compute == COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES) { |
|
relativeStackSize = numStack; |
|
for (int i = 0; i < numStack; ++i) { |
|
if (stack[i] == Opcodes.LONG || stack[i] == Opcodes.DOUBLE) { |
|
relativeStackSize++; |
|
} |
|
} |
|
if (relativeStackSize > maxRelativeStackSize) { |
|
maxRelativeStackSize = relativeStackSize; |
|
} |
|
} |
|
|
|
maxStack = Math.max(maxStack, numStack); |
|
maxLocals = Math.max(maxLocals, currentLocals); |
|
} |
|
|
|
@Override |
|
public void visitInsn(final int opcode) { |
|
lastBytecodeOffset = code.length; |
|
|
|
code.putByte(opcode); |
|
|
|
if (currentBasicBlock != null) { |
|
if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { |
|
currentBasicBlock.frame.execute(opcode, 0, null, null); |
|
} else { |
|
int size = relativeStackSize + STACK_SIZE_DELTA[opcode]; |
|
if (size > maxRelativeStackSize) { |
|
maxRelativeStackSize = size; |
|
} |
|
relativeStackSize = size; |
|
} |
|
if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) || opcode == Opcodes.ATHROW) { |
|
endCurrentBasicBlockWithNoSuccessor(); |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitIntInsn(final int opcode, final int operand) { |
|
lastBytecodeOffset = code.length; |
|
|
|
if (opcode == Opcodes.SIPUSH) { |
|
code.put12(opcode, operand); |
|
} else { |
|
code.put11(opcode, operand); |
|
} |
|
|
|
if (currentBasicBlock != null) { |
|
if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { |
|
currentBasicBlock.frame.execute(opcode, operand, null, null); |
|
} else if (opcode != Opcodes.NEWARRAY) { |
|
|
|
int size = relativeStackSize + 1; |
|
if (size > maxRelativeStackSize) { |
|
maxRelativeStackSize = size; |
|
} |
|
relativeStackSize = size; |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitVarInsn(final int opcode, final int var) { |
|
lastBytecodeOffset = code.length; |
|
|
|
if (var < 4 && opcode != Opcodes.RET) { |
|
int optimizedOpcode; |
|
if (opcode < Opcodes.ISTORE) { |
|
optimizedOpcode = Constants.ILOAD_0 + ((opcode - Opcodes.ILOAD) << 2) + var; |
|
} else { |
|
optimizedOpcode = Constants.ISTORE_0 + ((opcode - Opcodes.ISTORE) << 2) + var; |
|
} |
|
code.putByte(optimizedOpcode); |
|
} else if (var >= 256) { |
|
code.putByte(Constants.WIDE).put12(opcode, var); |
|
} else { |
|
code.put11(opcode, var); |
|
} |
|
|
|
if (currentBasicBlock != null) { |
|
if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { |
|
currentBasicBlock.frame.execute(opcode, var, null, null); |
|
} else { |
|
if (opcode == Opcodes.RET) { |
|
|
|
currentBasicBlock.flags |= Label.FLAG_SUBROUTINE_END; |
|
currentBasicBlock.outputStackSize = (short) relativeStackSize; |
|
endCurrentBasicBlockWithNoSuccessor(); |
|
} else { |
|
int size = relativeStackSize + STACK_SIZE_DELTA[opcode]; |
|
if (size > maxRelativeStackSize) { |
|
maxRelativeStackSize = size; |
|
} |
|
relativeStackSize = size; |
|
} |
|
} |
|
} |
|
if (compute != COMPUTE_NOTHING) { |
|
int currentMaxLocals; |
|
if (opcode == Opcodes.LLOAD |
|
|| opcode == Opcodes.DLOAD |
|
|| opcode == Opcodes.LSTORE |
|
|| opcode == Opcodes.DSTORE) { |
|
currentMaxLocals = var + 2; |
|
} else { |
|
currentMaxLocals = var + 1; |
|
} |
|
if (currentMaxLocals > maxLocals) { |
|
maxLocals = currentMaxLocals; |
|
} |
|
} |
|
if (opcode >= Opcodes.ISTORE && compute == COMPUTE_ALL_FRAMES && firstHandler != null) { |
|
// If there are exception handler blocks, each instruction within a handler range is, in |
|
// theory, a basic block (since execution can jump from this instruction to the exception |
|
// handler). As a consequence, the local variable types at the beginning of the handler |
|
// block should be the merge of the local variable types at all the instructions within the |
|
// handler range. However, instead of creating a basic block for each instruction, we can |
|
// get the same result in a more efficient way. Namely, by starting a new basic block after |
|
|
|
visitLabel(new Label()); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitTypeInsn(final int opcode, final String type) { |
|
lastBytecodeOffset = code.length; |
|
|
|
Symbol typeSymbol = symbolTable.addConstantClass(type); |
|
code.put12(opcode, typeSymbol.index); |
|
|
|
if (currentBasicBlock != null) { |
|
if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { |
|
currentBasicBlock.frame.execute(opcode, lastBytecodeOffset, typeSymbol, symbolTable); |
|
} else if (opcode == Opcodes.NEW) { |
|
|
|
int size = relativeStackSize + 1; |
|
if (size > maxRelativeStackSize) { |
|
maxRelativeStackSize = size; |
|
} |
|
relativeStackSize = size; |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitFieldInsn( |
|
final int opcode, final String owner, final String name, final String descriptor) { |
|
lastBytecodeOffset = code.length; |
|
|
|
Symbol fieldrefSymbol = symbolTable.addConstantFieldref(owner, name, descriptor); |
|
code.put12(opcode, fieldrefSymbol.index); |
|
|
|
if (currentBasicBlock != null) { |
|
if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { |
|
currentBasicBlock.frame.execute(opcode, 0, fieldrefSymbol, symbolTable); |
|
} else { |
|
int size; |
|
char firstDescChar = descriptor.charAt(0); |
|
switch (opcode) { |
|
case Opcodes.GETSTATIC: |
|
size = relativeStackSize + (firstDescChar == 'D' || firstDescChar == 'J' ? 2 : 1); |
|
break; |
|
case Opcodes.PUTSTATIC: |
|
size = relativeStackSize + (firstDescChar == 'D' || firstDescChar == 'J' ? -2 : -1); |
|
break; |
|
case Opcodes.GETFIELD: |
|
size = relativeStackSize + (firstDescChar == 'D' || firstDescChar == 'J' ? 1 : 0); |
|
break; |
|
case Opcodes.PUTFIELD: |
|
default: |
|
size = relativeStackSize + (firstDescChar == 'D' || firstDescChar == 'J' ? -3 : -2); |
|
break; |
|
} |
|
if (size > maxRelativeStackSize) { |
|
maxRelativeStackSize = size; |
|
} |
|
relativeStackSize = size; |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitMethodInsn( |
|
final int opcode, |
|
final String owner, |
|
final String name, |
|
final String descriptor, |
|
final boolean isInterface) { |
|
lastBytecodeOffset = code.length; |
|
|
|
Symbol methodrefSymbol = symbolTable.addConstantMethodref(owner, name, descriptor, isInterface); |
|
if (opcode == Opcodes.INVOKEINTERFACE) { |
|
code.put12(Opcodes.INVOKEINTERFACE, methodrefSymbol.index) |
|
.put11(methodrefSymbol.getArgumentsAndReturnSizes() >> 2, 0); |
|
} else { |
|
code.put12(opcode, methodrefSymbol.index); |
|
} |
|
|
|
if (currentBasicBlock != null) { |
|
if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { |
|
currentBasicBlock.frame.execute(opcode, 0, methodrefSymbol, symbolTable); |
|
} else { |
|
int argumentsAndReturnSize = methodrefSymbol.getArgumentsAndReturnSizes(); |
|
int stackSizeDelta = (argumentsAndReturnSize & 3) - (argumentsAndReturnSize >> 2); |
|
int size; |
|
if (opcode == Opcodes.INVOKESTATIC) { |
|
size = relativeStackSize + stackSizeDelta + 1; |
|
} else { |
|
size = relativeStackSize + stackSizeDelta; |
|
} |
|
if (size > maxRelativeStackSize) { |
|
maxRelativeStackSize = size; |
|
} |
|
relativeStackSize = size; |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitInvokeDynamicInsn( |
|
final String name, |
|
final String descriptor, |
|
final Handle bootstrapMethodHandle, |
|
final Object... bootstrapMethodArguments) { |
|
lastBytecodeOffset = code.length; |
|
|
|
Symbol invokeDynamicSymbol = |
|
symbolTable.addConstantInvokeDynamic( |
|
name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); |
|
code.put12(Opcodes.INVOKEDYNAMIC, invokeDynamicSymbol.index); |
|
code.putShort(0); |
|
|
|
if (currentBasicBlock != null) { |
|
if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { |
|
currentBasicBlock.frame.execute(Opcodes.INVOKEDYNAMIC, 0, invokeDynamicSymbol, symbolTable); |
|
} else { |
|
int argumentsAndReturnSize = invokeDynamicSymbol.getArgumentsAndReturnSizes(); |
|
int stackSizeDelta = (argumentsAndReturnSize & 3) - (argumentsAndReturnSize >> 2) + 1; |
|
int size = relativeStackSize + stackSizeDelta; |
|
if (size > maxRelativeStackSize) { |
|
maxRelativeStackSize = size; |
|
} |
|
relativeStackSize = size; |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitJumpInsn(final int opcode, final Label label) { |
|
lastBytecodeOffset = code.length; |
|
// Add the instruction to the bytecode of the method. |
|
|
|
int baseOpcode = |
|
opcode >= Constants.GOTO_W ? opcode - Constants.WIDE_JUMP_OPCODE_DELTA : opcode; |
|
boolean nextInsnIsJumpTarget = false; |
|
if ((label.flags & Label.FLAG_RESOLVED) != 0 |
|
&& label.bytecodeOffset - code.length < Short.MIN_VALUE) { |
|
// Case of a backward jump with an offset < -32768. In this case we automatically replace GOTO |
|
// with GOTO_W, JSR with JSR_W and IFxxx <l> with IFNOTxxx <L> GOTO_W <l> L:..., where |
|
// IFNOTxxx is the "opposite" opcode of IFxxx (e.g. IFNE for IFEQ) and where <L> designates |
|
|
|
if (baseOpcode == Opcodes.GOTO) { |
|
code.putByte(Constants.GOTO_W); |
|
} else if (baseOpcode == Opcodes.JSR) { |
|
code.putByte(Constants.JSR_W); |
|
} else { |
|
// Put the "opposite" opcode of baseOpcode. This can be done by flipping the least |
|
// significant bit for IFNULL and IFNONNULL, and similarly for IFEQ ... IF_ACMPEQ (with a |
|
|
|
code.putByte(baseOpcode >= Opcodes.IFNULL ? baseOpcode ^ 1 : ((baseOpcode + 1) ^ 1) - 1); |
|
code.putShort(8); |
|
// Here we could put a GOTO_W in theory, but if ASM specific instructions are used in this |
|
// method or another one, and if the class has frames, we will need to insert a frame after |
|
// this GOTO_W during the additional ClassReader -> ClassWriter round trip to remove the ASM |
|
// specific instructions. To not miss this additional frame, we need to use an ASM_GOTO_W |
|
// here, which has the unfortunate effect of forcing this additional round trip (which in |
|
|
|
code.putByte(Constants.ASM_GOTO_W); |
|
hasAsmInstructions = true; |
|
|
|
nextInsnIsJumpTarget = true; |
|
} |
|
label.put(code, code.length - 1, true); |
|
} else if (baseOpcode != opcode) { |
|
// Case of a GOTO_W or JSR_W specified by the user (normally ClassReader when used to remove |
|
|
|
code.putByte(opcode); |
|
label.put(code, code.length - 1, true); |
|
} else { |
|
// Case of a jump with an offset >= -32768, or of a jump with an unknown offset. In these |
|
// cases we store the offset in 2 bytes (which will be increased via a ClassReader -> |
|
|
|
code.putByte(baseOpcode); |
|
label.put(code, code.length - 1, false); |
|
} |
|
|
|
|
|
if (currentBasicBlock != null) { |
|
Label nextBasicBlock = null; |
|
if (compute == COMPUTE_ALL_FRAMES) { |
|
currentBasicBlock.frame.execute(baseOpcode, 0, null, null); |
|
|
|
label.getCanonicalInstance().flags |= Label.FLAG_JUMP_TARGET; |
|
|
|
addSuccessorToCurrentBasicBlock(Edge.JUMP, label); |
|
if (baseOpcode != Opcodes.GOTO) { |
|
// The next instruction starts a new basic block (except for GOTO: by default the code |
|
// following a goto is unreachable - unless there is an explicit label for it - and we |
|
|
|
nextBasicBlock = new Label(); |
|
} |
|
} else if (compute == COMPUTE_INSERTED_FRAMES) { |
|
currentBasicBlock.frame.execute(baseOpcode, 0, null, null); |
|
} else if (compute == COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES) { |
|
|
|
relativeStackSize += STACK_SIZE_DELTA[baseOpcode]; |
|
} else { |
|
if (baseOpcode == Opcodes.JSR) { |
|
|
|
if ((label.flags & Label.FLAG_SUBROUTINE_START) == 0) { |
|
label.flags |= Label.FLAG_SUBROUTINE_START; |
|
hasSubroutines = true; |
|
} |
|
currentBasicBlock.flags |= Label.FLAG_SUBROUTINE_CALLER; |
|
// Note that, by construction in this method, a block which calls a subroutine has at |
|
// least two successors in the control flow graph: the first one (added below) leads to |
|
// the instruction after the JSR, while the second one (added here) leads to the JSR |
|
// target. Note that the first successor is virtual (it does not correspond to a possible |
|
// execution path): it is only used to compute the successors of the basic blocks ending |
|
|
|
addSuccessorToCurrentBasicBlock(relativeStackSize + 1, label); |
|
|
|
nextBasicBlock = new Label(); |
|
} else { |
|
|
|
relativeStackSize += STACK_SIZE_DELTA[baseOpcode]; |
|
addSuccessorToCurrentBasicBlock(relativeStackSize, label); |
|
} |
|
} |
|
// If the next instruction starts a new basic block, call visitLabel to add the label of this |
|
|
|
if (nextBasicBlock != null) { |
|
if (nextInsnIsJumpTarget) { |
|
nextBasicBlock.flags |= Label.FLAG_JUMP_TARGET; |
|
} |
|
visitLabel(nextBasicBlock); |
|
} |
|
if (baseOpcode == Opcodes.GOTO) { |
|
endCurrentBasicBlockWithNoSuccessor(); |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitLabel(final Label label) { |
|
|
|
hasAsmInstructions |= label.resolve(code.data, code.length); |
|
// visitLabel starts a new basic block (except for debug only labels), so we need to update the |
|
|
|
if ((label.flags & Label.FLAG_DEBUG_ONLY) != 0) { |
|
return; |
|
} |
|
if (compute == COMPUTE_ALL_FRAMES) { |
|
if (currentBasicBlock != null) { |
|
if (label.bytecodeOffset == currentBasicBlock.bytecodeOffset) { |
|
// We use {@link Label#getCanonicalInstance} to store the state of a basic block in only |
|
// one place, but this does not work for labels which have not been visited yet. |
|
// Therefore, when we detect here two labels having the same bytecode offset, we need to |
|
|
|
currentBasicBlock.flags |= (label.flags & Label.FLAG_JUMP_TARGET); |
|
// - make sure the two instances share the same Frame instance (the implementation of |
|
// {@link Label#getCanonicalInstance} relies on this property; here label.frame should be |
|
|
|
label.frame = currentBasicBlock.frame; |
|
// - and make sure to NOT assign 'label' into 'currentBasicBlock' or 'lastBasicBlock', so |
|
|
|
return; |
|
} |
|
|
|
addSuccessorToCurrentBasicBlock(Edge.JUMP, label); |
|
} |
|
|
|
if (lastBasicBlock != null) { |
|
if (label.bytecodeOffset == lastBasicBlock.bytecodeOffset) { |
|
|
|
lastBasicBlock.flags |= (label.flags & Label.FLAG_JUMP_TARGET); |
|
|
|
label.frame = lastBasicBlock.frame; |
|
currentBasicBlock = lastBasicBlock; |
|
return; |
|
} |
|
lastBasicBlock.nextBasicBlock = label; |
|
} |
|
lastBasicBlock = label; |
|
|
|
currentBasicBlock = label; |
|
|
|
label.frame = new Frame(label); |
|
} else if (compute == COMPUTE_INSERTED_FRAMES) { |
|
if (currentBasicBlock == null) { |
|
// This case should happen only once, for the visitLabel call in the constructor. Indeed, if |
|
|
|
currentBasicBlock = label; |
|
} else { |
|
|
|
currentBasicBlock.frame.owner = label; |
|
} |
|
} else if (compute == COMPUTE_MAX_STACK_AND_LOCAL) { |
|
if (currentBasicBlock != null) { |
|
|
|
currentBasicBlock.outputStackMax = (short) maxRelativeStackSize; |
|
addSuccessorToCurrentBasicBlock(relativeStackSize, label); |
|
} |
|
|
|
currentBasicBlock = label; |
|
relativeStackSize = 0; |
|
maxRelativeStackSize = 0; |
|
|
|
if (lastBasicBlock != null) { |
|
lastBasicBlock.nextBasicBlock = label; |
|
} |
|
lastBasicBlock = label; |
|
} else if (compute == COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES && currentBasicBlock == null) { |
|
// This case should happen only once, for the visitLabel call in the constructor. Indeed, if |
|
// compute is equal to COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES, currentBasicBlock stays |
|
|
|
currentBasicBlock = label; |
|
} |
|
} |
|
|
|
@Override |
|
public void visitLdcInsn(final Object value) { |
|
lastBytecodeOffset = code.length; |
|
|
|
Symbol constantSymbol = symbolTable.addConstant(value); |
|
int constantIndex = constantSymbol.index; |
|
char firstDescriptorChar; |
|
boolean isLongOrDouble = |
|
constantSymbol.tag == Symbol.CONSTANT_LONG_TAG |
|
|| constantSymbol.tag == Symbol.CONSTANT_DOUBLE_TAG |
|
|| (constantSymbol.tag == Symbol.CONSTANT_DYNAMIC_TAG |
|
&& ((firstDescriptorChar = constantSymbol.value.charAt(0)) == 'J' |
|
|| firstDescriptorChar == 'D')); |
|
if (isLongOrDouble) { |
|
code.put12(Constants.LDC2_W, constantIndex); |
|
} else if (constantIndex >= 256) { |
|
code.put12(Constants.LDC_W, constantIndex); |
|
} else { |
|
code.put11(Opcodes.LDC, constantIndex); |
|
} |
|
|
|
if (currentBasicBlock != null) { |
|
if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { |
|
currentBasicBlock.frame.execute(Opcodes.LDC, 0, constantSymbol, symbolTable); |
|
} else { |
|
int size = relativeStackSize + (isLongOrDouble ? 2 : 1); |
|
if (size > maxRelativeStackSize) { |
|
maxRelativeStackSize = size; |
|
} |
|
relativeStackSize = size; |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitIincInsn(final int var, final int increment) { |
|
lastBytecodeOffset = code.length; |
|
|
|
if ((var > 255) || (increment > 127) || (increment < -128)) { |
|
code.putByte(Constants.WIDE).put12(Opcodes.IINC, var).putShort(increment); |
|
} else { |
|
code.putByte(Opcodes.IINC).put11(var, increment); |
|
} |
|
|
|
if (currentBasicBlock != null |
|
&& (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES)) { |
|
currentBasicBlock.frame.execute(Opcodes.IINC, var, null, null); |
|
} |
|
if (compute != COMPUTE_NOTHING) { |
|
int currentMaxLocals = var + 1; |
|
if (currentMaxLocals > maxLocals) { |
|
maxLocals = currentMaxLocals; |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitTableSwitchInsn( |
|
final int min, final int max, final Label dflt, final Label... labels) { |
|
lastBytecodeOffset = code.length; |
|
|
|
code.putByte(Opcodes.TABLESWITCH).putByteArray(null, 0, (4 - code.length % 4) % 4); |
|
dflt.put(code, lastBytecodeOffset, true); |
|
code.putInt(min).putInt(max); |
|
for (Label label : labels) { |
|
label.put(code, lastBytecodeOffset, true); |
|
} |
|
|
|
visitSwitchInsn(dflt, labels); |
|
} |
|
|
|
@Override |
|
public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { |
|
lastBytecodeOffset = code.length; |
|
|
|
code.putByte(Opcodes.LOOKUPSWITCH).putByteArray(null, 0, (4 - code.length % 4) % 4); |
|
dflt.put(code, lastBytecodeOffset, true); |
|
code.putInt(labels.length); |
|
for (int i = 0; i < labels.length; ++i) { |
|
code.putInt(keys[i]); |
|
labels[i].put(code, lastBytecodeOffset, true); |
|
} |
|
|
|
visitSwitchInsn(dflt, labels); |
|
} |
|
|
|
private void visitSwitchInsn(final Label dflt, final Label[] labels) { |
|
if (currentBasicBlock != null) { |
|
if (compute == COMPUTE_ALL_FRAMES) { |
|
currentBasicBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null); |
|
|
|
addSuccessorToCurrentBasicBlock(Edge.JUMP, dflt); |
|
dflt.getCanonicalInstance().flags |= Label.FLAG_JUMP_TARGET; |
|
for (Label label : labels) { |
|
addSuccessorToCurrentBasicBlock(Edge.JUMP, label); |
|
label.getCanonicalInstance().flags |= Label.FLAG_JUMP_TARGET; |
|
} |
|
} else if (compute == COMPUTE_MAX_STACK_AND_LOCAL) { |
|
|
|
--relativeStackSize; |
|
|
|
addSuccessorToCurrentBasicBlock(relativeStackSize, dflt); |
|
for (Label label : labels) { |
|
addSuccessorToCurrentBasicBlock(relativeStackSize, label); |
|
} |
|
} |
|
|
|
endCurrentBasicBlockWithNoSuccessor(); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitMultiANewArrayInsn(final String descriptor, final int numDimensions) { |
|
lastBytecodeOffset = code.length; |
|
|
|
Symbol descSymbol = symbolTable.addConstantClass(descriptor); |
|
code.put12(Opcodes.MULTIANEWARRAY, descSymbol.index).putByte(numDimensions); |
|
|
|
if (currentBasicBlock != null) { |
|
if (compute == COMPUTE_ALL_FRAMES || compute == COMPUTE_INSERTED_FRAMES) { |
|
currentBasicBlock.frame.execute( |
|
Opcodes.MULTIANEWARRAY, numDimensions, descSymbol, symbolTable); |
|
} else { |
|
|
|
relativeStackSize += 1 - numDimensions; |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitInsnAnnotation( |
|
final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { |
|
if (visible) { |
|
return lastCodeRuntimeVisibleTypeAnnotation = |
|
AnnotationWriter.create( |
|
symbolTable, |
|
(typeRef & 0xFF0000FF) | (lastBytecodeOffset << 8), |
|
typePath, |
|
descriptor, |
|
lastCodeRuntimeVisibleTypeAnnotation); |
|
} else { |
|
return lastCodeRuntimeInvisibleTypeAnnotation = |
|
AnnotationWriter.create( |
|
symbolTable, |
|
(typeRef & 0xFF0000FF) | (lastBytecodeOffset << 8), |
|
typePath, |
|
descriptor, |
|
lastCodeRuntimeInvisibleTypeAnnotation); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitTryCatchBlock( |
|
final Label start, final Label end, final Label handler, final String type) { |
|
Handler newHandler = |
|
new Handler( |
|
start, end, handler, type != null ? symbolTable.addConstantClass(type).index : 0, type); |
|
if (firstHandler == null) { |
|
firstHandler = newHandler; |
|
} else { |
|
lastHandler.nextHandler = newHandler; |
|
} |
|
lastHandler = newHandler; |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitTryCatchAnnotation( |
|
final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { |
|
if (visible) { |
|
return lastCodeRuntimeVisibleTypeAnnotation = |
|
AnnotationWriter.create( |
|
symbolTable, typeRef, typePath, descriptor, lastCodeRuntimeVisibleTypeAnnotation); |
|
} else { |
|
return lastCodeRuntimeInvisibleTypeAnnotation = |
|
AnnotationWriter.create( |
|
symbolTable, typeRef, typePath, descriptor, lastCodeRuntimeInvisibleTypeAnnotation); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitLocalVariable( |
|
final String name, |
|
final String descriptor, |
|
final String signature, |
|
final Label start, |
|
final Label end, |
|
final int index) { |
|
if (signature != null) { |
|
if (localVariableTypeTable == null) { |
|
localVariableTypeTable = new ByteVector(); |
|
} |
|
++localVariableTypeTableLength; |
|
localVariableTypeTable |
|
.putShort(start.bytecodeOffset) |
|
.putShort(end.bytecodeOffset - start.bytecodeOffset) |
|
.putShort(symbolTable.addConstantUtf8(name)) |
|
.putShort(symbolTable.addConstantUtf8(signature)) |
|
.putShort(index); |
|
} |
|
if (localVariableTable == null) { |
|
localVariableTable = new ByteVector(); |
|
} |
|
++localVariableTableLength; |
|
localVariableTable |
|
.putShort(start.bytecodeOffset) |
|
.putShort(end.bytecodeOffset - start.bytecodeOffset) |
|
.putShort(symbolTable.addConstantUtf8(name)) |
|
.putShort(symbolTable.addConstantUtf8(descriptor)) |
|
.putShort(index); |
|
if (compute != COMPUTE_NOTHING) { |
|
char firstDescChar = descriptor.charAt(0); |
|
int currentMaxLocals = index + (firstDescChar == 'J' || firstDescChar == 'D' ? 2 : 1); |
|
if (currentMaxLocals > maxLocals) { |
|
maxLocals = currentMaxLocals; |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitLocalVariableAnnotation( |
|
final int typeRef, |
|
final TypePath typePath, |
|
final Label[] start, |
|
final Label[] end, |
|
final int[] index, |
|
final String descriptor, |
|
final boolean visible) { |
|
// Create a ByteVector to hold a 'type_annotation' JVMS structure. |
|
|
|
ByteVector typeAnnotation = new ByteVector(); |
|
|
|
typeAnnotation.putByte(typeRef >>> 24).putShort(start.length); |
|
for (int i = 0; i < start.length; ++i) { |
|
typeAnnotation |
|
.putShort(start[i].bytecodeOffset) |
|
.putShort(end[i].bytecodeOffset - start[i].bytecodeOffset) |
|
.putShort(index[i]); |
|
} |
|
TypePath.put(typePath, typeAnnotation); |
|
|
|
typeAnnotation.putShort(symbolTable.addConstantUtf8(descriptor)).putShort(0); |
|
if (visible) { |
|
return lastCodeRuntimeVisibleTypeAnnotation = |
|
new AnnotationWriter( |
|
symbolTable, |
|
true, |
|
typeAnnotation, |
|
lastCodeRuntimeVisibleTypeAnnotation); |
|
} else { |
|
return lastCodeRuntimeInvisibleTypeAnnotation = |
|
new AnnotationWriter( |
|
symbolTable, |
|
true, |
|
typeAnnotation, |
|
lastCodeRuntimeInvisibleTypeAnnotation); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitLineNumber(final int line, final Label start) { |
|
if (lineNumberTable == null) { |
|
lineNumberTable = new ByteVector(); |
|
} |
|
++lineNumberTableLength; |
|
lineNumberTable.putShort(start.bytecodeOffset); |
|
lineNumberTable.putShort(line); |
|
} |
|
|
|
@Override |
|
public void visitMaxs(final int maxStack, final int maxLocals) { |
|
if (compute == COMPUTE_ALL_FRAMES) { |
|
computeAllFrames(); |
|
} else if (compute == COMPUTE_MAX_STACK_AND_LOCAL) { |
|
computeMaxStackAndLocal(); |
|
} else if (compute == COMPUTE_MAX_STACK_AND_LOCAL_FROM_FRAMES) { |
|
this.maxStack = maxRelativeStackSize; |
|
} else { |
|
this.maxStack = maxStack; |
|
this.maxLocals = maxLocals; |
|
} |
|
} |
|
|
|
|
|
private void computeAllFrames() { |
|
|
|
Handler handler = firstHandler; |
|
while (handler != null) { |
|
String catchTypeDescriptor = |
|
handler.catchTypeDescriptor == null ? "java/lang/Throwable" : handler.catchTypeDescriptor; |
|
int catchType = Frame.getAbstractTypeFromInternalName(symbolTable, catchTypeDescriptor); |
|
|
|
Label handlerBlock = handler.handlerPc.getCanonicalInstance(); |
|
handlerBlock.flags |= Label.FLAG_JUMP_TARGET; |
|
|
|
Label handlerRangeBlock = handler.startPc.getCanonicalInstance(); |
|
Label handlerRangeEnd = handler.endPc.getCanonicalInstance(); |
|
while (handlerRangeBlock != handlerRangeEnd) { |
|
handlerRangeBlock.outgoingEdges = |
|
new Edge(catchType, handlerBlock, handlerRangeBlock.outgoingEdges); |
|
handlerRangeBlock = handlerRangeBlock.nextBasicBlock; |
|
} |
|
handler = handler.nextHandler; |
|
} |
|
|
|
|
|
Frame firstFrame = firstBasicBlock.frame; |
|
firstFrame.setInputFrameFromDescriptor(symbolTable, accessFlags, descriptor, this.maxLocals); |
|
firstFrame.accept(this); |
|
|
|
// Fix point algorithm: add the first basic block to a list of blocks to process (i.e. blocks |
|
// whose stack map frame has changed) and, while there are blocks to process, remove one from |
|
// the list and update the stack map frames of its successor blocks in the control flow graph |
|
// (which might change them, in which case these blocks must be processed too, and are thus |
|
// added to the list of blocks to process). Also compute the maximum stack size of the method, |
|
|
|
Label listOfBlocksToProcess = firstBasicBlock; |
|
listOfBlocksToProcess.nextListElement = Label.EMPTY_LIST; |
|
int maxStackSize = 0; |
|
while (listOfBlocksToProcess != Label.EMPTY_LIST) { |
|
|
|
Label basicBlock = listOfBlocksToProcess; |
|
listOfBlocksToProcess = listOfBlocksToProcess.nextListElement; |
|
basicBlock.nextListElement = null; |
|
|
|
basicBlock.flags |= Label.FLAG_REACHABLE; |
|
|
|
int maxBlockStackSize = basicBlock.frame.getInputStackSize() + basicBlock.outputStackMax; |
|
if (maxBlockStackSize > maxStackSize) { |
|
maxStackSize = maxBlockStackSize; |
|
} |
|
|
|
Edge outgoingEdge = basicBlock.outgoingEdges; |
|
while (outgoingEdge != null) { |
|
Label successorBlock = outgoingEdge.successor.getCanonicalInstance(); |
|
boolean successorBlockChanged = |
|
basicBlock.frame.merge(symbolTable, successorBlock.frame, outgoingEdge.info); |
|
if (successorBlockChanged && successorBlock.nextListElement == null) { |
|
// If successorBlock has changed it must be processed. Thus, if it is not already in the |
|
|
|
successorBlock.nextListElement = listOfBlocksToProcess; |
|
listOfBlocksToProcess = successorBlock; |
|
} |
|
outgoingEdge = outgoingEdge.nextEdge; |
|
} |
|
} |
|
|
|
// Loop over all the basic blocks and visit the stack map frames that must be stored in the |
|
// StackMapTable attribute. Also replace unreachable code with NOP* ATHROW, and remove it from |
|
|
|
Label basicBlock = firstBasicBlock; |
|
while (basicBlock != null) { |
|
if ((basicBlock.flags & (Label.FLAG_JUMP_TARGET | Label.FLAG_REACHABLE)) |
|
== (Label.FLAG_JUMP_TARGET | Label.FLAG_REACHABLE)) { |
|
basicBlock.frame.accept(this); |
|
} |
|
if ((basicBlock.flags & Label.FLAG_REACHABLE) == 0) { |
|
|
|
Label nextBasicBlock = basicBlock.nextBasicBlock; |
|
int startOffset = basicBlock.bytecodeOffset; |
|
int endOffset = (nextBasicBlock == null ? code.length : nextBasicBlock.bytecodeOffset) - 1; |
|
if (endOffset >= startOffset) { |
|
|
|
for (int i = startOffset; i < endOffset; ++i) { |
|
code.data[i] = Opcodes.NOP; |
|
} |
|
code.data[endOffset] = (byte) Opcodes.ATHROW; |
|
// Emit a frame for this unreachable block, with no local and a Throwable on the stack |
|
|
|
int frameIndex = visitFrameStart(startOffset, 0, 1); |
|
currentFrame[frameIndex] = |
|
Frame.getAbstractTypeFromInternalName(symbolTable, "java/lang/Throwable"); |
|
visitFrameEnd(); |
|
|
|
firstHandler = Handler.removeRange(firstHandler, basicBlock, nextBasicBlock); |
|
|
|
maxStackSize = Math.max(maxStackSize, 1); |
|
} |
|
} |
|
basicBlock = basicBlock.nextBasicBlock; |
|
} |
|
|
|
this.maxStack = maxStackSize; |
|
} |
|
|
|
|
|
private void computeMaxStackAndLocal() { |
|
|
|
Handler handler = firstHandler; |
|
while (handler != null) { |
|
Label handlerBlock = handler.handlerPc; |
|
Label handlerRangeBlock = handler.startPc; |
|
Label handlerRangeEnd = handler.endPc; |
|
|
|
while (handlerRangeBlock != handlerRangeEnd) { |
|
if ((handlerRangeBlock.flags & Label.FLAG_SUBROUTINE_CALLER) == 0) { |
|
handlerRangeBlock.outgoingEdges = |
|
new Edge(Edge.EXCEPTION, handlerBlock, handlerRangeBlock.outgoingEdges); |
|
} else { |
|
// If handlerRangeBlock is a JSR block, add handlerBlock after the first two outgoing |
|
// edges to preserve the hypothesis about JSR block successors order (see |
|
|
|
handlerRangeBlock.outgoingEdges.nextEdge.nextEdge = |
|
new Edge( |
|
Edge.EXCEPTION, handlerBlock, handlerRangeBlock.outgoingEdges.nextEdge.nextEdge); |
|
} |
|
handlerRangeBlock = handlerRangeBlock.nextBasicBlock; |
|
} |
|
handler = handler.nextHandler; |
|
} |
|
|
|
|
|
if (hasSubroutines) { |
|
// First step: find the subroutines. This step determines, for each basic block, to which |
|
|
|
short numSubroutines = 1; |
|
firstBasicBlock.markSubroutine(numSubroutines); |
|
// Then, mark the subroutines called by the main subroutine, then the subroutines called by |
|
|
|
for (short currentSubroutine = 1; currentSubroutine <= numSubroutines; ++currentSubroutine) { |
|
Label basicBlock = firstBasicBlock; |
|
while (basicBlock != null) { |
|
if ((basicBlock.flags & Label.FLAG_SUBROUTINE_CALLER) != 0 |
|
&& basicBlock.subroutineId == currentSubroutine) { |
|
Label jsrTarget = basicBlock.outgoingEdges.nextEdge.successor; |
|
if (jsrTarget.subroutineId == 0) { |
|
|
|
jsrTarget.markSubroutine(++numSubroutines); |
|
} |
|
} |
|
basicBlock = basicBlock.nextBasicBlock; |
|
} |
|
} |
|
// Second step: find the successors in the control flow graph of each subroutine basic block |
|
// 'r' ending with a RET instruction. These successors are the virtual successors of the basic |
|
|
|
Label basicBlock = firstBasicBlock; |
|
while (basicBlock != null) { |
|
if ((basicBlock.flags & Label.FLAG_SUBROUTINE_CALLER) != 0) { |
|
// By construction, jsr targets are stored in the second outgoing edge of basic blocks |
|
|
|
Label subroutine = basicBlock.outgoingEdges.nextEdge.successor; |
|
subroutine.addSubroutineRetSuccessors(basicBlock); |
|
} |
|
basicBlock = basicBlock.nextBasicBlock; |
|
} |
|
} |
|
|
|
// Data flow algorithm: put the first basic block in a list of blocks to process (i.e. blocks |
|
// whose input stack size has changed) and, while there are blocks to process, remove one |
|
// from the list, update the input stack size of its successor blocks in the control flow |
|
|
|
Label listOfBlocksToProcess = firstBasicBlock; |
|
listOfBlocksToProcess.nextListElement = Label.EMPTY_LIST; |
|
int maxStackSize = maxStack; |
|
while (listOfBlocksToProcess != Label.EMPTY_LIST) { |
|
// Remove a basic block from the list of blocks to process. Note that we don't reset |
|
// basicBlock.nextListElement to null on purpose, to make sure we don't reprocess already |
|
|
|
Label basicBlock = listOfBlocksToProcess; |
|
listOfBlocksToProcess = listOfBlocksToProcess.nextListElement; |
|
|
|
int inputStackTop = basicBlock.inputStackSize; |
|
int maxBlockStackSize = inputStackTop + basicBlock.outputStackMax; |
|
|
|
if (maxBlockStackSize > maxStackSize) { |
|
maxStackSize = maxBlockStackSize; |
|
} |
|
// Update the input stack size of the successor blocks of basicBlock in the control flow |
|
|
|
Edge outgoingEdge = basicBlock.outgoingEdges; |
|
if ((basicBlock.flags & Label.FLAG_SUBROUTINE_CALLER) != 0) { |
|
// Ignore the first outgoing edge of the basic blocks ending with a jsr: these are virtual |
|
// edges which lead to the instruction just after the jsr, and do not correspond to a |
|
// possible execution path (see {@link #visitJumpInsn} and |
|
|
|
outgoingEdge = outgoingEdge.nextEdge; |
|
} |
|
while (outgoingEdge != null) { |
|
Label successorBlock = outgoingEdge.successor; |
|
if (successorBlock.nextListElement == null) { |
|
successorBlock.inputStackSize = |
|
(short) (outgoingEdge.info == Edge.EXCEPTION ? 1 : inputStackTop + outgoingEdge.info); |
|
successorBlock.nextListElement = listOfBlocksToProcess; |
|
listOfBlocksToProcess = successorBlock; |
|
} |
|
outgoingEdge = outgoingEdge.nextEdge; |
|
} |
|
} |
|
this.maxStack = maxStackSize; |
|
} |
|
|
|
@Override |
|
public void visitEnd() { |
|
// Nothing to do. |
|
} |
|
|
|
// ----------------------------------------------------------------------------------------------- |
|
// Utility methods: control flow analysis algorithm |
|
// ----------------------------------------------------------------------------------------------- |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void addSuccessorToCurrentBasicBlock(final int info, final Label successor) { |
|
currentBasicBlock.outgoingEdges = new Edge(info, successor, currentBasicBlock.outgoingEdges); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void endCurrentBasicBlockWithNoSuccessor() { |
|
if (compute == COMPUTE_ALL_FRAMES) { |
|
Label nextBasicBlock = new Label(); |
|
nextBasicBlock.frame = new Frame(nextBasicBlock); |
|
nextBasicBlock.resolve(code.data, code.length); |
|
lastBasicBlock.nextBasicBlock = nextBasicBlock; |
|
lastBasicBlock = nextBasicBlock; |
|
currentBasicBlock = null; |
|
} else if (compute == COMPUTE_MAX_STACK_AND_LOCAL) { |
|
currentBasicBlock.outputStackMax = (short) maxRelativeStackSize; |
|
currentBasicBlock = null; |
|
} |
|
} |
|
|
|
// ----------------------------------------------------------------------------------------------- |
|
// Utility methods: stack map frames |
|
// ----------------------------------------------------------------------------------------------- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
int visitFrameStart(final int offset, final int numLocal, final int numStack) { |
|
int frameLength = 3 + numLocal + numStack; |
|
if (currentFrame == null || currentFrame.length < frameLength) { |
|
currentFrame = new int[frameLength]; |
|
} |
|
currentFrame[0] = offset; |
|
currentFrame[1] = numLocal; |
|
currentFrame[2] = numStack; |
|
return 3; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
void visitAbstractType(final int frameIndex, final int abstractType) { |
|
currentFrame[frameIndex] = abstractType; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
void visitFrameEnd() { |
|
if (previousFrame != null) { |
|
if (stackMapTableEntries == null) { |
|
stackMapTableEntries = new ByteVector(); |
|
} |
|
putFrame(); |
|
++stackMapTableNumberOfEntries; |
|
} |
|
previousFrame = currentFrame; |
|
currentFrame = null; |
|
} |
|
|
|
|
|
private void putFrame() { |
|
final int numLocal = currentFrame[1]; |
|
final int numStack = currentFrame[2]; |
|
if (symbolTable.getMajorVersion() < Opcodes.V1_6) { |
|
|
|
stackMapTableEntries.putShort(currentFrame[0]).putShort(numLocal); |
|
putAbstractTypes(3, 3 + numLocal); |
|
stackMapTableEntries.putShort(numStack); |
|
putAbstractTypes(3 + numLocal, 3 + numLocal + numStack); |
|
return; |
|
} |
|
final int offsetDelta = |
|
stackMapTableNumberOfEntries == 0 |
|
? currentFrame[0] |
|
: currentFrame[0] - previousFrame[0] - 1; |
|
final int previousNumlocal = previousFrame[1]; |
|
final int numLocalDelta = numLocal - previousNumlocal; |
|
int type = Frame.FULL_FRAME; |
|
if (numStack == 0) { |
|
switch (numLocalDelta) { |
|
case -3: |
|
case -2: |
|
case -1: |
|
type = Frame.CHOP_FRAME; |
|
break; |
|
case 0: |
|
type = offsetDelta < 64 ? Frame.SAME_FRAME : Frame.SAME_FRAME_EXTENDED; |
|
break; |
|
case 1: |
|
case 2: |
|
case 3: |
|
type = Frame.APPEND_FRAME; |
|
break; |
|
default: |
|
|
|
break; |
|
} |
|
} else if (numLocalDelta == 0 && numStack == 1) { |
|
type = |
|
offsetDelta < 63 |
|
? Frame.SAME_LOCALS_1_STACK_ITEM_FRAME |
|
: Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED; |
|
} |
|
if (type != Frame.FULL_FRAME) { |
|
|
|
int frameIndex = 3; |
|
for (int i = 0; i < previousNumlocal && i < numLocal; i++) { |
|
if (currentFrame[frameIndex] != previousFrame[frameIndex]) { |
|
type = Frame.FULL_FRAME; |
|
break; |
|
} |
|
frameIndex++; |
|
} |
|
} |
|
switch (type) { |
|
case Frame.SAME_FRAME: |
|
stackMapTableEntries.putByte(offsetDelta); |
|
break; |
|
case Frame.SAME_LOCALS_1_STACK_ITEM_FRAME: |
|
stackMapTableEntries.putByte(Frame.SAME_LOCALS_1_STACK_ITEM_FRAME + offsetDelta); |
|
putAbstractTypes(3 + numLocal, 4 + numLocal); |
|
break; |
|
case Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED: |
|
stackMapTableEntries |
|
.putByte(Frame.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) |
|
.putShort(offsetDelta); |
|
putAbstractTypes(3 + numLocal, 4 + numLocal); |
|
break; |
|
case Frame.SAME_FRAME_EXTENDED: |
|
stackMapTableEntries.putByte(Frame.SAME_FRAME_EXTENDED).putShort(offsetDelta); |
|
break; |
|
case Frame.CHOP_FRAME: |
|
stackMapTableEntries |
|
.putByte(Frame.SAME_FRAME_EXTENDED + numLocalDelta) |
|
.putShort(offsetDelta); |
|
break; |
|
case Frame.APPEND_FRAME: |
|
stackMapTableEntries |
|
.putByte(Frame.SAME_FRAME_EXTENDED + numLocalDelta) |
|
.putShort(offsetDelta); |
|
putAbstractTypes(3 + previousNumlocal, 3 + numLocal); |
|
break; |
|
case Frame.FULL_FRAME: |
|
default: |
|
stackMapTableEntries.putByte(Frame.FULL_FRAME).putShort(offsetDelta).putShort(numLocal); |
|
putAbstractTypes(3, 3 + numLocal); |
|
stackMapTableEntries.putShort(numStack); |
|
putAbstractTypes(3 + numLocal, 3 + numLocal + numStack); |
|
break; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void putAbstractTypes(final int start, final int end) { |
|
for (int i = start; i < end; ++i) { |
|
Frame.putAbstractType(symbolTable, currentFrame[i], stackMapTableEntries); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void putFrameType(final Object type) { |
|
if (type instanceof Integer) { |
|
stackMapTableEntries.putByte(((Integer) type).intValue()); |
|
} else if (type instanceof String) { |
|
stackMapTableEntries |
|
.putByte(Frame.ITEM_OBJECT) |
|
.putShort(symbolTable.addConstantClass((String) type).index); |
|
} else { |
|
stackMapTableEntries |
|
.putByte(Frame.ITEM_UNINITIALIZED) |
|
.putShort(((Label) type).bytecodeOffset); |
|
} |
|
} |
|
|
|
// ----------------------------------------------------------------------------------------------- |
|
// Utility methods |
|
// ----------------------------------------------------------------------------------------------- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
boolean canCopyMethodAttributes( |
|
final ClassReader source, |
|
final boolean hasSyntheticAttribute, |
|
final boolean hasDeprecatedAttribute, |
|
final int descriptorIndex, |
|
final int signatureIndex, |
|
final int exceptionsOffset) { |
|
// If the method descriptor has changed, with more locals than the max_locals field of the |
|
// original Code attribute, if any, then the original method attributes can't be copied. A |
|
// conservative check on the descriptor changes alone ensures this (being more precise is not |
|
// worth the additional complexity, because these cases should be rare -- if a transform changes |
|
|
|
if (source != symbolTable.getSource() |
|
|| descriptorIndex != this.descriptorIndex |
|
|| signatureIndex != this.signatureIndex |
|
|| hasDeprecatedAttribute != ((accessFlags & Opcodes.ACC_DEPRECATED) != 0)) { |
|
return false; |
|
} |
|
boolean needSyntheticAttribute = |
|
symbolTable.getMajorVersion() < Opcodes.V1_5 && (accessFlags & Opcodes.ACC_SYNTHETIC) != 0; |
|
if (hasSyntheticAttribute != needSyntheticAttribute) { |
|
return false; |
|
} |
|
if (exceptionsOffset == 0) { |
|
if (numberOfExceptions != 0) { |
|
return false; |
|
} |
|
} else if (source.readUnsignedShort(exceptionsOffset) == numberOfExceptions) { |
|
int currentExceptionOffset = exceptionsOffset + 2; |
|
for (int i = 0; i < numberOfExceptions; ++i) { |
|
if (source.readUnsignedShort(currentExceptionOffset) != exceptionIndexTable[i]) { |
|
return false; |
|
} |
|
currentExceptionOffset += 2; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
void setMethodAttributesSource(final int methodInfoOffset, final int methodInfoLength) { |
|
// Don't copy the attributes yet, instead store their location in the source class reader so |
|
// they can be copied later, in {@link #putMethodInfo}. Note that we skip the 6 header bytes |
|
|
|
this.sourceOffset = methodInfoOffset + 6; |
|
this.sourceLength = methodInfoLength - 6; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
int computeMethodInfoSize() { |
|
|
|
if (sourceOffset != 0) { |
|
|
|
return 6 + sourceLength; |
|
} |
|
|
|
int size = 8; |
|
|
|
if (code.length > 0) { |
|
if (code.length > 65535) { |
|
throw new MethodTooLargeException( |
|
symbolTable.getClassName(), name, descriptor, code.length); |
|
} |
|
symbolTable.addConstantUtf8(Constants.CODE); |
|
// The Code attribute has 6 header bytes, plus 2, 2, 4 and 2 bytes respectively for max_stack, |
|
|
|
size += 16 + code.length + Handler.getExceptionTableSize(firstHandler); |
|
if (stackMapTableEntries != null) { |
|
boolean useStackMapTable = symbolTable.getMajorVersion() >= Opcodes.V1_6; |
|
symbolTable.addConstantUtf8(useStackMapTable ? Constants.STACK_MAP_TABLE : "StackMap"); |
|
|
|
size += 8 + stackMapTableEntries.length; |
|
} |
|
if (lineNumberTable != null) { |
|
symbolTable.addConstantUtf8(Constants.LINE_NUMBER_TABLE); |
|
|
|
size += 8 + lineNumberTable.length; |
|
} |
|
if (localVariableTable != null) { |
|
symbolTable.addConstantUtf8(Constants.LOCAL_VARIABLE_TABLE); |
|
|
|
size += 8 + localVariableTable.length; |
|
} |
|
if (localVariableTypeTable != null) { |
|
symbolTable.addConstantUtf8(Constants.LOCAL_VARIABLE_TYPE_TABLE); |
|
|
|
size += 8 + localVariableTypeTable.length; |
|
} |
|
if (lastCodeRuntimeVisibleTypeAnnotation != null) { |
|
size += |
|
lastCodeRuntimeVisibleTypeAnnotation.computeAnnotationsSize( |
|
Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS); |
|
} |
|
if (lastCodeRuntimeInvisibleTypeAnnotation != null) { |
|
size += |
|
lastCodeRuntimeInvisibleTypeAnnotation.computeAnnotationsSize( |
|
Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS); |
|
} |
|
if (firstCodeAttribute != null) { |
|
size += |
|
firstCodeAttribute.computeAttributesSize( |
|
symbolTable, code.data, code.length, maxStack, maxLocals); |
|
} |
|
} |
|
if (numberOfExceptions > 0) { |
|
symbolTable.addConstantUtf8(Constants.EXCEPTIONS); |
|
size += 8 + 2 * numberOfExceptions; |
|
} |
|
size += Attribute.computeAttributesSize(symbolTable, accessFlags, signatureIndex); |
|
size += |
|
AnnotationWriter.computeAnnotationsSize( |
|
lastRuntimeVisibleAnnotation, |
|
lastRuntimeInvisibleAnnotation, |
|
lastRuntimeVisibleTypeAnnotation, |
|
lastRuntimeInvisibleTypeAnnotation); |
|
if (lastRuntimeVisibleParameterAnnotations != null) { |
|
size += |
|
AnnotationWriter.computeParameterAnnotationsSize( |
|
Constants.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS, |
|
lastRuntimeVisibleParameterAnnotations, |
|
visibleAnnotableParameterCount == 0 |
|
? lastRuntimeVisibleParameterAnnotations.length |
|
: visibleAnnotableParameterCount); |
|
} |
|
if (lastRuntimeInvisibleParameterAnnotations != null) { |
|
size += |
|
AnnotationWriter.computeParameterAnnotationsSize( |
|
Constants.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS, |
|
lastRuntimeInvisibleParameterAnnotations, |
|
invisibleAnnotableParameterCount == 0 |
|
? lastRuntimeInvisibleParameterAnnotations.length |
|
: invisibleAnnotableParameterCount); |
|
} |
|
if (defaultValue != null) { |
|
symbolTable.addConstantUtf8(Constants.ANNOTATION_DEFAULT); |
|
size += 6 + defaultValue.length; |
|
} |
|
if (parameters != null) { |
|
symbolTable.addConstantUtf8(Constants.METHOD_PARAMETERS); |
|
|
|
size += 7 + parameters.length; |
|
} |
|
if (firstAttribute != null) { |
|
size += firstAttribute.computeAttributesSize(symbolTable); |
|
} |
|
return size; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
void putMethodInfo(final ByteVector output) { |
|
boolean useSyntheticAttribute = symbolTable.getMajorVersion() < Opcodes.V1_5; |
|
int mask = useSyntheticAttribute ? Opcodes.ACC_SYNTHETIC : 0; |
|
output.putShort(accessFlags & ~mask).putShort(nameIndex).putShort(descriptorIndex); |
|
|
|
if (sourceOffset != 0) { |
|
output.putByteArray(symbolTable.getSource().classFileBuffer, sourceOffset, sourceLength); |
|
return; |
|
} |
|
|
|
int attributeCount = 0; |
|
if (code.length > 0) { |
|
++attributeCount; |
|
} |
|
if (numberOfExceptions > 0) { |
|
++attributeCount; |
|
} |
|
if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0 && useSyntheticAttribute) { |
|
++attributeCount; |
|
} |
|
if (signatureIndex != 0) { |
|
++attributeCount; |
|
} |
|
if ((accessFlags & Opcodes.ACC_DEPRECATED) != 0) { |
|
++attributeCount; |
|
} |
|
if (lastRuntimeVisibleAnnotation != null) { |
|
++attributeCount; |
|
} |
|
if (lastRuntimeInvisibleAnnotation != null) { |
|
++attributeCount; |
|
} |
|
if (lastRuntimeVisibleParameterAnnotations != null) { |
|
++attributeCount; |
|
} |
|
if (lastRuntimeInvisibleParameterAnnotations != null) { |
|
++attributeCount; |
|
} |
|
if (lastRuntimeVisibleTypeAnnotation != null) { |
|
++attributeCount; |
|
} |
|
if (lastRuntimeInvisibleTypeAnnotation != null) { |
|
++attributeCount; |
|
} |
|
if (defaultValue != null) { |
|
++attributeCount; |
|
} |
|
if (parameters != null) { |
|
++attributeCount; |
|
} |
|
if (firstAttribute != null) { |
|
attributeCount += firstAttribute.getAttributeCount(); |
|
} |
|
|
|
output.putShort(attributeCount); |
|
if (code.length > 0) { |
|
// 2, 2, 4 and 2 bytes respectively for max_stack, max_locals, code_length and |
|
|
|
int size = 10 + code.length + Handler.getExceptionTableSize(firstHandler); |
|
int codeAttributeCount = 0; |
|
if (stackMapTableEntries != null) { |
|
|
|
size += 8 + stackMapTableEntries.length; |
|
++codeAttributeCount; |
|
} |
|
if (lineNumberTable != null) { |
|
|
|
size += 8 + lineNumberTable.length; |
|
++codeAttributeCount; |
|
} |
|
if (localVariableTable != null) { |
|
|
|
size += 8 + localVariableTable.length; |
|
++codeAttributeCount; |
|
} |
|
if (localVariableTypeTable != null) { |
|
|
|
size += 8 + localVariableTypeTable.length; |
|
++codeAttributeCount; |
|
} |
|
if (lastCodeRuntimeVisibleTypeAnnotation != null) { |
|
size += |
|
lastCodeRuntimeVisibleTypeAnnotation.computeAnnotationsSize( |
|
Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS); |
|
++codeAttributeCount; |
|
} |
|
if (lastCodeRuntimeInvisibleTypeAnnotation != null) { |
|
size += |
|
lastCodeRuntimeInvisibleTypeAnnotation.computeAnnotationsSize( |
|
Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS); |
|
++codeAttributeCount; |
|
} |
|
if (firstCodeAttribute != null) { |
|
size += |
|
firstCodeAttribute.computeAttributesSize( |
|
symbolTable, code.data, code.length, maxStack, maxLocals); |
|
codeAttributeCount += firstCodeAttribute.getAttributeCount(); |
|
} |
|
output |
|
.putShort(symbolTable.addConstantUtf8(Constants.CODE)) |
|
.putInt(size) |
|
.putShort(maxStack) |
|
.putShort(maxLocals) |
|
.putInt(code.length) |
|
.putByteArray(code.data, 0, code.length); |
|
Handler.putExceptionTable(firstHandler, output); |
|
output.putShort(codeAttributeCount); |
|
if (stackMapTableEntries != null) { |
|
boolean useStackMapTable = symbolTable.getMajorVersion() >= Opcodes.V1_6; |
|
output |
|
.putShort( |
|
symbolTable.addConstantUtf8( |
|
useStackMapTable ? Constants.STACK_MAP_TABLE : "StackMap")) |
|
.putInt(2 + stackMapTableEntries.length) |
|
.putShort(stackMapTableNumberOfEntries) |
|
.putByteArray(stackMapTableEntries.data, 0, stackMapTableEntries.length); |
|
} |
|
if (lineNumberTable != null) { |
|
output |
|
.putShort(symbolTable.addConstantUtf8(Constants.LINE_NUMBER_TABLE)) |
|
.putInt(2 + lineNumberTable.length) |
|
.putShort(lineNumberTableLength) |
|
.putByteArray(lineNumberTable.data, 0, lineNumberTable.length); |
|
} |
|
if (localVariableTable != null) { |
|
output |
|
.putShort(symbolTable.addConstantUtf8(Constants.LOCAL_VARIABLE_TABLE)) |
|
.putInt(2 + localVariableTable.length) |
|
.putShort(localVariableTableLength) |
|
.putByteArray(localVariableTable.data, 0, localVariableTable.length); |
|
} |
|
if (localVariableTypeTable != null) { |
|
output |
|
.putShort(symbolTable.addConstantUtf8(Constants.LOCAL_VARIABLE_TYPE_TABLE)) |
|
.putInt(2 + localVariableTypeTable.length) |
|
.putShort(localVariableTypeTableLength) |
|
.putByteArray(localVariableTypeTable.data, 0, localVariableTypeTable.length); |
|
} |
|
if (lastCodeRuntimeVisibleTypeAnnotation != null) { |
|
lastCodeRuntimeVisibleTypeAnnotation.putAnnotations( |
|
symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_TYPE_ANNOTATIONS), output); |
|
} |
|
if (lastCodeRuntimeInvisibleTypeAnnotation != null) { |
|
lastCodeRuntimeInvisibleTypeAnnotation.putAnnotations( |
|
symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS), output); |
|
} |
|
if (firstCodeAttribute != null) { |
|
firstCodeAttribute.putAttributes( |
|
symbolTable, code.data, code.length, maxStack, maxLocals, output); |
|
} |
|
} |
|
if (numberOfExceptions > 0) { |
|
output |
|
.putShort(symbolTable.addConstantUtf8(Constants.EXCEPTIONS)) |
|
.putInt(2 + 2 * numberOfExceptions) |
|
.putShort(numberOfExceptions); |
|
for (int exceptionIndex : exceptionIndexTable) { |
|
output.putShort(exceptionIndex); |
|
} |
|
} |
|
Attribute.putAttributes(symbolTable, accessFlags, signatureIndex, output); |
|
AnnotationWriter.putAnnotations( |
|
symbolTable, |
|
lastRuntimeVisibleAnnotation, |
|
lastRuntimeInvisibleAnnotation, |
|
lastRuntimeVisibleTypeAnnotation, |
|
lastRuntimeInvisibleTypeAnnotation, |
|
output); |
|
if (lastRuntimeVisibleParameterAnnotations != null) { |
|
AnnotationWriter.putParameterAnnotations( |
|
symbolTable.addConstantUtf8(Constants.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS), |
|
lastRuntimeVisibleParameterAnnotations, |
|
visibleAnnotableParameterCount == 0 |
|
? lastRuntimeVisibleParameterAnnotations.length |
|
: visibleAnnotableParameterCount, |
|
output); |
|
} |
|
if (lastRuntimeInvisibleParameterAnnotations != null) { |
|
AnnotationWriter.putParameterAnnotations( |
|
symbolTable.addConstantUtf8(Constants.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS), |
|
lastRuntimeInvisibleParameterAnnotations, |
|
invisibleAnnotableParameterCount == 0 |
|
? lastRuntimeInvisibleParameterAnnotations.length |
|
: invisibleAnnotableParameterCount, |
|
output); |
|
} |
|
if (defaultValue != null) { |
|
output |
|
.putShort(symbolTable.addConstantUtf8(Constants.ANNOTATION_DEFAULT)) |
|
.putInt(defaultValue.length) |
|
.putByteArray(defaultValue.data, 0, defaultValue.length); |
|
} |
|
if (parameters != null) { |
|
output |
|
.putShort(symbolTable.addConstantUtf8(Constants.METHOD_PARAMETERS)) |
|
.putInt(1 + parameters.length) |
|
.putByte(parametersCount) |
|
.putByteArray(parameters.data, 0, parameters.length); |
|
} |
|
if (firstAttribute != null) { |
|
firstAttribute.putAttributes(symbolTable, output); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
final void collectAttributePrototypes(final Attribute.Set attributePrototypes) { |
|
attributePrototypes.addAttributes(firstAttribute); |
|
attributePrototypes.addAttributes(firstCodeAttribute); |
|
} |
|
} |