Skip to content

Commit

Permalink
8246774: implement Record Classes as a standard feature in Java
Browse files Browse the repository at this point in the history
Co-authored-by: Vicente Romero <vromero@openjdk.org>
Co-authored-by: Harold Seigel <hseigel@openjdk.org>
Co-authored-by: Chris Hegarty <chegar@openjdk.org>
Reviewed-by: coleenp, jlahoda, sspitsyn, chegar
  • Loading branch information
3 people committed Oct 18, 2020
1 parent 0b3e6c5 commit c17d585
Show file tree
Hide file tree
Showing 109 changed files with 294 additions and 784 deletions.
63 changes: 21 additions & 42 deletions src/hotspot/share/classfile/classFileParser.cpp
Expand Up @@ -3567,12 +3567,6 @@ bool ClassFileParser::supports_sealed_types() {
Arguments::enable_preview();
}

bool ClassFileParser::supports_records() {
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 @@ -3820,12 +3814,28 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf
"Nest-host class_info_index %u has bad constant type in class file %s",
class_info_index, CHECK);
_nest_host = class_info_index;
} else if (_major_version >= JAVA_14_VERSION) {

} else if (_major_version >= JAVA_15_VERSION) {
// Check for PermittedSubclasses tag
if (tag == vmSymbols::tag_permitted_subclasses()) {
if (supports_sealed_types()) {
if (parsed_permitted_subclasses_attribute) {
classfile_parse_error("Multiple PermittedSubclasses attributes in class file %s", CHECK);
}
// Classes marked ACC_FINAL cannot have a PermittedSubclasses attribute.
if (_access_flags.is_final()) {
classfile_parse_error("PermittedSubclasses attribute in final class file %s", CHECK);
}
parsed_permitted_subclasses_attribute = true;
permitted_subclasses_attribute_start = cfs->current();
permitted_subclasses_attribute_length = attribute_length;
}
cfs->skip_u1(attribute_length, CHECK);

} else if (_major_version >= JAVA_16_VERSION) {
if (tag == vmSymbols::tag_record()) {
// Skip over Record attribute if not supported or if super class is
// not java.lang.Record.
if (supports_records() &&
cp->klass_name_at(_super_class_index) == vmSymbols::java_lang_Record()) {
// Skip over Record attribute if super class is not java.lang.Record.
if (cp->klass_name_at(_super_class_index) == vmSymbols::java_lang_Record()) {
if (parsed_record_attribute) {
classfile_parse_error("Multiple Record attributes in class file %s", THREAD);
return;
Expand All @@ -3839,44 +3849,13 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf
record_attribute_start = cfs->current();
record_attribute_length = attribute_length;
} else if (log_is_enabled(Info, class, record)) {
// Log why the Record attribute was ignored. Note that if the
// class file version is JVM_CLASSFILE_MAJOR_VERSION.65535 and
// --enable-preview wasn't specified then a java.lang.UnsupportedClassVersionError
// exception would have been thrown.
ResourceMark rm(THREAD);
if (supports_records()) {
log_info(class, record)(
"Ignoring Record attribute in class %s because super type is not java.lang.Record",
_class_name->as_C_string());
} else {
log_info(class, record)(
"Ignoring Record attribute in class %s because class file version is not %d.65535",
_class_name->as_C_string(), JVM_CLASSFILE_MAJOR_VERSION);
}
}
cfs->skip_u1(attribute_length, CHECK);
} else if (_major_version >= JAVA_15_VERSION) {
// Check for PermittedSubclasses tag
if (tag == vmSymbols::tag_permitted_subclasses()) {
if (supports_sealed_types()) {
if (parsed_permitted_subclasses_attribute) {
classfile_parse_error("Multiple PermittedSubclasses attributes in class file %s", THREAD);
return;
}
// Classes marked ACC_FINAL cannot have a PermittedSubclasses attribute.
if (_access_flags.is_final()) {
classfile_parse_error("PermittedSubclasses attribute in final class file %s", THREAD);
return;
}
parsed_permitted_subclasses_attribute = true;
permitted_subclasses_attribute_start = cfs->current();
permitted_subclasses_attribute_length = attribute_length;
}
cfs->skip_u1(attribute_length, CHECK);
} else {
// Unknown attribute
cfs->skip_u1(attribute_length, CHECK);
}
} else {
// Unknown attribute
cfs->skip_u1(attribute_length, CHECK);
Expand Down
23 changes: 2 additions & 21 deletions src/java.base/share/classes/java/lang/Class.java
Expand Up @@ -2342,13 +2342,6 @@ public Field[] getDeclaredFields() throws SecurityException {
}

/**
* {@preview Associated with records, a preview feature of the Java language.
*
* This method is associated with <i>records</i>, a preview
* feature of the Java language. Preview features
* may be removed in a future release, or upgraded to permanent
* features of the Java language.}
*
* Returns an array of {@code RecordComponent} objects representing all the
* record components of this record class, or {@code null} if this class is
* not a record class.
Expand Down Expand Up @@ -2385,11 +2378,8 @@ public Field[] getDeclaredFields() throws SecurityException {
* </ul>
*
* @jls 8.10 Record Types
* @since 14
* @since 16
*/
@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS,
essentialAPI=false)
@SuppressWarnings("preview")
@CallerSensitive
public RecordComponent[] getRecordComponents() {
SecurityManager sm = System.getSecurityManager();
Expand Down Expand Up @@ -3688,13 +3678,6 @@ private static Class<?> javaLangRecordClass() {
}

/**
* {@preview Associated with records, a preview feature of the Java language.
*
* This method is associated with <i>records</i>, a preview
* feature of the Java language. Preview features
* may be removed in a future release, or upgraded to permanent
* features of the Java language.}
*
* Returns {@code true} if and only if this class is a record class.
*
* <p> The {@linkplain #getSuperclass() direct superclass} of a record
Expand All @@ -3707,10 +3690,8 @@ private static Class<?> javaLangRecordClass() {
*
* @return true if and only if this class is a record class, otherwise false
* @jls 8.10 Record Types
* @since 14
* @since 16
*/
@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS,
essentialAPI=false)
public boolean isRecord() {
return getSuperclass() == JAVA_LANG_RECORD_CLASS && isRecord0();
}
Expand Down
12 changes: 1 addition & 11 deletions src/java.base/share/classes/java/lang/Record.java
Expand Up @@ -25,14 +25,6 @@
package java.lang;

/**
* {@preview Associated with records, a preview feature of the Java language.
*
* This class is associated with <i>records</i>, a preview
* feature of the Java language. Programs can only use this
* class when preview features are enabled. Preview features
* may be removed in a future release, or upgraded to permanent
* features of the Java language.}
*
* This is the common base class of all Java language record classes.
*
* <p>More information about records, including descriptions of the
Expand Down Expand Up @@ -86,10 +78,8 @@
* <a href="{@docRoot}/java.base/java/io/ObjectInputStream.html#record-serialization">record serialization</a>.
*
* @jls 8.10 Record Types
* @since 14
* @since 16
*/
@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS,
essentialAPI=true)
public abstract class Record {
/**
* Constructor for record classes to call.
Expand Down
Expand Up @@ -118,22 +118,12 @@ public enum ElementType {
MODULE,

/**
* {@preview Associated with records, a preview feature of the Java language.
*
* This constant is associated with <i>records</i>, a preview
* feature of the Java language. Programs can only use this
* constant when preview features are enabled. Preview features
* may be removed in a future release, or upgraded to permanent
* features of the Java language.}
*
* Record component
*
* @jls 8.10.3 Record Members
* @jls 9.7.4 Where Annotations May Appear
*
* @since 14
* @since 16
*/
@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS,
essentialAPI=true)
RECORD_COMPONENT;
}
Expand Up @@ -38,23 +38,14 @@
import java.util.Objects;

/**
* {@preview Associated with records, a preview feature of the Java language.
*
* This class is associated with <i>records</i>, a preview
* feature of the Java language. Preview features
* may be removed in a future release, or upgraded to permanent
* features of the Java language.}
*
* A {@code RecordComponent} provides information about, and dynamic access to, a
* component of a record class.
*
* @see Class#getRecordComponents()
* @see java.lang.Record
* @jls 8.10 Record Types
* @since 14
* @since 16
*/
@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS,
essentialAPI=false)
public final class RecordComponent implements AnnotatedElement {
// declaring class
private Class<?> clazz;
Expand Down
11 changes: 1 addition & 10 deletions src/java.base/share/classes/java/lang/runtime/ObjectMethods.java
Expand Up @@ -38,23 +38,14 @@
import java.util.Objects;

/**
* {@preview Associated with records, a preview feature of the Java language.
*
* This class is associated with <i>records</i>, a preview
* feature of the Java language. Preview features
* may be removed in a future release, or upgraded to permanent
* features of the Java language.}
*
* Bootstrap methods for state-driven implementations of core methods,
* including {@link Object#equals(Object)}, {@link Object#hashCode()}, and
* {@link Object#toString()}. These methods may be used, for example, by
* Java compiler implementations to implement the bodies of {@link Object}
* methods for record classes.
*
* @since 14
* @since 16
*/
@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS,
essentialAPI=false)
public class ObjectMethods {

private ObjectMethods() { }
Expand Down
Expand Up @@ -92,16 +92,8 @@ public static enum TypeAnnotationTarget {
METHOD_FORMAL_PARAMETER,
THROWS,
/**
* {@preview Associated with records, a preview feature of the Java language.
*
* This enum constant is associated with <i>records</i>, a preview
* feature of the Java language. Preview features
* may be removed in a future release, or upgraded to permanent
* features of the Java language.}
* @since 14
* @since 16
*/
@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS,
essentialAPI=false)
RECORD_COMPONENT;
}

Expand Down
12 changes: 1 addition & 11 deletions src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java
Expand Up @@ -649,19 +649,9 @@ public enum Kind {
PROVIDES(ProvidesTree.class),

/**
* {@preview Associated with records, a preview feature of the Java language.
*
* This enum constant is associated with <i>records</i>, a preview
* feature of the Java language. Preview features
* may be removed in a future release, or upgraded to permanent
* features of the Java language.}
*
* Used for instances of {@link ClassTree} representing records.
*
* @since 14
* @since 16
*/
@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS,
essentialAPI=false)
RECORD(ClassTree.class),

/**
Expand Down
Expand Up @@ -167,7 +167,6 @@ public boolean isEnabled() {
public boolean isPreview(Feature feature) {
if (feature == Feature.PATTERN_MATCHING_IN_INSTANCEOF ||
feature == Feature.REIFIABLE_TYPES_INSTANCEOF ||
feature == Feature.RECORDS ||
feature == Feature.SEALED_CLASSES)
return true;
//Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing).
Expand Down
Expand Up @@ -159,8 +159,7 @@ protected Check(Context context) {

deferredLintHandler = DeferredLintHandler.instance(context);

allowRecords = (!preview.isPreview(Feature.RECORDS) || preview.isEnabled()) &&
Feature.RECORDS.allowedInSource(source);
allowRecords = Feature.RECORDS.allowedInSource(source);
allowSealed = (!preview.isPreview(Feature.SEALED_CLASSES) || preview.isEnabled()) &&
Feature.SEALED_CLASSES.allowedInSource(source);
}
Expand Down
Expand Up @@ -150,8 +150,7 @@ protected Resolve(Context context) {
Feature.POST_APPLICABILITY_VARARGS_ACCESS_CHECK.allowedInSource(source);
polymorphicSignatureScope = WriteableScope.create(syms.noSymbol);
allowModules = Feature.MODULES.allowedInSource(source);
allowRecords = (!preview.isPreview(Feature.RECORDS) || preview.isEnabled()) &&
Feature.RECORDS.allowedInSource(source);
allowRecords = Feature.RECORDS.allowedInSource(source);
}

/** error symbols, which are returned when resolution fails
Expand Down
Expand Up @@ -272,8 +272,7 @@ protected ClassReader(Context context) {
Source source = Source.instance(context);
preview = Preview.instance(context);
allowModules = Feature.MODULES.allowedInSource(source);
allowRecords = (!preview.isPreview(Feature.RECORDS) || preview.isEnabled()) &&
Feature.RECORDS.allowedInSource(source);
allowRecords = Feature.RECORDS.allowedInSource(source);
allowSealedTypes = (!preview.isPreview(Feature.SEALED_CLASSES) || preview.isEnabled()) &&
Feature.SEALED_CLASSES.allowedInSource(source);

Expand Down
Expand Up @@ -184,8 +184,7 @@ protected JavacParser(ParserFactory fac,
endPosTable = newEndPosTable(keepEndPositions);
this.allowYieldStatement = (!preview.isPreview(Feature.SWITCH_EXPRESSION) || preview.isEnabled()) &&
Feature.SWITCH_EXPRESSION.allowedInSource(source);
this.allowRecords = (!preview.isPreview(Feature.RECORDS) || preview.isEnabled()) &&
Feature.RECORDS.allowedInSource(source);
this.allowRecords = Feature.RECORDS.allowedInSource(source);
this.allowSealedTypes = (!preview.isPreview(Feature.SEALED_CLASSES) || preview.isEnabled()) &&
Feature.SEALED_CLASSES.allowedInSource(source);
}
Expand Down Expand Up @@ -3717,7 +3716,7 @@ protected JCStatement classOrRecordOrInterfaceOrEnumDeclaration(JCModifiers mods
} else {
int pos = token.pos;
List<JCTree> errs;
if (token.kind == IDENTIFIER && token.name() == names.record && preview.isEnabled()) {
if (token.kind == IDENTIFIER && token.name() == names.record) {
checkSourceLevel(Feature.RECORDS);
JCErroneous erroneousTree = syntaxError(token.pos, List.of(mods), Errors.RecordHeaderExpected);
return toP(F.Exec(erroneousTree));
Expand Down Expand Up @@ -4214,7 +4213,7 @@ protected boolean isRecordStart() {
(peekToken(TokenKind.IDENTIFIER, TokenKind.LPAREN) ||
peekToken(TokenKind.IDENTIFIER, TokenKind.EOF) ||
peekToken(TokenKind.IDENTIFIER, TokenKind.LT))) {
checkSourceLevel(Feature.RECORDS);
checkSourceLevel(Feature.RECORDS);
return true;
} else {
return false;
Expand Down
7 changes: 3 additions & 4 deletions test/hotspot/jtreg/runtime/records/RedefineRecord.java
Expand Up @@ -23,15 +23,15 @@

/*
* @test
* @bug 8246774
* @library /test/lib
* @summary Test that a class that is a record can be redefined.
* @modules java.base/jdk.internal.misc
* @modules java.instrument
* jdk.jartool/sun.tools.jar
* @requires vm.jvmti
* @compile --enable-preview -source ${jdk.version} RedefineRecord.java
* @run main/othervm --enable-preview RedefineRecord buildagent
* @run main/othervm/timeout=6000 --enable-preview RedefineRecord runtest
* @run main RedefineRecord buildagent
* @run main/othervm/timeout=6000 RedefineRecord runtest
*/

import java.io.FileNotFoundException;
Expand Down Expand Up @@ -100,7 +100,6 @@ public static void main(String argv[]) throws Exception {
"-XX:MetaspaceSize=12m",
"-XX:MaxMetaspaceSize=12m",
"-javaagent:redefineagent.jar",
"--enable-preview",
"RedefineRecord");
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldNotContain("processing of -javaagent failed");
Expand Down
2 changes: 1 addition & 1 deletion test/hotspot/jtreg/runtime/records/abstractRecord.jcod
Expand Up @@ -28,7 +28,7 @@
// ClassFormatError exception.
class abstractRecord {
0xCAFEBABE;
65535; // minor version
0; // minor version
60; // version
[69] { // Constant Pool
; // first element is empty
Expand Down
2 changes: 1 addition & 1 deletion test/hotspot/jtreg/runtime/records/badRecordAttribute.jcod
Expand Up @@ -28,7 +28,7 @@
// Utf8. It should result in a ClassFormatError exception.
class badRecordAttribute {
0xCAFEBABE;
65535; // minor version
0; // minor version
60; // version
[69] { // Constant Pool
; // first element is empty
Expand Down

1 comment on commit c17d585

@bridgekeeper
Copy link

@bridgekeeper bridgekeeper bot commented on c17d585 Oct 18, 2020

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.