Skip to content

Commit 93a16f0

Browse files
committed
[GR-69666] JVMCI: Add ResolvedJavaType#{isSealed,getPermittedSubclasses}
PullRequest: labsjdk-ce/207
2 parents e5b616d + a7bc6bf commit 93a16f0

File tree

6 files changed

+169
-9
lines changed

6 files changed

+169
-9
lines changed

src/hotspot/share/jvmci/vmStructs_jvmci.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@
165165
unchecked_nonstatic_field(Array<u2>, _data, sizeof(u2)) \
166166
nonstatic_field(Array<Klass*>, _length, int) \
167167
nonstatic_field(Array<Klass*>, _data[0], Klass*) \
168+
unchecked_nonstatic_field(Array<jushort>, _data, sizeof(jushort)) \
169+
nonstatic_field(Array<jushort>, _length, int) \
168170
\
169171
volatile_nonstatic_field(BasicLock, _metadata, uintptr_t) \
170172
\
@@ -221,6 +223,7 @@
221223
volatile_nonstatic_field(InstanceKlass, _init_thread, JavaThread*) \
222224
nonstatic_field(InstanceKlass, _misc_flags._flags, u2) \
223225
nonstatic_field(InstanceKlass, _annotations, Annotations*) \
226+
nonstatic_field(InstanceKlass, _permitted_subclasses, Array<jushort>*) \
224227
\
225228
volatile_nonstatic_field(JavaFrameAnchor, _last_Java_sp, intptr_t*) \
226229
volatile_nonstatic_field(JavaFrameAnchor, _last_Java_pc, address) \

src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedObjectTypeImpl.java

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
import java.lang.reflect.Field;
3535
import java.lang.reflect.Modifier;
3636
import java.nio.ByteOrder;
37+
import java.util.Collections;
38+
import java.util.ArrayList;
3739
import java.util.HashMap;
3840
import java.util.List;
3941

@@ -100,7 +102,7 @@ static HotSpotResolvedObjectTypeImpl getJavaLangObject() {
100102

101103
/**
102104
* Gets the JVMCI mirror from a HotSpot type.
103-
*
105+
* <p>
104106
* Called from the VM.
105107
*
106108
* @param klassPointer a native pointer to the Klass*
@@ -183,6 +185,67 @@ public ResolvedJavaType getComponentType() {
183185
return this.equals(componentType) ? null : componentType;
184186
}
185187

188+
@Override
189+
public List<JavaType> getPermittedSubclasses() {
190+
if (isArray() || isPrimitive()) {
191+
return null;
192+
}
193+
HotSpotVMConfig config = config();
194+
final long metaspacePermittedSubclasses = UNSAFE.getAddress(getKlassPointer() + config.instanceKlassPermittedSubclassesOffset);
195+
if (metaspacePermittedSubclasses == 0) {
196+
return null;
197+
}
198+
final int length = UNSAFE.getInt(metaspacePermittedSubclasses + config.arrayJUShortLengthOffset);
199+
if (length == 0) {
200+
return null;
201+
}
202+
// is_sealed
203+
ArrayList<JavaType> permittedSubclasses = new ArrayList<>(length);
204+
for (long i = 0; i < length; i++) {
205+
int cpIndex = UNSAFE.getShort(metaspacePermittedSubclasses + config.arrayJUShortDataOffset + compilerToVM().ARRAY_SHORT_INDEX_SCALE * i);
206+
Object cpEntry = getConstantPool().lookupConstant(cpIndex);
207+
if (cpEntry instanceof ResolvedJavaType rjt) {
208+
if (isDirectSubType(rjt)) {
209+
// only adding direct subtypes
210+
permittedSubclasses.add(rjt);
211+
}
212+
} else if (cpEntry instanceof UnresolvedJavaType ujr) {
213+
// Unresolved - cannot tell if it is a direct or indirect subtype
214+
permittedSubclasses.add(ujr);
215+
} else {
216+
throw new InternalError("Unexpected ConstantPool entry: " + cpEntry);
217+
}
218+
}
219+
return Collections.unmodifiableList(permittedSubclasses);
220+
}
221+
222+
@Override
223+
public boolean isSealed() {
224+
if (isArray()) {
225+
return false;
226+
}
227+
HotSpotVMConfig config = config();
228+
final long metaspacePermittedSubclasses = UNSAFE.getAddress(getKlassPointer() + config.instanceKlassPermittedSubclassesOffset);
229+
if (metaspacePermittedSubclasses == 0) {
230+
return false;
231+
}
232+
final int length = UNSAFE.getInt(metaspacePermittedSubclasses + config.arrayJUShortLengthOffset);
233+
return length != 0;
234+
}
235+
236+
private boolean isDirectSubType(ResolvedJavaType c) {
237+
if (isInterface()) {
238+
for (ResolvedJavaType i : c.getInterfaces()) {
239+
if (i == this) {
240+
return true;
241+
}
242+
}
243+
} else {
244+
return c.getSuperclass() == this;
245+
}
246+
return false;
247+
}
248+
186249
@Override
187250
public AssumptionResult<ResolvedJavaType> findLeafConcreteSubtype() {
188251
if (isLeaf()) {
@@ -694,11 +757,11 @@ static class FieldInfo {
694757
/**
695758
* Creates a field info with the provided indices.
696759
*
697-
* @param nameIndex index of field's name in the constant pool
698-
* @param signatureIndex index of field's signature in the constant pool
699-
* @param offset field's offset
700-
* @param classfileFlags field's access flags (from the class file)
701-
* @param internalFlags field's internal flags (from the VM)
760+
* @param nameIndex index of field's name in the constant pool
761+
* @param signatureIndex index of field's signature in the constant pool
762+
* @param offset field's offset
763+
* @param classfileFlags field's access flags (from the class file)
764+
* @param internalFlags field's internal flags (from the VM)
702765
* @param initializerIndex field's initial value index in the constant pool
703766
*/
704767
FieldInfo(int nameIndex, int signatureIndex, int offset, int classfileFlags, int internalFlags, int initializerIndex) {
@@ -737,6 +800,7 @@ public int getOffset() {
737800
/**
738801
* Returns the name of this field as a {@link String}. If the field is an internal field the
739802
* name index is pointing into the vmSymbols table.
803+
*
740804
* @param klass field's holder class
741805
*/
742806
public String getName(HotSpotResolvedObjectTypeImpl klass) {
@@ -746,6 +810,7 @@ public String getName(HotSpotResolvedObjectTypeImpl klass) {
746810
/**
747811
* Returns the signature of this field as {@link String}. If the field is an internal field
748812
* the signature index is pointing into the vmSymbols table.
813+
*
749814
* @param klass field's holder class
750815
*/
751816
public String getSignature(HotSpotResolvedObjectTypeImpl klass) {
@@ -828,7 +893,7 @@ public ResolvedJavaField[] getStaticFields() {
828893
* Gets the instance or static fields of this class.
829894
*
830895
* @param retrieveStaticFields specifies whether to return instance or static fields
831-
* @param prepend an array to be prepended to the returned result
896+
* @param prepend an array to be prepended to the returned result
832897
*/
833898
private HotSpotResolvedJavaField[] getFields(boolean retrieveStaticFields, HotSpotResolvedJavaField[] prepend) {
834899
HotSpotVMConfig config = config();
@@ -954,7 +1019,7 @@ public boolean isDefinitelyResolvedWithRespectTo(ResolvedJavaType accessingClass
9541019

9551020
private boolean hasSameClassLoader(HotSpotResolvedObjectTypeImpl otherMirror) {
9561021
return UnsafeAccess.UNSAFE.getAddress(getKlassPointer() + config().classLoaderDataOffset) == UnsafeAccess.UNSAFE.getAddress(
957-
otherMirror.getKlassPointer() + config().classLoaderDataOffset);
1022+
otherMirror.getKlassPointer() + config().classLoaderDataOffset);
9581023
}
9591024

9601025
@Override

src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotResolvedPrimitiveType.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,16 @@ public ResolvedJavaType getElementalType() {
102102
return this;
103103
}
104104

105+
@Override
106+
public List<JavaType> getPermittedSubclasses() {
107+
return null;
108+
}
109+
110+
@Override
111+
public boolean isSealed() {
112+
return false;
113+
}
114+
105115
@Override
106116
public ResolvedJavaType getComponentType() {
107117
return null;

src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotVMConfig.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import static jdk.vm.ci.hotspot.UnsafeAccess.UNSAFE;
2727

2828
import jdk.vm.ci.common.JVMCIError;
29-
import jdk.vm.ci.services.Services;
3029
import jdk.internal.misc.Unsafe;
3130
import jdk.internal.util.Architecture;
3231

@@ -97,6 +96,7 @@ static String getHostArchitectureName() {
9796
final int instanceKlassConstantsOffset = getFieldOffset("InstanceKlass::_constants", Integer.class, "ConstantPool*");
9897
final int instanceKlassFieldInfoStreamOffset = getFieldOffset("InstanceKlass::_fieldinfo_stream", Integer.class, "Array<u1>*");
9998
final int instanceKlassAnnotationsOffset = getFieldOffset("InstanceKlass::_annotations", Integer.class, "Annotations*");
99+
final int instanceKlassPermittedSubclassesOffset = getFieldOffset("InstanceKlass::_permitted_subclasses", Integer.class, "Array<jushort>*");
100100
final int instanceKlassMiscFlagsOffset = getFieldOffset("InstanceKlass::_misc_flags._flags", Integer.class, "u2");
101101
final int klassMiscFlagsOffset = getFieldOffset("Klass::_misc_flags._flags", Integer.class, "u1");
102102
final int klassVtableStartOffset = getFieldValue("CompilerToVM::Data::Klass_vtable_start_offset", Integer.class, "int");
@@ -113,6 +113,8 @@ static String getHostArchitectureName() {
113113
final int arrayU1LengthOffset = getFieldOffset("Array<int>::_length", Integer.class, "int");
114114
final int arrayU1DataOffset = getFieldOffset("Array<u1>::_data", Integer.class);
115115
final int arrayU2DataOffset = getFieldOffset("Array<u2>::_data", Integer.class);
116+
final int arrayJUShortDataOffset = getFieldOffset("Array<jushort>::_data", Integer.class);
117+
final int arrayJUShortLengthOffset = getFieldOffset("Array<jushort>::_length", Integer.class, "int");
116118

117119
final int jvmAccHasFinalizer = getConstant("KlassFlags::_misc_has_finalizer", Integer.class);
118120
final int jvmFieldFlagInternalShift = getConstant("FieldInfo::FieldFlags::_ff_injected", Integer.class);

src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/meta/ResolvedJavaType.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,42 @@ default ResolvedJavaType getElementalType() {
247247
@Override
248248
ResolvedJavaType getArrayClass();
249249

250+
/**
251+
* Returns an unmodifiable list of {@link JavaType} objects representing the subclasses or
252+
* subinterfaces that are explicitly permitted to extend or implement this sealed class or
253+
* interface, as declared in its <em>PermittedSubclasses</em> class file attribute. Only direct
254+
* subtypes listed by the sealing declaration are included.
255+
* <p>
256+
* For each subtype, if it is resolved, the entry will be a {@link ResolvedJavaType}; otherwise
257+
* it will be an {@link UnresolvedJavaType}. For unresolved subtypes, callers must not assume
258+
* whether a type is a direct or indirect permitted subtype. The order of the results matches
259+
* that of {@link Class#getPermittedSubclasses()}.
260+
* <p>
261+
* If the type is not sealed, returns {@code null}.
262+
*
263+
* @return unmodifiable list of permitted subtypes, or {@code null} if this type is not sealed
264+
* @see Class#getPermittedSubclasses()
265+
*/
266+
List<JavaType> getPermittedSubclasses();
267+
268+
/**
269+
* Returns {@code true} if and only if this type represents a sealed class or
270+
* interface. If this type represents a primitive type, {@code void}, or an array
271+
* type, this method returns {@code false}. A sealed class or interface has
272+
* (possibly zero) permitted subclasses; {@link #getPermittedSubclasses()} returns
273+
* a non-null but possibly empty value for a sealed class or interface.
274+
*
275+
* @return {@code true} if and only if this type represents a sealed class
276+
* or interface.
277+
* @see Class#isSealed()
278+
*/
279+
default boolean isSealed() {
280+
if (isArray() || isPrimitive()) {
281+
return false;
282+
}
283+
return getPermittedSubclasses() != null;
284+
}
285+
250286
/**
251287
* Resolves the method implementation for virtual dispatches on objects of this dynamic type.
252288
* This resolution process only searches "up" the class hierarchy of this type. A broader search

test/hotspot/jtreg/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import static org.junit.Assert.assertNotNull;
6060
import static org.junit.Assert.assertNull;
6161
import static org.junit.Assert.assertTrue;
62+
import static org.junit.Assert.fail;
6263

6364
import java.io.DataInputStream;
6465
import java.io.IOException;
@@ -708,6 +709,48 @@ public void getArrayClassTest() {
708709
}
709710
}
710711

712+
private static sealed class SealedTestClass
713+
permits PermittedTestClass1, PermittedTestClass3 {
714+
}
715+
716+
private static sealed class PermittedTestClass1 extends SealedTestClass
717+
permits PermittedTestClass2 {
718+
}
719+
720+
private static final class PermittedTestClass2 extends PermittedTestClass1 {
721+
}
722+
723+
private static final class PermittedTestClass3 extends SealedTestClass {
724+
}
725+
726+
private static class UnsealedTestClass {
727+
}
728+
729+
private static final class UnsealedTestSubClass extends UnsealedTestClass {
730+
}
731+
732+
@Test
733+
public void getPermittedSubclassesTest() {
734+
assertGetPermittedSubclasses(int.class);
735+
assertGetPermittedSubclasses(void.class);
736+
assertGetPermittedSubclasses(UnsealedTestClass.class);
737+
assertGetPermittedSubclasses(SealedTestClass.class);
738+
}
739+
740+
private void assertGetPermittedSubclasses(Class<?> clazz) {
741+
ResolvedJavaType type = metaAccess.lookupJavaType(clazz);
742+
assertEquals("Sealed status mismatch for class '" + clazz.getName(), clazz.isSealed(), type.isSealed());
743+
List<JavaType> actual = type.getPermittedSubclasses();
744+
Class<?>[] expected = clazz.getPermittedSubclasses();
745+
if (expected == null) {
746+
assertNull(actual);
747+
} else {
748+
Set<JavaType> actualSet = new HashSet<>(actual);
749+
Set<JavaType> expectedSet = Arrays.stream(expected).map(metaAccess::lookupJavaType).collect(Collectors.toSet());
750+
assertEquals(expectedSet, actualSet);
751+
}
752+
}
753+
711754
static class Declarations {
712755

713756
final Method implementation;
@@ -1313,6 +1356,7 @@ public void getAnnotationDataTest() throws Exception {
13131356
"getElementalType",
13141357
"getEnclosingType",
13151358
"lookupType",
1359+
"isSealed", // tested with getPermittedSubsclasses
13161360
"resolveField",
13171361
"$jacocoInit"
13181362
};

0 commit comments

Comments
 (0)