Commit 6e219625 authored by Eric Bruneton's avatar Eric Bruneton

Merge branch '317748-non-regression' into 'master'

Add a non-regression test for issue #317748.

See merge request asm/asm!97
parents adad7843 6fada1f7
......@@ -277,4 +277,47 @@ public class Attribute {
attribute = attribute.nextAttribute;
}
}
/** A set of attribute prototypes (attributes with the same type are considered equal). */
static final class Set {
private static final int SIZE_INCREMENT = 6;
private int size;
private Attribute[] data = new Attribute[SIZE_INCREMENT];
void addAttributes(final Attribute attributeList) {
Attribute attribute = attributeList;
while (attribute != null) {
if (!contains(attribute)) {
add(attribute);
}
attribute = attribute.nextAttribute;
}
}
Attribute[] toArray() {
Attribute[] result = new Attribute[size];
System.arraycopy(data, 0, result, 0, size);
return result;
}
private boolean contains(final Attribute attribute) {
for (int i = 0; i < size; ++i) {
if (data[i].type.equals(attribute.type)) {
return true;
}
}
return false;
}
private void add(final Attribute attribute) {
if (size >= data.length) {
Attribute[] newData = new Attribute[data.length + SIZE_INCREMENT];
System.arraycopy(data, 0, newData, 0, size);
data = newData;
}
data[size++] = attribute;
}
}
}
......@@ -601,6 +601,7 @@ public class ClassWriter extends ClassVisitor {
// Third step: do a ClassReader->ClassWriter round trip if the generated class contains ASM
// specific instructions due to large forward jumps.
if (hasAsmInstructions) {
Attribute[] attributes = getAttributePrototypes();
firstField = null;
lastField = null;
firstMethod = null;
......@@ -613,13 +614,37 @@ public class ClassWriter extends ClassVisitor {
firstAttribute = null;
compute = hasFrames ? MethodWriter.COMPUTE_INSERTED_FRAMES : MethodWriter.COMPUTE_NOTHING;
new ClassReader(result.data, 0, /* checkClassVersion = */ false)
.accept(this, (hasFrames ? ClassReader.EXPAND_FRAMES : 0) | ClassReader.EXPAND_ASM_INSNS);
.accept(
this,
attributes,
(hasFrames ? ClassReader.EXPAND_FRAMES : 0) | ClassReader.EXPAND_ASM_INSNS);
return toByteArray();
} else {
return result.data;
}
}
/**
* Returns the prototypes of the attributes used by this class, its fields and its methods.
*
* @return the prototypes of the attributes used by this class, its fields and its methods.
*/
private Attribute[] getAttributePrototypes() {
Attribute.Set attributePrototypes = new Attribute.Set();
attributePrototypes.addAttributes(firstAttribute);
FieldWriter fieldWriter = firstField;
while (fieldWriter != null) {
fieldWriter.collectAttributePrototypes(attributePrototypes);
fieldWriter = (FieldWriter) fieldWriter.fv;
}
MethodWriter methodWriter = firstMethod;
while (methodWriter != null) {
methodWriter.collectAttributePrototypes(attributePrototypes);
methodWriter = (MethodWriter) methodWriter.mv;
}
return attributePrototypes.toArray();
}
// -----------------------------------------------------------------------------------------------
// Utility methods: constant pool management for Attribute sub classes
// -----------------------------------------------------------------------------------------------
......
......@@ -334,4 +334,13 @@ final class FieldWriter extends FieldVisitor {
firstAttribute.putAttributes(symbolTable, output);
}
}
/**
* Collects the attributes of this field into the given set of attribute prototypes.
*
* @param attributePrototypes a set of attribute prototypes.
*/
final void collectAttributePrototypes(final Attribute.Set attributePrototypes) {
attributePrototypes.addAttributes(firstAttribute);
}
}
......@@ -2303,4 +2303,14 @@ final class MethodWriter extends MethodVisitor {
firstAttribute.putAttributes(symbolTable, output);
}
}
/**
* Collects the attributes of this method into the given set of attribute prototypes.
*
* @param attributePrototypes a set of attribute prototypes.
*/
final void collectAttributePrototypes(final Attribute.Set attributePrototypes) {
attributePrototypes.addAttributes(firstAttribute);
attributePrototypes.addAttributes(firstCodeAttribute);
}
}
......@@ -325,13 +325,21 @@ public class ClassWriterTest extends AsmTest {
ClassReader classReader = new ClassReader(classFile);
ClassWriter classWriter = new ClassWriter(0);
ClassVisitor classVisitor = new NopInserter(apiParameter.value(), classWriter);
ForwardJumpNopInserter forwardJumpNopInserter =
new ForwardJumpNopInserter(apiParameter.value(), classWriter);
if (classParameter.isMoreRecentThan(apiParameter)) {
assertThrows(RuntimeException.class, () -> classReader.accept(classVisitor, attributes(), 0));
assertThrows(
RuntimeException.class,
() -> classReader.accept(forwardJumpNopInserter, attributes(), 0));
return;
}
classReader.accept(classVisitor, attributes(), 0);
classReader.accept(forwardJumpNopInserter, attributes(), 0);
if (!forwardJumpNopInserter.transformed) {
classWriter = new ClassWriter(0);
classReader.accept(
new WideForwardJumpInserter(apiParameter.value(), classWriter), attributes(), 0);
}
byte[] transformedClass = classWriter.toByteArray();
assertThat(() -> loadAndInstantiate(classParameter.getName(), transformedClass))
......@@ -559,11 +567,12 @@ public class ClassWriterTest extends AsmTest {
}
}
private static class NopInserter extends ClassVisitor {
/** Inserts NOP instructions after the first forward jump found, to get a wide jump. */
private static class ForwardJumpNopInserter extends ClassVisitor {
boolean transformed = false;
boolean transformed;
NopInserter(final int api, final ClassVisitor classVisitor) {
ForwardJumpNopInserter(final int api, final ClassVisitor classVisitor) {
super(api, classVisitor);
}
......@@ -597,4 +606,61 @@ public class ClassWriterTest extends AsmTest {
};
}
}
/** Inserts a wide forward jump in the first non-abstract method that is found. */
private static class WideForwardJumpInserter extends ClassVisitor {
private boolean needFrames;
private boolean transformed;
WideForwardJumpInserter(final int api, final ClassVisitor classVisitor) {
super(api, classVisitor);
}
@Override
public void visit(
final int version,
final int access,
final String name,
final String signature,
final String superName,
final String[] interfaces) {
needFrames = (version & 0xFFFF) >= Opcodes.V1_7;
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public MethodVisitor visitMethod(
final int access,
final String name,
final String descriptor,
final String signature,
final String[] exceptions) {
return new MethodVisitor(
api, super.visitMethod(access, name, descriptor, signature, exceptions)) {
@Override
public void visitCode() {
super.visitCode();
if (!transformed) {
Label startLabel = new Label();
visitJumpInsn(Opcodes.GOTO, startLabel);
if (needFrames) {
visitLabel(new Label());
visitFrame(Opcodes.F_SAME, 0, null, 0, null);
}
for (int i = 0; i <= Short.MAX_VALUE; ++i) {
visitInsn(Opcodes.NOP);
}
visitLabel(startLabel);
if (needFrames) {
visitFrame(Opcodes.F_SAME, 0, null, 0, null);
visitInsn(Opcodes.NOP);
}
transformed = true;
}
}
};
}
}
}
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