Commit 855dbcdc authored by Eric Bruneton's avatar Eric Bruneton

Improve the asm-test precompiled classes.

Merge the precompiled classes containing structures that can't be produced by javac, add code with several line numbers per bytecode position, and remove the code for this in ClassWriterTest (it is safer to have a precompiled class than rebuilding it on the fly - the later approach cannot detect some bugs).
parent ba49829a
Pipeline #472 passed with stage
in 10 minutes and 28 seconds
......@@ -106,11 +106,9 @@ public abstract class AsmTest {
JDK3_ALL_INSTRUCTIONS("jdk3.AllInstructions"),
JDK3_ALL_STRUCTURES("jdk3.AllStructures"),
JDK3_ANONYMOUS_INNER_CLASS("jdk3.AllStructures$1"),
JDK3_ATTRIBUTE("jdk3.Attribute"),
JDK3_ARTIFICIAL_STRUCTURES("jdk3.ArtificialStructures"),
JDK3_INNER_CLASS("jdk3.AllStructures$InnerClass"),
JDK3_LARGE_METHOD("jdk3.LargeMethod"),
JDK3_STACK_MAP_ATTRIBUTE("jdk3.StackMapAttribute"),
JDK3_SWAP("jdk3.Swap"),
JDK5_ALL_INSTRUCTIONS("jdk5.AllInstructions"),
JDK5_ALL_STRUCTURES("jdk5.AllStructures"),
JDK5_ANNOTATION("jdk5.AllStructures$InvisibleAnnotation"),
......@@ -346,7 +344,7 @@ public abstract class AsmTest {
public static boolean loadAndInstantiate(String className, byte[] classContent) {
try {
new ClassDump(classContent);
} catch (IOException e) {
} catch (IOException | IllegalArgumentException e) {
fail("Class can't be dumped, probably invalid");
}
return doLoadAndInstantiate(className, classContent);
......
......@@ -31,35 +31,47 @@ import java.io.FileOutputStream;
import java.io.IOException;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.CodeComment;
import org.objectweb.asm.Comment;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.attrs.CodeComment;
import org.objectweb.asm.attrs.Comment;
/**
* Generates a class with non standard attributes. Covers class, field, method and code attributes.
* Also covers the class, field and method Synthetic attributes. Ideally we should not use ASM to
* generate this class (which is later used to test ASM), but this would be hard to do.
* Generates a class with structures, instructions and patterns that cannot be produced by compiling
* a Java source file with the javac compiler. This includes:
*
* <ul>
* <li>the class, field and method Synthetic attribute (now replaced with an access flag),
* <li>the StackMap attribute (which was used for pre-verification in J2ME CLDC 1.1),
* <li>non standard class, field, method and code attributes,
* <li>the nop and swap instructions,
* <li>several line numbers per bytecode offset.
* </ul>
*
* Ideally we should not use ASM to generate this class (which is later used to test ASM), but this
* would be hard to do.
*
* @author Eric Bruneton
*/
public class DumpAttribute implements Opcodes {
public class DumpArtificialStructures implements Opcodes {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("Attribute.class");
FileOutputStream fos = new FileOutputStream("ArtificialStructures.class");
fos.write(dump());
fos.close();
}
private static byte[] dump() {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
FieldVisitor fv;
MethodVisitor mv;
cw.visit(V1_3, ACC_PUBLIC + ACC_SYNTHETIC, "jdk3/Attribute", null, "java/lang/Object", null);
cw.visit(
V1_3, ACC_PUBLIC + ACC_SUPER, "jdk3/ArtificialStructures", null, "java/lang/Object", null);
cw.visitSource("Attribute.java", "source-debug");
cw.visitSource("ArtificialStructures.java", "source-debug");
cw.visitAttribute(new Comment());
......@@ -67,16 +79,43 @@ public class DumpAttribute implements Opcodes {
fv.visitAttribute(new Comment());
fv.visitEnd();
mv = cw.visitMethod(ACC_PUBLIC + ACC_SYNTHETIC, "<init>", "()V", null, null);
mv = cw.visitMethod(ACC_PUBLIC + ACC_SYNTHETIC, "<init>", "(Ljava/lang/String;)V", null, null);
mv.visitAttribute(new Comment());
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(NOP);
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitAttribute(new CodeComment());
mv.visitEnd();
mv = cw.visitMethod(0, "<init>", "(Z)V", null, null);
mv.visitCode();
mv.visitVarInsn(ILOAD, 1);
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(SWAP);
Label elseLabel = new Label();
mv.visitJumpInsn(IFEQ, elseLabel);
mv.visitLdcInsn("1");
Label endIfLabel = new Label();
mv.visitJumpInsn(GOTO, endIfLabel);
mv.visitLabel(elseLabel);
mv.visitLineNumber(1, elseLabel);
mv.visitLineNumber(3, elseLabel);
mv.visitLdcInsn("0");
mv.visitLabel(endIfLabel);
mv.visitLineNumber(5, endIfLabel);
mv.visitLineNumber(7, endIfLabel);
mv.visitLineNumber(11, endIfLabel);
mv.visitLineNumber(13, endIfLabel);
mv.visitLineNumber(17, endIfLabel);
mv.visitMethodInsn(
INVOKESPECIAL, "jdk3/ArtificialStructures", "<init>", "(Ljava/lang/String;)V", false);
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
cw.visitEnd();
return cw.toByteArray();
}
......
// ASM: a very small and fast Java bytecode manipulation framework
// Copyright (c) 2000-2011 INRIA, France Telecom
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holders nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
package jdk3;
/**
* Class which, compiled with the J2ME CLDC 1.1 toolchain, produces a StackMap
* attribute that was introduced for pre-verification.
*/
public class StackMapAttribute {
public int toInt(boolean b) {
return b ? 1 : 0;
}
}
......@@ -32,7 +32,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.fail;
import static org.objectweb.asm.test.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.MethodSource;
......@@ -92,40 +91,11 @@ public class AsmTestTest extends AsmTest {
* is not well formed.
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_LATEST_API)
public void testLoadAndInstantiate_invalidClass(
PrecompiledClass classParameter, Api apiParameter) {
byte[] classContent = classParameter.getBytes();
switch (classParameter) {
case DEFAULT_PACKAGE:
case JDK3_ATTRIBUTE:
case JDK5_ANNOTATION:
case JDK9_MODULE:
return;
case JDK3_ALL_INSTRUCTIONS:
case JDK3_ALL_STRUCTURES:
case JDK3_ANONYMOUS_INNER_CLASS:
case JDK3_INNER_CLASS:
case JDK3_LARGE_METHOD:
case JDK3_STACK_MAP_ATTRIBUTE:
case JDK3_SWAP:
case JDK5_ALL_INSTRUCTIONS:
case JDK5_ALL_STRUCTURES:
case JDK5_ENUM:
case JDK5_LOCAL_CLASS:
case JDK8_ALL_STRUCTURES:
case JDK8_ANONYMOUS_INNER_CLASS:
case JDK8_INNER_CLASS:
case JDK8_ALL_FRAMES:
case JDK8_ALL_INSTRUCTIONS:
case JDK8_LARGE_METHOD:
removeAttributes(classContent, "Code");
assertThrows(
AssertionError.class, () -> loadAndInstantiate(classParameter.getName(), classContent));
break;
default:
fail("Unknown precompiled class");
}
@EnumSource(InvalidClass.class)
public void testLoadAndInstantiate_invalidClass(InvalidClass invalidClass) {
byte[] classContent = invalidClass.getBytes();
assertThrows(
AssertionError.class, () -> loadAndInstantiate(invalidClass.toString(), classContent));
}
/**
......@@ -133,80 +103,32 @@ public class AsmTestTest extends AsmTest {
* or unverifiable class.
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_LATEST_API)
public void testDoLoadAndInstantiate_invalidClass(
PrecompiledClass classParameter, Api apiParameter) {
if (classParameter.isMoreRecentThanCurrentJdk()) {
return;
}
byte[] classContent = classParameter.getBytes();
switch (classParameter) {
case DEFAULT_PACKAGE:
case JDK3_ATTRIBUTE:
case JDK5_ANNOTATION:
case JDK9_MODULE:
doLoadAndInstantiate(classParameter.getName(), classContent);
@EnumSource(InvalidClass.class)
public void testDoLoadAndInstantiate_invalidClass(InvalidClass invalidClass) {
byte[] classContent = invalidClass.getBytes();
switch (invalidClass) {
case INVALID_ELEMENT_VALUE:
case INVALID_TYPE_ANNOTATION_TARGET_TYPE:
case INVALID_INSN_TYPE_ANNOTATION_TARGET_TYPE:
break;
case JDK3_ALL_INSTRUCTIONS:
case JDK3_ALL_STRUCTURES:
case JDK3_ANONYMOUS_INNER_CLASS:
case JDK3_INNER_CLASS:
case JDK3_LARGE_METHOD:
case JDK3_STACK_MAP_ATTRIBUTE:
case JDK3_SWAP:
case JDK5_ALL_INSTRUCTIONS:
case JDK5_ALL_STRUCTURES:
case JDK5_ENUM:
case JDK5_LOCAL_CLASS:
case JDK8_ALL_STRUCTURES:
case JDK8_ANONYMOUS_INNER_CLASS:
case JDK8_INNER_CLASS:
removeAttributes(classContent, "Code");
case INVALID_BYTECODE_OFFSET:
case INVALID_OPCODE:
case INVALID_WIDE_OPCODE:
assertThrows(
ClassFormatError.class,
() -> doLoadAndInstantiate(classParameter.getName(), classContent));
VerifyError.class, () -> doLoadAndInstantiate(invalidClass.toString(), classContent));
break;
case JDK8_ALL_FRAMES:
case JDK8_ALL_INSTRUCTIONS:
case JDK8_LARGE_METHOD:
removeAttributes(classContent, "StackMapTable");
case INVALID_CLASS_VERSION:
case INVALID_CONSTANT_POOL_INDEX:
case INVALID_CONSTANT_POOL_REFERENCE:
case INVALID_CP_INFO_TAG:
case INVALID_STACK_MAP_FRAME_TYPE:
case INVALID_VERIFICATION_TYPE_INFO:
assertThrows(
VerifyError.class, () -> doLoadAndInstantiate(classParameter.getName(), classContent));
ClassFormatError.class,
() -> doLoadAndInstantiate(invalidClass.toString(), classContent));
break;
default:
fail("Unknown precompiled class");
}
}
/**
* "Removes" all the attributes of the given type in a class by altering its name in the constant
* pool of the class, to make it unrecognizable. Fails if there is not exactly one occurrence of
* attributeName in classContent.
*/
private static void removeAttributes(byte[] classContent, String attributeName) {
int occurrenceCount = 0;
for (int i = 0; i < classContent.length - attributeName.length(); ++i) {
boolean occurrenceFound = true;
for (int j = 0; j < attributeName.length(); ++j) {
if (classContent[i + j] != attributeName.charAt(j)) {
occurrenceFound = false;
break;
}
}
if (occurrenceFound) {
classContent[i] += 1;
occurrenceCount += 1;
}
fail("Unknown invalid class");
}
assertEquals(1, occurrenceCount);
}
/** Tests that dumping an invalid class fails with an IOException. */
@ParameterizedTest
@EnumSource(InvalidClass.class)
public void testDumpInvalidClass(InvalidClass invalidClass) {
byte[] classContent = invalidClass.getBytes();
assertThrows(AssertionError.class, () -> assertThatClass(classContent).contains("invalid"));
assertThrows(AssertionError.class, () -> assertThatClass(classContent).isEqualTo(classContent));
}
}
......@@ -119,7 +119,7 @@ public class SAXAdapterTest extends AsmTest {
public void testSAXAdapter_classUnchanged(PrecompiledClass classParameter, Api apiParameter)
throws TransformerConfigurationException, TransformerFactoryConfigurationError, SAXException {
// Non standard attributes are not supported by the XML API.
if (classParameter == PrecompiledClass.JDK3_ATTRIBUTE) return;
if (classParameter == PrecompiledClass.JDK3_ARTIFICIAL_STRUCTURES) return;
byte[] classFile = classParameter.getBytes();
ClassReader classReader = new ClassReader(classFile);
ClassWriter classWriter = new ClassWriter(0);
......
......@@ -329,44 +329,6 @@ public class ClassWriterTest extends AsmTest {
classWriter.toByteArray();
}
/**
* Tests classes with several line numbers for the same bytecode offset. Ideally we should use a
* precompiled class having this property, but this seems really hard to find. Instead, we
* generate some classes with this property using ASM, with {@link #duplicateLineNumbers}.
*/
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_LATEST_API)
public void testMultipleLineNumbers(PrecompiledClass classParameter, Api apiParameter) {
byte[] classFile = duplicateLineNumbers(classParameter.getBytes());
ClassWriter classWriter = new ClassWriter(0);
new ClassReader(classFile).accept(classWriter, 0);
assertThatClass(classWriter.toByteArray()).isEqualTo(classFile);
}
private static byte[] duplicateLineNumbers(byte[] classFile) {
ClassWriter classWriter = new ClassWriter(0);
ClassVisitor resetLineNumberVisitor =
new ClassVisitor(Opcodes.ASM6, classWriter) {
@Override
public MethodVisitor visitMethod(
int access, String name, String descriptor, String signature, String[] exceptions) {
return new MethodVisitor(
api, super.visitMethod(access, name, descriptor, signature, exceptions)) {
@Override
public void visitLineNumber(int line, Label start) {
for (int i = 0; i < line % 6; ++i) {
super.visitLineNumber(line + i, start);
}
}
};
}
};
new ClassReader(classFile).accept(resetLineNumberVisitor, 0);
return classWriter.toByteArray();
}
private static Attribute[] attributes() {
return new Attribute[] {new Comment(), new CodeComment()};
}
......
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