8246778: Compiler implementation for Sealed Classes (Second Preview)

Co-authored-by: Vicente Romero <vromero@openjdk.org>
Co-authored-by: Harold Seigel <hseigel@openjdk.org>
Reviewed-by: lfoltan, mchung, alanb, mcimadamore, chegar
This commit is contained in:
Jan Lahoda 2020-12-07 11:11:31 +00:00
parent 09707dd4f2
commit 637b0c64b0
16 changed files with 1127 additions and 83 deletions

View file

@ -58,12 +58,14 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import jdk.internal.loader.BootLoader;
@ -200,8 +202,6 @@ 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();
@ -3020,6 +3020,37 @@ public final class Class<T> implements java.io.Serializable,
}
}
/*
* Checks if a client loaded in ClassLoader ccl is allowed to access the provided
* classes under the current package access policy. If access is denied,
* throw a SecurityException.
*
* NOTE: this method should only be called if a SecurityManager is active
* classes must be non-empty
* all classes provided must be loaded by the same ClassLoader
* NOTE: this method does not support Proxy classes
*/
private static void checkPackageAccessForPermittedSubclasses(SecurityManager sm,
final ClassLoader ccl, Class<?>[] subClasses) {
final ClassLoader cl = subClasses[0].getClassLoader0();
if (ReflectUtil.needsPackageAccessCheck(ccl, cl)) {
Set<String> packages = new HashSet<>();
for (Class<?> c : subClasses) {
if (Proxy.isProxyClass(c))
throw new InternalError("a permitted subclass should not be a proxy class: " + c);
String pkg = c.getPackageName();
if (pkg != null && !pkg.isEmpty()) {
packages.add(pkg);
}
}
for (String pkg : packages) {
sm.checkPackageAccess(pkg);
}
}
}
/**
* Add a package name prefix if the name is not absolute Remove leading "/"
* if name is absolute
@ -4357,47 +4388,87 @@ public final class Class<T> implements java.io.Serializable,
* 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
* Returns an array containing {@code Class} objects representing the
* direct subinterfaces or subclasses permitted to extend or
* implement this class or interface if it is sealed. The order of such elements
* is unspecified. 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
* For each class or interface {@code C} which is recorded as a permitted
* direct subinterface or subclass of this class or interface,
* this method attempts to obtain the {@code Class}
* object for {@code C} (using {@linkplain #getClassLoader() the defining class
* loader} of the current {@code Class} object).
* The {@code Class} objects which can be obtained and which are direct
* subinterfaces or subclasses of this class or interface,
* are indicated by elements of the returned array. If a {@code Class} object
* cannot be obtained, it is silently ignored, and not included in the result
* array.
*
* @return an array of {@code Class} objects of the permitted subclasses of this class or interface
*
* @throws SecurityException
* If a security manager, <i>s</i>, is present and the caller's
* class loader is not the same as or an ancestor of the class
* loader for that returned class and invocation of {@link
* SecurityManager#checkPackageAccess s.checkPackageAccess()}
* denies access to the package of any class in the returned array.
*
* @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;
@CallerSensitive
public Class<?>[] getPermittedSubclasses() {
Class<?>[] subClasses;
if (isArray() || isPrimitive() || (subClasses = getPermittedSubclasses0()).length == 0) {
return EMPTY_CLASS_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);
if (subClasses.length > 0) {
if (Arrays.stream(subClasses).anyMatch(c -> !isDirectSubType(c))) {
subClasses = Arrays.stream(subClasses)
.filter(this::isDirectSubType)
.toArray(s -> new Class<?>[s]);
}
}
return constants;
if (subClasses.length > 0) {
// If we return some classes we need a security check:
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkPackageAccessForPermittedSubclasses(sm,
ClassLoader.getClassLoader(Reflection.getCallerClass()),
subClasses);
}
}
return subClasses;
}
private boolean isDirectSubType(Class<?> c) {
if (isInterface()) {
for (Class<?> i : c.getInterfaces(/* cloneArray */ false)) {
if (i == this) {
return true;
}
}
} else {
return c.getSuperclass() == this;
}
return false;
}
/**
* * {@preview Associated with sealed classes, a preview feature of the Java language.
* {@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
* 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.
@ -4412,8 +4483,8 @@ public final class Class<T> implements java.io.Serializable,
if (isArray() || isPrimitive()) {
return false;
}
return permittedSubclasses().length != 0;
return getPermittedSubclasses().length != 0;
}
private native String[] getPermittedSubclasses0();
private native Class<?>[] getPermittedSubclasses0();
}