mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 22:34:27 +02:00
8227046: compiler implementation for sealed classes
8225056: VM support for sealed classes 8227044: javax.lang.model for sealed classes 8227045: Preview APIs support for sealed classes 8227047: Javadoc for sealed types 8245854: JVM TI Specification for sealed classes Co-authored-by: Harold Seigel <harold.seigel@oracle.com> Co-authored-by: Jan Lahoda <jan.lahoda@oracle.com> Reviewed-by: mcimadamore, forax, darcy, dholmes, jlahoda, lfoltan, mchung, sspitsyn, vromero
This commit is contained in:
parent
567692e4ae
commit
d42bfef8a4
139 changed files with 6877 additions and 192 deletions
|
@ -3212,6 +3212,41 @@ u2 ClassFileParser::parse_classfile_nest_members_attribute(const ClassFileStream
|
|||
return length;
|
||||
}
|
||||
|
||||
u2 ClassFileParser::parse_classfile_permitted_subclasses_attribute(const ClassFileStream* const cfs,
|
||||
const u1* const permitted_subclasses_attribute_start,
|
||||
TRAPS) {
|
||||
const u1* const current_mark = cfs->current();
|
||||
u2 length = 0;
|
||||
if (permitted_subclasses_attribute_start != NULL) {
|
||||
cfs->set_current(permitted_subclasses_attribute_start);
|
||||
cfs->guarantee_more(2, CHECK_0); // length
|
||||
length = cfs->get_u2_fast();
|
||||
}
|
||||
if (length < 1) {
|
||||
classfile_parse_error("PermittedSubclasses attribute is empty in class file %s", CHECK_0);
|
||||
}
|
||||
const int size = length;
|
||||
Array<u2>* const permitted_subclasses = MetadataFactory::new_array<u2>(_loader_data, size, CHECK_0);
|
||||
_permitted_subclasses = permitted_subclasses;
|
||||
|
||||
int index = 0;
|
||||
cfs->guarantee_more(2 * length, CHECK_0);
|
||||
for (int n = 0; n < length; n++) {
|
||||
const u2 class_info_index = cfs->get_u2_fast();
|
||||
check_property(
|
||||
valid_klass_reference_at(class_info_index),
|
||||
"Permitted subclass class_info_index %u has bad constant type in class file %s",
|
||||
class_info_index, CHECK_0);
|
||||
permitted_subclasses->at_put(index++, class_info_index);
|
||||
}
|
||||
assert(index == size, "wrong size");
|
||||
|
||||
// Restore buffer's current position.
|
||||
cfs->set_current(current_mark);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
// Record {
|
||||
// u2 attribute_name_index;
|
||||
// u4 attribute_length;
|
||||
|
@ -3476,10 +3511,16 @@ void ClassFileParser::parse_classfile_bootstrap_methods_attribute(const ClassFil
|
|||
CHECK);
|
||||
}
|
||||
|
||||
bool ClassFileParser::supports_sealed_types() {
|
||||
return _major_version == JVM_CLASSFILE_MAJOR_VERSION &&
|
||||
_minor_version == JAVA_PREVIEW_MINOR_VERSION &&
|
||||
Arguments::enable_preview();
|
||||
}
|
||||
|
||||
bool ClassFileParser::supports_records() {
|
||||
return _major_version == JVM_CLASSFILE_MAJOR_VERSION &&
|
||||
_minor_version == JAVA_PREVIEW_MINOR_VERSION &&
|
||||
Arguments::enable_preview();
|
||||
_minor_version == JAVA_PREVIEW_MINOR_VERSION &&
|
||||
Arguments::enable_preview();
|
||||
}
|
||||
|
||||
void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cfs,
|
||||
|
@ -3494,11 +3535,14 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf
|
|||
_inner_classes = Universe::the_empty_short_array();
|
||||
// Set nest members attribute to default sentinel
|
||||
_nest_members = Universe::the_empty_short_array();
|
||||
// Set _permitted_subclasses attribute to default sentinel
|
||||
_permitted_subclasses = Universe::the_empty_short_array();
|
||||
cfs->guarantee_more(2, CHECK); // attributes_count
|
||||
u2 attributes_count = cfs->get_u2_fast();
|
||||
bool parsed_sourcefile_attribute = false;
|
||||
bool parsed_innerclasses_attribute = false;
|
||||
bool parsed_nest_members_attribute = false;
|
||||
bool parsed_permitted_subclasses_attribute = false;
|
||||
bool parsed_nest_host_attribute = false;
|
||||
bool parsed_record_attribute = false;
|
||||
bool parsed_enclosingmethod_attribute = false;
|
||||
|
@ -3522,6 +3566,8 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf
|
|||
u4 nest_members_attribute_length = 0;
|
||||
const u1* record_attribute_start = NULL;
|
||||
u4 record_attribute_length = 0;
|
||||
const u1* permitted_subclasses_attribute_start = NULL;
|
||||
u4 permitted_subclasses_attribute_length = 0;
|
||||
|
||||
// Iterate over attributes
|
||||
while (attributes_count--) {
|
||||
|
@ -3738,6 +3784,26 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf
|
|||
}
|
||||
}
|
||||
cfs->skip_u1(attribute_length, CHECK);
|
||||
} else if (_major_version >= JAVA_15_VERSION) {
|
||||
// Check for PermittedSubclasses tag
|
||||
if (tag == vmSymbols::tag_permitted_subclasses()) {
|
||||
if (supports_sealed_types()) {
|
||||
if (parsed_permitted_subclasses_attribute) {
|
||||
classfile_parse_error("Multiple PermittedSubclasses attributes in class file %s", CHECK);
|
||||
}
|
||||
// Classes marked ACC_FINAL cannot have a PermittedSubclasses attribute.
|
||||
if (_access_flags.is_final()) {
|
||||
classfile_parse_error("PermittedSubclasses attribute in final class file %s", CHECK);
|
||||
}
|
||||
parsed_permitted_subclasses_attribute = true;
|
||||
permitted_subclasses_attribute_start = cfs->current();
|
||||
permitted_subclasses_attribute_length = attribute_length;
|
||||
}
|
||||
cfs->skip_u1(attribute_length, CHECK);
|
||||
} else {
|
||||
// Unknown attribute
|
||||
cfs->skip_u1(attribute_length, CHECK);
|
||||
}
|
||||
} else {
|
||||
// Unknown attribute
|
||||
cfs->skip_u1(attribute_length, CHECK);
|
||||
|
@ -3806,6 +3872,18 @@ void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cf
|
|||
}
|
||||
}
|
||||
|
||||
if (parsed_permitted_subclasses_attribute) {
|
||||
const u2 num_subclasses = parse_classfile_permitted_subclasses_attribute(
|
||||
cfs,
|
||||
permitted_subclasses_attribute_start,
|
||||
CHECK);
|
||||
if (_need_verify) {
|
||||
guarantee_property(
|
||||
permitted_subclasses_attribute_length == sizeof(num_subclasses) + sizeof(u2) * num_subclasses,
|
||||
"Wrong PermittedSubclasses attribute length in class file %s", CHECK);
|
||||
}
|
||||
}
|
||||
|
||||
if (_max_bootstrap_specifier_index >= 0) {
|
||||
guarantee_property(parsed_bootstrap_methods_attribute,
|
||||
"Missing BootstrapMethods attribute in class file %s", CHECK);
|
||||
|
@ -3873,6 +3951,7 @@ void ClassFileParser::apply_parsed_class_metadata(
|
|||
this_klass->set_nest_host_index(_nest_host);
|
||||
this_klass->set_local_interfaces(_local_interfaces);
|
||||
this_klass->set_annotations(_combined_annotations);
|
||||
this_klass->set_permitted_subclasses(_permitted_subclasses);
|
||||
this_klass->set_record_components(_record_components);
|
||||
// Delay the setting of _transitive_interfaces until after initialize_supers() in
|
||||
// fill_instance_klass(). It is because the _transitive_interfaces may be shared with
|
||||
|
@ -4681,12 +4760,34 @@ static void check_super_class_access(const InstanceKlass* this_klass, TRAPS) {
|
|||
const Klass* const super = this_klass->super();
|
||||
|
||||
if (super != NULL) {
|
||||
const InstanceKlass* super_ik = InstanceKlass::cast(super);
|
||||
|
||||
if (super->is_final()) {
|
||||
ResourceMark rm(THREAD);
|
||||
Exceptions::fthrow(
|
||||
THREAD_AND_LOCATION,
|
||||
vmSymbols::java_lang_VerifyError(),
|
||||
"class %s cannot inherit from final class %s",
|
||||
this_klass->external_name(),
|
||||
super_ik->external_name());
|
||||
return;
|
||||
}
|
||||
|
||||
if (super_ik->is_sealed() && !super_ik->has_as_permitted_subclass(this_klass)) {
|
||||
ResourceMark rm(THREAD);
|
||||
Exceptions::fthrow(
|
||||
THREAD_AND_LOCATION,
|
||||
vmSymbols::java_lang_IncompatibleClassChangeError(),
|
||||
"class %s cannot inherit from sealed class %s",
|
||||
this_klass->external_name(),
|
||||
super_ik->external_name());
|
||||
return;
|
||||
}
|
||||
|
||||
// If the loader is not the boot loader then throw an exception if its
|
||||
// superclass is in package jdk.internal.reflect and its loader is not a
|
||||
// special reflection class loader
|
||||
if (!this_klass->class_loader_data()->is_the_null_class_loader_data()) {
|
||||
assert(super->is_instance_klass(), "super is not instance klass");
|
||||
PackageEntry* super_package = super->package();
|
||||
if (super_package != NULL &&
|
||||
super_package->name()->fast_compare(vmSymbols::jdk_internal_reflect()) == 0 &&
|
||||
|
@ -4742,6 +4843,19 @@ static void check_super_interface_access(const InstanceKlass* this_klass, TRAPS)
|
|||
for (int i = lng - 1; i >= 0; i--) {
|
||||
InstanceKlass* const k = local_interfaces->at(i);
|
||||
assert (k != NULL && k->is_interface(), "invalid interface");
|
||||
|
||||
if (k->is_sealed() && !k->has_as_permitted_subclass(this_klass)) {
|
||||
ResourceMark rm(THREAD);
|
||||
Exceptions::fthrow(
|
||||
THREAD_AND_LOCATION,
|
||||
vmSymbols::java_lang_IncompatibleClassChangeError(),
|
||||
"class %s cannot %s sealed interface %s",
|
||||
this_klass->external_name(),
|
||||
this_klass->is_interface() ? "extend" : "implement",
|
||||
k->external_name());
|
||||
return;
|
||||
}
|
||||
|
||||
Reflection::VerifyClassAccessResults vca_result =
|
||||
Reflection::verify_class_access(this_klass, k, false);
|
||||
if (vca_result != Reflection::ACCESS_OK) {
|
||||
|
@ -5677,6 +5791,7 @@ void ClassFileParser::fill_instance_klass(InstanceKlass* ik,
|
|||
assert(NULL == _local_interfaces, "invariant");
|
||||
assert(NULL == _combined_annotations, "invariant");
|
||||
assert(NULL == _record_components, "invariant");
|
||||
assert(NULL == _permitted_subclasses, "invariant");
|
||||
|
||||
if (_has_final_method) {
|
||||
ik->set_has_final_method();
|
||||
|
@ -5965,6 +6080,7 @@ ClassFileParser::ClassFileParser(ClassFileStream* stream,
|
|||
_inner_classes(NULL),
|
||||
_nest_members(NULL),
|
||||
_nest_host(0),
|
||||
_permitted_subclasses(NULL),
|
||||
_record_components(NULL),
|
||||
_local_interfaces(NULL),
|
||||
_transitive_interfaces(NULL),
|
||||
|
@ -6073,6 +6189,7 @@ void ClassFileParser::clear_class_metadata() {
|
|||
_methods = NULL;
|
||||
_inner_classes = NULL;
|
||||
_nest_members = NULL;
|
||||
_permitted_subclasses = NULL;
|
||||
_local_interfaces = NULL;
|
||||
_combined_annotations = NULL;
|
||||
_class_annotations = _class_type_annotations = NULL;
|
||||
|
@ -6109,6 +6226,10 @@ ClassFileParser::~ClassFileParser() {
|
|||
InstanceKlass::deallocate_record_components(_loader_data, _record_components);
|
||||
}
|
||||
|
||||
if (_permitted_subclasses != NULL && _permitted_subclasses != Universe::the_empty_short_array()) {
|
||||
MetadataFactory::free_array<u2>(_loader_data, _permitted_subclasses);
|
||||
}
|
||||
|
||||
// Free interfaces
|
||||
InstanceKlass::deallocate_interfaces(_loader_data, _super_klass,
|
||||
_local_interfaces, _transitive_interfaces);
|
||||
|
@ -6507,10 +6628,6 @@ void ClassFileParser::post_process_parsed_stream(const ClassFileStream* const st
|
|||
);
|
||||
return;
|
||||
}
|
||||
// Make sure super class is not final
|
||||
if (_super_klass->is_final()) {
|
||||
THROW_MSG(vmSymbols::java_lang_VerifyError(), "Cannot inherit from final class");
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the transitive list of all unique interfaces implemented by this class
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue