Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JDK-8251986: [lworld] implement Class::valueType and Class::referenceType in Java #152

Closed
wants to merge 11 commits into from
@@ -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('/', '.');
mlchung marked this conversation as resolved.
Show resolved Hide resolved
int index = cn.lastIndexOf('.');
String pn = index > 0 ? cn.substring(0, index) : "";
if (pn.equals(getPackageName())) {
try {
Class<?> valType = Class.forName(cn, false, getClassLoader());
mlchung marked this conversation as resolved.
Show resolved Hide resolved
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);