Commit 6673a08b authored by ebruneton's avatar ebruneton
Browse files

Fixed bug #316360.

parent 5d18119f
...@@ -1142,7 +1142,7 @@ public class ClassReader { ...@@ -1142,7 +1142,7 @@ public class ClassReader {
u += 2; u += 2;
// generates the first (implicit) stack map frame // generates the first (implicit) stack map frame
if (FRAMES && (stackMap != 0 || unzip)) { if (FRAMES && stackMap != 0) {
/* /*
* for the first explicit frame the offset is not offset_delta + 1 * for the first explicit frame the offset is not offset_delta + 1
* but only offset_delta; setting the implicit frame offset to -1 * but only offset_delta; setting the implicit frame offset to -1
...@@ -1159,8 +1159,6 @@ public class ClassReader { ...@@ -1159,8 +1159,6 @@ public class ClassReader {
if (unzip) { if (unzip) {
getImplicitFrame(context); getImplicitFrame(context);
} }
}
if (FRAMES && stackMap != 0) {
/* /*
* Finds labels for UNINITIALIZED frame types. Instead of decoding * Finds labels for UNINITIALIZED frame types. Instead of decoding
* each element of the stack map table, we look for 3 consecutive * each element of the stack map table, we look for 3 consecutive
...@@ -1198,17 +1196,19 @@ public class ClassReader { ...@@ -1198,17 +1196,19 @@ public class ClassReader {
} }
} }
// visits the frame(s) for this offset, if any // visits the frame for this offset, if any
while (FRAMES && frame != null while (FRAMES && frame != null
&& (frame.offset == offset || frame.offset == -1)) { && (frame.offset == offset || frame.offset == -1)) {
// if there is a frame for this offset, makes the visitor visit // if there is a frame for this offset, makes the visitor visit
// it, and reads the next frame if there is one. // it, and reads the next frame if there is one.
if (!zip || unzip) { if (frame.offset != -1) {
mv.visitFrame(Opcodes.F_NEW, frame.localCount, frame.local, if (!zip || unzip) {
frame.stackCount, frame.stack); mv.visitFrame(Opcodes.F_NEW, frame.localCount,
} else if (frame.offset != -1) { frame.local, frame.stackCount, frame.stack);
mv.visitFrame(frame.mode, frame.localDiff, frame.local, } else {
frame.stackCount, frame.stack); mv.visitFrame(frame.mode, frame.localDiff, frame.local,
frame.stackCount, frame.stack);
}
} }
if (frameCount > 0) { if (frameCount > 0) {
stackMap = readFrame(stackMap, zip, unzip, labels, frame); stackMap = readFrame(stackMap, zip, unzip, labels, frame);
......
...@@ -184,13 +184,9 @@ public abstract class MethodVisitor { ...@@ -184,13 +184,9 @@ public abstract class MethodVisitor {
* compressed form (all frames must use the same format, i.e. you must not * compressed form (all frames must use the same format, i.e. you must not
* mix expanded and compressed frames within a single method): * mix expanded and compressed frames within a single method):
* <ul> * <ul>
* <li>In expanded form, all frames must have the F_NEW type, and a first * <li>In expanded form, all frames must have the F_NEW type.</li>
* frame corresponding to the method signature must be explicitly visited
* before the first instruction.</li>
* <li>In compressed form, frames are basically "deltas" from the state of * <li>In compressed form, frames are basically "deltas" from the state of
* the previous frame (the first frame, corresponding to the method's * the previous frame:
* parameters and access flags, is implicit in this form, and must not be
* visited):
* <ul> * <ul>
* <li>{@link Opcodes#F_SAME} representing frame with exactly the same * <li>{@link Opcodes#F_SAME} representing frame with exactly the same
* locals as the previous frame and with the empty stack.</li> * locals as the previous frame and with the empty stack.</li>
...@@ -207,7 +203,11 @@ public abstract class MethodVisitor { ...@@ -207,7 +203,11 @@ public abstract class MethodVisitor {
* are absent and with the empty stack (<code>nLocals</code> is 1, 2 or 3).</li> * are absent and with the empty stack (<code>nLocals</code> is 1, 2 or 3).</li>
* <li>{@link Opcodes#F_FULL} representing complete frame data.</li></li> * <li>{@link Opcodes#F_FULL} representing complete frame data.</li></li>
* </ul> * </ul>
* </ul> * </ul> <br>
* In both cases the first frame, corresponding to the method's parameters
* and access flags, is implicit and must not be visited. Also, it is
* illegal to visit two or more frames for the same code location (i.e., at
* least one instruction must be visited between two calls to visitFrame).
* *
* @param type * @param type
* the type of this stack map frame. Must be * the type of this stack map frame. Must be
......
...@@ -42,8 +42,8 @@ class MethodWriter extends MethodVisitor { ...@@ -42,8 +42,8 @@ class MethodWriter extends MethodVisitor {
/** /**
* Pseudo access flag used to denote constructors. * Pseudo access flag used to denote constructors.
*/ */
static final int ACC_CONSTRUCTOR = 262144; static final int ACC_CONSTRUCTOR = 0x80000;
/** /**
* Frame has exactly the same locals as the previous stack map frame and * Frame has exactly the same locals as the previous stack map frame and
* number of stack items is zero. * number of stack items is zero.
...@@ -256,11 +256,6 @@ class MethodWriter extends MethodVisitor { ...@@ -256,11 +256,6 @@ class MethodWriter extends MethodVisitor {
*/ */
private int[] previousFrame; private int[] previousFrame;
/**
* Index of the next element to be added in {@link #frame}.
*/
private int frameIndex;
/** /**
* The current stack map frame. The first element contains the offset of the * The current stack map frame. The first element contains the offset of the
* instruction to which the frame corresponds, the second element is the * instruction to which the frame corresponds, the second element is the
...@@ -430,6 +425,9 @@ class MethodWriter extends MethodVisitor { ...@@ -430,6 +425,9 @@ class MethodWriter extends MethodVisitor {
cw.lastMethod = this; cw.lastMethod = this;
this.cw = cw; this.cw = cw;
this.access = access; this.access = access;
if ("<init>".equals(name)) {
this.access |= ACC_CONSTRUCTOR;
}
this.name = cw.newUTF8(name); this.name = cw.newUTF8(name);
this.desc = cw.newUTF8(desc); this.desc = cw.newUTF8(desc);
this.descriptor = desc; this.descriptor = desc;
...@@ -445,9 +443,6 @@ class MethodWriter extends MethodVisitor { ...@@ -445,9 +443,6 @@ class MethodWriter extends MethodVisitor {
} }
this.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING); this.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING);
if (computeMaxs || computeFrames) { if (computeMaxs || computeFrames) {
if (computeFrames && "<init>".equals(name)) {
this.access |= ACC_CONSTRUCTOR;
}
// updates maxLocals // updates maxLocals
int size = Type.getArgumentsAndReturnSizes(descriptor) >> 2; int size = Type.getArgumentsAndReturnSizes(descriptor) >> 2;
if ((access & Opcodes.ACC_STATIC) != 0) { if ((access & Opcodes.ACC_STATIC) != 0) {
...@@ -550,8 +545,11 @@ class MethodWriter extends MethodVisitor { ...@@ -550,8 +545,11 @@ class MethodWriter extends MethodVisitor {
} }
if (type == Opcodes.F_NEW) { if (type == Opcodes.F_NEW) {
if (previousFrame == null) {
visitImplicitFirstFrame();
}
currentLocals = nLocal; currentLocals = nLocal;
startFrame(code.length, nLocal, nStack); int frameIndex = startFrame(code.length, nLocal, nStack);
for (int i = 0; i < nLocal; ++i) { for (int i = 0; i < nLocal; ++i) {
if (local[i] instanceof String) { if (local[i] instanceof String) {
frame[frameIndex++] = Frame.OBJECT frame[frameIndex++] = Frame.OBJECT
...@@ -1334,8 +1332,8 @@ class MethodWriter extends MethodVisitor { ...@@ -1334,8 +1332,8 @@ class MethodWriter extends MethodVisitor {
} }
code.data[end] = (byte) Opcodes.ATHROW; code.data[end] = (byte) Opcodes.ATHROW;
// emits a frame for this unreachable block // emits a frame for this unreachable block
startFrame(start, 0, 1); int frameIndex = startFrame(start, 0, 1);
frame[frameIndex++] = Frame.OBJECT frame[frameIndex] = Frame.OBJECT
| cw.addType("java/lang/Throwable"); | cw.addType("java/lang/Throwable");
endFrame(); endFrame();
// removes the start-end range from the exception // removes the start-end range from the exception
...@@ -1562,7 +1560,7 @@ class MethodWriter extends MethodVisitor { ...@@ -1562,7 +1560,7 @@ class MethodWriter extends MethodVisitor {
} }
} }
// visits the frame and its content // visits the frame and its content
startFrame(f.owner.position, nLocal, nStack); int frameIndex = startFrame(f.owner.position, nLocal, nStack);
for (i = 0; nLocal > 0; ++i, --nLocal) { for (i = 0; nLocal > 0; ++i, --nLocal) {
t = locals[i]; t = locals[i];
frame[frameIndex++] = t; frame[frameIndex++] = t;
...@@ -1580,6 +1578,67 @@ class MethodWriter extends MethodVisitor { ...@@ -1580,6 +1578,67 @@ class MethodWriter extends MethodVisitor {
endFrame(); endFrame();
} }
/**
* Visit the implicit first frame of this method.
*/
private void visitImplicitFirstFrame() {
// There can be at most descriptor.length() + 1 locals
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++] = 6; // Opcodes.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++] = 1; // Opcodes.INTEGER;
break;
case 'F':
frame[frameIndex++] = 2; // Opcodes.FLOAT;
break;
case 'J':
frame[frameIndex++] = 4; // Opcodes.LONG;
break;
case 'D':
frame[frameIndex++] = 3; // Opcodes.DOUBLE;
break;
case '[':
while (descriptor.charAt(i) == '[') {
++i;
}
if (descriptor.charAt(i) == 'L') {
++i;
while (descriptor.charAt(i) != ';') {
++i;
}
}
frame[frameIndex++] = Frame.OBJECT
| cw.addType(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();
}
/** /**
* Starts the visit of a stack map frame. * Starts the visit of a stack map frame.
* *
...@@ -1589,8 +1648,9 @@ class MethodWriter extends MethodVisitor { ...@@ -1589,8 +1648,9 @@ class MethodWriter extends MethodVisitor {
* the number of local variables in the frame. * the number of local variables in the frame.
* @param nStack * @param nStack
* the number of stack elements in the frame. * the number of stack elements in the frame.
* @return the index of the next element to be written in this frame.
*/ */
private void startFrame(final int offset, final int nLocal, final int nStack) { private int startFrame(final int offset, final int nLocal, final int nStack) {
int n = 3 + nLocal + nStack; int n = 3 + nLocal + nStack;
if (frame == null || frame.length < n) { if (frame == null || frame.length < n) {
frame = new int[n]; frame = new int[n];
...@@ -1598,7 +1658,7 @@ class MethodWriter extends MethodVisitor { ...@@ -1598,7 +1658,7 @@ class MethodWriter extends MethodVisitor {
frame[0] = offset; frame[0] = offset;
frame[1] = nLocal; frame[1] = nLocal;
frame[2] = nStack; frame[2] = nStack;
frameIndex = 3; return 3;
} }
/** /**
...@@ -1896,7 +1956,8 @@ class MethodWriter extends MethodVisitor { ...@@ -1896,7 +1956,8 @@ class MethodWriter extends MethodVisitor {
*/ */
final void put(final ByteVector out) { final void put(final ByteVector out) {
final int FACTOR = ClassWriter.TO_ACC_SYNTHETIC; final int FACTOR = ClassWriter.TO_ACC_SYNTHETIC;
int mask = Opcodes.ACC_DEPRECATED | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE int mask = ACC_CONSTRUCTOR | Opcodes.ACC_DEPRECATED
| ClassWriter.ACC_SYNTHETIC_ATTRIBUTE
| ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / FACTOR); | ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / FACTOR);
out.putShort(access & ~mask).putShort(name).putShort(desc); out.putShort(access & ~mask).putShort(name).putShort(desc);
if (classReaderOffset != 0) { if (classReaderOffset != 0) {
......
...@@ -194,7 +194,7 @@ org/objectweb/asm/MethodWriter.frameCount=u ...@@ -194,7 +194,7 @@ org/objectweb/asm/MethodWriter.frameCount=u
org/objectweb/asm/MethodWriter.stackMap=v org/objectweb/asm/MethodWriter.stackMap=v
org/objectweb/asm/MethodWriter.previousFrameOffset=w org/objectweb/asm/MethodWriter.previousFrameOffset=w
org/objectweb/asm/MethodWriter.previousFrame=x org/objectweb/asm/MethodWriter.previousFrame=x
org/objectweb/asm/MethodWriter.frameIndex=y #org/objectweb/asm/MethodWriter.frameIndex=y
org/objectweb/asm/MethodWriter.frame=z org/objectweb/asm/MethodWriter.frame=z
org/objectweb/asm/MethodWriter.handlerCount=A org/objectweb/asm/MethodWriter.handlerCount=A
org/objectweb/asm/MethodWriter.firstHandler=B org/objectweb/asm/MethodWriter.firstHandler=B
...@@ -333,7 +333,8 @@ org/objectweb/asm/MethodWriter.readShort([BI)S=b ...@@ -333,7 +333,8 @@ org/objectweb/asm/MethodWriter.readShort([BI)S=b
org/objectweb/asm/MethodWriter.readUnsignedShort([BI)I=c org/objectweb/asm/MethodWriter.readUnsignedShort([BI)I=c
org/objectweb/asm/MethodWriter.writeShort([BII)V=a org/objectweb/asm/MethodWriter.writeShort([BII)V=a
org/objectweb/asm/MethodWriter.visitFrame(Lorg/objectweb/asm/Frame;)V=b org/objectweb/asm/MethodWriter.visitFrame(Lorg/objectweb/asm/Frame;)V=b
org/objectweb/asm/MethodWriter.startFrame(III)V=a org/objectweb/asm/MethodWriter.visitImplicitFirstFrame()V=f
org/objectweb/asm/MethodWriter.startFrame(III)I=a
org/objectweb/asm/MethodWriter.endFrame()V=b org/objectweb/asm/MethodWriter.endFrame()V=b
org/objectweb/asm/MethodWriter.writeFrame()V=c org/objectweb/asm/MethodWriter.writeFrame()V=c
org/objectweb/asm/MethodWriter.resizeInstructions()V=d org/objectweb/asm/MethodWriter.resizeInstructions()V=d
......
...@@ -111,11 +111,6 @@ public class CheckMethodAdapter extends MethodVisitor { ...@@ -111,11 +111,6 @@ public class CheckMethodAdapter extends MethodVisitor {
*/ */
private Set<Label> usedLabels; private Set<Label> usedLabels;
/**
* If an explicit first frame has been visited before the first instruction.
*/
private boolean hasExplicitFirstFrame;
/** /**
* Number of visited frames in expanded form. * Number of visited frames in expanded form.
*/ */
...@@ -126,6 +121,11 @@ public class CheckMethodAdapter extends MethodVisitor { ...@@ -126,6 +121,11 @@ public class CheckMethodAdapter extends MethodVisitor {
*/ */
private int compressedFrames; private int compressedFrames;
/**
* Number of instructions before the last visited frame.
*/
private int lastFrame = -1;
/** /**
* The exception handler ranges. Each pair of list element contains the * The exception handler ranges. Each pair of list element contains the
* start and end labels of an exception handler block. * start and end labels of an exception handler block.
...@@ -506,6 +506,11 @@ public class CheckMethodAdapter extends MethodVisitor { ...@@ -506,6 +506,11 @@ public class CheckMethodAdapter extends MethodVisitor {
@Override @Override
public void visitFrame(final int type, final int nLocal, public void visitFrame(final int type, final int nLocal,
final Object[] local, final int nStack, final Object[] stack) { final Object[] local, final int nStack, final Object[] stack) {
if (insnCount == lastFrame) {
throw new IllegalStateException(
"At most one frame can be visited at a given code location.");
}
lastFrame = insnCount;
int mLocal; int mLocal;
int mStack; int mStack;
switch (type) { switch (type) {
...@@ -561,13 +566,6 @@ public class CheckMethodAdapter extends MethodVisitor { ...@@ -561,13 +566,6 @@ public class CheckMethodAdapter extends MethodVisitor {
checkFrameValue(stack[i]); checkFrameValue(stack[i]);
} }
if (type == Opcodes.F_NEW) { if (type == Opcodes.F_NEW) {
if (insnCount == 0) {
hasExplicitFirstFrame = true;
} else if (!hasExplicitFirstFrame) {
throw new RuntimeException(
"In expanded form, a first frame must be explicitly "
+ "visited before the first instruction.");
}
++expandedFrames; ++expandedFrames;
} else { } else {
++compressedFrames; ++compressedFrames;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment