Skip to content

Commit

Permalink
8260517: implement Sealed Classes as a standard feature in Java
Browse files Browse the repository at this point in the history
Co-authored-by: Harold Seigel <hseigel@openjdk.org>
Co-authored-by: Vicente Romero <vromero@openjdk.org>
Reviewed-by: dholmes, mcimadamore, jlahoda
  • Loading branch information
Vicente Romero and Harold Seigel committed May 20, 2021
1 parent 31b98e1 commit 0fa9223
Show file tree
Hide file tree
Showing 54 changed files with 169 additions and 393 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@
import com.sun.tools.classfile.Module_attribute.RequiresEntry;
import com.sun.tools.classfile.NestHost_attribute;
import com.sun.tools.classfile.NestMembers_attribute;
import com.sun.tools.classfile.PermittedSubclasses_attribute;
import com.sun.tools.classfile.Record_attribute;
import com.sun.tools.classfile.Record_attribute.ComponentInfo;
import com.sun.tools.classfile.RuntimeAnnotations_attribute;
Expand Down Expand Up @@ -978,6 +979,16 @@ private void addAttributes(ClassHeaderDescription header,
attributes.put(Attribute.Record,
new Record_attribute(attributeString, recordComponents));
}
if (header.isSealed) {
int attributeString = addString(constantPool, Attribute.PermittedSubclasses);
int[] subclasses = new int[header.permittedSubclasses.size()];
int i = 0;
for (String intf : header.permittedSubclasses) {
subclasses[i++] = addClass(constantPool, intf);
}
attributes.put(Attribute.PermittedSubclasses,
new PermittedSubclasses_attribute(attributeString, subclasses));
}
addInnerClassesAttribute(header, constantPool, attributes);
}

Expand Down Expand Up @@ -2229,6 +2240,16 @@ private boolean readAttribute(ClassFile cf, FeatureDescription feature, Attribut
}
break;
}
case Attribute.PermittedSubclasses: {
assert feature instanceof ClassHeaderDescription;
PermittedSubclasses_attribute permittedSubclasses = (PermittedSubclasses_attribute) attr;
ClassHeaderDescription chd = (ClassHeaderDescription) feature;
chd.permittedSubclasses = Arrays.stream(permittedSubclasses.subtypes)
.mapToObj(i -> getClassName(cf, i))
.collect(Collectors.toList());
chd.isSealed = true;
break;
}
default:
throw new IllegalStateException("Unhandled attribute: " +
attrName);
Expand Down Expand Up @@ -3077,6 +3098,8 @@ static class ClassHeaderDescription extends HeaderDescription {
List<String> nestMembers;
boolean isRecord;
List<RecordComponentDescription> recordComponents;
boolean isSealed;
List<String> permittedSubclasses;

@Override
public int hashCode() {
Expand All @@ -3087,6 +3110,8 @@ public int hashCode() {
hash = 17 * hash + Objects.hashCode(this.nestMembers);
hash = 17 * hash + Objects.hashCode(this.isRecord);
hash = 17 * hash + Objects.hashCode(this.recordComponents);
hash = 17 * hash + Objects.hashCode(this.isSealed);
hash = 17 * hash + Objects.hashCode(this.permittedSubclasses);
return hash;
}

Expand Down Expand Up @@ -3117,6 +3142,12 @@ public boolean equals(Object obj) {
if (!listEquals(this.recordComponents, other.recordComponents)) {
return false;
}
if (this.isSealed != other.isSealed) {
return false;
}
if (!listEquals(this.permittedSubclasses, other.permittedSubclasses)) {
return false;
}
return true;
}

Expand All @@ -3137,6 +3168,9 @@ public void write(Appendable output, String baselineVersion, String version) thr
if (isRecord) {
output.append(" record true");
}
if (isSealed) {
output.append(" sealed true");
}
writeAttributes(output);
output.append("\n");
writeRecordComponents(output, baselineVersion, version);
Expand All @@ -3163,6 +3197,11 @@ public boolean read(LineBasedReader reader) throws IOException {
readRecordComponents(reader);
}
readInnerClasses(reader);
isSealed = reader.attributes.containsKey("permittedSubclasses");
if (isSealed) {
String subclassesList = reader.attributes.get("permittedSubclasses");
permittedSubclasses = deserializeList(subclassesList);
}

return true;
}
Expand Down
12 changes: 3 additions & 9 deletions src/hotspot/share/classfile/classFileParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3532,12 +3532,6 @@ void ClassFileParser::parse_classfile_bootstrap_methods_attribute(const ClassFil
CHECK);
}

bool ClassFileParser::supports_sealed_types() {
return _major_version == JVM_CLASSFILE_MAJOR_VERSION &&
_minor_version == JAVA_PREVIEW_MINOR_VERSION &&
Arguments::enable_preview();
}

void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cfs,
ConstantPool* cp,
ClassFileParser::ClassAnnotationCollector* parsed_annotations,
Expand Down Expand Up @@ -3794,8 +3788,8 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf
parsed_record_attribute = true;
record_attribute_start = cfs->current();
record_attribute_length = attribute_length;
} else if (tag == vmSymbols::tag_permitted_subclasses()) {
if (supports_sealed_types()) {
} else if (_major_version >= JAVA_17_VERSION) {
if (tag == vmSymbols::tag_permitted_subclasses()) {
if (parsed_permitted_subclasses_attribute) {
classfile_parse_error("Multiple PermittedSubclasses attributes in class file %s", CHECK);
return;
Expand All @@ -3810,7 +3804,7 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf
permitted_subclasses_attribute_length = attribute_length;
}
}
// Skip attribute_length for any attribute where major_verson >= JAVA_16_VERSION
// Skip attribute_length for any attribute where major_verson >= JAVA_17_VERSION
cfs->skip_u1(attribute_length, CHECK);
} else {
// Unknown attribute
Expand Down
3 changes: 0 additions & 3 deletions src/hotspot/share/classfile/classFileParser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -333,9 +333,6 @@ class ClassFileParser {
const u1* const record_attribute_start,
TRAPS);

bool supports_sealed_types();
bool supports_records();

void parse_classfile_attributes(const ClassFileStream* const cfs,
ConstantPool* cp,
ClassAnnotationCollector* parsed_annotations,
Expand Down
4 changes: 2 additions & 2 deletions src/hotspot/share/include/jvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -606,15 +606,15 @@ JVM_GetNestHost(JNIEnv *env, jclass current);
JNIEXPORT jobjectArray JNICALL
JVM_GetNestMembers(JNIEnv *env, jclass current);

/* Records - since JDK 14 */
/* Records - since JDK 16 */

JNIEXPORT jboolean JNICALL
JVM_IsRecord(JNIEnv *env, jclass cls);

JNIEXPORT jobjectArray JNICALL
JVM_GetRecordComponents(JNIEnv *env, jclass ofClass);

/* Sealed types - since JDK 15 */
/* Sealed classes - since JDK 17 */

JNIEXPORT jobjectArray JNICALL
JVM_GetPermittedSubclasses(JNIEnv *env, jclass current);
Expand Down
10 changes: 4 additions & 6 deletions src/java.base/share/classes/java/lang/Class.java
Original file line number Diff line number Diff line change
Expand Up @@ -4475,9 +4475,8 @@ public Optional<ClassDesc> describeConstable() {
*
* @jls 8.1 Class Declarations
* @jls 9.1 Interface Declarations
* @since 15
* @since 17
*/
@jdk.internal.javac.PreviewFeature(feature=jdk.internal.javac.PreviewFeature.Feature.SEALED_CLASSES, reflective=true)
@CallerSensitive
public Class<?>[] getPermittedSubclasses() {
Class<?>[] subClasses;
Expand Down Expand Up @@ -4524,14 +4523,13 @@ private boolean isDirectSubType(Class<?> c) {
* subclasses; {@link #getPermittedSubclasses()} returns a non-null but
* possibly empty value for a sealed class or interface.
*
* @return {@code true} if and only if this {@code Class} object represents a sealed class or interface.
* @return {@code true} if and only if this {@code Class} object represents
* a sealed class or interface.
*
* @jls 8.1 Class Declarations
* @jls 9.1 Interface Declarations
* @since 15
* @since 17
*/
@jdk.internal.javac.PreviewFeature(feature=jdk.internal.javac.PreviewFeature.Feature.SEALED_CLASSES, reflective=true)
@SuppressWarnings("preview")
public boolean isSealed() {
if (isArray() || isPrimitive()) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@
public boolean reflective() default false;

public enum Feature {
/*
* This one can only be removed after JDK 17
*/
SEALED_CLASSES,
/**
* A key for testing.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,8 @@ public interface ClassTree extends StatementTree {
*
* @return the subclasses
*
* @since 15
* @since 17
*/
@jdk.internal.javac.PreviewFeature(feature=jdk.internal.javac.PreviewFeature.Feature.SEALED_CLASSES,
reflective=true)
default List<? extends Tree> getPermitsClause() {
return List.of();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,6 @@ public boolean isEnabled() {
*/
public boolean isPreview(Feature feature) {
return switch (feature) {
case SEALED_CLASSES -> true;

//Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing).
//When real preview features will be added, this method can be implemented to return 'true'
//for those selected features, and 'false' for all the others.
Expand Down Expand Up @@ -224,9 +222,7 @@ public Error disabledError(JavaFileObject classfile, int majorVersion) {
* @return true iff sym has been declared using a preview language feature
*/
public boolean declaredUsingPreviewFeature(Symbol sym) {
return ((sym.flags() & RECORD) != 0 && isPreview(Feature.RECORDS)) ||
((sym.flags() & SEALED) != 0 && isPreview(Feature.SEALED_CLASSES)) ||
((sym.flags() & NON_SEALED) != 0 && isPreview(Feature.SEALED_CLASSES));
return false;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,7 @@ protected Check(Context context) {
deferredLintHandler = DeferredLintHandler.instance(context);

allowRecords = Feature.RECORDS.allowedInSource(source);
allowSealed = (!preview.isPreview(Feature.SEALED_CLASSES) || preview.isEnabled()) &&
Feature.SEALED_CLASSES.allowedInSource(source);
allowSealed = Feature.SEALED_CLASSES.allowedInSource(source);
}

/** Character for synthetic names
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,7 @@ protected ClassReader(Context context) {
preview = Preview.instance(context);
allowModules = Feature.MODULES.allowedInSource(source);
allowRecords = Feature.RECORDS.allowedInSource(source);
allowSealedTypes = (!preview.isPreview(Feature.SEALED_CLASSES) || preview.isEnabled()) &&
Feature.SEALED_CLASSES.allowedInSource(source);
allowSealedTypes = Feature.SEALED_CLASSES.allowedInSource(source);

saveParameterNames = options.isSet(PARAMETERS);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,7 @@ protected JavacParser(ParserFactory fac,
this.allowYieldStatement = (!preview.isPreview(Feature.SWITCH_EXPRESSION) || preview.isEnabled()) &&
Feature.SWITCH_EXPRESSION.allowedInSource(source);
this.allowRecords = Feature.RECORDS.allowedInSource(source);
this.allowSealedTypes = (!preview.isPreview(Feature.SEALED_CLASSES) || preview.isEnabled()) &&
Feature.SEALED_CLASSES.allowedInSource(source);
this.allowSealedTypes = Feature.SEALED_CLASSES.allowedInSource(source);
}

protected AbstractEndPosTable newEndPosTable(boolean keepEndPositions) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@
* @modules java.base/jdk.internal.misc
* @library /test/lib ..
* @compile sealedP1/SuperInterface.jcod
* @compile --enable-preview --source ${jdk.version} sealedP1/C1.java sealedP2/C2.java sealedP3/C3.java
* @compile sealedP1/C1.java sealedP2/C2.java sealedP3/C3.java
* @build sun.hotspot.WhiteBox
* @compile/module=java.base java/lang/ModuleHelper.java
* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox
* @run main/othervm -Xbootclasspath/a:. --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI SealedInterfaceModuleTest
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI SealedInterfaceModuleTest
*/

public class SealedInterfaceModuleTest {
Expand Down
4 changes: 2 additions & 2 deletions test/hotspot/jtreg/runtime/modules/SealedModuleTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@
* @modules java.base/jdk.internal.misc
* @library /test/lib ..
* @compile sealedP1/SuperClass.jcod
* @compile --enable-preview --source ${jdk.version} sealedP1/C1.java sealedP2/C2.java sealedP3/C3.java
* @compile sealedP1/C1.java sealedP2/C2.java sealedP3/C3.java
* @build sun.hotspot.WhiteBox
* @compile/module=java.base java/lang/ModuleHelper.java
* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox
* @run main/othervm -Xbootclasspath/a:. --enable-preview -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI SealedModuleTest
* @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI SealedModuleTest
*/

public class SealedModuleTest {
Expand Down
4 changes: 2 additions & 2 deletions test/hotspot/jtreg/runtime/modules/sealedP1/SuperClass.jcod
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2021, 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
Expand Down Expand Up @@ -32,7 +32,7 @@

class sealedP1/SuperClass {
0xCAFEBABE;
65535; // minor version
0; // minor version
61; // version
[20] { // Constant Pool
; // first element is empty
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2021, 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
Expand Down Expand Up @@ -32,7 +32,7 @@
//
class sealedP1/SuperInterface {
0xCAFEBABE;
65535; // minor version
0; // minor version
61; // version
[14] { // Constant Pool
; // first element is empty
Expand Down
45 changes: 0 additions & 45 deletions test/hotspot/jtreg/runtime/sealedClasses/AbstractSealedTest.java

This file was deleted.

Loading

1 comment on commit 0fa9223

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.