|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
package jdk.internal.org.objectweb.asm; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
class MethodWriter extends MethodVisitor { |
|
|
|
|
|
|
|
*/ |
|
static final int ACC_CONSTRUCTOR = 0x80000; |
|
|
|
/** |
|
* 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; |
|
|
|
/** |
|
* 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; |
|
|
|
|
|
|
|
*/ |
|
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; |
|
|
|
/** |
|
* 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; |
|
|
|
/** |
|
* 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; |
|
|
|
/** |
|
* 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; |
|
|
|
/** |
|
* Full frame |
|
*/ |
|
static final int FULL_FRAME = 255; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static final int FRAMES = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static final int INSERTED_FRAMES = 1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static final int MAXS = 2; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
static final int NOTHING = 3; |
|
|
|
|
|
|
|
*/ |
|
final ClassWriter cw; |
|
|
|
|
|
|
|
*/ |
|
private int access; |
|
|
|
|
|
|
|
|
|
*/ |
|
private final int name; |
|
|
|
|
|
|
|
|
|
*/ |
|
private final int desc; |
|
|
|
|
|
|
|
*/ |
|
private final String descriptor; |
|
|
|
|
|
|
|
*/ |
|
String signature; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
int classReaderOffset; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
int classReaderLength; |
|
|
|
|
|
|
|
*/ |
|
int exceptionCount; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
int[] exceptions; |
|
|
|
|
|
|
|
*/ |
|
private ByteVector annd; |
|
|
|
|
|
|
|
*/ |
|
private AnnotationWriter anns; |
|
|
|
|
|
|
|
*/ |
|
private AnnotationWriter ianns; |
|
|
|
|
|
|
|
|
|
*/ |
|
private AnnotationWriter tanns; |
|
|
|
|
|
|
|
|
|
*/ |
|
private AnnotationWriter itanns; |
|
|
|
|
|
|
|
|
|
*/ |
|
private AnnotationWriter[] panns; |
|
|
|
|
|
|
|
|
|
*/ |
|
private AnnotationWriter[] ipanns; |
|
|
|
|
|
|
|
*/ |
|
private int synthetics; |
|
|
|
|
|
|
|
*/ |
|
private Attribute attrs; |
|
|
|
|
|
|
|
*/ |
|
private ByteVector code = new ByteVector(); |
|
|
|
|
|
|
|
*/ |
|
private int maxStack; |
|
|
|
|
|
|
|
*/ |
|
private int maxLocals; |
|
|
|
|
|
|
|
*/ |
|
private int currentLocals; |
|
|
|
|
|
|
|
*/ |
|
int frameCount; |
|
|
|
|
|
|
|
*/ |
|
private ByteVector stackMap; |
|
|
|
|
|
|
|
|
|
*/ |
|
private int previousFrameOffset; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private int[] previousFrame; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private int[] frame; |
|
|
|
|
|
|
|
*/ |
|
private int handlerCount; |
|
|
|
|
|
|
|
*/ |
|
private Handler firstHandler; |
|
|
|
|
|
|
|
*/ |
|
private Handler lastHandler; |
|
|
|
|
|
|
|
*/ |
|
private int methodParametersCount; |
|
|
|
|
|
|
|
*/ |
|
private ByteVector methodParameters; |
|
|
|
|
|
|
|
*/ |
|
private int localVarCount; |
|
|
|
|
|
|
|
*/ |
|
private ByteVector localVar; |
|
|
|
|
|
|
|
*/ |
|
private int localVarTypeCount; |
|
|
|
|
|
|
|
*/ |
|
private ByteVector localVarType; |
|
|
|
|
|
|
|
*/ |
|
private int lineNumberCount; |
|
|
|
|
|
|
|
*/ |
|
private ByteVector lineNumber; |
|
|
|
|
|
|
|
*/ |
|
private int lastCodeOffset; |
|
|
|
|
|
|
|
*/ |
|
private AnnotationWriter ctanns; |
|
|
|
|
|
|
|
*/ |
|
private AnnotationWriter ictanns; |
|
|
|
|
|
|
|
*/ |
|
private Attribute cattrs; |
|
|
|
|
|
|
|
*/ |
|
private int subroutines; |
|
|
|
// ------------------------------------------------------------------------ |
|
|
|
/* |
|
* 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. |
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private final int compute; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private Label labels; |
|
|
|
|
|
|
|
*/ |
|
private Label previousBlock; |
|
|
|
|
|
|
|
*/ |
|
private Label currentBlock; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private int stackSize; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private int maxStackSize; |
|
|
|
// ------------------------------------------------------------------------ |
|
// Constructor |
|
// ------------------------------------------------------------------------ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
MethodWriter(final ClassWriter cw, final int access, final String name, |
|
final String desc, final String signature, |
|
final String[] exceptions, final int compute) { |
|
super(Opcodes.ASM6); |
|
if (cw.firstMethod == null) { |
|
cw.firstMethod = this; |
|
} else { |
|
cw.lastMethod.mv = this; |
|
} |
|
cw.lastMethod = this; |
|
this.cw = cw; |
|
this.access = access; |
|
if ("<init>".equals(name)) { |
|
this.access |= ACC_CONSTRUCTOR; |
|
} |
|
this.name = cw.newUTF8(name); |
|
this.desc = cw.newUTF8(desc); |
|
this.descriptor = desc; |
|
this.signature = signature; |
|
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]); |
|
} |
|
} |
|
this.compute = compute; |
|
if (compute != NOTHING) { |
|
|
|
int size = Type.getArgumentsAndReturnSizes(descriptor) >> 2; |
|
if ((access & Opcodes.ACC_STATIC) != 0) { |
|
--size; |
|
} |
|
maxLocals = size; |
|
currentLocals = size; |
|
|
|
labels = new Label(); |
|
labels.status |= Label.PUSHED; |
|
visitLabel(labels); |
|
} |
|
} |
|
|
|
// ------------------------------------------------------------------------ |
|
// Implementation of the MethodVisitor abstract class |
|
// ------------------------------------------------------------------------ |
|
|
|
@Override |
|
public void visitParameter(String name, int access) { |
|
if (methodParameters == null) { |
|
methodParameters = new ByteVector(); |
|
} |
|
++methodParametersCount; |
|
methodParameters.putShort((name == null) ? 0 : cw.newUTF8(name)) |
|
.putShort(access); |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitAnnotationDefault() { |
|
annd = new ByteVector(); |
|
return new AnnotationWriter(cw, false, annd, null, 0); |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitAnnotation(final String desc, |
|
final boolean visible) { |
|
ByteVector bv = new ByteVector(); |
|
|
|
bv.putShort(cw.newUTF8(desc)).putShort(0); |
|
AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); |
|
if (visible) { |
|
aw.next = anns; |
|
anns = aw; |
|
} else { |
|
aw.next = ianns; |
|
ianns = aw; |
|
} |
|
return aw; |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitTypeAnnotation(final int typeRef, |
|
final TypePath typePath, final String desc, final boolean visible) { |
|
ByteVector bv = new ByteVector(); |
|
|
|
AnnotationWriter.putTarget(typeRef, typePath, bv); |
|
|
|
bv.putShort(cw.newUTF8(desc)).putShort(0); |
|
AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, |
|
bv.length - 2); |
|
if (visible) { |
|
aw.next = tanns; |
|
tanns = aw; |
|
} else { |
|
aw.next = itanns; |
|
itanns = aw; |
|
} |
|
return aw; |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitParameterAnnotation(final int parameter, |
|
final String desc, final boolean visible) { |
|
ByteVector bv = new ByteVector(); |
|
if ("Ljava/lang/Synthetic;".equals(desc)) { |
|
// workaround for a bug in javac with synthetic parameters |
|
|
|
synthetics = Math.max(synthetics, parameter + 1); |
|
return new AnnotationWriter(cw, false, bv, null, 0); |
|
} |
|
|
|
bv.putShort(cw.newUTF8(desc)).putShort(0); |
|
AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); |
|
if (visible) { |
|
if (panns == null) { |
|
panns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; |
|
} |
|
aw.next = panns[parameter]; |
|
panns[parameter] = aw; |
|
} else { |
|
if (ipanns == null) { |
|
ipanns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; |
|
} |
|
aw.next = ipanns[parameter]; |
|
ipanns[parameter] = aw; |
|
} |
|
return aw; |
|
} |
|
|
|
@Override |
|
public void visitAttribute(final Attribute attr) { |
|
if (attr.isCodeAttribute()) { |
|
attr.next = cattrs; |
|
cattrs = attr; |
|
} else { |
|
attr.next = attrs; |
|
attrs = attr; |
|
} |
|
} |
|
|
|
@Override |
|
public void visitCode() { |
|
} |
|
|
|
@Override |
|
public void visitFrame(final int type, final int nLocal, |
|
final Object[] local, final int nStack, final Object[] stack) { |
|
if (compute == FRAMES) { |
|
return; |
|
} |
|
|
|
if (compute == INSERTED_FRAMES) { |
|
if (currentBlock.frame == null) { |
|
// This should happen only once, for the implicit first frame |
|
// (which is explicitly visited in ClassReader if the |
|
|
|
currentBlock.frame = new CurrentFrame(); |
|
currentBlock.frame.owner = currentBlock; |
|
currentBlock.frame.initInputFrame(cw, access, |
|
Type.getArgumentTypes(descriptor), nLocal); |
|
visitImplicitFirstFrame(); |
|
} else { |
|
if (type == Opcodes.F_NEW) { |
|
currentBlock.frame.set(cw, nLocal, local, nStack, stack); |
|
} else { |
|
// In this case type is equal to F_INSERT by hypothesis, and |
|
// currentBlock.frame contains the stack map frame at the |
|
// current instruction, computed from the last F_NEW frame |
|
// and the bytecode instructions in between (via calls to |
|
// CurrentFrame#execute). |
|
} |
|
visitFrame(currentBlock.frame); |
|
} |
|
} else if (type == Opcodes.F_NEW) { |
|
if (previousFrame == null) { |
|
visitImplicitFirstFrame(); |
|
} |
|
currentLocals = nLocal; |
|
int frameIndex = startFrame(code.length, nLocal, nStack); |
|
for (int i = 0; i < nLocal; ++i) { |
|
if (local[i] instanceof String) { |
|
String desc = Type.getObjectType((String) local[i]).getDescriptor(); |
|
frame[frameIndex++] = Frame.type(cw, desc); |
|
} else if (local[i] instanceof Integer) { |
|
frame[frameIndex++] = Frame.BASE | ((Integer) local[i]).intValue(); |
|
} else { |
|
frame[frameIndex++] = Frame.UNINITIALIZED |
|
| cw.addUninitializedType("", |
|
((Label) local[i]).position); |
|
} |
|
} |
|
for (int i = 0; i < nStack; ++i) { |
|
if (stack[i] instanceof String) { |
|
String desc = Type.getObjectType((String) stack[i]).getDescriptor(); |
|
frame[frameIndex++] = Frame.type(cw, desc); |
|
} else if (stack[i] instanceof Integer) { |
|
frame[frameIndex++] = Frame.BASE | ((Integer) stack[i]).intValue(); |
|
} else { |
|
frame[frameIndex++] = Frame.UNINITIALIZED |
|
| cw.addUninitializedType("", |
|
((Label) stack[i]).position); |
|
} |
|
} |
|
endFrame(); |
|
} else { |
|
int delta; |
|
if (stackMap == null) { |
|
stackMap = new ByteVector(); |
|
delta = code.length; |
|
} else { |
|
delta = code.length - previousFrameOffset - 1; |
|
if (delta < 0) { |
|
if (type == Opcodes.F_SAME) { |
|
return; |
|
} else { |
|
throw new IllegalStateException(); |
|
} |
|
} |
|
} |
|
|
|
switch (type) { |
|
case Opcodes.F_FULL: |
|
currentLocals = nLocal; |
|
stackMap.putByte(FULL_FRAME).putShort(delta).putShort(nLocal); |
|
for (int i = 0; i < nLocal; ++i) { |
|
writeFrameType(local[i]); |
|
} |
|
stackMap.putShort(nStack); |
|
for (int i = 0; i < nStack; ++i) { |
|
writeFrameType(stack[i]); |
|
} |
|
break; |
|
case Opcodes.F_APPEND: |
|
currentLocals += nLocal; |
|
stackMap.putByte(SAME_FRAME_EXTENDED + nLocal).putShort(delta); |
|
for (int i = 0; i < nLocal; ++i) { |
|
writeFrameType(local[i]); |
|
} |
|
break; |
|
case Opcodes.F_CHOP: |
|
currentLocals -= nLocal; |
|
stackMap.putByte(SAME_FRAME_EXTENDED - nLocal).putShort(delta); |
|
break; |
|
case Opcodes.F_SAME: |
|
if (delta < 64) { |
|
stackMap.putByte(delta); |
|
} else { |
|
stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta); |
|
} |
|
break; |
|
case Opcodes.F_SAME1: |
|
if (delta < 64) { |
|
stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); |
|
} else { |
|
stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) |
|
.putShort(delta); |
|
} |
|
writeFrameType(stack[0]); |
|
break; |
|
} |
|
|
|
previousFrameOffset = code.length; |
|
++frameCount; |
|
} |
|
|
|
maxStack = Math.max(maxStack, nStack); |
|
maxLocals = Math.max(maxLocals, currentLocals); |
|
} |
|
|
|
@Override |
|
public void visitInsn(final int opcode) { |
|
lastCodeOffset = code.length; |
|
|
|
code.putByte(opcode); |
|
// update currentBlock |
|
|
|
if (currentBlock != null) { |
|
if (compute == FRAMES || compute == INSERTED_FRAMES) { |
|
currentBlock.frame.execute(opcode, 0, null, null); |
|
} else { |
|
|
|
int size = stackSize + Frame.SIZE[opcode]; |
|
if (size > maxStackSize) { |
|
maxStackSize = size; |
|
} |
|
stackSize = size; |
|
} |
|
|
|
if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) |
|
|| opcode == Opcodes.ATHROW) { |
|
noSuccessor(); |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitIntInsn(final int opcode, final int operand) { |
|
lastCodeOffset = code.length; |
|
|
|
if (currentBlock != null) { |
|
if (compute == FRAMES || compute == INSERTED_FRAMES) { |
|
currentBlock.frame.execute(opcode, operand, null, null); |
|
} else if (opcode != Opcodes.NEWARRAY) { |
|
// updates current and max stack sizes only for NEWARRAY |
|
|
|
int size = stackSize + 1; |
|
if (size > maxStackSize) { |
|
maxStackSize = size; |
|
} |
|
stackSize = size; |
|
} |
|
} |
|
|
|
if (opcode == Opcodes.SIPUSH) { |
|
code.put12(opcode, operand); |
|
} else { |
|
code.put11(opcode, operand); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitVarInsn(final int opcode, final int var) { |
|
lastCodeOffset = code.length; |
|
|
|
if (currentBlock != null) { |
|
if (compute == FRAMES || compute == INSERTED_FRAMES) { |
|
currentBlock.frame.execute(opcode, var, null, null); |
|
} else { |
|
|
|
if (opcode == Opcodes.RET) { |
|
|
|
currentBlock.status |= Label.RET; |
|
// save 'stackSize' here for future use |
|
|
|
currentBlock.inputStackTop = stackSize; |
|
noSuccessor(); |
|
} else { |
|
int size = stackSize + Frame.SIZE[opcode]; |
|
if (size > maxStackSize) { |
|
maxStackSize = size; |
|
} |
|
stackSize = size; |
|
} |
|
} |
|
} |
|
if (compute != NOTHING) { |
|
|
|
int n; |
|
if (opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD |
|
|| opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE) { |
|
n = var + 2; |
|
} else { |
|
n = var + 1; |
|
} |
|
if (n > maxLocals) { |
|
maxLocals = n; |
|
} |
|
} |
|
|
|
if (var < 4 && opcode != Opcodes.RET) { |
|
int opt; |
|
if (opcode < Opcodes.ISTORE) { |
|
|
|
opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var; |
|
} else { |
|
|
|
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); |
|
} |
|
if (opcode >= Opcodes.ISTORE && compute == FRAMES && handlerCount > 0) { |
|
visitLabel(new Label()); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitTypeInsn(final int opcode, final String type) { |
|
lastCodeOffset = code.length; |
|
Item i = cw.newStringishItem(ClassWriter.CLASS, type); |
|
|
|
if (currentBlock != null) { |
|
if (compute == FRAMES || compute == INSERTED_FRAMES) { |
|
currentBlock.frame.execute(opcode, code.length, cw, i); |
|
} else if (opcode == Opcodes.NEW) { |
|
// updates current and max stack sizes only if opcode == NEW |
|
|
|
int size = stackSize + 1; |
|
if (size > maxStackSize) { |
|
maxStackSize = size; |
|
} |
|
stackSize = size; |
|
} |
|
} |
|
|
|
code.put12(opcode, i.index); |
|
} |
|
|
|
@Override |
|
public void visitFieldInsn(final int opcode, final String owner, |
|
final String name, final String desc) { |
|
lastCodeOffset = code.length; |
|
Item i = cw.newFieldItem(owner, name, desc); |
|
|
|
if (currentBlock != null) { |
|
if (compute == FRAMES || compute == INSERTED_FRAMES) { |
|
currentBlock.frame.execute(opcode, 0, cw, i); |
|
} else { |
|
int size; |
|
|
|
char c = desc.charAt(0); |
|
switch (opcode) { |
|
case Opcodes.GETSTATIC: |
|
size = stackSize + (c == 'D' || c == 'J' ? 2 : 1); |
|
break; |
|
case Opcodes.PUTSTATIC: |
|
size = stackSize + (c == 'D' || c == 'J' ? -2 : -1); |
|
break; |
|
case Opcodes.GETFIELD: |
|
size = stackSize + (c == 'D' || c == 'J' ? 1 : 0); |
|
break; |
|
|
|
default: |
|
size = stackSize + (c == 'D' || c == 'J' ? -3 : -2); |
|
break; |
|
} |
|
|
|
if (size > maxStackSize) { |
|
maxStackSize = size; |
|
} |
|
stackSize = size; |
|
} |
|
} |
|
|
|
code.put12(opcode, i.index); |
|
} |
|
|
|
@Override |
|
public void visitMethodInsn(final int opcode, final String owner, |
|
final String name, final String desc, final boolean itf) { |
|
lastCodeOffset = code.length; |
|
Item i = cw.newMethodItem(owner, name, desc, itf); |
|
int argSize = i.intVal; |
|
|
|
if (currentBlock != null) { |
|
if (compute == FRAMES || compute == INSERTED_FRAMES) { |
|
currentBlock.frame.execute(opcode, 0, cw, i); |
|
} else { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
if (argSize == 0) { |
|
// the above sizes have not been computed yet, |
|
|
|
argSize = Type.getArgumentsAndReturnSizes(desc); |
|
// ... and we save them in order |
|
|
|
i.intVal = argSize; |
|
} |
|
int size; |
|
if (opcode == Opcodes.INVOKESTATIC) { |
|
size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; |
|
} else { |
|
size = stackSize - (argSize >> 2) + (argSize & 0x03); |
|
} |
|
|
|
if (size > maxStackSize) { |
|
maxStackSize = size; |
|
} |
|
stackSize = size; |
|
} |
|
} |
|
|
|
if (opcode == Opcodes.INVOKEINTERFACE) { |
|
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); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitInvokeDynamicInsn(final String name, final String desc, |
|
final Handle bsm, final Object... bsmArgs) { |
|
lastCodeOffset = code.length; |
|
Item i = cw.newInvokeDynamicItem(name, desc, bsm, bsmArgs); |
|
int argSize = i.intVal; |
|
|
|
if (currentBlock != null) { |
|
if (compute == FRAMES || compute == INSERTED_FRAMES) { |
|
currentBlock.frame.execute(Opcodes.INVOKEDYNAMIC, 0, cw, i); |
|
} else { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
if (argSize == 0) { |
|
// the above sizes have not been computed yet, |
|
|
|
argSize = Type.getArgumentsAndReturnSizes(desc); |
|
// ... and we save them in order |
|
|
|
i.intVal = argSize; |
|
} |
|
int size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; |
|
|
|
|
|
if (size > maxStackSize) { |
|
maxStackSize = size; |
|
} |
|
stackSize = size; |
|
} |
|
} |
|
|
|
code.put12(Opcodes.INVOKEDYNAMIC, i.index); |
|
code.putShort(0); |
|
} |
|
|
|
@Override |
|
public void visitJumpInsn(int opcode, final Label label) { |
|
boolean isWide = opcode >= 200; |
|
opcode = isWide ? opcode - 33 : opcode; |
|
lastCodeOffset = code.length; |
|
Label nextInsn = null; |
|
|
|
if (currentBlock != null) { |
|
if (compute == FRAMES) { |
|
currentBlock.frame.execute(opcode, 0, null, null); |
|
|
|
label.getFirst().status |= Label.TARGET; |
|
|
|
addSuccessor(Edge.NORMAL, label); |
|
if (opcode != Opcodes.GOTO) { |
|
|
|
nextInsn = new Label(); |
|
} |
|
} else if (compute == INSERTED_FRAMES) { |
|
currentBlock.frame.execute(opcode, 0, null, null); |
|
} else { |
|
if (opcode == Opcodes.JSR) { |
|
if ((label.status & Label.SUBROUTINE) == 0) { |
|
label.status |= Label.SUBROUTINE; |
|
++subroutines; |
|
} |
|
currentBlock.status |= Label.JSR; |
|
addSuccessor(stackSize + 1, label); |
|
|
|
nextInsn = new Label(); |
|
/* |
|
* note that, by construction in this method, a JSR block |
|
* has at least two successors in the control flow graph: |
|
* the first one leads the next instruction after the JSR, |
|
* while the second one leads to the JSR target. |
|
*/ |
|
} else { |
|
// updates current stack size (max stack size unchanged |
|
// because stack size variation always negative in this |
|
|
|
stackSize += Frame.SIZE[opcode]; |
|
addSuccessor(stackSize, label); |
|
} |
|
} |
|
} |
|
|
|
if ((label.status & Label.RESOLVED) != 0 |
|
&& label.position - code.length < Short.MIN_VALUE) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
if (opcode == Opcodes.GOTO) { |
|
code.putByte(200); |
|
} else if (opcode == Opcodes.JSR) { |
|
code.putByte(201); |
|
} else { |
|
// if the IF instruction is transformed into IFNOT GOTO_W the |
|
|
|
if (nextInsn != null) { |
|
nextInsn.status |= Label.TARGET; |
|
} |
|
code.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1 |
|
: opcode ^ 1); |
|
code.putShort(8); |
|
// ASM pseudo GOTO_W insn, see ClassReader. We don't use a real |
|
// GOTO_W because we might need to insert a frame just after (as |
|
|
|
code.putByte(220); |
|
cw.hasAsmInsns = true; |
|
} |
|
label.put(this, code, code.length - 1, true); |
|
} else if (isWide) { |
|
|
|
|
|
|
|
|
|
*/ |
|
code.putByte(opcode + 33); |
|
label.put(this, code, code.length - 1, true); |
|
} else { |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
code.putByte(opcode); |
|
label.put(this, code, code.length - 1, false); |
|
} |
|
if (currentBlock != null) { |
|
if (nextInsn != null) { |
|
// if the jump instruction is not a GOTO, the next instruction |
|
// is also a successor of this instruction. Calling visitLabel |
|
// adds the label of this next instruction as a successor of the |
|
|
|
visitLabel(nextInsn); |
|
} |
|
if (opcode == Opcodes.GOTO) { |
|
noSuccessor(); |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public void visitLabel(final Label label) { |
|
|
|
cw.hasAsmInsns |= label.resolve(this, code.length, code.data); |
|
|
|
if ((label.status & Label.DEBUG) != 0) { |
|
return; |
|
} |
|
if (compute == FRAMES) { |
|
if (currentBlock != null) { |
|
if (label.position == currentBlock.position) { |
|
|
|
currentBlock.status |= (label.status & Label.TARGET); |
|
label.frame = currentBlock.frame; |
|
return; |
|
} |
|
|
|
addSuccessor(Edge.NORMAL, label); |
|
} |
|
|
|
currentBlock = label; |
|
if (label.frame == null) { |
|
label.frame = new Frame(); |
|
label.frame.owner = label; |
|
} |
|
|
|
if (previousBlock != null) { |
|
if (label.position == previousBlock.position) { |
|
previousBlock.status |= (label.status & Label.TARGET); |
|
label.frame = previousBlock.frame; |
|
currentBlock = previousBlock; |
|
return; |
|
} |
|
previousBlock.successor = label; |
|
} |
|
previousBlock = label; |
|
} else if (compute == INSERTED_FRAMES) { |
|
if (currentBlock == null) { |
|
// This case should happen only once, for the visitLabel call in |
|
// the constructor. Indeed, if compute is equal to |
|
// INSERTED_FRAMES currentBlock can not be set back to null (see |
|
|
|
currentBlock = label; |
|
} else { |
|
// Updates the frame owner so that a correct frame offset is |
|
|
|
currentBlock.frame.owner = label; |
|
} |
|
} else if (compute == MAXS) { |
|
if (currentBlock != null) { |
|
|
|
currentBlock.outputStackMax = maxStackSize; |
|
addSuccessor(stackSize, label); |
|
} |
|
|
|
currentBlock = label; |
|
|
|
stackSize = 0; |
|
maxStackSize = 0; |
|
|
|
if (previousBlock != null) { |
|
previousBlock.successor = label; |
|
} |
|
previousBlock = label; |
|
} |
|
} |
|
|
|
@Override |
|
public void visitLdcInsn(final Object cst) { |
|
lastCodeOffset = code.length; |
|
Item i = cw.newConstItem(cst); |
|
|
|
if (currentBlock != null) { |
|
if (compute == FRAMES || compute == INSERTED_FRAMES) { |
|
currentBlock.frame.execute(Opcodes.LDC, 0, cw, i); |
|
} else { |
|
int size; |
|
|
|
if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) { |
|
size = stackSize + 2; |
|
} else { |
|
size = stackSize + 1; |
|
} |
|
|
|
if (size > maxStackSize) { |
|
maxStackSize = size; |
|
} |
|
stackSize = size; |
|
} |
|
} |
|
|
|
int index = i.index; |
|
if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) { |
|
code.put12(20 , index); |
|
} else if (index >= 256) { |
|
code.put12(19 , index); |
|
} else { |
|
code.put11(Opcodes.LDC, index); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitIincInsn(final int var, final int increment) { |
|
lastCodeOffset = code.length; |
|
if (currentBlock != null) { |
|
if (compute == FRAMES || compute == INSERTED_FRAMES) { |
|
currentBlock.frame.execute(Opcodes.IINC, var, null, null); |
|
} |
|
} |
|
if (compute != NOTHING) { |
|
|
|
int n = var + 1; |
|
if (n > maxLocals) { |
|
maxLocals = n; |
|
} |
|
} |
|
|
|
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); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitTableSwitchInsn(final int min, final int max, |
|
final Label dflt, final Label... labels) { |
|
lastCodeOffset = code.length; |
|
|
|
int source = code.length; |
|
code.putByte(Opcodes.TABLESWITCH); |
|
code.putByteArray(null, 0, (4 - code.length % 4) % 4); |
|
dflt.put(this, code, source, true); |
|
code.putInt(min).putInt(max); |
|
for (int i = 0; i < labels.length; ++i) { |
|
labels[i].put(this, code, source, true); |
|
} |
|
|
|
visitSwitchInsn(dflt, labels); |
|
} |
|
|
|
@Override |
|
public void visitLookupSwitchInsn(final Label dflt, final int[] keys, |
|
final Label[] labels) { |
|
lastCodeOffset = code.length; |
|
|
|
int source = code.length; |
|
code.putByte(Opcodes.LOOKUPSWITCH); |
|
code.putByteArray(null, 0, (4 - code.length % 4) % 4); |
|
dflt.put(this, code, source, true); |
|
code.putInt(labels.length); |
|
for (int i = 0; i < labels.length; ++i) { |
|
code.putInt(keys[i]); |
|
labels[i].put(this, code, source, true); |
|
} |
|
|
|
visitSwitchInsn(dflt, labels); |
|
} |
|
|
|
private void visitSwitchInsn(final Label dflt, final Label[] labels) { |
|
|
|
if (currentBlock != null) { |
|
if (compute == FRAMES) { |
|
currentBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null); |
|
|
|
addSuccessor(Edge.NORMAL, dflt); |
|
dflt.getFirst().status |= Label.TARGET; |
|
for (int i = 0; i < labels.length; ++i) { |
|
addSuccessor(Edge.NORMAL, labels[i]); |
|
labels[i].getFirst().status |= Label.TARGET; |
|
} |
|
} else { |
|
|
|
--stackSize; |
|
|
|
addSuccessor(stackSize, dflt); |
|
for (int i = 0; i < labels.length; ++i) { |
|
addSuccessor(stackSize, labels[i]); |
|
} |
|
} |
|
|
|
noSuccessor(); |
|
} |
|
} |
|
|
|
@Override |
|
public void visitMultiANewArrayInsn(final String desc, final int dims) { |
|
lastCodeOffset = code.length; |
|
Item i = cw.newStringishItem(ClassWriter.CLASS, desc); |
|
|
|
if (currentBlock != null) { |
|
if (compute == FRAMES || compute == INSERTED_FRAMES) { |
|
currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i); |
|
} else { |
|
// updates current stack size (max stack size unchanged because |
|
|
|
stackSize += 1 - dims; |
|
} |
|
} |
|
|
|
code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims); |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitInsnAnnotation(int typeRef, |
|
TypePath typePath, String desc, boolean visible) { |
|
ByteVector bv = new ByteVector(); |
|
|
|
typeRef = (typeRef & 0xFF0000FF) | (lastCodeOffset << 8); |
|
AnnotationWriter.putTarget(typeRef, typePath, bv); |
|
|
|
bv.putShort(cw.newUTF8(desc)).putShort(0); |
|
AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, |
|
bv.length - 2); |
|
if (visible) { |
|
aw.next = ctanns; |
|
ctanns = aw; |
|
} else { |
|
aw.next = ictanns; |
|
ictanns = aw; |
|
} |
|
return aw; |
|
} |
|
|
|
@Override |
|
public void visitTryCatchBlock(final Label start, final Label end, |
|
final Label handler, final String type) { |
|
++handlerCount; |
|
Handler h = new Handler(); |
|
h.start = start; |
|
h.end = end; |
|
h.handler = handler; |
|
h.desc = type; |
|
h.type = type != null ? cw.newClass(type) : 0; |
|
if (lastHandler == null) { |
|
firstHandler = h; |
|
} else { |
|
lastHandler.next = h; |
|
} |
|
lastHandler = h; |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitTryCatchAnnotation(int typeRef, |
|
TypePath typePath, String desc, boolean visible) { |
|
ByteVector bv = new ByteVector(); |
|
|
|
AnnotationWriter.putTarget(typeRef, typePath, bv); |
|
|
|
bv.putShort(cw.newUTF8(desc)).putShort(0); |
|
AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, |
|
bv.length - 2); |
|
if (visible) { |
|
aw.next = ctanns; |
|
ctanns = aw; |
|
} else { |
|
aw.next = ictanns; |
|
ictanns = aw; |
|
} |
|
return aw; |
|
} |
|
|
|
@Override |
|
public void visitLocalVariable(final String name, final String desc, |
|
final String signature, final Label start, final Label end, |
|
final int index) { |
|
if (signature != null) { |
|
if (localVarType == null) { |
|
localVarType = new ByteVector(); |
|
} |
|
++localVarTypeCount; |
|
localVarType.putShort(start.position) |
|
.putShort(end.position - start.position) |
|
.putShort(cw.newUTF8(name)).putShort(cw.newUTF8(signature)) |
|
.putShort(index); |
|
} |
|
if (localVar == null) { |
|
localVar = new ByteVector(); |
|
} |
|
++localVarCount; |
|
localVar.putShort(start.position) |
|
.putShort(end.position - start.position) |
|
.putShort(cw.newUTF8(name)).putShort(cw.newUTF8(desc)) |
|
.putShort(index); |
|
if (compute != NOTHING) { |
|
|
|
char c = desc.charAt(0); |
|
int n = index + (c == 'J' || c == 'D' ? 2 : 1); |
|
if (n > maxLocals) { |
|
maxLocals = n; |
|
} |
|
} |
|
} |
|
|
|
@Override |
|
public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, |
|
TypePath typePath, Label[] start, Label[] end, int[] index, |
|
String desc, boolean visible) { |
|
ByteVector bv = new ByteVector(); |
|
|
|
bv.putByte(typeRef >>> 24).putShort(start.length); |
|
for (int i = 0; i < start.length; ++i) { |
|
bv.putShort(start[i].position) |
|
.putShort(end[i].position - start[i].position) |
|
.putShort(index[i]); |
|
} |
|
if (typePath == null) { |
|
bv.putByte(0); |
|
} else { |
|
int length = typePath.b[typePath.offset] * 2 + 1; |
|
bv.putByteArray(typePath.b, typePath.offset, length); |
|
} |
|
|
|
bv.putShort(cw.newUTF8(desc)).putShort(0); |
|
AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, |
|
bv.length - 2); |
|
if (visible) { |
|
aw.next = ctanns; |
|
ctanns = aw; |
|
} else { |
|
aw.next = ictanns; |
|
ictanns = aw; |
|
} |
|
return aw; |
|
} |
|
|
|
@Override |
|
public void visitLineNumber(final int line, final Label start) { |
|
if (lineNumber == null) { |
|
lineNumber = new ByteVector(); |
|
} |
|
++lineNumberCount; |
|
lineNumber.putShort(start.position); |
|
lineNumber.putShort(line); |
|
} |
|
|
|
@Override |
|
public void visitMaxs(final int maxStack, final int maxLocals) { |
|
if (compute == FRAMES) { |
|
|
|
Handler handler = firstHandler; |
|
while (handler != null) { |
|
Label l = handler.start.getFirst(); |
|
Label h = handler.handler.getFirst(); |
|
Label e = handler.end.getFirst(); |
|
|
|
String t = handler.desc == null ? "java/lang/Throwable" |
|
: handler.desc; |
|
int kind = Frame.OBJECT | cw.addType(t); |
|
|
|
h.status |= Label.TARGET; |
|
|
|
while (l != e) { |
|
|
|
Edge b = new Edge(); |
|
b.info = kind; |
|
b.successor = h; |
|
|
|
b.next = l.successors; |
|
l.successors = b; |
|
|
|
l = l.successor; |
|
} |
|
handler = handler.next; |
|
} |
|
|
|
|
|
Frame f = labels.frame; |
|
f.initInputFrame(cw, access, Type.getArgumentTypes(descriptor), |
|
this.maxLocals); |
|
visitFrame(f); |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
int max = 0; |
|
Label changed = labels; |
|
while (changed != null) { |
|
|
|
Label l = changed; |
|
changed = changed.next; |
|
l.next = null; |
|
f = l.frame; |
|
|
|
if ((l.status & Label.TARGET) != 0) { |
|
l.status |= Label.STORE; |
|
} |
|
|
|
l.status |= Label.REACHABLE; |
|
|
|
int blockMax = f.inputStack.length + l.outputStackMax; |
|
if (blockMax > max) { |
|
max = blockMax; |
|
} |
|
|
|
Edge e = l.successors; |
|
while (e != null) { |
|
Label n = e.successor.getFirst(); |
|
boolean change = f.merge(cw, n.frame, e.info); |
|
if (change && n.next == null) { |
|
// if n has changed and is not already in the 'changed' |
|
|
|
n.next = changed; |
|
changed = n; |
|
} |
|
e = e.next; |
|
} |
|
} |
|
|
|
|
|
Label l = labels; |
|
while (l != null) { |
|
f = l.frame; |
|
if ((l.status & Label.STORE) != 0) { |
|
visitFrame(f); |
|
} |
|
if ((l.status & Label.REACHABLE) == 0) { |
|
|
|
Label k = l.successor; |
|
int start = l.position; |
|
int end = (k == null ? code.length : k.position) - 1; |
|
|
|
if (end >= start) { |
|
max = Math.max(max, 1); |
|
|
|
for (int i = start; i < end; ++i) { |
|
code.data[i] = Opcodes.NOP; |
|
} |
|
code.data[end] = (byte) Opcodes.ATHROW; |
|
|
|
int frameIndex = startFrame(start, 0, 1); |
|
frame[frameIndex] = Frame.OBJECT |
|
| cw.addType("java/lang/Throwable"); |
|
endFrame(); |
|
// removes the start-end range from the exception |
|
|
|
firstHandler = Handler.remove(firstHandler, l, k); |
|
} |
|
} |
|
l = l.successor; |
|
} |
|
|
|
handler = firstHandler; |
|
handlerCount = 0; |
|
while (handler != null) { |
|
handlerCount += 1; |
|
handler = handler.next; |
|
} |
|
|
|
this.maxStack = max; |
|
} else if (compute == MAXS) { |
|
|
|
Handler handler = firstHandler; |
|
while (handler != null) { |
|
Label l = handler.start; |
|
Label h = handler.handler; |
|
Label e = handler.end; |
|
|
|
while (l != e) { |
|
|
|
Edge b = new Edge(); |
|
b.info = Edge.EXCEPTION; |
|
b.successor = h; |
|
|
|
if ((l.status & Label.JSR) == 0) { |
|
b.next = l.successors; |
|
l.successors = b; |
|
} else { |
|
// if l is a JSR block, adds b after the first two edges |
|
// to preserve the hypothesis about JSR block successors |
|
|
|
b.next = l.successors.next.next; |
|
l.successors.next.next = b; |
|
} |
|
|
|
l = l.successor; |
|
} |
|
handler = handler.next; |
|
} |
|
|
|
if (subroutines > 0) { |
|
// completes the control flow graph with the RET successors |
|
/* |
|
* first step: finds the subroutines. This step determines, for |
|
* each basic block, to which subroutine(s) it belongs. |
|
*/ |
|
|
|
int id = 0; |
|
labels.visitSubroutine(null, 1, subroutines); |
|
|
|
Label l = labels; |
|
while (l != null) { |
|
if ((l.status & Label.JSR) != 0) { |
|
|
|
Label subroutine = l.successors.next.successor; |
|
|
|
if ((subroutine.status & Label.VISITED) == 0) { |
|
|
|
id += 1; |
|
subroutine.visitSubroutine(null, (id / 32L) << 32 |
|
| (1L << (id % 32)), subroutines); |
|
} |
|
} |
|
l = l.successor; |
|
} |
|
|
|
l = labels; |
|
while (l != null) { |
|
if ((l.status & Label.JSR) != 0) { |
|
Label L = labels; |
|
while (L != null) { |
|
L.status &= ~Label.VISITED2; |
|
L = L.successor; |
|
} |
|
|
|
Label subroutine = l.successors.next.successor; |
|
subroutine.visitSubroutine(l, 0, subroutines); |
|
} |
|
l = l.successor; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
int max = 0; |
|
Label stack = labels; |
|
while (stack != null) { |
|
|
|
Label l = stack; |
|
stack = stack.next; |
|
|
|
int start = l.inputStackTop; |
|
int blockMax = start + l.outputStackMax; |
|
|
|
if (blockMax > max) { |
|
max = blockMax; |
|
} |
|
|
|
Edge b = l.successors; |
|
if ((l.status & Label.JSR) != 0) { |
|
|
|
b = b.next; |
|
} |
|
while (b != null) { |
|
l = b.successor; |
|
|
|
if ((l.status & Label.PUSHED) == 0) { |
|
|
|
l.inputStackTop = b.info == Edge.EXCEPTION ? 1 : start |
|
+ b.info; |
|
|
|
l.status |= Label.PUSHED; |
|
l.next = stack; |
|
stack = l; |
|
} |
|
b = b.next; |
|
} |
|
} |
|
this.maxStack = Math.max(maxStack, max); |
|
} else { |
|
this.maxStack = maxStack; |
|
this.maxLocals = maxLocals; |
|
} |
|
} |
|
|
|
@Override |
|
public void visitEnd() { |
|
} |
|
|
|
// ------------------------------------------------------------------------ |
|
// Utility methods: control flow analysis algorithm |
|
// ------------------------------------------------------------------------ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void addSuccessor(final int info, final Label successor) { |
|
|
|
Edge b = new Edge(); |
|
b.info = info; |
|
b.successor = successor; |
|
|
|
b.next = currentBlock.successors; |
|
currentBlock.successors = b; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private void noSuccessor() { |
|
if (compute == FRAMES) { |
|
Label l = new Label(); |
|
l.frame = new Frame(); |
|
l.frame.owner = l; |
|
l.resolve(this, code.length, code.data); |
|
previousBlock.successor = l; |
|
previousBlock = l; |
|
} else { |
|
currentBlock.outputStackMax = maxStackSize; |
|
} |
|
if (compute != INSERTED_FRAMES) { |
|
currentBlock = null; |
|
} |
|
} |
|
|
|
// ------------------------------------------------------------------------ |
|
// Utility methods: stack map frames |
|
// ------------------------------------------------------------------------ |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void visitFrame(final Frame f) { |
|
int i, t; |
|
int nTop = 0; |
|
int nLocal = 0; |
|
int nStack = 0; |
|
int[] locals = f.inputLocals; |
|
int[] stacks = f.inputStack; |
|
// computes the number of locals (ignores TOP types that are just after |
|
|
|
for (i = 0; i < locals.length; ++i) { |
|
t = locals[i]; |
|
if (t == Frame.TOP) { |
|
++nTop; |
|
} else { |
|
nLocal += nTop + 1; |
|
nTop = 0; |
|
} |
|
if (t == Frame.LONG || t == Frame.DOUBLE) { |
|
++i; |
|
} |
|
} |
|
// computes the stack size (ignores TOP types that are just after |
|
|
|
for (i = 0; i < stacks.length; ++i) { |
|
t = stacks[i]; |
|
++nStack; |
|
if (t == Frame.LONG || t == Frame.DOUBLE) { |
|
++i; |
|
} |
|
} |
|
|
|
int frameIndex = startFrame(f.owner.position, nLocal, nStack); |
|
for (i = 0; nLocal > 0; ++i, --nLocal) { |
|
t = locals[i]; |
|
frame[frameIndex++] = t; |
|
if (t == Frame.LONG || t == Frame.DOUBLE) { |
|
++i; |
|
} |
|
} |
|
for (i = 0; i < stacks.length; ++i) { |
|
t = stacks[i]; |
|
frame[frameIndex++] = t; |
|
if (t == Frame.LONG || t == Frame.DOUBLE) { |
|
++i; |
|
} |
|
} |
|
endFrame(); |
|
} |
|
|
|
|
|
|
|
*/ |
|
private void visitImplicitFirstFrame() { |
|
|
|
int frameIndex = startFrame(0, descriptor.length() + 1, 0); |
|
if ((access & Opcodes.ACC_STATIC) == 0) { |
|
if ((access & ACC_CONSTRUCTOR) == 0) { |
|
frame[frameIndex++] = Frame.OBJECT | cw.addType(cw.thisName); |
|
} else { |
|
frame[frameIndex++] = Frame.UNINITIALIZED_THIS; |
|
} |
|
} |
|
int i = 1; |
|
loop: while (true) { |
|
int j = i; |
|
switch (descriptor.charAt(i++)) { |
|
case 'Z': |
|
case 'C': |
|
case 'B': |
|
case 'S': |
|
case 'I': |
|
frame[frameIndex++] = Frame.INTEGER; |
|
break; |
|
case 'F': |
|
frame[frameIndex++] = Frame.FLOAT; |
|
break; |
|
case 'J': |
|
frame[frameIndex++] = Frame.LONG; |
|
break; |
|
case 'D': |
|
frame[frameIndex++] = Frame.DOUBLE; |
|
break; |
|
case '[': |
|
while (descriptor.charAt(i) == '[') { |
|
++i; |
|
} |
|
if (descriptor.charAt(i) == 'L') { |
|
++i; |
|
while (descriptor.charAt(i) != ';') { |
|
++i; |
|
} |
|
} |
|
frame[frameIndex++] = Frame.type(cw, descriptor.substring(j, ++i)); |
|
break; |
|
case 'L': |
|
while (descriptor.charAt(i) != ';') { |
|
++i; |
|
} |
|
frame[frameIndex++] = Frame.OBJECT |
|
| cw.addType(descriptor.substring(j + 1, i++)); |
|
break; |
|
default: |
|
break loop; |
|
} |
|
} |
|
frame[1] = frameIndex - 3; |
|
endFrame(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private int startFrame(final int offset, final int nLocal, final int nStack) { |
|
int n = 3 + nLocal + nStack; |
|
if (frame == null || frame.length < n) { |
|
frame = new int[n]; |
|
} |
|
frame[0] = offset; |
|
frame[1] = nLocal; |
|
frame[2] = nStack; |
|
return 3; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private void endFrame() { |
|
if (previousFrame != null) { |
|
if (stackMap == null) { |
|
stackMap = new ByteVector(); |
|
} |
|
writeFrame(); |
|
++frameCount; |
|
} |
|
previousFrame = frame; |
|
frame = null; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private void writeFrame() { |
|
int clocalsSize = frame[1]; |
|
int cstackSize = frame[2]; |
|
if ((cw.version & 0xFFFF) < Opcodes.V1_6) { |
|
stackMap.putShort(frame[0]).putShort(clocalsSize); |
|
writeFrameTypes(3, 3 + clocalsSize); |
|
stackMap.putShort(cstackSize); |
|
writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize); |
|
return; |
|
} |
|
int localsSize = previousFrame[1]; |
|
int type = FULL_FRAME; |
|
int k = 0; |
|
int delta; |
|
if (frameCount == 0) { |
|
delta = frame[0]; |
|
} else { |
|
delta = frame[0] - previousFrame[0] - 1; |
|
} |
|
if (cstackSize == 0) { |
|
k = clocalsSize - localsSize; |
|
switch (k) { |
|
case -3: |
|
case -2: |
|
case -1: |
|
type = CHOP_FRAME; |
|
localsSize = clocalsSize; |
|
break; |
|
case 0: |
|
type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED; |
|
break; |
|
case 1: |
|
case 2: |
|
case 3: |
|
type = APPEND_FRAME; |
|
break; |
|
} |
|
} else if (clocalsSize == localsSize && cstackSize == 1) { |
|
type = delta < 63 ? SAME_LOCALS_1_STACK_ITEM_FRAME |
|
: SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED; |
|
} |
|
if (type != FULL_FRAME) { |
|
|
|
int l = 3; |
|
for (int j = 0; j < localsSize; j++) { |
|
if (frame[l] != previousFrame[l]) { |
|
type = FULL_FRAME; |
|
break; |
|
} |
|
l++; |
|
} |
|
} |
|
switch (type) { |
|
case SAME_FRAME: |
|
stackMap.putByte(delta); |
|
break; |
|
case SAME_LOCALS_1_STACK_ITEM_FRAME: |
|
stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); |
|
writeFrameTypes(3 + clocalsSize, 4 + clocalsSize); |
|
break; |
|
case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED: |
|
stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED).putShort( |
|
delta); |
|
writeFrameTypes(3 + clocalsSize, 4 + clocalsSize); |
|
break; |
|
case SAME_FRAME_EXTENDED: |
|
stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta); |
|
break; |
|
case CHOP_FRAME: |
|
stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta); |
|
break; |
|
case APPEND_FRAME: |
|
stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta); |
|
writeFrameTypes(3 + localsSize, 3 + clocalsSize); |
|
break; |
|
|
|
default: |
|
stackMap.putByte(FULL_FRAME).putShort(delta).putShort(clocalsSize); |
|
writeFrameTypes(3, 3 + clocalsSize); |
|
stackMap.putShort(cstackSize); |
|
writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private void writeFrameTypes(final int start, final int end) { |
|
for (int i = start; i < end; ++i) { |
|
int t = frame[i]; |
|
int d = t & Frame.DIM; |
|
if (d == 0) { |
|
int v = t & Frame.BASE_VALUE; |
|
switch (t & Frame.BASE_KIND) { |
|
case Frame.OBJECT: |
|
stackMap.putByte(7).putShort( |
|
cw.newClass(cw.typeTable[v].strVal1)); |
|
break; |
|
case Frame.UNINITIALIZED: |
|
stackMap.putByte(8).putShort(cw.typeTable[v].intVal); |
|
break; |
|
default: |
|
stackMap.putByte(v); |
|
} |
|
} else { |
|
StringBuilder sb = new StringBuilder(); |
|
d >>= 28; |
|
while (d-- > 0) { |
|
sb.append('['); |
|
} |
|
if ((t & Frame.BASE_KIND) == Frame.OBJECT) { |
|
sb.append('L'); |
|
sb.append(cw.typeTable[t & Frame.BASE_VALUE].strVal1); |
|
sb.append(';'); |
|
} else { |
|
switch (t & 0xF) { |
|
case 1: |
|
sb.append('I'); |
|
break; |
|
case 2: |
|
sb.append('F'); |
|
break; |
|
case 3: |
|
sb.append('D'); |
|
break; |
|
case 9: |
|
sb.append('Z'); |
|
break; |
|
case 10: |
|
sb.append('B'); |
|
break; |
|
case 11: |
|
sb.append('C'); |
|
break; |
|
case 12: |
|
sb.append('S'); |
|
break; |
|
default: |
|
sb.append('J'); |
|
} |
|
} |
|
stackMap.putByte(7).putShort(cw.newClass(sb.toString())); |
|
} |
|
} |
|
} |
|
|
|
private void writeFrameType(final Object type) { |
|
if (type instanceof String) { |
|
stackMap.putByte(7).putShort(cw.newClass((String) type)); |
|
} else if (type instanceof Integer) { |
|
stackMap.putByte(((Integer) type).intValue()); |
|
} else { |
|
stackMap.putByte(8).putShort(((Label) type).position); |
|
} |
|
} |
|
|
|
// ------------------------------------------------------------------------ |
|
// Utility methods: dump bytecode array |
|
// ------------------------------------------------------------------------ |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
final int getSize() { |
|
if (classReaderOffset != 0) { |
|
return 6 + classReaderLength; |
|
} |
|
int size = 8; |
|
if (code.length > 0) { |
|
if (code.length > 65535) { |
|
throw new RuntimeException("Method code too large!"); |
|
} |
|
cw.newUTF8("Code"); |
|
size += 18 + code.length + 8 * handlerCount; |
|
if (localVar != null) { |
|
cw.newUTF8("LocalVariableTable"); |
|
size += 8 + localVar.length; |
|
} |
|
if (localVarType != null) { |
|
cw.newUTF8("LocalVariableTypeTable"); |
|
size += 8 + localVarType.length; |
|
} |
|
if (lineNumber != null) { |
|
cw.newUTF8("LineNumberTable"); |
|
size += 8 + lineNumber.length; |
|
} |
|
if (stackMap != null) { |
|
boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; |
|
cw.newUTF8(zip ? "StackMapTable" : "StackMap"); |
|
size += 8 + stackMap.length; |
|
} |
|
if (ctanns != null) { |
|
cw.newUTF8("RuntimeVisibleTypeAnnotations"); |
|
size += 8 + ctanns.getSize(); |
|
} |
|
if (ictanns != null) { |
|
cw.newUTF8("RuntimeInvisibleTypeAnnotations"); |
|
size += 8 + ictanns.getSize(); |
|
} |
|
if (cattrs != null) { |
|
size += cattrs.getSize(cw, code.data, code.length, maxStack, |
|
maxLocals); |
|
} |
|
} |
|
if (exceptionCount > 0) { |
|
cw.newUTF8("Exceptions"); |
|
size += 8 + 2 * exceptionCount; |
|
} |
|
if ((access & Opcodes.ACC_SYNTHETIC) != 0) { |
|
if ((cw.version & 0xFFFF) < Opcodes.V1_5 |
|
|| (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { |
|
cw.newUTF8("Synthetic"); |
|
size += 6; |
|
} |
|
} |
|
if ((access & Opcodes.ACC_DEPRECATED) != 0) { |
|
cw.newUTF8("Deprecated"); |
|
size += 6; |
|
} |
|
if (signature != null) { |
|
cw.newUTF8("Signature"); |
|
cw.newUTF8(signature); |
|
size += 8; |
|
} |
|
if (methodParameters != null) { |
|
cw.newUTF8("MethodParameters"); |
|
size += 7 + methodParameters.length; |
|
} |
|
if (annd != null) { |
|
cw.newUTF8("AnnotationDefault"); |
|
size += 6 + annd.length; |
|
} |
|
if (anns != null) { |
|
cw.newUTF8("RuntimeVisibleAnnotations"); |
|
size += 8 + anns.getSize(); |
|
} |
|
if (ianns != null) { |
|
cw.newUTF8("RuntimeInvisibleAnnotations"); |
|
size += 8 + ianns.getSize(); |
|
} |
|
if (tanns != null) { |
|
cw.newUTF8("RuntimeVisibleTypeAnnotations"); |
|
size += 8 + tanns.getSize(); |
|
} |
|
if (itanns != null) { |
|
cw.newUTF8("RuntimeInvisibleTypeAnnotations"); |
|
size += 8 + itanns.getSize(); |
|
} |
|
if (panns != null) { |
|
cw.newUTF8("RuntimeVisibleParameterAnnotations"); |
|
size += 7 + 2 * (panns.length - synthetics); |
|
for (int i = panns.length - 1; i >= synthetics; --i) { |
|
size += panns[i] == null ? 0 : panns[i].getSize(); |
|
} |
|
} |
|
if (ipanns != null) { |
|
cw.newUTF8("RuntimeInvisibleParameterAnnotations"); |
|
size += 7 + 2 * (ipanns.length - synthetics); |
|
for (int i = ipanns.length - 1; i >= synthetics; --i) { |
|
size += ipanns[i] == null ? 0 : ipanns[i].getSize(); |
|
} |
|
} |
|
if (attrs != null) { |
|
size += attrs.getSize(cw, null, 0, -1, -1); |
|
} |
|
return size; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
final void put(final ByteVector out) { |
|
final int FACTOR = ClassWriter.TO_ACC_SYNTHETIC; |
|
int mask = ACC_CONSTRUCTOR | Opcodes.ACC_DEPRECATED |
|
| ClassWriter.ACC_SYNTHETIC_ATTRIBUTE |
|
| ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / FACTOR); |
|
out.putShort(access & ~mask).putShort(name).putShort(desc); |
|
if (classReaderOffset != 0) { |
|
out.putByteArray(cw.cr.b, classReaderOffset, classReaderLength); |
|
return; |
|
} |
|
int attributeCount = 0; |
|
if (code.length > 0) { |
|
++attributeCount; |
|
} |
|
if (exceptionCount > 0) { |
|
++attributeCount; |
|
} |
|
if ((access & Opcodes.ACC_SYNTHETIC) != 0) { |
|
if ((cw.version & 0xFFFF) < Opcodes.V1_5 |
|
|| (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { |
|
++attributeCount; |
|
} |
|
} |
|
if ((access & Opcodes.ACC_DEPRECATED) != 0) { |
|
++attributeCount; |
|
} |
|
if (signature != null) { |
|
++attributeCount; |
|
} |
|
if (methodParameters != null) { |
|
++attributeCount; |
|
} |
|
if (annd != null) { |
|
++attributeCount; |
|
} |
|
if (anns != null) { |
|
++attributeCount; |
|
} |
|
if (ianns != null) { |
|
++attributeCount; |
|
} |
|
if (tanns != null) { |
|
++attributeCount; |
|
} |
|
if (itanns != null) { |
|
++attributeCount; |
|
} |
|
if (panns != null) { |
|
++attributeCount; |
|
} |
|
if (ipanns != null) { |
|
++attributeCount; |
|
} |
|
if (attrs != null) { |
|
attributeCount += attrs.getCount(); |
|
} |
|
out.putShort(attributeCount); |
|
if (code.length > 0) { |
|
int size = 12 + code.length + 8 * handlerCount; |
|
if (localVar != null) { |
|
size += 8 + localVar.length; |
|
} |
|
if (localVarType != null) { |
|
size += 8 + localVarType.length; |
|
} |
|
if (lineNumber != null) { |
|
size += 8 + lineNumber.length; |
|
} |
|
if (stackMap != null) { |
|
size += 8 + stackMap.length; |
|
} |
|
if (ctanns != null) { |
|
size += 8 + ctanns.getSize(); |
|
} |
|
if (ictanns != null) { |
|
size += 8 + ictanns.getSize(); |
|
} |
|
if (cattrs != null) { |
|
size += cattrs.getSize(cw, code.data, code.length, maxStack, |
|
maxLocals); |
|
} |
|
out.putShort(cw.newUTF8("Code")).putInt(size); |
|
out.putShort(maxStack).putShort(maxLocals); |
|
out.putInt(code.length).putByteArray(code.data, 0, code.length); |
|
out.putShort(handlerCount); |
|
if (handlerCount > 0) { |
|
Handler h = firstHandler; |
|
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 (localVar != null) { |
|
++attributeCount; |
|
} |
|
if (localVarType != null) { |
|
++attributeCount; |
|
} |
|
if (lineNumber != null) { |
|
++attributeCount; |
|
} |
|
if (stackMap != null) { |
|
++attributeCount; |
|
} |
|
if (ctanns != null) { |
|
++attributeCount; |
|
} |
|
if (ictanns != null) { |
|
++attributeCount; |
|
} |
|
if (cattrs != null) { |
|
attributeCount += cattrs.getCount(); |
|
} |
|
out.putShort(attributeCount); |
|
if (localVar != null) { |
|
out.putShort(cw.newUTF8("LocalVariableTable")); |
|
out.putInt(localVar.length + 2).putShort(localVarCount); |
|
out.putByteArray(localVar.data, 0, localVar.length); |
|
} |
|
if (localVarType != null) { |
|
out.putShort(cw.newUTF8("LocalVariableTypeTable")); |
|
out.putInt(localVarType.length + 2).putShort(localVarTypeCount); |
|
out.putByteArray(localVarType.data, 0, localVarType.length); |
|
} |
|
if (lineNumber != null) { |
|
out.putShort(cw.newUTF8("LineNumberTable")); |
|
out.putInt(lineNumber.length + 2).putShort(lineNumberCount); |
|
out.putByteArray(lineNumber.data, 0, lineNumber.length); |
|
} |
|
if (stackMap != null) { |
|
boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; |
|
out.putShort(cw.newUTF8(zip ? "StackMapTable" : "StackMap")); |
|
out.putInt(stackMap.length + 2).putShort(frameCount); |
|
out.putByteArray(stackMap.data, 0, stackMap.length); |
|
} |
|
if (ctanns != null) { |
|
out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); |
|
ctanns.put(out); |
|
} |
|
if (ictanns != null) { |
|
out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); |
|
ictanns.put(out); |
|
} |
|
if (cattrs != null) { |
|
cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out); |
|
} |
|
} |
|
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]); |
|
} |
|
} |
|
if ((access & Opcodes.ACC_SYNTHETIC) != 0) { |
|
if ((cw.version & 0xFFFF) < Opcodes.V1_5 |
|
|| (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { |
|
out.putShort(cw.newUTF8("Synthetic")).putInt(0); |
|
} |
|
} |
|
if ((access & Opcodes.ACC_DEPRECATED) != 0) { |
|
out.putShort(cw.newUTF8("Deprecated")).putInt(0); |
|
} |
|
if (signature != null) { |
|
out.putShort(cw.newUTF8("Signature")).putInt(2) |
|
.putShort(cw.newUTF8(signature)); |
|
} |
|
if (methodParameters != null) { |
|
out.putShort(cw.newUTF8("MethodParameters")); |
|
out.putInt(methodParameters.length + 1).putByte( |
|
methodParametersCount); |
|
out.putByteArray(methodParameters.data, 0, methodParameters.length); |
|
} |
|
if (annd != null) { |
|
out.putShort(cw.newUTF8("AnnotationDefault")); |
|
out.putInt(annd.length); |
|
out.putByteArray(annd.data, 0, annd.length); |
|
} |
|
if (anns != null) { |
|
out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); |
|
anns.put(out); |
|
} |
|
if (ianns != null) { |
|
out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); |
|
ianns.put(out); |
|
} |
|
if (tanns != null) { |
|
out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations")); |
|
tanns.put(out); |
|
} |
|
if (itanns != null) { |
|
out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations")); |
|
itanns.put(out); |
|
} |
|
if (panns != null) { |
|
out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations")); |
|
AnnotationWriter.put(panns, synthetics, out); |
|
} |
|
if (ipanns != null) { |
|
out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations")); |
|
AnnotationWriter.put(ipanns, synthetics, out); |
|
} |
|
if (attrs != null) { |
|
attrs.put(cw, null, 0, -1, -1, out); |
|
} |
|
} |
|
} |