Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/hotspot/share/jvmci/vmStructs_jvmci.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@
unchecked_nonstatic_field(Array<u2>, _data, sizeof(u2)) \
nonstatic_field(Array<Klass*>, _length, int) \
nonstatic_field(Array<Klass*>, _data[0], Klass*) \
unchecked_nonstatic_field(Array<jushort>, _data, sizeof(jushort)) \
nonstatic_field(Array<jushort>, _length, int) \
\
volatile_nonstatic_field(BasicLock, _metadata, uintptr_t) \
\
Expand Down Expand Up @@ -221,6 +223,7 @@
volatile_nonstatic_field(InstanceKlass, _init_thread, JavaThread*) \
nonstatic_field(InstanceKlass, _misc_flags._flags, u2) \
nonstatic_field(InstanceKlass, _annotations, Annotations*) \
nonstatic_field(InstanceKlass, _permitted_subclasses, Array<jushort>*) \
\
volatile_nonstatic_field(JavaFrameAnchor, _last_Java_sp, intptr_t*) \
volatile_nonstatic_field(JavaFrameAnchor, _last_Java_pc, address) \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.ByteOrder;
import java.util.Collections;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

Expand Down Expand Up @@ -100,7 +102,7 @@ static HotSpotResolvedObjectTypeImpl getJavaLangObject() {

/**
* Gets the JVMCI mirror from a HotSpot type.
*
* <p>
* Called from the VM.
*
* @param klassPointer a native pointer to the Klass*
Expand Down Expand Up @@ -183,6 +185,67 @@ public ResolvedJavaType getComponentType() {
return this.equals(componentType) ? null : componentType;
}

@Override
public List<JavaType> getPermittedSubclasses() {
if (isArray() || isPrimitive()) {
return null;
}
HotSpotVMConfig config = config();
final long metaspacePermittedSubclasses = UNSAFE.getAddress(getKlassPointer() + config.instanceKlassPermittedSubclassesOffset);
if (metaspacePermittedSubclasses == 0) {
return null;
}
final int length = UNSAFE.getInt(metaspacePermittedSubclasses + config.arrayJUShortLengthOffset);
if (length == 0) {
return null;
}
// is_sealed
ArrayList<JavaType> permittedSubclasses = new ArrayList<>(length);
for (long i = 0; i < length; i++) {
int cpIndex = UNSAFE.getShort(metaspacePermittedSubclasses + config.arrayJUShortDataOffset + compilerToVM().ARRAY_SHORT_INDEX_SCALE * i);
Object cpEntry = getConstantPool().lookupConstant(cpIndex);
if (cpEntry instanceof ResolvedJavaType rjt) {
if (isDirectSubType(rjt)) {
// only adding direct subtypes
permittedSubclasses.add(rjt);
}
} else if (cpEntry instanceof UnresolvedJavaType ujr) {
// Unresolved - cannot tell if it is a direct or indirect subtype
permittedSubclasses.add(ujr);
} else {
throw new InternalError("Unexpected ConstantPool entry: " + cpEntry);
}
}
return Collections.unmodifiableList(permittedSubclasses);
}

@Override
public boolean isSealed() {
if (isArray()) {
return false;
}
HotSpotVMConfig config = config();
final long metaspacePermittedSubclasses = UNSAFE.getAddress(getKlassPointer() + config.instanceKlassPermittedSubclassesOffset);
if (metaspacePermittedSubclasses == 0) {
return false;
}
final int length = UNSAFE.getInt(metaspacePermittedSubclasses + config.arrayJUShortLengthOffset);
return length != 0;
}

private boolean isDirectSubType(ResolvedJavaType c) {
if (isInterface()) {
for (ResolvedJavaType i : c.getInterfaces()) {
if (i == this) {
return true;
}
}
} else {
return c.getSuperclass() == this;
}
return false;
}

@Override
public AssumptionResult<ResolvedJavaType> findLeafConcreteSubtype() {
if (isLeaf()) {
Expand Down Expand Up @@ -694,11 +757,11 @@ static class FieldInfo {
/**
* Creates a field info with the provided indices.
*
* @param nameIndex index of field's name in the constant pool
* @param signatureIndex index of field's signature in the constant pool
* @param offset field's offset
* @param classfileFlags field's access flags (from the class file)
* @param internalFlags field's internal flags (from the VM)
* @param nameIndex index of field's name in the constant pool
* @param signatureIndex index of field's signature in the constant pool
* @param offset field's offset
* @param classfileFlags field's access flags (from the class file)
* @param internalFlags field's internal flags (from the VM)
* @param initializerIndex field's initial value index in the constant pool
*/
FieldInfo(int nameIndex, int signatureIndex, int offset, int classfileFlags, int internalFlags, int initializerIndex) {
Expand Down Expand Up @@ -737,6 +800,7 @@ public int getOffset() {
/**
* Returns the name of this field as a {@link String}. If the field is an internal field the
* name index is pointing into the vmSymbols table.
*
* @param klass field's holder class
*/
public String getName(HotSpotResolvedObjectTypeImpl klass) {
Expand All @@ -746,6 +810,7 @@ public String getName(HotSpotResolvedObjectTypeImpl klass) {
/**
* Returns the signature of this field as {@link String}. If the field is an internal field
* the signature index is pointing into the vmSymbols table.
*
* @param klass field's holder class
*/
public String getSignature(HotSpotResolvedObjectTypeImpl klass) {
Expand Down Expand Up @@ -828,7 +893,7 @@ public ResolvedJavaField[] getStaticFields() {
* Gets the instance or static fields of this class.
*
* @param retrieveStaticFields specifies whether to return instance or static fields
* @param prepend an array to be prepended to the returned result
* @param prepend an array to be prepended to the returned result
*/
private HotSpotResolvedJavaField[] getFields(boolean retrieveStaticFields, HotSpotResolvedJavaField[] prepend) {
HotSpotVMConfig config = config();
Expand Down Expand Up @@ -954,7 +1019,7 @@ public boolean isDefinitelyResolvedWithRespectTo(ResolvedJavaType accessingClass

private boolean hasSameClassLoader(HotSpotResolvedObjectTypeImpl otherMirror) {
return UnsafeAccess.UNSAFE.getAddress(getKlassPointer() + config().classLoaderDataOffset) == UnsafeAccess.UNSAFE.getAddress(
otherMirror.getKlassPointer() + config().classLoaderDataOffset);
otherMirror.getKlassPointer() + config().classLoaderDataOffset);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,16 @@ public ResolvedJavaType getElementalType() {
return this;
}

@Override
public List<JavaType> getPermittedSubclasses() {
return null;
}

@Override
public boolean isSealed() {
return false;
}

@Override
public ResolvedJavaType getComponentType() {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import static jdk.vm.ci.hotspot.UnsafeAccess.UNSAFE;

import jdk.vm.ci.common.JVMCIError;
import jdk.vm.ci.services.Services;
import jdk.internal.misc.Unsafe;
import jdk.internal.util.Architecture;

Expand Down Expand Up @@ -97,6 +96,7 @@ static String getHostArchitectureName() {
final int instanceKlassConstantsOffset = getFieldOffset("InstanceKlass::_constants", Integer.class, "ConstantPool*");
final int instanceKlassFieldInfoStreamOffset = getFieldOffset("InstanceKlass::_fieldinfo_stream", Integer.class, "Array<u1>*");
final int instanceKlassAnnotationsOffset = getFieldOffset("InstanceKlass::_annotations", Integer.class, "Annotations*");
final int instanceKlassPermittedSubclassesOffset = getFieldOffset("InstanceKlass::_permitted_subclasses", Integer.class, "Array<jushort>*");
final int instanceKlassMiscFlagsOffset = getFieldOffset("InstanceKlass::_misc_flags._flags", Integer.class, "u2");
final int klassMiscFlagsOffset = getFieldOffset("Klass::_misc_flags._flags", Integer.class, "u1");
final int klassVtableStartOffset = getFieldValue("CompilerToVM::Data::Klass_vtable_start_offset", Integer.class, "int");
Expand All @@ -113,6 +113,8 @@ static String getHostArchitectureName() {
final int arrayU1LengthOffset = getFieldOffset("Array<int>::_length", Integer.class, "int");
final int arrayU1DataOffset = getFieldOffset("Array<u1>::_data", Integer.class);
final int arrayU2DataOffset = getFieldOffset("Array<u2>::_data", Integer.class);
final int arrayJUShortDataOffset = getFieldOffset("Array<jushort>::_data", Integer.class);
final int arrayJUShortLengthOffset = getFieldOffset("Array<jushort>::_length", Integer.class, "int");

final int jvmAccHasFinalizer = getConstant("KlassFlags::_misc_has_finalizer", Integer.class);
final int jvmFieldFlagInternalShift = getConstant("FieldInfo::FieldFlags::_ff_injected", Integer.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,42 @@ default ResolvedJavaType getElementalType() {
@Override
ResolvedJavaType getArrayClass();

/**
* Returns an unmodifiable list of {@link JavaType} objects representing the subclasses or
* subinterfaces that are explicitly permitted to extend or implement this sealed class or
* interface, as declared in its <em>PermittedSubclasses</em> class file attribute. Only direct
* subtypes listed by the sealing declaration are included.
* <p>
* For each subtype, if it is resolved, the entry will be a {@link ResolvedJavaType}; otherwise
* it will be an {@link UnresolvedJavaType}. For unresolved subtypes, callers must not assume
* whether a type is a direct or indirect permitted subtype. The order of the results matches
* that of {@link Class#getPermittedSubclasses()}.
* <p>
* If the type is not sealed, returns {@code null}.
*
* @return unmodifiable list of permitted subtypes, or {@code null} if this type is not sealed
* @see Class#getPermittedSubclasses()
*/
List<JavaType> getPermittedSubclasses();

/**
* Returns {@code true} if and only if this type represents a sealed class or
* interface. If this type represents a primitive type, {@code void}, or an array
* type, this method returns {@code false}. A sealed class or interface has
* (possibly zero) permitted subclasses; {@link #getPermittedSubclasses()} returns
* a non-null but possibly empty value for a sealed class or interface.
*
* @return {@code true} if and only if this type represents a sealed class
* or interface.
* @see Class#isSealed()
*/
default boolean isSealed() {
if (isArray() || isPrimitive()) {
return false;
}
return getPermittedSubclasses() != null;
}

/**
* Resolves the method implementation for virtual dispatches on objects of this dynamic type.
* This resolution process only searches "up" the class hierarchy of this type. A broader search
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.DataInputStream;
import java.io.IOException;
Expand Down Expand Up @@ -708,6 +709,48 @@ public void getArrayClassTest() {
}
}

private static sealed class SealedTestClass
permits PermittedTestClass1, PermittedTestClass3 {
}

private static sealed class PermittedTestClass1 extends SealedTestClass
permits PermittedTestClass2 {
}

private static final class PermittedTestClass2 extends PermittedTestClass1 {
}

private static final class PermittedTestClass3 extends SealedTestClass {
}

private static class UnsealedTestClass {
}

private static final class UnsealedTestSubClass extends UnsealedTestClass {
}

@Test
public void getPermittedSubclassesTest() {
assertGetPermittedSubclasses(int.class);
assertGetPermittedSubclasses(void.class);
assertGetPermittedSubclasses(UnsealedTestClass.class);
assertGetPermittedSubclasses(SealedTestClass.class);
}

private void assertGetPermittedSubclasses(Class<?> clazz) {
ResolvedJavaType type = metaAccess.lookupJavaType(clazz);
assertEquals("Sealed status mismatch for class '" + clazz.getName(), clazz.isSealed(), type.isSealed());
List<JavaType> actual = type.getPermittedSubclasses();
Class<?>[] expected = clazz.getPermittedSubclasses();
if (expected == null) {
assertNull(actual);
} else {
Set<JavaType> actualSet = new HashSet<>(actual);
Set<JavaType> expectedSet = Arrays.stream(expected).map(metaAccess::lookupJavaType).collect(Collectors.toSet());
assertEquals(expectedSet, actualSet);
}
}

static class Declarations {

final Method implementation;
Expand Down Expand Up @@ -1313,6 +1356,7 @@ public void getAnnotationDataTest() throws Exception {
"getElementalType",
"getEnclosingType",
"lookupType",
"isSealed", // tested with getPermittedSubsclasses
"resolveField",
"$jacocoInit"
};
Expand Down
Loading