changeset 56950:6ef931c06a1a records-and-sealed

make JVM_GetPermittedSubtypes return an array of Strings
author vromero
date Mon, 29 Jul 2019 18:02:50 -0400
parents 7880389d8172
children 960e2b3680da 29bd151b3e52
files src/hotspot/share/classfile/classFileParser.cpp src/hotspot/share/prims/jvm.cpp src/java.base/share/classes/java/lang/Class.java src/java.base/share/native/libjava/Class.c test/hotspot/jtreg/runtime/sealedTypes/abstractSealedTest.java test/hotspot/jtreg/runtime/sealedTypes/getPermittedSubtypesTest.java test/hotspot/jtreg/runtime/sealedTypes/noLoadSubtypes.jcod test/hotspot/jtreg/runtime/sealedTypes/overrideSealedTest.java
diffstat 8 files changed, 367 insertions(+), 43 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/share/classfile/classFileParser.cpp	Tue Jul 23 18:36:43 2019 -0400
+++ b/src/hotspot/share/classfile/classFileParser.cpp	Mon Jul 29 18:02:50 2019 -0400
@@ -1749,7 +1749,6 @@
       _fields->at_put(i++, fa[j]);
     }
     assert(_fields->length() == i, "");
-    //tty->print_cr("length of the _fields array %d for class %s", i, _class_name->as_klass_external_name());
   }
 
   if (_need_verify && length > 1) {
@@ -4759,8 +4758,9 @@
         Exceptions::fthrow(
           THREAD_AND_LOCATION,
           vmSymbols::java_lang_VerifyError(),
-          "class %s cannot implement sealed interface %s",
+          "class %s cannot %s sealed interface %s",
           this_klass->external_name(),
+          this_klass->is_interface() ? "extend" : "implement",
           k->external_name());
         return;
       }
@@ -4906,10 +4906,10 @@
   const bool is_enum       = (flags & JVM_ACC_ENUM)       != 0;
   const bool is_annotation = (flags & JVM_ACC_ANNOTATION) != 0;
   const bool major_gte_1_5 = _major_version >= JAVA_1_5_VERSION;
-  const bool major_gte_12  = _major_version >= JAVA_12_VERSION;
-
-  if ((is_abstract && is_final && !major_gte_12) ||
-      (is_interface && !is_abstract && !major_gte_12) ||
+  const bool major_gte_14  = _major_version >= JAVA_14_VERSION;
+
+  if ((is_abstract && is_final && !major_gte_14) ||
+      (is_interface && !is_abstract) ||
       (is_interface && major_gte_1_5 && (is_super || is_enum)) ||
       (!is_interface && major_gte_1_5 && is_annotation)) {
     ResourceMark rm(THREAD);
--- a/src/hotspot/share/prims/jvm.cpp	Tue Jul 23 18:36:43 2019 -0400
+++ b/src/hotspot/share/prims/jvm.cpp	Mon Jul 29 18:02:50 2019 -0400
@@ -1910,35 +1910,30 @@
   JVMWrapper("JVM_GetPermittedSubtypes");
   Klass* c = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(current));
   assert(c->is_instance_klass(), "must be");
-  InstanceKlass* ck = InstanceKlass::cast(c);
-  Symbol* icce = vmSymbols::java_lang_IncompatibleClassChangeError();
+  InstanceKlass* ik = InstanceKlass::cast(c);
+
   {
     JvmtiVMObjectAllocEventCollector oam;
-    Array<u2>* subtypes = ck->permitted_subtypes();
+    Array<u2>* subtypes = ik->permitted_subtypes();
     int length = subtypes == NULL ? 0 : subtypes->length();
-    if (length == 0) {
-        return NULL;
+    if (length != 0) {
+      objArrayOop r = oopFactory::new_objArray(SystemDictionary::String_klass(),
+                                               length, CHECK_NULL);
+      objArrayHandle result (THREAD, r);
+      int i;
+      for (i = 0; i < length; i++) {
+        int cp_index = subtypes->at(i);
+        // This returns <package-name>/<class-name>.
+        Symbol* klass_name = ik->constants()->klass_name_at(cp_index);
+        assert(klass_name != NULL, "Unexpected null klass_name");
+        Handle perm_subtype_h = java_lang_String::create_from_symbol(klass_name, CHECK_NULL);
+        result->obj_at_put(i, perm_subtype_h());
+      }
+      return (jobjectArray)JNIHandles::make_local(THREAD, result());
+    } else {
+      objArrayOop result = oopFactory::new_objArray(SystemDictionary::String_klass(), 0, CHECK_NULL);
+      return (jobjectArray)JNIHandles::make_local(env, result);
     }
-    objArrayOop r = oopFactory::new_objArray(SystemDictionary::Class_klass(), length, CHECK_NULL);
-    objArrayHandle result (THREAD, r);
-    int i;
-    for (i = 0; i < length; i++) {
-      int cp_index = subtypes->at(i);
-      Klass* k = ck->constants()->klass_at(cp_index, CHECK_NULL);
-      if (k->is_instance_klass()) {
-        result->obj_at_put(i, k->java_mirror());
-      } else {
-        ResourceMark rm(THREAD);
-        Exceptions::fthrow(THREAD_AND_LOCATION,
-                           icce,
-                           "Class %s can not be a permitted subtype of %s",
-                           k->external_name(),
-                           ck->external_name()
-                           );
-        return NULL;
-      }
-    }
-    return (jobjectArray)JNIHandles::make_local(THREAD, result());
   }
 }
 JVM_END
--- a/src/java.base/share/classes/java/lang/Class.java	Tue Jul 23 18:36:43 2019 -0400
+++ b/src/java.base/share/classes/java/lang/Class.java	Mon Jul 29 18:02:50 2019 -0400
@@ -4136,26 +4136,35 @@
     }
 
     /**
-     * Returns an array containing {@code Class} objects representing all the permitted subtypes of this class
-     * if it is sealed. Returns an empty array if this class is not sealed.
-     * @return an array of all the permitted subtypes of this class
-     * @since 12
+     * Returns an array containing {@code ClassDesc} objects representing all the
+     * permitted subtypes of this class if it is sealed. Returns an empty array if this
+     * class is not sealed.
+     * @return an array of class descriptors of all the permitted subtypes of this class
+     * @throws IllegalArgumentException if a class descriptor is not in the correct format
+     * @since 14
      */
-    public Class<?>[] getPermittedSubtypes() {
-        Class<?>[] result = getPermittedSubtypes0();
-        return (result == null) ?
-            new Class<?>[0] :
-            result;
+    public ClassDesc[] getPermittedSubtypes() {
+        String[] descriptors = getPermittedSubtypes0();
+        if (descriptors == null || descriptors.length == 0) {
+            return new ClassDesc[0];
+        }
+        ClassDesc[] constants = new ClassDesc[descriptors.length];
+        int i = 0;
+        for (String descriptor : descriptors) {
+            ClassDesc cd = ClassDesc.of(descriptor);
+            constants[i++] = cd;
+        }
+        return constants;
     }
 
     /**
      * Returns true if this class or interface is sealed.
      * @return returns true if the class or interface is sealed
-     * @since 12
+     * @since 14
      */
     public boolean isSealed() {
         return Modifier.isFinal(getModifiers()) && getPermittedSubtypes().length != 0;
     }
 
-    private native Class<?>[] getPermittedSubtypes0();
+    private native String[] getPermittedSubtypes0();
 }
--- a/src/java.base/share/native/libjava/Class.c	Tue Jul 23 18:36:43 2019 -0400
+++ b/src/java.base/share/native/libjava/Class.c	Mon Jul 29 18:02:50 2019 -0400
@@ -76,7 +76,7 @@
     {"getRawTypeAnnotations", "()" BA,          (void *)&JVM_GetClassTypeAnnotations},
     {"getNestHost0",         "()" CLS,          (void *)&JVM_GetNestHost},
     {"getNestMembers0",      "()[" CLS,         (void *)&JVM_GetNestMembers},
-    {"getPermittedSubtypes0", "()[" CLS,        (void *)&JVM_GetPermittedSubtypes},
+    {"getPermittedSubtypes0", "()[" STR,        (void *)&JVM_GetPermittedSubtypes},
     {"getRecordComponentNames0",  "()[" STR,    (void *)&JVM_GetRecordComponentNames},
     {"isRecord0",                 "()Z",        (void *)&JVM_IsRecord},
 };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/sealedTypes/abstractSealedTest.java	Mon Jul 29 18:02:50 2019 -0400
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019, 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
+ * @run main abstractSealedTest
+ */
+
+// Test that sealed class abstractShape can be both ACC_FINAL and ACC_ABSTRACT
+public class abstractSealedTest {
+
+    abstract sealed class abstractShape permits Circle {
+        abstract void draw();
+    }
+
+    class Circle extends abstractShape {
+        void draw() {}
+    }
+
+    Circle circle = new Circle();
+
+    public static void main(String... args) { }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/sealedTypes/getPermittedSubtypesTest.java	Mon Jul 29 18:02:50 2019 -0400
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2019, 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
+ * @compile noLoadSubtypes.jcod
+ * @run main getPermittedSubtypesTest
+ */
+
+import java.lang.constant.ClassDesc;
+import java.util.ArrayList;
+
+// Test Class.getPermittedSubtpes() and Class.isSealed() APIs.
+public class getPermittedSubtypesTest {
+
+    sealed class Sealed1 permits Sub1 {}
+
+    class Sub1 extends Sealed1 {}
+
+    sealed interface SealedI1 permits notSealed, Sub1, Extender {}
+
+    interface Extender extends SealedI1 { }
+
+    class notSealed implements SealedI1 {}
+
+    sealed class noPermits {}
+
+    final class Final4 {}
+
+    public static void testSealedInfo(Class<?> c, String[] expected) {
+        Object[] permitted = c.getPermittedSubtypes();
+
+        if (permitted.length != expected.length) {
+            throw new RuntimeException(
+                "Unexpected number of permitted subtypes for: " + c.toString());
+        }
+
+        if (permitted.length > 0) {
+            if (!c.isSealed()) {
+                throw new RuntimeException("Expected sealed type: " + c.toString());
+            }
+
+            // Create ArrayList of permitted subtypes class names.
+            ArrayList<String> permittedNames = new ArrayList<String>();
+            for (int i = 0; i < permitted.length; i++) {
+                permittedNames.add(((ClassDesc)permitted[i]).displayName());
+            }
+
+            if (permittedNames.size() != expected.length) {
+                throw new RuntimeException(
+                    "Unexpected number of permitted names for: " + c.toString());
+            }
+
+            // Check that expected class names are in the permitted subtypes list.
+            for (int i = 0; i < expected.length; i++) {
+                if (!permittedNames.contains(expected[i])) {
+                    throw new RuntimeException(
+                         "Expected class not found in permitted subtypes list, super type: " +
+                         c.getName() + ", expected class: " + expected[i]);
+                }
+            }
+        } else {
+            // Must not be sealed if no permitted subtypes.
+            if (c.isSealed()) {
+                throw new RuntimeException("Unexpected sealed type: " + c.toString());
+            }
+        }
+    }
+
+    public static void main(String... args) {
+        testSealedInfo(SealedI1.class, new String[] {"getPermittedSubtypesTest$notSealed",
+                                                     "getPermittedSubtypesTest$Sub1",
+                                                     "getPermittedSubtypesTest$Extender"});
+        testSealedInfo(Sealed1.class, new String[] {"getPermittedSubtypesTest$Sub1"});
+        testSealedInfo(noPermits.class, new String[] { });
+        testSealedInfo(Final4.class, new String[] { });
+        testSealedInfo(notSealed.class, new String[] { });
+        // Test returning names of non-existing classes.
+        try {
+            testSealedInfo(noLoadSubtypes.class, new String[]{"iDontExist",
+                    "I/Dont/Exist/Either"});
+            throw new AssertionError("should fail");
+        } catch (IllegalArgumentException iae) {
+            // ok
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/sealedTypes/noLoadSubtypes.jcod	Mon Jul 29 18:02:50 2019 -0400
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+// This class has PermittedSubtypes that do not exist.  Test that this does
+// not prevent JVM_GetPermittedSubtypes() from returning their names.
+//
+// sealed class noLoadSubtypes permits iDontExist, I/Dont/Exist/Either { }
+//
+class noLoadSubtypes {
+  0xCAFEBABE;
+  0; // minor version
+  58; // version
+  [18] { // Constant Pool
+    ; // first element is empty
+    Method #2 #3; // #1     at 0x0A
+    class #4; // #2     at 0x0F
+    NameAndType #5 #6; // #3     at 0x12
+    Utf8 "java/lang/Object"; // #4     at 0x17
+    Utf8 "<init>"; // #5     at 0x2A
+    Utf8 "()V"; // #6     at 0x33
+    class #8; // #7     at 0x39
+    Utf8 "noLoadSubtypes"; // #8     at 0x3C
+    Utf8 "Code"; // #9     at 0x4D
+    Utf8 "LineNumberTable"; // #10     at 0x54
+    Utf8 "SourceFile"; // #11     at 0x66
+    Utf8 "noLoadSubtypes.java"; // #12     at 0x73
+    Utf8 "PermittedSubtypes"; // #13     at 0x89
+    class #15; // #14     at 0x9D
+    Utf8 "iDontExist"; // #15     at 0xA0
+    class #17; // #16     at 0xAA
+    Utf8 "I/Dont/Exist/Either"; // #17     at 0xAD
+  } // Constant Pool
+
+  0x0030; // access [ ACC_SUPER ACC_FINAL ]
+  #7;// this_cpx
+  #2;// super_cpx
+
+  [0] { // Interfaces
+  } // Interfaces
+
+  [0] { // fields
+  } // fields
+
+  [1] { // methods
+    { // Member at 0xC1
+      0x0000; // access
+      #5; // name_cpx
+      #6; // sig_cpx
+      [1] { // Attributes
+        Attr(#9, 29) { // Code at 0xC9
+          1; // max_stack
+          1; // max_locals
+          Bytes[5]{
+            0x2AB70001B1;
+          }
+          [0] { // Traps
+          } // end Traps
+          [1] { // Attributes
+            Attr(#10, 6) { // LineNumberTable at 0xE0
+              [1] { // LineNumberTable
+                0  1; //  at 0xEC
+              }
+            } // end LineNumberTable
+          } // Attributes
+        } // end Code
+      } // Attributes
+    } // Member
+  } // methods
+
+  [2] { // Attributes
+    Attr(#11, 2) { // SourceFile at 0xEE
+      #12;
+    } // end SourceFile
+    ;
+    Attr(#13, 6) { // PermittedSubtypes at 0xF6
+      0x0002000E0010;
+    } // end PermittedSubtypes
+  } // Attributes
+} // end class noLoadSubtypes
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/sealedTypes/overrideSealedTest.java	Mon Jul 29 18:02:50 2019 -0400
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2019, 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
+ * @run main overrideSealedTest
+ */
+
+// Test that a method in a sealed class or interface can be overridden.
+public class overrideSealedTest {
+
+    sealed class Rectangle permits Square {
+        public String draw() { return "rectangle"; }
+    }
+
+    class Square extends Rectangle {
+        public String draw() { return "square"; }
+    }
+
+    Rectangle r = new Rectangle();
+    Rectangle rs = new Square();
+    Square s = new Square();
+
+
+    public sealed interface Shape permits Circle {
+        default String name() { return "shape"; }
+    }
+
+    class Circle implements Shape {
+        public String name() { return "circle"; }
+    }
+
+    Shape sc = new Circle();
+    Circle c = new Circle();
+
+
+    public static void main(String... args) {
+        overrideSealedTest ost = new overrideSealedTest();
+        if (ost.r.draw() != "rectangle")
+            throw new RuntimeException("Bad value returned by draw(): " + ost.r.draw());
+        if (ost.rs.draw() != "square")
+            throw new RuntimeException("Bad value returned by draw(): " + ost.rs.draw());
+        if (ost.s.draw() != "square")
+            throw new RuntimeException("Bad value returned by draw(): " + ost.s.draw());
+
+        if (ost.sc.name() != "circle")
+            throw new RuntimeException("Bad value returned by name(): " + ost.sc.name());
+        if (ost.c.name() != "circle")
+            throw new RuntimeException("Bad value returned by name(): " + ost.c.name());
+    }
+}