8170595: Optimize Class.isAnonymousClass, isLocalClass, and isMemberClass
Reviewed-by: mchung, darcy
Contributed-by: claes.redestad@oracle.com, christoph.dreis@freenet.de
--- a/jdk/src/java.base/share/classes/java/lang/Class.java Sat Dec 10 14:19:53 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/Class.java Sun Dec 11 12:20:45 2016 +0100
@@ -1277,33 +1277,40 @@
}
private static final class EnclosingMethodInfo {
- private Class<?> enclosingClass;
- private String name;
- private String descriptor;
-
- private EnclosingMethodInfo(Object[] enclosingInfo) {
+ private final Class<?> enclosingClass;
+ private final String name;
+ private final String descriptor;
+
+ static void validate(Object[] enclosingInfo) {
if (enclosingInfo.length != 3)
throw new InternalError("Malformed enclosing method information");
try {
// The array is expected to have three elements:
// the immediately enclosing class
- enclosingClass = (Class<?>) enclosingInfo[0];
+ Class<?> enclosingClass = (Class<?>)enclosingInfo[0];
assert(enclosingClass != null);
// the immediately enclosing method or constructor's
// name (can be null).
- name = (String) enclosingInfo[1];
+ String name = (String)enclosingInfo[1];
// the immediately enclosing method or constructor's
// descriptor (null iff name is).
- descriptor = (String) enclosingInfo[2];
+ String descriptor = (String)enclosingInfo[2];
assert((name != null && descriptor != null) || name == descriptor);
} catch (ClassCastException cce) {
throw new InternalError("Invalid type in enclosing method information", cce);
}
}
+ EnclosingMethodInfo(Object[] enclosingInfo) {
+ validate(enclosingInfo);
+ this.enclosingClass = (Class<?>)enclosingInfo[0];
+ this.name = (String)enclosingInfo[1];
+ this.descriptor = (String)enclosingInfo[2];
+ }
+
boolean isPartial() {
return enclosingClass == null || name == null || descriptor == null;
}
@@ -1481,7 +1488,7 @@
if (enclosingInfo == null) {
// This is a top level or a nested class or an inner class (a, b, or c)
- enclosingCandidate = getDeclaringClass();
+ enclosingCandidate = getDeclaringClass0();
} else {
Class<?> enclosingClass = enclosingInfo.getEnclosingClass();
// This is a local class or an anonymous class (d or e)
@@ -1548,14 +1555,6 @@
}
/**
- * Character.isDigit answers {@code true} to some non-ascii
- * digits. This one does not.
- */
- private static boolean isAsciiDigit(char c) {
- return '0' <= c && c <= '9';
- }
-
- /**
* Returns the canonical name of the underlying class as
* defined by the Java Language Specification. Returns null if
* the underlying class does not have a canonical name (i.e., if
@@ -1594,7 +1593,8 @@
* @since 1.5
*/
public boolean isAnonymousClass() {
- return "".equals(getSimpleName());
+ return !isArray() && isLocalOrAnonymousClass() &&
+ getSimpleBinaryName0() == null;
}
/**
@@ -1605,7 +1605,8 @@
* @since 1.5
*/
public boolean isLocalClass() {
- return isLocalOrAnonymousClass() && !isAnonymousClass();
+ return isLocalOrAnonymousClass() &&
+ (isArray() || getSimpleBinaryName0() != null);
}
/**
@@ -1616,7 +1617,7 @@
* @since 1.5
*/
public boolean isMemberClass() {
- return getSimpleBinaryName() != null && !isLocalOrAnonymousClass();
+ return !isLocalOrAnonymousClass() && getDeclaringClass0() != null;
}
/**
@@ -1626,8 +1627,7 @@
* class.
*/
private String getSimpleBinaryName() {
- Class<?> enclosingClass = getEnclosingClass();
- if (enclosingClass == null) // top level class
+ if (isTopLevelClass())
return null;
String name = getSimpleBinaryName0();
if (name == null) // anonymous class
@@ -1638,6 +1638,14 @@
private native String getSimpleBinaryName0();
/**
+ * Returns {@code true} if this is a top level class. Returns {@code false}
+ * otherwise.
+ */
+ private boolean isTopLevelClass() {
+ return !isLocalOrAnonymousClass() && getDeclaringClass0() == null;
+ }
+
+ /**
* Returns {@code true} if this is a local class or an anonymous
* class. Returns {@code false} otherwise.
*/
@@ -1645,7 +1653,16 @@
// JVM Spec 4.7.7: A class must have an EnclosingMethod
// attribute if and only if it is a local class or an
// anonymous class.
- return getEnclosingMethodInfo() != null;
+ return hasEnclosingMethodInfo();
+ }
+
+ private boolean hasEnclosingMethodInfo() {
+ Object[] enclosingInfo = getEnclosingMethod0();
+ if (enclosingInfo != null) {
+ EnclosingMethodInfo.validate(enclosingInfo);
+ return true;
+ }
+ return false;
}
/**
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/Class/attributes/ClassAttributesTest.java Sun Dec 11 12:20:45 2016 +0100
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8170595
+ * @summary Checks Class.isAnonymousClass, isMemberClass and isLocalClass
+ * attributes
+ */
+
+public class ClassAttributesTest {
+
+ class NestedClass {}
+
+ static int test(Class<?> clazz, boolean anonymous, boolean local, boolean member) {
+ if (clazz.isAnonymousClass() != anonymous) {
+ System.err.println("Unexpected isAnonymousClass value for " +
+ clazz.getName() + " expected: " + anonymous +
+ " got: " + (!anonymous));
+ return 1;
+ }
+ if (clazz.isLocalClass() != local) {
+ System.err.println("Unexpected isLocalClass value for " +
+ clazz.getName() + " expected: " + local +
+ " got: " + (!local));
+ return 1;
+ }
+ if (clazz.isMemberClass() != member) {
+ System.err.println("Unexpected isMemberClass status for " +
+ clazz.getName() + " expected: " + member +
+ " got: " + (!member));
+ return 1;
+ }
+ return 0;
+ }
+
+ public static void main(String argv[]) {
+ int failures = 0;
+
+ class LocalClass {}
+ Cloneable clone = new Cloneable() {};
+ Runnable lambda = () -> System.out.println("run");
+
+ failures += test(ClassAttributesTest.class, false, false, false);
+ failures += test(NestedClass.class, false, false, true);
+ failures += test(LocalClass.class, false, true, false);
+ failures += test(clone.getClass(), true, false, false);
+
+ // Lambdas may be VM anonymous classes, but are named, non-local classes
+ // in this sense
+ failures += test(lambda.getClass(), false, false, false);
+
+ if (failures != 0)
+ throw new RuntimeException("Test failed with " + failures + " failures.");
+ }
+}