changeset 16243:98665911deff

8170595: Optimize Class.isAnonymousClass, isLocalClass, and isMemberClass Reviewed-by: mchung, darcy Contributed-by: claes.redestad@oracle.com, christoph.dreis@freenet.de
author redestad
date Sun, 11 Dec 2016 12:20:45 +0100
parents 688318f6e1a5
children 3bc6686e3883
files src/java.base/share/classes/java/lang/Class.java test/java/lang/Class/attributes/ClassAttributesTest.java
diffstat 2 files changed, 116 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/lang/Class.java	Sat Dec 10 14:19:53 2016 +0000
+++ b/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/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.");
+    }
+}