Skip to content
Permalink
Browse files
8251986: [lworld] implement Class::valueType and Class::referenceType…
… in Java

Reviewed-by: rriggs
  • Loading branch information
Mandy Chung committed Aug 20, 2020
1 parent 2bde31f commit a5aa3a9d262d20caaa58f9306ec6cd02f62d8bbe
@@ -3667,9 +3667,8 @@ void ClassFileParser::parse_classfile_bootstrap_methods_attribute(const ClassFil
}

bool ClassFileParser::supports_sealed_types() {
return _major_version == JVM_CLASSFILE_MAJOR_VERSION &&
_minor_version == JAVA_PREVIEW_MINOR_VERSION &&
Arguments::enable_preview();
// temporarily disable the sealed type preview feature check
return _major_version == JVM_CLASSFILE_MAJOR_VERSION;
}

bool ClassFileParser::supports_records() {
@@ -810,8 +810,6 @@ int java_lang_Class::_class_loader_offset;
int java_lang_Class::_module_offset;
int java_lang_Class::_protection_domain_offset;
int java_lang_Class::_component_mirror_offset;
int java_lang_Class::_val_type_mirror_offset;
int java_lang_Class::_ref_type_mirror_offset;
int java_lang_Class::_init_lock_offset;
int java_lang_Class::_signers_offset;
int java_lang_Class::_name_offset;
@@ -1059,25 +1057,6 @@ void java_lang_Class::create_mirror(Klass* k, Handle class_loader,
// concurrently doesn't expect a k to have a null java_mirror.
release_set_array_klass(comp_mirror(), k);
}

if (k->is_inline_klass()) {
InstanceKlass* super = k->java_super();
set_val_type_mirror(mirror(), mirror());

// if the supertype is a restricted abstract class
if (super != SystemDictionary::Object_klass()) {
assert(super->access_flags().is_abstract(), "must be an abstract class");
oop ref_type_oop = super->java_mirror();
// set the reference projection type
set_ref_type_mirror(mirror(), ref_type_oop);

assert(oopDesc::is_oop(ref_type_oop), "Sanity check");

// set the value and reference projection types
set_val_type_mirror(ref_type_oop, mirror());
set_ref_type_mirror(ref_type_oop, ref_type_oop);
}
}
} else {
assert(fixup_mirror_list() != NULL, "fixup_mirror_list not initialized");
fixup_mirror_list()->push(k);
@@ -1532,26 +1511,6 @@ void java_lang_Class::set_source_file(oop java_class, oop source_file) {
java_class->obj_field_put(_source_file_offset, source_file);
}

oop java_lang_Class::val_type_mirror(oop java_class) {
assert(_val_type_mirror_offset != 0, "must be set");
return java_class->obj_field(_val_type_mirror_offset);
}

void java_lang_Class::set_val_type_mirror(oop java_class, oop mirror) {
assert(_val_type_mirror_offset != 0, "must be set");
java_class->obj_field_put(_val_type_mirror_offset, mirror);
}

oop java_lang_Class::ref_type_mirror(oop java_class) {
assert(_ref_type_mirror_offset != 0, "must be set");
return java_class->obj_field(_ref_type_mirror_offset);
}

void java_lang_Class::set_ref_type_mirror(oop java_class, oop mirror) {
assert(_ref_type_mirror_offset != 0, "must be set");
java_class->obj_field_put(_ref_type_mirror_offset, mirror);
}

oop java_lang_Class::create_basic_type_mirror(const char* basic_type_name, BasicType type, TRAPS) {
// This should be improved by adding a field at the Java level or by
// introducing a new VM klass (see comment in ClassFileParser)
@@ -1711,8 +1670,6 @@ oop java_lang_Class::primitive_mirror(BasicType t) {
macro(_component_mirror_offset, k, "componentType", class_signature, false); \
macro(_module_offset, k, "module", module_signature, false); \
macro(_name_offset, k, "name", string_signature, false); \
macro(_val_type_mirror_offset, k, "valType", class_signature, false); \
macro(_ref_type_mirror_offset, k, "refType", class_signature, false); \
macro(_classData_offset, k, "classData", object_signature, false);

void java_lang_Class::compute_offsets() {
@@ -243,8 +243,6 @@ class java_lang_Class : AllStatic {
static int _component_mirror_offset;
static int _name_offset;
static int _source_file_offset;
static int _val_type_mirror_offset;
static int _ref_type_mirror_offset;
static int _classData_offset;
static int _classRedefinedCount_offset;

@@ -325,12 +323,6 @@ class java_lang_Class : AllStatic {
static void set_module(oop java_class, oop module);
static oop module(oop java_class);

static void set_ref_type_mirror(oop java_class, oop mirror);
static oop ref_type_mirror(oop java_class);

static void set_val_type_mirror(oop java_class, oop mirror);
static oop val_type_mirror(oop java_class);

static oop name(Handle java_class, TRAPS);

static oop source_file(oop java_class);
@@ -579,34 +579,159 @@ public boolean isInlineClass() {
* an empty {@link Optional} otherwise
* @since Valhalla
*/
public Optional<Class<T>> valueType() {
return Optional.ofNullable(valType);
public Optional<Class<?>> valueType() {
if (isPrimitive() || isInterface() || isArray())
return Optional.empty();

Class<?>[] valRefTypes = getProjectionTypes();
return valRefTypes.length > 0 ? Optional.of(valRefTypes[0]) : Optional.empty();
}

/**
* Returns a {@code Class} object representing the <em>reference projection</em>
* type of this class if this {@code Class} represents an
* {@linkplain #isInlineClass() inline class} with a reference projection
* type. If this {@code Class} represents the reference projection type
* Returns a {@code Class} object representing the reference type
* of this class.
* <p>
* If this {@code Class} represents an {@linkplain #isInlineClass()
* inline class} with a reference projection type, then this method
* returns the <em>reference projection</em> type of this inline class.
* <p>
* If this {@code Class} represents the reference projection type
* of an inline class, then this method returns this class.
* If this class is not an {@linkplain #isInlineClass() inline class}
* or this class is an inline class without a reference projection,
* then this method returns an empty {@link Optional}.
* <p>
* If this class is an {@linkplain #isInlineClass() inline class}
* without a reference projection, then this method returns an empty
* {@code Optional}.
* <p>
* If this class is an identity class, then this method returns this
* class.
* <p>
* Otherwise this method returns an empty {@code Optional}.
*
* @return the {@code Class} object representing the value projection type of
* this class if this class is an inline class with a reference
* projection type or this class is the reference projection type;
* an empty {@link Optional} otherwise
* @return the {@code Class} object representing a reference type for
* this class if present; an empty {@link Optional} otherwise.
* @since Valhalla
*/
public Optional<Class<T>> referenceType() {
return valType != null ? Optional.ofNullable(refType) : Optional.of(this);
public Optional<Class<?>> referenceType() {
if (isPrimitive()) return Optional.empty();
if (isInterface() || isArray()) return Optional.of(this);

Class<?>[] valRefTypes = getProjectionTypes();
return valRefTypes.length == 2 ? Optional.of(valRefTypes[1]) : Optional.empty();
}

// set by VM if this class is an inline type
// otherwise, these two fields are null
private transient Class<T> valType;
private transient Class<T> refType;
/*
* Returns true if this Class object represents a reference projection
* type for an inline class.
*
* A reference projection type must be a sealed abstract class that
* permits the inline projection type to extend. The inline projection
* type and reference projection type for an inline type must be of
* the same package.
*/
private boolean isReferenceProjectionType() {
if (isPrimitive() || isArray() || isInterface() || isInlineClass())
return false;

int mods = getModifiers();
if (!Modifier.isAbstract(mods)) {
return false;
}

Class<?>[] valRefTypes = getProjectionTypes();
return valRefTypes.length == 2 && valRefTypes[1] == this;
}

private transient Class<?>[] projectionTypes;
private Class<?>[] getProjectionTypes() {
if (isPrimitive() || isArray() || isInterface())
return null;

Class<?>[] valRefTypes = projectionTypes;
if (valRefTypes == null) {
// C.ensureProjectionTypesInited calls initProjectionTypes that may
// call D.ensureProjectionTypesInited where D is its superclass.
// So initProjectionTypes is called without holding any lock to
// avoid potential deadlock when multiple threads attempt to
// initialize the projection types for C and E where D is
// the superclass of both C and E (which is an error case)
valRefTypes = newProjectionTypeArray();
}
synchronized (this) {
// set the projection types if not set
if (projectionTypes == null) {
projectionTypes = valRefTypes;
}
}
return projectionTypes;
}

/*
* Returns an array of Class object whose element at index 0 represents the
* value projection type and element at index 1 represents the reference
* projection type if present.
*
* If this Class object is neither a value projection type nor
* a reference projection type for an inline class, then an empty array
* is returned.
*/
private Class<?>[] newProjectionTypeArray() {
if (isPrimitive() || isArray() || isInterface())
return null;

if (isInlineClass()) {
Class<?> superClass = getSuperclass();
if (superClass != Object.class && superClass.isReferenceProjectionType()) {
return new Class<?>[] { this, superClass };
} else {
return new Class<?>[] { this };
}
} else {
Class<?> valType = valueProjectionType();
if (valType != null) {
return new Class<?>[] { valType, this};
} else {
return EMPTY_CLASS_ARRAY;
}
}
}

/*
* Returns the value projection type if this Class represents
* a reference projection type. If this class is an inline class
* then this method returns this class. Otherwise, returns null.
*/
private Class<?> valueProjectionType() {
if (isPrimitive() || isArray() || isInterface())
return null;

if (isInlineClass())
return this;

int mods = getModifiers();
if (!Modifier.isAbstract(mods)) {
return null;
}

// A reference projection type must be a sealed abstract class
// that permits the inline projection type to extend.
// The inline projection type and reference projection type for
// an inline type must be of the same package.
String[] subclassNames = getPermittedSubclasses0();
if (subclassNames.length == 1) {
String cn = subclassNames[0].replace('/', '.');
int index = cn.lastIndexOf('.');
String pn = index > 0 ? cn.substring(0, index) : "";
if (pn.equals(getPackageName())) {
try {
Class<?> valType = Class.forName(cn, false, getClassLoader());
if (valType.isInlineClass()) {
return valType;
}
} catch (ClassNotFoundException e) {}
}
}
return null;
}

/**
* Creates a new instance of the class represented by this {@code Class}
@@ -25,7 +25,8 @@
/*
* @test
* @summary test reflection on inline types
* @run testng/othervm Reflection
* @compile --enable-preview --source ${jdk.version} Reflection.java
* @run testng/othervm --enable-preview Reflection
*/

import java.lang.reflect.Array;
@@ -37,6 +38,13 @@
import static org.testng.Assert.*;

public class Reflection {
@Test
public static void sanityTest() {
assertTrue(Point.ref.class.permittedSubclasses().length == 1);
assertTrue(Line.ref.class.permittedSubclasses().length == 1);
assertTrue(NonFlattenValue.ref.class.permittedSubclasses().length == 1);
}

@Test
public static void testPointClass() throws Exception {
Point o = Point.makePoint(10, 20);

0 comments on commit a5aa3a9

Please sign in to comment.