Commit 9b2153da authored by Eric Bruneton's avatar Eric Bruneton

Add more checkstyle modules

parent 6896b689
......@@ -282,7 +282,8 @@ public class Analyzer<V extends Value> implements Opcodes {
} catch (AnalyzerException e) {
throw new AnalyzerException(
e.node, "Error at instruction " + insnIndex + ": " + e.getMessage(), e);
} catch (Exception e) {
} catch (RuntimeException e) {
// DontCheck(IllegalCatch): can't be fixed, for backward compatibility.
throw new AnalyzerException(
insnNode, "Error at instruction " + insnIndex + ": " + e.getMessage(), e);
}
......
......@@ -90,43 +90,6 @@ final class SmallSet<T> extends AbstractSet<T> {
return new IteratorImpl<T>(element1, element2);
}
static class IteratorImpl<T> implements Iterator<T> {
/** The next element to return in {@link #next}. Maybe {@literal null}. */
private T firstElement;
/**
* The element to return in {@link #next}, after {@link #firstElement} is returned. If {@link
* #firstElement} is {@literal null} then this field must be {@literal null}, otherwise it must
* be different from {@link #firstElement}.
*/
private T secondElement;
IteratorImpl(final T firstElement, final T secondElement) {
this.firstElement = firstElement;
this.secondElement = secondElement;
}
public boolean hasNext() {
return firstElement != null;
}
public T next() {
if (firstElement == null) {
throw new NoSuchElementException();
}
T element = firstElement;
firstElement = secondElement;
secondElement = null;
return element;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
@Override
public int size() {
return element1 == null ? 0 : (element2 == null ? 1 : 2);
......@@ -187,4 +150,41 @@ final class SmallSet<T> extends AbstractSet<T> {
}
return result;
}
static class IteratorImpl<T> implements Iterator<T> {
/** The next element to return in {@link #next}. Maybe {@literal null}. */
private T firstElement;
/**
* The element to return in {@link #next}, after {@link #firstElement} is returned. If {@link
* #firstElement} is {@literal null} then this field must be {@literal null}, otherwise it must
* be different from {@link #firstElement}.
*/
private T secondElement;
IteratorImpl(final T firstElement, final T secondElement) {
this.firstElement = firstElement;
this.secondElement = secondElement;
}
public boolean hasNext() {
return firstElement != null;
}
public T next() {
if (firstElement == null) {
throw new NoSuchElementException();
}
T element = firstElement;
firstElement = secondElement;
secondElement = null;
return element;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}
......@@ -164,15 +164,6 @@ public class AnalyzerTest {
methodVisitor.visitTryCatchBlock(start, end, handler, null);
}
protected static class TestClassLoader extends ClassLoader {
public TestClassLoader() {}
public Class<?> defineClass(final String name, final byte[] classFile) {
return defineClass(name, classFile, 0, classFile.length);
}
}
/**
* Tests a method which has the most basic <code>try{}finally{}</code> form imaginable. That is:
*
......@@ -863,7 +854,7 @@ public class AnalyzerTest {
}
assertEquals(maxStack, actualMaxStack, "maxStack");
assertEquals(maxLocals, actualMaxLocals, "maxLocals");
} catch (Exception e) {
} catch (AnalyzerException e) {
fail(e.getMessage());
}
}
......@@ -875,11 +866,20 @@ public class AnalyzerTest {
},
0);
TestClassLoader loader = new TestClassLoader();
try {
TestClassLoader loader = new TestClassLoader();
loader.defineClass("C", classFile).newInstance();
} catch (Throwable t) {
fail(t.getMessage());
} catch (InstantiationException | IllegalAccessException e) {
fail(e.getMessage());
}
}
protected static class TestClassLoader extends ClassLoader {
public TestClassLoader() {}
public Class<?> defineClass(final String name, final byte[] classFile) {
return defineClass(name, classFile, 0, classFile.length);
}
}
}
......@@ -90,7 +90,7 @@ public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes
* The stack map frames corresponding to the labels of the forward jumps made *before* the super
* class constructor has been called (note that the Java Virtual Machine forbids backward jumps
* before the super class constructor is called). Note that by definition (cf. the 'before'), when
* we reach a label from this map, {@link superClassConstructorCalled} must be reset to false.
* we reach a label from this map, {@link #superClassConstructorCalled} must be reset to false.
* This field is only maintained for constructors.
*/
private Map<Label, List<Object>> forwardJumpStackFrames;
......
......@@ -112,23 +112,21 @@ public class RemappingMethodAdapter extends LocalVariablesSorter {
type, numLocal, remapEntries(numLocal, local), numStack, remapEntries(numStack, stack));
}
private Object[] remapEntries(final int numEntries, final Object[] entries) {
if (entries != null) {
for (int i = 0; i < numEntries; i++) {
if (entries[i] instanceof String) {
Object[] newEntries = new Object[numEntries];
if (i > 0) {
System.arraycopy(entries, 0, newEntries, 0, i);
}
do {
Object t = entries[i];
newEntries[i++] = t instanceof String ? remapper.mapType((String) t) : t;
} while (i < numEntries);
return newEntries;
private Object[] remapEntries(final int numTypes, final Object[] entries) {
if (entries == null) {
return entries;
}
Object[] remappedEntries = null;
for (int i = 0; i < numTypes; ++i) {
if (entries[i] instanceof String) {
if (remappedEntries == null) {
remappedEntries = new Object[numTypes];
System.arraycopy(entries, 0, remappedEntries, 0, numTypes);
}
remappedEntries[i] = remapper.mapType((String) entries[i]);
}
}
return entries;
return remappedEntries == null ? entries : remappedEntries;
}
@Override
......
......@@ -32,6 +32,7 @@ import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
......@@ -432,7 +433,7 @@ public class SerialVersionUIDAdder extends ClassVisitor {
protected byte[] computeSHAdigest(final byte[] value) {
try {
return MessageDigest.getInstance("SHA").digest(value);
} catch (Exception e) {
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException(e);
}
}
......
......@@ -472,28 +472,6 @@ public class AdviceAdapterTest extends AsmTest {
return classWriter.toByteArray();
}
private static class MethodGenerator extends MethodVisitor {
private final boolean expectedClass;
MethodGenerator(final MethodVisitor methodVisitor, final boolean expectedClass) {
super(Opcodes.ASM7, methodVisitor);
this.expectedClass = expectedClass;
}
public void expectMethodEnter() {
if (expectedClass) {
generateAdvice(this, /* enter= */ true);
}
}
public void expectMethodExit() {
if (expectedClass) {
generateAdvice(this, /* enter= */ false);
}
}
}
private static void generateAdvice(final MethodVisitor methodVisitor, final boolean enter) {
methodVisitor.visitFieldInsn(
Opcodes.GETSTATIC, "java/lang/System", "err", "Ljava/io/PrintStream;");
......@@ -531,6 +509,28 @@ public class AdviceAdapterTest extends AsmTest {
assertThatClass(actualClassWriter.toByteArray()).isEqualTo(expectedClassWriter.toByteArray());
}
private static class MethodGenerator extends MethodVisitor {
private final boolean expectedClass;
MethodGenerator(final MethodVisitor methodVisitor, final boolean expectedClass) {
super(Opcodes.ASM7, methodVisitor);
this.expectedClass = expectedClass;
}
public void expectMethodEnter() {
if (expectedClass) {
generateAdvice(this, /* enter= */ true);
}
}
public void expectMethodExit() {
if (expectedClass) {
generateAdvice(this, /* enter= */ false);
}
}
}
private static class ReferenceClassAdapter extends ClassVisitor {
ReferenceClassAdapter(final int api, final ClassVisitor classVisitor) {
......
......@@ -187,12 +187,10 @@ public class AnalyzerAdapterTest extends AsmTest {
*/
private ArrayList<Object> toFrameTypes(final List<Object> analyzerTypes) {
ArrayList<Object> frameTypes = new ArrayList<Object>();
for (int i = 0; i < analyzerTypes.size(); ++i) {
for (int i = 0; i < analyzerTypes.size(); ) {
Object value = analyzerTypes.get(i);
frameTypes.add(value);
if (value == Opcodes.LONG || value == Opcodes.DOUBLE) {
++i;
}
i += (value == Opcodes.LONG || value == Opcodes.DOUBLE) ? 2 : 1;
}
return frameTypes;
}
......
......@@ -197,6 +197,18 @@ public class ClassRemapperTest extends AsmTest {
.when(classParameter.isMoreRecentThanCurrentJdk());
}
private static void checkDescriptor(final String descriptor) {
CheckMethodAdapter checkMethodAdapter = new CheckMethodAdapter(null);
checkMethodAdapter.visitCode();
checkMethodAdapter.visitFieldInsn(Opcodes.GETFIELD, "Owner", "name", descriptor);
}
private static void checkInternalName(final String internalName) {
CheckMethodAdapter checkMethodAdapter = new CheckMethodAdapter(null);
checkMethodAdapter.visitCode();
checkMethodAdapter.visitFieldInsn(Opcodes.GETFIELD, internalName, "name", "I");
}
static class UpperCaseRemapper extends Remapper {
private final String internalClassName;
......@@ -272,16 +284,4 @@ public class ClassRemapperTest extends AsmTest {
throw new IllegalArgumentException("Unsupported type of value: " + value);
}
}
private static void checkDescriptor(final String descriptor) {
CheckMethodAdapter checkMethodAdapter = new CheckMethodAdapter(null);
checkMethodAdapter.visitCode();
checkMethodAdapter.visitFieldInsn(Opcodes.GETFIELD, "Owner", "name", descriptor);
}
private static void checkInternalName(final String internalName) {
CheckMethodAdapter checkMethodAdapter = new CheckMethodAdapter(null);
checkMethodAdapter.visitCode();
checkMethodAdapter.visitFieldInsn(Opcodes.GETFIELD, internalName, "name", "I");
}
}
......@@ -1424,6 +1424,51 @@ public class JsrInlinerAdapterTest extends AsmTest {
assertMethodEquals(expectedMethod, inlinedMethod);
}
private void assertMethodEquals(final MethodNode expected, final MethodNode actual) {
String expectedText = getText(expected);
String actualText = getText(actual);
assertEquals(expectedText, actualText);
}
private String getText(final MethodNode methodNode) {
Textifier textifier = new Textifier();
methodNode.accept(new TraceMethodVisitor(textifier));
StringBuilder stringBuilder = new StringBuilder();
for (Object o : textifier.text) {
stringBuilder.append(o);
}
return stringBuilder.toString();
}
/** Tests that classes transformed with JSRInlinerAdapter can be loaded and instantiated. */
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_LATEST_API)
public void testInlineJsrAndInstantiate(
final PrecompiledClass classParameter, final Api apiParameter) {
ClassReader classReader = new ClassReader(classParameter.getBytes());
ClassWriter classWriter = new ClassWriter(0);
classReader.accept(
new ClassVisitor(apiParameter.value(), classWriter) {
@Override
public MethodVisitor visitMethod(
final int access,
final String name,
final String descriptor,
final String signature,
final String[] exceptions) {
MethodVisitor methodVisitor =
super.visitMethod(access, name, descriptor, signature, exceptions);
return new JSRInlinerAdapter(
api, methodVisitor, access, name, descriptor, signature, exceptions);
}
},
0);
assertThat(() -> loadAndInstantiate(classParameter.getName(), classWriter.toByteArray()))
.succeedsOrThrows(UnsupportedClassVersionError.class)
.when(classParameter.isMoreRecentThanCurrentJdk());
}
private static class Generator {
private final MethodNode methodNode;
......@@ -1568,49 +1613,4 @@ public class JsrInlinerAdapterTest extends AsmTest {
assertTrue(loadAndInstantiate("C", classWriter.toByteArray()));
}
}
private void assertMethodEquals(final MethodNode expected, final MethodNode actual) {
String expectedText = getText(expected);
String actualText = getText(actual);
assertEquals(expectedText, actualText);
}
private String getText(final MethodNode methodNode) {
Textifier textifier = new Textifier();
methodNode.accept(new TraceMethodVisitor(textifier));
StringBuilder stringBuilder = new StringBuilder();
for (Object o : textifier.text) {
stringBuilder.append(o);
}
return stringBuilder.toString();
}
/** Tests that classes transformed with JSRInlinerAdapter can be loaded and instantiated. */
@ParameterizedTest
@MethodSource(ALL_CLASSES_AND_LATEST_API)
public void testInlineJsrAndInstantiate(
final PrecompiledClass classParameter, final Api apiParameter) {
ClassReader classReader = new ClassReader(classParameter.getBytes());
ClassWriter classWriter = new ClassWriter(0);
classReader.accept(
new ClassVisitor(apiParameter.value(), classWriter) {
@Override
public MethodVisitor visitMethod(
final int access,
final String name,
final String descriptor,
final String signature,
final String[] exceptions) {
MethodVisitor methodVisitor =
super.visitMethod(access, name, descriptor, signature, exceptions);
return new JSRInlinerAdapter(
api, methodVisitor, access, name, descriptor, signature, exceptions);
}
},
0);
assertThat(() -> loadAndInstantiate(classParameter.getName(), classWriter.toByteArray()))
.succeedsOrThrows(UnsupportedClassVersionError.class)
.when(classParameter.isMoreRecentThanCurrentJdk());
}
}
......@@ -38,4 +38,6 @@ class SerialVersionAnonymousInnerClass implements Serializable {
public static final SerialVersionAnonymousInnerClass anonymousInnerClass =
new SerialVersionAnonymousInnerClass() {};
private SerialVersionAnonymousInnerClass() {}
}
......@@ -41,6 +41,8 @@ class SerialVersionClass implements Serializable {
static {
}
private SerialVersionClass() {}
public static Object[] someMethod() {
return null;
}
......
......@@ -68,32 +68,32 @@ public class SerialVersionUidAdderTest extends AsmTest {
}
@Test
public void testClass() throws Throwable {
public void testClass() throws IOException {
long actualSvuid = computeSerialVersionUid(SerialVersionClass.class.getName());
assertEquals(-5814053375729447190L, actualSvuid);
assertEquals(7983131370903707377L, actualSvuid);
}
@Test
public void testAnonymousInnerClass() throws Throwable {
public void testAnonymousInnerClass() throws IOException {
long actualSvuid =
computeSerialVersionUid(SerialVersionAnonymousInnerClass.class.getName() + "$1");
assertEquals(-1842070664294792585L, actualSvuid);
}
@Test
public void testInterface() throws Throwable {
public void testInterface() throws IOException {
long actualSvuid = computeSerialVersionUid(SerialVersionInterface.class.getName());
assertEquals(-3857402532274192875L, actualSvuid);
}
@Test
public void testEmptyInterface() throws Throwable {
public void testEmptyInterface() throws IOException {
long actualSvuid = computeSerialVersionUid(SerialVersionEmptyInterface.class.getName());
assertEquals(8675733916152748550L, actualSvuid);
}
@Test
public void testEnum() throws Throwable {
public void testEnum() throws IOException {
long actualSvuid = computeSerialVersionUid(SerialVersionEnum.class.getName());
assertEquals(0L, actualSvuid);
}
......
......@@ -327,48 +327,6 @@ public abstract class AsmTest {
return new ClassSubject(classFile);
}
/** Helper to make assertions about a class. */
public static class ClassSubject {
/** The content of the class to be tested. */
private final byte[] classFile;
ClassSubject(final byte[] classFile) {
this.classFile = classFile;
}
/**
* Asserts that a dump of the subject class into a string representation contains the given
* string.
*
* @param expectedString a string which should be contained in a dump of the subject class.
*/
public void contains(final String expectedString) {
try {
String dump = new ClassDump(classFile).toString();
assertTrue(dump.contains(expectedString));
} catch (IOException | IllegalArgumentException e) {
fail("Class can't be dumped", e);
}
}
/**
* Asserts that the subject class is equal to the given class, modulo some low level bytecode
* representation details (e.g. the order of the constants in the constant pool, the order of
* attributes and annotations, and low level details such as ldc vs ldc_w instructions).
*
* @param expectedClassFile a class file content which should be equal to the subject class.
*/
public void isEqualTo(final byte[] expectedClassFile) {
try {
String dump = new ClassDump(classFile).toString();
String expectedDump = new ClassDump(expectedClassFile).toString();
assertEquals(expectedDump, dump);
} catch (IOException | IllegalArgumentException e) {
fail("Class can't be dumped", e);
}
}
}
/**
* Loads the given class in a new class loader. Also tries to instantiate the loaded class (if it
* is not an abstract or enum class, or a module-info class), in order to check that it passes the
......@@ -433,6 +391,66 @@ public abstract class AsmTest {
return byteClassLoader.classLoaded();
}
private static byte[] getBytes(final String name) {
String resourceName = name.replace('.', '/') + ".class";
try (InputStream inputStream = ClassLoader.getSystemResourceAsStream(resourceName)) {
assertNotNull(inputStream, "Class not found " + name);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] data = new byte[INPUT_STREAM_DATA_CHUNK_SIZE];
int bytesRead;
while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) {
outputStream.write(data, 0, bytesRead);
}
outputStream.flush();
return outputStream.toByteArray();
} catch (IOException e) {
fail("Can't read " + name, e);
return new byte[0];
}
}
/** Helper to make assertions about a class. */
public static class ClassSubject {
/** The content of the class to be tested. */
private final byte[] classFile;
ClassSubject(final byte[] classFile) {
this.classFile = classFile;
}
/**
* Asserts that a dump of the subject class into a string representation contains the given
* string.
*
* @param expectedString a string which should be contained in a dump of the subject class.
*/
public void contains(final String expectedString) {
try {
String dump = new ClassDump(classFile).toString();
assertTrue(dump.contains(expectedString));
} catch (IOException | IllegalArgumentException e) {
fail("Class can't be dumped", e);
}
}
/**
* Asserts that the subject class is equal to the given class, modulo some low level bytecode
* representation details (e.g. the order of the constants in the constant pool, the order of
* attributes and annotations, and low level details such as ldc vs ldc_w instructions).
*
* @param expectedClassFile a class file content which should be equal to the subject class.
*/
public void isEqualTo(final byte[] expectedClassFile) {
try {
String dump = new ClassDump(classFile).toString();
String expectedDump = new ClassDump(expectedClassFile).toString();
assertEquals(expectedDump, dump);
} catch (IOException | IllegalArgumentException e) {
fail("Class can't be dumped", e);
}
}
}
/** A simple ClassLoader to test that a class can be loaded in the JVM. */
private static class ByteClassLoader extends ClassLoader {
private final String className;
......@@ -459,22 +477,4 @@ public abstract class AsmTest {
}
}
}
private static byte[] getBytes(final String name) {
String resourceName = name.replace('.', '/') + ".class";
try (InputStream inputStream = ClassLoader.getSystemResourceAsStream(resourceName)) {
assertNotNull(inputStream, "Class not found " + name);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] data = new byte[INPUT_STREAM_DATA_CHUNK_SIZE];
int bytesRead;
while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) {
outputStream.write(data, 0, bytesRead);
}
outputStream.flush();
return outputStream.toByteArray();
} catch (IOException e) {
fail("Can't read " + name, e);
return new byte[0];
}
}
}
......@@ -41,19 +41,19 @@ public class CheckModuleAdapter extends ModuleVisitor {
private final boolean isOpen;
/** The fully qualified names of the dependencies of the visited module. */
private final HashSet<String> requiredModules = new HashSet<String>();
private final NameSet requiredModules = new NameSet("Modules requires");
/** The internal names of the packages exported by the visited module. */
private final HashSet<String> exportedPackages = new HashSet<String>();
private final NameSet exportedPackages = new NameSet("Module exports");
/** The internal names of the packages opened by the visited module. */
private final HashSet<String> openedPackages = new HashSet<String>();
private final NameSet openedPackages = new NameSet("Module opens");
/** The internal names of the services used by the visited module. */
private final HashSet<String> usedServices = new HashSet<String>();
private final NameSet usedServices = new NameSet("Module uses");
/** The internal names of the services provided by the visited module. */
private final HashSet<String> providedServices = new HashSet<String>();
private final NameSet providedServices = new NameSet("Module provides");
/** The class version number. */
int classVersion;
......@@ -109,7 +109,7 @@ public class CheckModuleAdapter extends ModuleVisitor {
public void visitRequire(final String module, final int access, final String version) {
checkVisitEndNotCalled();
CheckClassAdapter.checkFullyQualifiedName(Opcodes.V9, module, "required module");
checkNameNotAlreadyDeclared(module, requiredModules, "Modules requires");
requiredModules.checkNameNotAlreadyDeclared(module);
CheckClassAdapter.checkAccess(
access,
Opcodes.ACC_STATIC_PHASE
......@@ -131,7 +131,7 @@ public class CheckModuleAdapter extends ModuleVisitor {
public void visitExport(final String packaze, final int access, final String... modules) {
checkVisitEndNotCalled();
CheckMethodAdapter.checkInternalName(Opcodes.V9, packaze, "package name");
checkNameNotAlreadyDeclared(packaze, exportedPackages, "Module exports");
exportedPackages.checkNameNotAlreadyDeclared(packaze);