Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/main/java/org/plumelib/reflection/ReflectionPlume.java
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ public static boolean isSubtype(Class<?> sub, Class<?> sup) {
* fully-qualified name (in addition to a binary name).
*
* <p>If the given name can't be found, this method changes the last '.' to a dollar sign ($) and
* tries again. This accounts for inner classes that are incorrectly passed in fully-qualified
* format instead of binary format. (It should try multiple dollar signs, not just at the last
* position.)
* tries again, repeating with successive dots (from right to left) until the class is found or no
* dots remain. This accounts for inner classes that are incorrectly passed in fully-qualified
* format instead of binary format.
*
* <p>Recall the rather odd specification for {@link Class#forName(String)}: the argument is a
* binary name for non-arrays, but a field descriptor for arrays. This method uses the same rules,
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/plumelib/reflection/SignatureRegexes.java
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,8 @@ private SignatureRegexes() {

/** An anchored regex that matches FieldDescriptorWithoutPackage strings. */
public static final @Regex String FieldDescriptorWithoutPackageRegex =
ANCHORED("(" + FD_PRIMITIVE + "|\\[+" + FD_PRIMITIVE + "|\\[L" + IDENTIFIER + NESTED + ";)");
// Non-arrays of reference types are field descriptors, but not "without package" ones.
ANCHORED("(\\[*" + FD_PRIMITIVE + "|\\[+L" + IDENTIFIER + NESTED + ";)");

/** An anchored pattern that matches FieldDescriptorWithoutPackage strings. */
public static final Pattern FieldDescriptorWithoutPackagePattern =
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/plumelib/reflection/Signatures.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ private Signatures() {
if (!classfilename.endsWith(".class")) {
throw new IllegalArgumentException("Bad class file name: " + classfilename);
}
@SuppressWarnings("index:assignment") // "/" is not the last character
@IndexFor("classfilename") int start = classfilename.lastIndexOf('/') + 1;
@SuppressWarnings("index:assignment") // the separator is not the last character
@IndexFor("classfilename") int start = Math.max(classfilename.lastIndexOf('/'), classfilename.lastIndexOf(dirSep)) + 1;
int end = classfilename.length() - 6;
return classfilename.substring(start, end);
}
Expand Down
39 changes: 39 additions & 0 deletions src/test/java/org/plumelib/reflection/TestSignatures.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.Collections;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.signature.qual.BinaryName;
import org.checkerframework.checker.signature.qual.ClassGetName;
Expand Down Expand Up @@ -161,6 +162,44 @@ void test_isFqBinaryName() {
assertTrue(Signatures.isFqBinaryName("pkg.Outer$Inner[][]"));
}

/**
* A field descriptor for an array: {@code dims} copies of "[" followed by {@code base}.
*
* <p>Multidimensional field descriptors are constructed here at run time rather than written as
* source literals, to avoid a crash in the Checker Framework's literal annotator. Its bundled
* FieldDescriptorWithoutPackage regex predates the fix that makes strings such as "[[LMyClass;"
* match, so writing them as literals triggers a {@code BugInCF}.
*
* @param dims the number of array dimensions
* @param base the field descriptor of the element type
* @return a field descriptor for the array type
*/
private static String nDimArray(@NonNegative int dims, String base) {
return "[".repeat(dims) + base;
}

/** Returns true if the argument has the format of a FieldDescriptorWithoutPackage. */
@Test
void test_isFieldDescriptorWithoutPackage() {
assertTrue(Signatures.isFieldDescriptorWithoutPackage("I"));
assertTrue(Signatures.isFieldDescriptorWithoutPackage("[[J"));
assertTrue(Signatures.isFieldDescriptorWithoutPackage("[LMyClass;"));
assertTrue(Signatures.isFieldDescriptorWithoutPackage("[LMyClass$22;"));
// Multidimensional arrays of unnamed-package classes (see nDimArray).
assertTrue(Signatures.isFieldDescriptorWithoutPackage(nDimArray(2, "LMyClass;")));
assertTrue(Signatures.isFieldDescriptorWithoutPackage(nDimArray(3, "LMyClass;")));
assertTrue(Signatures.isFieldDescriptorWithoutPackage(nDimArray(2, "LMyClass$22;")));
// Non-arrays of reference types are field descriptors, but not "without package" ones.
assertTrue(!Signatures.isFieldDescriptorWithoutPackage("LMyClass;"));
// A field descriptor whose element type has a package is not "without package".
assertTrue(!Signatures.isFieldDescriptorWithoutPackage("Ljava/lang/Object;"));
assertTrue(!Signatures.isFieldDescriptorWithoutPackage("[Ljava/lang/Object;"));
assertTrue(!Signatures.isFieldDescriptorWithoutPackage(nDimArray(2, "Ljava/lang/Object;")));
// Not field descriptors at all.
assertTrue(!Signatures.isFieldDescriptorWithoutPackage("int"));
assertTrue(!Signatures.isFieldDescriptorWithoutPackage("MyClass"));
}

/**
* Returns true if the argument has the format of an Identifier. The type it refers to might or
* might not exist.
Expand Down