Skip to content

Commit 637b0c6

Browse files
lahodajVicente RomeroHarold Seigel
committed
8246778: Compiler implementation for Sealed Classes (Second Preview)
Co-authored-by: Vicente Romero <[email protected]> Co-authored-by: Harold Seigel <[email protected]> Reviewed-by: lfoltan, mchung, alanb, mcimadamore, chegar
1 parent 09707dd commit 637b0c6

File tree

16 files changed

+1127
-83
lines changed

16 files changed

+1127
-83
lines changed

src/hotspot/share/prims/jvm.cpp

+22-6
Original file line numberDiff line numberDiff line change
@@ -2128,16 +2128,32 @@ JVM_ENTRY(jobjectArray, JVM_GetPermittedSubclasses(JNIEnv* env, jclass current))
21282128
JvmtiVMObjectAllocEventCollector oam;
21292129
Array<u2>* subclasses = ik->permitted_subclasses();
21302130
int length = subclasses == NULL ? 0 : subclasses->length();
2131-
objArrayOop r = oopFactory::new_objArray(SystemDictionary::String_klass(),
2131+
objArrayOop r = oopFactory::new_objArray(SystemDictionary::Class_klass(),
21322132
length, CHECK_NULL);
21332133
objArrayHandle result(THREAD, r);
2134+
int count = 0;
21342135
for (int i = 0; i < length; i++) {
21352136
int cp_index = subclasses->at(i);
2136-
// This returns <package-name>/<class-name>.
2137-
Symbol* klass_name = ik->constants()->klass_name_at(cp_index);
2138-
assert(klass_name != NULL, "Unexpected null klass_name");
2139-
Handle perm_subtype_h = java_lang_String::create_from_symbol(klass_name, CHECK_NULL);
2140-
result->obj_at_put(i, perm_subtype_h());
2137+
Klass* k = ik->constants()->klass_at(cp_index, THREAD);
2138+
if (HAS_PENDING_EXCEPTION) {
2139+
if (PENDING_EXCEPTION->is_a(SystemDictionary::VirtualMachineError_klass())) {
2140+
return NULL; // propagate VMEs
2141+
}
2142+
CLEAR_PENDING_EXCEPTION;
2143+
continue;
2144+
}
2145+
if (k->is_instance_klass()) {
2146+
result->obj_at_put(count++, k->java_mirror());
2147+
}
2148+
}
2149+
if (count < length) {
2150+
objArrayOop r2 = oopFactory::new_objArray(SystemDictionary::Class_klass(),
2151+
count, CHECK_NULL);
2152+
objArrayHandle result2(THREAD, r2);
2153+
for (int i = 0; i < count; i++) {
2154+
result2->obj_at_put(i, result->obj_at(i));
2155+
}
2156+
return (jobjectArray)JNIHandles::make_local(THREAD, result2());
21412157
}
21422158
return (jobjectArray)JNIHandles::make_local(THREAD, result());
21432159
}

src/java.base/share/classes/java/lang/Class.java

+94-23
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,14 @@
5858
import java.util.Arrays;
5959
import java.util.Collection;
6060
import java.util.HashMap;
61+
import java.util.HashSet;
6162
import java.util.LinkedHashMap;
6263
import java.util.LinkedHashSet;
6364
import java.util.List;
6465
import java.util.Map;
6566
import java.util.Objects;
6667
import java.util.Optional;
68+
import java.util.Set;
6769
import java.util.stream.Collectors;
6870

6971
import jdk.internal.loader.BootLoader;
@@ -200,8 +202,6 @@ public final class Class<T> implements java.io.Serializable,
200202
private static final int ENUM = 0x00004000;
201203
private static final int SYNTHETIC = 0x00001000;
202204

203-
private static final ClassDesc[] EMPTY_CLASS_DESC_ARRAY = new ClassDesc[0];
204-
205205
private static native void registerNatives();
206206
static {
207207
registerNatives();
@@ -3020,6 +3020,37 @@ private void checkPackageAccess(SecurityManager sm, final ClassLoader ccl,
30203020
}
30213021
}
30223022

3023+
/*
3024+
* Checks if a client loaded in ClassLoader ccl is allowed to access the provided
3025+
* classes under the current package access policy. If access is denied,
3026+
* throw a SecurityException.
3027+
*
3028+
* NOTE: this method should only be called if a SecurityManager is active
3029+
* classes must be non-empty
3030+
* all classes provided must be loaded by the same ClassLoader
3031+
* NOTE: this method does not support Proxy classes
3032+
*/
3033+
private static void checkPackageAccessForPermittedSubclasses(SecurityManager sm,
3034+
final ClassLoader ccl, Class<?>[] subClasses) {
3035+
final ClassLoader cl = subClasses[0].getClassLoader0();
3036+
3037+
if (ReflectUtil.needsPackageAccessCheck(ccl, cl)) {
3038+
Set<String> packages = new HashSet<>();
3039+
3040+
for (Class<?> c : subClasses) {
3041+
if (Proxy.isProxyClass(c))
3042+
throw new InternalError("a permitted subclass should not be a proxy class: " + c);
3043+
String pkg = c.getPackageName();
3044+
if (pkg != null && !pkg.isEmpty()) {
3045+
packages.add(pkg);
3046+
}
3047+
}
3048+
for (String pkg : packages) {
3049+
sm.checkPackageAccess(pkg);
3050+
}
3051+
}
3052+
}
3053+
30233054
/**
30243055
* Add a package name prefix if the name is not absolute Remove leading "/"
30253056
* if name is absolute
@@ -4357,47 +4388,87 @@ public Optional<ClassDesc> describeConstable() {
43574388
* may be removed in a future release, or upgraded to permanent
43584389
* features of the Java language.}
43594390
*
4360-
* Returns an array containing {@code ClassDesc} objects representing all the
4361-
* direct subclasses or direct implementation classes permitted to extend or
4391+
* Returns an array containing {@code Class} objects representing the
4392+
* direct subinterfaces or subclasses permitted to extend or
43624393
* implement this class or interface if it is sealed. The order of such elements
43634394
* is unspecified. If this {@code Class} object represents a primitive type,
43644395
* {@code void}, an array type, or a class or interface that is not sealed,
43654396
* an empty array is returned.
43664397
*
4367-
* @return an array of class descriptors of all the permitted subclasses of this class or interface
4398+
* For each class or interface {@code C} which is recorded as a permitted
4399+
* direct subinterface or subclass of this class or interface,
4400+
* this method attempts to obtain the {@code Class}
4401+
* object for {@code C} (using {@linkplain #getClassLoader() the defining class
4402+
* loader} of the current {@code Class} object).
4403+
* The {@code Class} objects which can be obtained and which are direct
4404+
* subinterfaces or subclasses of this class or interface,
4405+
* are indicated by elements of the returned array. If a {@code Class} object
4406+
* cannot be obtained, it is silently ignored, and not included in the result
4407+
* array.
4408+
*
4409+
* @return an array of {@code Class} objects of the permitted subclasses of this class or interface
4410+
*
4411+
* @throws SecurityException
4412+
* If a security manager, <i>s</i>, is present and the caller's
4413+
* class loader is not the same as or an ancestor of the class
4414+
* loader for that returned class and invocation of {@link
4415+
* SecurityManager#checkPackageAccess s.checkPackageAccess()}
4416+
* denies access to the package of any class in the returned array.
43684417
*
43694418
* @jls 8.1 Class Declarations
43704419
* @jls 9.1 Interface Declarations
43714420
* @since 15
43724421
*/
43734422
@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.SEALED_CLASSES, essentialAPI=false)
4374-
public ClassDesc[] permittedSubclasses() {
4375-
String[] subclassNames;
4376-
if (isArray() || isPrimitive() || (subclassNames = getPermittedSubclasses0()).length == 0) {
4377-
return EMPTY_CLASS_DESC_ARRAY;
4378-
}
4379-
ClassDesc[] constants = new ClassDesc[subclassNames.length];
4380-
int i = 0;
4381-
for (String subclassName : subclassNames) {
4382-
try {
4383-
constants[i++] = ClassDesc.of(subclassName.replace('/', '.'));
4384-
} catch (IllegalArgumentException iae) {
4385-
throw new InternalError("Invalid type in permitted subclasses information: " + subclassName, iae);
4423+
@CallerSensitive
4424+
public Class<?>[] getPermittedSubclasses() {
4425+
Class<?>[] subClasses;
4426+
if (isArray() || isPrimitive() || (subClasses = getPermittedSubclasses0()).length == 0) {
4427+
return EMPTY_CLASS_ARRAY;
4428+
}
4429+
if (subClasses.length > 0) {
4430+
if (Arrays.stream(subClasses).anyMatch(c -> !isDirectSubType(c))) {
4431+
subClasses = Arrays.stream(subClasses)
4432+
.filter(this::isDirectSubType)
4433+
.toArray(s -> new Class<?>[s]);
43864434
}
43874435
}
4388-
return constants;
4436+
if (subClasses.length > 0) {
4437+
// If we return some classes we need a security check:
4438+
SecurityManager sm = System.getSecurityManager();
4439+
if (sm != null) {
4440+
checkPackageAccessForPermittedSubclasses(sm,
4441+
ClassLoader.getClassLoader(Reflection.getCallerClass()),
4442+
subClasses);
4443+
}
4444+
}
4445+
return subClasses;
4446+
}
4447+
4448+
private boolean isDirectSubType(Class<?> c) {
4449+
if (isInterface()) {
4450+
for (Class<?> i : c.getInterfaces(/* cloneArray */ false)) {
4451+
if (i == this) {
4452+
return true;
4453+
}
4454+
}
4455+
} else {
4456+
return c.getSuperclass() == this;
4457+
}
4458+
return false;
43894459
}
43904460

43914461
/**
4392-
* * {@preview Associated with sealed classes, a preview feature of the Java language.
4462+
* {@preview Associated with sealed classes, a preview feature of the Java language.
43934463
*
43944464
* This method is associated with <i>sealed classes</i>, a preview
43954465
* feature of the Java language. Preview features
43964466
* may be removed in a future release, or upgraded to permanent
43974467
* features of the Java language.}
43984468
*
4399-
* Returns {@code true} if and only if this {@code Class} object represents a sealed class or interface.
4400-
* If this {@code Class} object represents a primitive type, {@code void}, or an array type, this method returns
4469+
* Returns {@code true} if and only if this {@code Class} object represents
4470+
* a sealed class or interface. If this {@code Class} object represents a
4471+
* primitive type, {@code void}, or an array type, this method returns
44014472
* {@code false}.
44024473
*
44034474
* @return {@code true} if and only if this {@code Class} object represents a sealed class or interface.
@@ -4412,8 +4483,8 @@ public boolean isSealed() {
44124483
if (isArray() || isPrimitive()) {
44134484
return false;
44144485
}
4415-
return permittedSubclasses().length != 0;
4486+
return getPermittedSubclasses().length != 0;
44164487
}
44174488

4418-
private native String[] getPermittedSubclasses0();
4489+
private native Class<?>[] getPermittedSubclasses0();
44194490
}

src/java.base/share/native/libjava/Class.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ static JNINativeMethod methods[] = {
8181
{"getNestMembers0", "()[" CLS, (void *)&JVM_GetNestMembers},
8282
{"getRecordComponents0", "()[" RC, (void *)&JVM_GetRecordComponents},
8383
{"isRecord0", "()Z", (void *)&JVM_IsRecord},
84-
{"getPermittedSubclasses0", "()[" STR, (void *)&JVM_GetPermittedSubclasses},
84+
{"getPermittedSubclasses0", "()[" CLS, (void *)&JVM_GetPermittedSubclasses},
8585
};
8686

8787
#undef OBJ

src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java

+40-3
Original file line numberDiff line numberDiff line change
@@ -1629,32 +1629,69 @@ public boolean isCastable(Type t, Type s) {
16291629
}
16301630

16311631
/**
1632-
* Is t is castable to s?<br>
1632+
* Is t castable to s?<br>
16331633
* s is assumed to be an erased type.<br>
16341634
* (not defined for Method and ForAll types).
16351635
*/
16361636
public boolean isCastable(Type t, Type s, Warner warn) {
1637+
// if same type
16371638
if (t == s)
16381639
return true;
1640+
// if one of the types is primitive
16391641
if (t.isPrimitive() != s.isPrimitive()) {
16401642
t = skipTypeVars(t, false);
16411643
return (isConvertible(t, s, warn)
16421644
|| (s.isPrimitive() &&
16431645
isSubtype(boxedClass(s).type, t)));
16441646
}
1647+
boolean result;
16451648
if (warn != warnStack.head) {
16461649
try {
16471650
warnStack = warnStack.prepend(warn);
16481651
checkUnsafeVarargsConversion(t, s, warn);
1649-
return isCastable.visit(t,s);
1652+
result = isCastable.visit(t,s);
16501653
} finally {
16511654
warnStack = warnStack.tail;
16521655
}
16531656
} else {
1654-
return isCastable.visit(t,s);
1657+
result = isCastable.visit(t,s);
16551658
}
1659+
if (result && (t.tsym.isSealed() || s.tsym.isSealed())) {
1660+
return (t.isCompound() || s.isCompound()) ?
1661+
false :
1662+
!areDisjoint((ClassSymbol)t.tsym, (ClassSymbol)s.tsym);
1663+
}
1664+
return result;
16561665
}
16571666
// where
1667+
private boolean areDisjoint(ClassSymbol ts, ClassSymbol ss) {
1668+
if (isSubtype(ts.type, ss.type)) {
1669+
return false;
1670+
}
1671+
// if both are classes or both are interfaces, shortcut
1672+
if (ts.isInterface() == ss.isInterface() && isSubtype(ss.type, ts.type)) {
1673+
return false;
1674+
}
1675+
if (ts.isInterface() && !ss.isInterface()) {
1676+
/* so ts is interface but ss is a class
1677+
* an interface is disjoint from a class if the class is disjoint form the interface
1678+
*/
1679+
return areDisjoint(ss, ts);
1680+
}
1681+
// a final class that is not subtype of ss is disjoint
1682+
if (!ts.isInterface() && ts.isFinal()) {
1683+
return true;
1684+
}
1685+
// if at least one is sealed
1686+
if (ts.isSealed() || ss.isSealed()) {
1687+
// permitted subtypes have to be disjoint with the other symbol
1688+
ClassSymbol sealedOne = ts.isSealed() ? ts : ss;
1689+
ClassSymbol other = sealedOne == ts ? ss : ts;
1690+
return sealedOne.permitted.stream().allMatch(sym -> areDisjoint((ClassSymbol)sym, other));
1691+
}
1692+
return false;
1693+
}
1694+
16581695
private TypeRelation isCastable = new TypeRelation() {
16591696

16601697
public Boolean visitType(Type t, Type s) {

src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -1303,7 +1303,10 @@ && checkDisjoint(pos, flags,
13031303
SEALED | NON_SEALED)
13041304
&& checkDisjoint(pos, flags,
13051305
SEALED,
1306-
FINAL | NON_SEALED)) {
1306+
FINAL | NON_SEALED)
1307+
&& checkDisjoint(pos, flags,
1308+
SEALED,
1309+
ANNOTATION)) {
13071310
// skip
13081311
}
13091312
return flags & (mask | ~ExtendedStandardFlags) | implicit;

src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -4264,11 +4264,19 @@ protected boolean isSealedClassStart(boolean local) {
42644264
private boolean allowedAfterSealedOrNonSealed(Token next, boolean local, boolean currentIsNonSealed) {
42654265
return local ?
42664266
switch (next.kind) {
4267-
case MONKEYS_AT, ABSTRACT, FINAL, STRICTFP, CLASS, INTERFACE, ENUM -> true;
4267+
case MONKEYS_AT -> {
4268+
Token afterNext = S.token(2);
4269+
yield afterNext.kind != INTERFACE || currentIsNonSealed;
4270+
}
4271+
case ABSTRACT, FINAL, STRICTFP, CLASS, INTERFACE, ENUM -> true;
42684272
default -> false;
42694273
} :
42704274
switch (next.kind) {
4271-
case MONKEYS_AT, PUBLIC, PROTECTED, PRIVATE, ABSTRACT, STATIC, FINAL, STRICTFP, CLASS, INTERFACE, ENUM -> true;
4275+
case MONKEYS_AT -> {
4276+
Token afterNext = S.token(2);
4277+
yield afterNext.kind != INTERFACE || currentIsNonSealed;
4278+
}
4279+
case PUBLIC, PROTECTED, PRIVATE, ABSTRACT, STATIC, FINAL, STRICTFP, CLASS, INTERFACE, ENUM -> true;
42724280
case IDENTIFIER -> isNonSealedIdentifier(next, currentIsNonSealed ? 3 : 1) || next.name() == names.sealed;
42734281
default -> false;
42744282
};

0 commit comments

Comments
 (0)