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

Fixed bug #316360.

parent 5d18119f
......@@ -1142,7 +1142,7 @@ public class ClassReader {
u += 2;
// 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
* but only offset_delta; setting the implicit frame offset to -1
......@@ -1159,8 +1159,6 @@ public class ClassReader {
if (unzip) {
getImplicitFrame(context);
}
}
if (FRAMES && stackMap != 0) {
/*
* Finds labels for UNINITIALIZED frame types. Instead of decoding
* each element of the stack map table, we look for 3 consecutive
......@@ -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
&& (frame.offset == offset || frame.offset == -1)) {
// if there is a frame for this offset, makes the visitor visit
// it, and reads the next frame if there is one.
if (!zip || unzip) {
mv.visitFrame(Opcodes.F_NEW, frame.localCount, frame.local,
frame.stackCount, frame.stack);
} else if (frame.offset != -1) {
mv.visitFrame(frame.mode, frame.localDiff, frame.local,
frame.stackCount, frame.stack);
if (frame.offset != -1) {
if (!zip || unzip) {
mv.visitFrame(Opcodes.F_NEW, frame.localCount,
frame.local, frame.stackCount, frame.stack);
} else {
mv.visitFrame(frame.mode, frame.localDiff, frame.local,
frame.stackCount, frame.stack);
}
}
if (frameCount > 0) {
stackMap = readFrame(stackMap, zip, unzip, labels, frame);
......
......@@ -184,13 +184,9 @@ public abstract class MethodVisitor {
* compressed form (all frames must use the same format, i.e. you must not
* mix expanded and compressed frames within a single method):
* <ul>
* <li>In expanded form, all frames must have the F_NEW type, and a first
* frame corresponding to the method signature must be explicitly visited
* before the first instruction.</li>
* <li>In expanded form, all frames must have the F_NEW type.</li>
* <li>In compressed form, frames are basically "deltas" from the state of
* the previous frame (the first frame, corresponding to the method's
* parameters and access flags, is implicit in this form, and must not be
* visited):
* the previous frame:
* <ul>
* <li>{@link Opcodes#F_SAME} representing frame with exactly the same
* locals as the previous frame and with the empty stack.</li>
......@@ -207,7 +203,11 @@ public abstract class MethodVisitor {
* 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>
* </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
* the type of this stack map frame. Must be
......
......@@ -42,8 +42,8 @@ class MethodWriter extends MethodVisitor {
/**
* 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
* number of stack items is zero.
......@@ -256,11 +256,6 @@ class MethodWriter extends MethodVisitor {
*/
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
* instruction to which the frame corresponds, the second element is the
......@@ -430,6 +425,9 @@ class MethodWriter extends MethodVisitor {
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;
......@@ -445,9 +443,6 @@ class MethodWriter extends MethodVisitor {
}
this.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING);
if (computeMaxs || computeFrames) {
if (computeFrames && "<init>".equals(name)) {
this.access |= ACC_CONSTRUCTOR;
}
// updates maxLocals
int size = Type.getArgumentsAndReturnSizes(descriptor) >> 2;
if ((access & Opcodes.ACC_STATIC) != 0) {
......@@ -550,8 +545,11 @@ class MethodWriter extends MethodVisitor {
}
if (type == Opcodes.F_NEW) {
if (previousFrame == null) {
visitImplicitFirstFrame();
}
currentLocals = nLocal;
startFrame(code.length, nLocal, nStack);
int frameIndex = startFrame(code.length, nLocal, nStack);
for (int i = 0; i < nLocal; ++i) {
if (local[i] instanceof String) {
frame[frameIndex++] = Frame.OBJECT
......@@ -1334,8 +1332,8 @@ class MethodWriter extends MethodVisitor {
}
code.data[end] = (byte) Opcodes.ATHROW;
// emits a frame for this unreachable block
startFrame(start, 0, 1);
frame[frameIndex++] = Frame.OBJECT
int frameIndex = startFrame(start, 0, 1);
frame[frameIndex] = Frame.OBJECT
| cw.addType("java/lang/Throwable");
endFrame();
// removes the start-end range from the exception
......@@ -1562,7 +1560,7 @@ class MethodWriter extends MethodVisitor {
}
}
// 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) {
t = locals[i];
frame[frameIndex++] = t;
......@@ -1580,6 +1578,67 @@ class MethodWriter extends MethodVisitor {
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.
*
......@@ -1589,8 +1648,9 @@ class MethodWriter extends MethodVisitor {
* the number of local variables in the frame.
* @param nStack
* 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;
if (frame == null || frame.length < n) {
frame = new int[n];
......@@ -1598,7 +1658,7 @@ class MethodWriter extends MethodVisitor {
frame[0] = offset;
frame[1] = nLocal;
frame[2] = nStack;
frameIndex = 3;
return 3;
}
/**
......@@ -1896,7 +1956,8 @@ class MethodWriter extends MethodVisitor {
*/
final void put(final ByteVector out) {
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);
out.putShort(access & ~mask).putShort(name).putShort(desc);
if (classReaderOffset != 0) {
......
......@@ -194,7 +194,7 @@ org/objectweb/asm/MethodWriter.frameCount=u
org/objectweb/asm/MethodWriter.stackMap=v
org/objectweb/asm/MethodWriter.previousFrameOffset=w
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.handlerCount=A
org/objectweb/asm/MethodWriter.firstHandler=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.writeShort([BII)V=a
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.writeFrame()V=c
org/objectweb/asm/MethodWriter.resizeInstructions()V=d
......
......@@ -111,11 +111,6 @@ public class CheckMethodAdapter extends MethodVisitor {
*/
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.
*/
......@@ -126,6 +121,11 @@ public class CheckMethodAdapter extends MethodVisitor {
*/
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
* start and end labels of an exception handler block.
......@@ -506,6 +506,11 @@ public class CheckMethodAdapter extends MethodVisitor {
@Override
public void visitFrame(final int type, final int nLocal,
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 mStack;
switch (type) {
......@@ -561,13 +566,6 @@ public class CheckMethodAdapter extends MethodVisitor {
checkFrameValue(stack[i]);
}
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;
} else {
++compressedFrames;
......
Markdown is supported
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