8227046: compiler implementation for sealed classes

8225056: VM support for sealed classes
8227044: javax.lang.model for sealed classes
8227045: Preview APIs support for sealed classes
8227047: Javadoc for sealed types
8245854: JVM TI Specification for sealed classes

Co-authored-by: Harold Seigel <harold.seigel@oracle.com>
Co-authored-by: Jan Lahoda <jan.lahoda@oracle.com>
Reviewed-by: mcimadamore, forax, darcy, dholmes, jlahoda, lfoltan, mchung, sspitsyn, vromero
This commit is contained in:
Vicente Romero 2020-06-01 17:00:40 -04:00
parent 567692e4ae
commit d42bfef8a4
139 changed files with 6877 additions and 192 deletions

View file

@ -201,6 +201,8 @@ public final class Class<T> implements java.io.Serializable,
private static final int ENUM = 0x00004000;
private static final int SYNTHETIC = 0x00001000;
private static final ClassDesc[] EMPTY_CLASS_DESC_ARRAY = new ClassDesc[0];
private static native void registerNatives();
static {
registerNatives();
@ -4382,4 +4384,69 @@ public final class Class<T> implements java.io.Serializable,
@HotSpotIntrinsicCandidate
public native boolean isHidden();
/**
* {@preview Associated with sealed classes, a preview feature of the Java language.
*
* This method is associated with <i>sealed classes</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 containing {@code ClassDesc} objects representing all the
* direct subclasses or direct implementation classes permitted to extend or implement this class or interface
* if it is sealed. If this {@code Class} object represents a primitive type, {@code void}, an array type,
* or a class or interface that is not sealed, an empty array is returned.
*
* @return an array of class descriptors of all the permitted subclasses of this class or interface
*
* @jls 8.1 Class Declarations
* @jls 9.1 Interface Declarations
* @since 15
*/
@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.SEALED_CLASSES, essentialAPI=false)
public ClassDesc[] permittedSubclasses() {
String[] subclassNames;
if (isArray() || isPrimitive() || (subclassNames = getPermittedSubclasses0()).length == 0) {
return EMPTY_CLASS_DESC_ARRAY;
}
ClassDesc[] constants = new ClassDesc[subclassNames.length];
int i = 0;
for (String subclassName : subclassNames) {
try {
constants[i++] = ClassDesc.of(subclassName.replace('/', '.'));
} catch (IllegalArgumentException iae) {
throw new InternalError("Invalid type in permitted subclasses information: " + subclassName, iae);
}
}
return constants;
}
/**
* * {@preview Associated with sealed classes, a preview feature of the Java language.
*
* This method is associated with <i>sealed classes</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 {@code Class} object represents a sealed class or interface.
* If this {@code Class} object represents a primitive type, {@code void}, or an array type, this method returns
* {@code false}.
*
* @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
*/
@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.SEALED_CLASSES, essentialAPI=false)
@SuppressWarnings("preview")
public boolean isSealed() {
if (isArray() || isPrimitive()) {
return false;
}
return permittedSubclasses().length != 0;
}
private native String[] getPermittedSubclasses0();
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2020, 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
@ -63,6 +63,7 @@ public @interface PreviewFeature {
// necessary for PreviewFeature in JDK 15 to declare the enum constant.
TEXT_BLOCKS,
RECORDS,
SEALED_CLASSES,
;
}
}

View file

@ -499,8 +499,8 @@ public class ClassReader {
String nestHostClass = null;
// - The offset of the NestMembers attribute, or 0.
int nestMembersOffset = 0;
// - The offset of the PermittedSubtypes attribute, or 0
int permittedSubtypesOffset = 0;
// - The offset of the PermittedSubclasses attribute, or 0
int permittedSubclassesOffset = 0;
// - The offset of the Record attribute, or 0.
int recordOffset = 0;
// - The non standard attributes (linked with their {@link Attribute#nextAttribute} field).
@ -525,8 +525,8 @@ public class ClassReader {
nestHostClass = readClass(currentAttributeOffset, charBuffer);
} else if (Constants.NEST_MEMBERS.equals(attributeName)) {
nestMembersOffset = currentAttributeOffset;
} else if (Constants.PERMITTED_SUBTYPES.equals(attributeName)) {
permittedSubtypesOffset = currentAttributeOffset;
} else if (Constants.PERMITTED_SUBCLASSES.equals(attributeName)) {
permittedSubclassesOffset = currentAttributeOffset;
} else if (Constants.SIGNATURE.equals(attributeName)) {
signature = readUTF8(currentAttributeOffset, charBuffer);
} else if (Constants.RUNTIME_VISIBLE_ANNOTATIONS.equals(attributeName)) {
@ -704,14 +704,14 @@ public class ClassReader {
}
}
// Visit the PermittedSubtypes attribute.
if (permittedSubtypesOffset != 0) {
int numberOfPermittedSubtypes = readUnsignedShort(permittedSubtypesOffset);
int currentPermittedSubtypeOffset = permittedSubtypesOffset + 2;
while (numberOfPermittedSubtypes-- > 0) {
classVisitor.visitPermittedSubtypeExperimental(
readClass(currentPermittedSubtypeOffset, charBuffer));
currentPermittedSubtypeOffset += 2;
// Visit the PermittedSubclasses attribute.
if (permittedSubclassesOffset != 0) {
int numberOfPermittedSubclasses = readUnsignedShort(permittedSubclassesOffset);
int currentPermittedSubclassOffset = permittedSubclassesOffset + 2;
while (numberOfPermittedSubclasses-- > 0) {
classVisitor.visitPermittedSubclassExperimental(
readClass(currentPermittedSubclassOffset, charBuffer));
currentPermittedSubclassOffset += 2;
}
}

View file

@ -61,7 +61,7 @@ package jdk.internal.org.objectweb.asm;
/**
* A visitor to visit a Java class. The methods of this class must be called in the following order:
* {@code visit} [ {@code visitSource} ] [ {@code visitModule} ][ {@code visitNestHost} ][ {@code
* visitPermittedSubtype} ][ {@code visitOuterClass} ] ( {@code visitAnnotation} | {@code
* visitPermittedSubclass} ][ {@code visitOuterClass} ] ( {@code visitAnnotation} | {@code
* visitTypeAnnotation} | {@code visitAttribute} )* ( {@code visitNestMember} | {@code
* visitInnerClass} | {@code visitField} | {@code visitMethod} )* {@code visitEnd}.
*
@ -287,19 +287,19 @@ public abstract class ClassVisitor {
/**
* <b>Experimental, use at your own risk. This method will be renamed when it becomes stable, this
* will break existing code using it</b>. Visits a permitted subtypes. A permitted subtypes is one
* of the allowed subtypes of the current class.
* will break existing code using it</b>. Visits a permitted subclass. A permitted subclass is one
* of the allowed subclasses of the current class.
*
* @param permittedSubtype the internal name of a permitted subtype.
* @param permittedSubclass the internal name of a permitted subclass.
* @deprecated this API is experimental.
*/
@Deprecated
public void visitPermittedSubtypeExperimental(final String permittedSubtype) {
public void visitPermittedSubclassExperimental(final String permittedSubclass) {
if (api != Opcodes.ASM9_EXPERIMENTAL) {
throw new UnsupportedOperationException("This feature requires ASM9_EXPERIMENTAL");
}
if (cv != null) {
cv.visitPermittedSubtypeExperimental(permittedSubtype);
cv.visitPermittedSubclassExperimental(permittedSubclass);
}
}

View file

@ -208,11 +208,11 @@ public class ClassWriter extends ClassVisitor {
/** The 'classes' array of the NestMembers attribute, or {@literal null}. */
private ByteVector nestMemberClasses;
/** The number_of_classes field of the PermittedSubtypes attribute, or 0. */
private int numberOfPermittedSubtypeClasses;
/** The number_of_classes field of the PermittedSubclasses attribute, or 0. */
private int numberOfPermittedSubclassClasses;
/** The 'classes' array of the PermittedSubtypes attribute, or {@literal null}. */
private ByteVector permittedSubtypeClasses;
/** The 'classes' array of the PermittedSubclasses attribute, or {@literal null}. */
private ByteVector permittedSubclassClasses;
/**
* The record components of this class, stored in a linked list of {@link RecordComponentWriter}
@ -406,17 +406,17 @@ public class ClassWriter extends ClassVisitor {
/**
* <b>Experimental, use at your own risk.</b>
*
* @param permittedSubtype the internal name of a permitted subtype.
* @param permittedSubclass the internal name of a permitted subclass.
* @deprecated this API is experimental.
*/
@Override
@Deprecated
public final void visitPermittedSubtypeExperimental(final String permittedSubtype) {
if (permittedSubtypeClasses == null) {
permittedSubtypeClasses = new ByteVector();
public final void visitPermittedSubclassExperimental(final String permittedSubclass) {
if (permittedSubclassClasses == null) {
permittedSubclassClasses = new ByteVector();
}
++numberOfPermittedSubtypeClasses;
permittedSubtypeClasses.putShort(symbolTable.addConstantClass(permittedSubtype).index);
++numberOfPermittedSubclassClasses;
permittedSubclassClasses.putShort(symbolTable.addConstantClass(permittedSubclass).index);
}
@Override
@ -607,10 +607,10 @@ public class ClassWriter extends ClassVisitor {
size += 8 + nestMemberClasses.length;
symbolTable.addConstantUtf8(Constants.NEST_MEMBERS);
}
if (permittedSubtypeClasses != null) {
if (permittedSubclassClasses != null) {
++attributesCount;
size += 8 + permittedSubtypeClasses.length;
symbolTable.addConstantUtf8(Constants.PERMITTED_SUBTYPES);
size += 8 + permittedSubclassClasses.length;
symbolTable.addConstantUtf8(Constants.PERMITTED_SUBCLASSES);
}
int recordComponentCount = 0;
int recordSize = 0;
@ -729,12 +729,12 @@ public class ClassWriter extends ClassVisitor {
.putShort(numberOfNestMemberClasses)
.putByteArray(nestMemberClasses.data, 0, nestMemberClasses.length);
}
if (permittedSubtypeClasses != null) {
if (permittedSubclassClasses != null) {
result
.putShort(symbolTable.addConstantUtf8(Constants.PERMITTED_SUBTYPES))
.putInt(permittedSubtypeClasses.length + 2)
.putShort(numberOfPermittedSubtypeClasses)
.putByteArray(permittedSubtypeClasses.data, 0, permittedSubtypeClasses.length);
.putShort(symbolTable.addConstantUtf8(Constants.PERMITTED_SUBCLASSES))
.putInt(permittedSubclassClasses.length + 2)
.putShort(numberOfPermittedSubclassClasses)
.putByteArray(permittedSubclassClasses.data, 0, permittedSubclassClasses.length);
}
if ((accessFlags & Opcodes.ACC_RECORD) != 0 || firstRecordComponent != null) {
result
@ -783,8 +783,8 @@ public class ClassWriter extends ClassVisitor {
nestHostClassIndex = 0;
numberOfNestMemberClasses = 0;
nestMemberClasses = null;
numberOfPermittedSubtypeClasses = 0;
permittedSubtypeClasses = null;
numberOfPermittedSubclassClasses = 0;
permittedSubclassClasses = null;
firstRecordComponent = null;
lastRecordComponent = null;
firstAttribute = null;

View file

@ -104,7 +104,7 @@ final class Constants {
static final String MODULE_MAIN_CLASS = "ModuleMainClass";
static final String NEST_HOST = "NestHost";
static final String NEST_MEMBERS = "NestMembers";
static final String PERMITTED_SUBTYPES = "PermittedSubtypes";
static final String PERMITTED_SUBCLASSES = "PermittedSubclasses";
static final String RECORD = "Record";
// ASM specific access flags.

View file

@ -252,13 +252,13 @@ public class ClassRemapper extends ClassVisitor {
/**
* <b>Experimental, use at your own risk.</b>.
*
* @param permittedSubtype the internal name of a permitted subtype.
* @param permittedSubclass the internal name of a permitted subclass.
* @deprecated this API is experimental.
*/
@Override
@Deprecated
public void visitPermittedSubtypeExperimental(final String permittedSubtype) {
super.visitPermittedSubtypeExperimental(remapper.mapType(permittedSubtype));
public void visitPermittedSubclassExperimental(final String permittedSubclass) {
super.visitPermittedSubclassExperimental(remapper.mapType(permittedSubclass));
}
/**

View file

@ -160,12 +160,12 @@ public class ClassNode extends ClassVisitor {
/**
* <b>Experimental, use at your own risk. This method will be renamed when it becomes stable, this
* will break existing code using it</b>. The internal names of the permitted subtypes of this
* will break existing code using it</b>. The internal names of the permitted subclasses of this
* class. May be {@literal null}.
*
* @deprecated this API is experimental.
*/
@Deprecated public List<String> permittedSubtypesExperimental;
@Deprecated public List<String> permittedSubclassesExperimental;
/** The record components of this class. May be {@literal null}. */
public List<RecordComponentNode> recordComponents;
@ -284,13 +284,13 @@ public class ClassNode extends ClassVisitor {
/**
* <b>Experimental, use at your own risk.</b>.
*
* @param permittedSubtype the internal name of a permitted subtype.
* @param permittedSubclass the internal name of a permitted subclass.
* @deprecated this API is experimental.
*/
@Override
@Deprecated
public void visitPermittedSubtypeExperimental(final String permittedSubtype) {
permittedSubtypesExperimental = Util.add(permittedSubtypesExperimental, permittedSubtype);
public void visitPermittedSubclassExperimental(final String permittedSubclass) {
permittedSubclassesExperimental = Util.add(permittedSubclassesExperimental, permittedSubclass);
}
@Override
@ -351,7 +351,7 @@ public class ClassNode extends ClassVisitor {
*/
@SuppressWarnings("deprecation")
public void check(final int api) {
if (api != Opcodes.ASM9_EXPERIMENTAL && permittedSubtypesExperimental != null) {
if (api != Opcodes.ASM9_EXPERIMENTAL && permittedSubclassesExperimental != null) {
throw new UnsupportedClassVersionException();
}
if (api < Opcodes.ASM8 && ((access & Opcodes.ACC_RECORD) != 0 || recordComponents != null)) {
@ -473,10 +473,10 @@ public class ClassNode extends ClassVisitor {
classVisitor.visitNestMember(nestMembers.get(i));
}
}
// Visit the permitted subtypes.
if (permittedSubtypesExperimental != null) {
for (int i = 0, n = permittedSubtypesExperimental.size(); i < n; ++i) {
classVisitor.visitPermittedSubtypeExperimental(permittedSubtypesExperimental.get(i));
// Visit the permitted subclass.
if (permittedSubclassesExperimental != null) {
for (int i = 0, n = permittedSubclassesExperimental.size(); i < n; ++i) {
classVisitor.visitPermittedSubclassExperimental(permittedSubclassesExperimental.get(i));
}
}
// Visit the inner classes.

View file

@ -357,15 +357,15 @@ public class ASMifier extends Printer {
/**
* <b>Experimental, use at your own risk.</b>.
*
* @param permittedSubtype the internal name of a permitted subtype.
* @param permittedSubclass the internal name of a permitted subclass.
* @deprecated this API is experimental.
*/
@Override
@Deprecated
public void visitPermittedSubtypeExperimental(final String permittedSubtype) {
public void visitPermittedSubclassExperimental(final String permittedSubclass) {
stringBuilder.setLength(0);
stringBuilder.append("classWriter.visitPermittedSubtypeExperimental(");
appendConstant(permittedSubtype);
stringBuilder.append("classWriter.visitPermittedSubclassExperimental(");
appendConstant(permittedSubclass);
stringBuilder.append(END_PARAMETERS);
text.add(stringBuilder.toString());
}

View file

@ -356,15 +356,15 @@ public class CheckClassAdapter extends ClassVisitor {
/**
* <b>Experimental, use at your own risk.</b>.
*
* @param permittedSubtype the internal name of a permitted subtype.
* @param permittedSubclass the internal name of a permitted subclass.
* @deprecated this API is experimental.
*/
@Override
@Deprecated
public void visitPermittedSubtypeExperimental(final String permittedSubtype) {
public void visitPermittedSubclassExperimental(final String permittedSubclass) {
checkState();
CheckMethodAdapter.checkInternalName(version, permittedSubtype, "permittedSubtype");
super.visitPermittedSubtypeExperimental(permittedSubtype);
CheckMethodAdapter.checkInternalName(version, permittedSubclass, "permittedSubclass");
super.visitPermittedSubclassExperimental(permittedSubclass);
}
@Override

View file

@ -488,15 +488,15 @@ public abstract class Printer {
* <b>Experimental, use at your own risk. This method will be renamed when it becomes stable, this
* will break existing code using it</b>.
*
* <p>Visits a permitted subtypes. A permitted subtypes is one of the allowed subtypes of the
* <p>Visits a permitted subclass. A permitted subtclass is one of the allowed subclasses of the
* current class. See {@link
* jdk.internal.org.objectweb.asm.ClassVisitor#visitPermittedSubtypeExperimental(String)}.
* jdk.internal.org.objectweb.asm.ClassVisitor#visitPermittedSubclassExperimental(String)}.
*
* @param permittedSubtype the internal name of a permitted subtype.
* @param permittedSubclass the internal name of a permitted subclass.
* @deprecated this API is experimental.
*/
@Deprecated
public void visitPermittedSubtypeExperimental(final String permittedSubtype) {
public void visitPermittedSubclassExperimental(final String permittedSubclass) {
throw new UnsupportedOperationException(UNSUPPORTED_OPERATION);
}

View file

@ -340,15 +340,15 @@ public class Textifier extends Printer {
/**
* <b>Experimental, use at your own risk.</b>.
*
* @param permittedSubtype the internal name of a permitted subtype.
* @param permittedSubclass the internal name of a permitted subclass.
* @deprecated this API is experimental.
*/
@Override
@Deprecated
public void visitPermittedSubtypeExperimental(final String permittedSubtype) {
public void visitPermittedSubclassExperimental(final String permittedSubclass) {
stringBuilder.setLength(0);
stringBuilder.append(tab).append("PERMITTEDSUBTYPE ");
appendDescriptor(INTERNAL_NAME, permittedSubtype);
stringBuilder.append(tab).append("PERMITTEDSUBCLASS ");
appendDescriptor(INTERNAL_NAME, permittedSubclass);
stringBuilder.append('\n');
text.add(stringBuilder.toString());
}

View file

@ -222,14 +222,14 @@ public final class TraceClassVisitor extends ClassVisitor {
/**
* <b>Experimental, use at your own risk.</b>.
*
* @param permittedSubtype the internal name of a permitted subtype.
* @param permittedSubclass the internal name of a permitted subclass.
* @deprecated this API is experimental.
*/
@Override
@Deprecated
public void visitPermittedSubtypeExperimental(final String permittedSubtype) {
p.visitPermittedSubtypeExperimental(permittedSubtype);
super.visitPermittedSubtypeExperimental(permittedSubtype);
public void visitPermittedSubclassExperimental(final String permittedSubclass) {
p.visitPermittedSubclassExperimental(permittedSubclass);
super.visitPermittedSubclassExperimental(permittedSubclass);
}
@Override

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1994, 2020, 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
@ -81,6 +81,7 @@ static JNINativeMethod methods[] = {
{"getNestMembers0", "()[" CLS, (void *)&JVM_GetNestMembers},
{"getRecordComponents0", "()[" RC, (void *)&JVM_GetRecordComponents},
{"isRecord0", "()Z", (void *)&JVM_IsRecord},
{"getPermittedSubclasses0", "()[" STR, (void *)&JVM_GetPermittedSubclasses},
};
#undef OBJ