# HG changeset patch # User redestad # Date 1481194704 -3600 # Thu Dec 08 11:58:24 2016 +0100 # Node ID da12e755ba8eb760aad39c333afefc008960a15a # Parent 5c9389804cbceb2a43815b71be0f3c98b9f5dc75 8170595: Optimize Class.isAnonymousClass Reviewed-by: mchung, darcy Contributed-by: claes.redestad@oracle.com, christoph.dreis@freenet.de diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java --- a/src/java.base/share/classes/java/lang/Class.java +++ b/src/java.base/share/classes/java/lang/Class.java @@ -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; } /** diff --git a/test/java/lang/Class/attributes/ClassAttributesTest.java b/test/java/lang/Class/attributes/ClassAttributesTest.java new file mode 100644 --- /dev/null +++ b/test/java/lang/Class/attributes/ClassAttributesTest.java @@ -0,0 +1,74 @@ +/* + * 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 { + + static class StaticNestedClass {} + 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 r = () -> 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); + failures += test(r.getClass(), false, false, false); + + if (failures != 0) + throw new RuntimeException("Test failed with " + failures + " failures."); + } +}