< prev index next > src/hotspot/share/classfile/classFileParser.cpp
Print this page
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
+
+ #include "oops/inlineKlass.hpp"
#include "precompiled.hpp"
#include "cds/cdsConfig.hpp"
#include "classfile/classFileParser.hpp"
#include "classfile/classFileStream.hpp"
#include "classfile/classLoader.hpp"
#include "memory/universe.hpp"
#include "oops/annotations.hpp"
#include "oops/constantPool.inline.hpp"
#include "oops/fieldInfo.hpp"
#include "oops/fieldStreams.inline.hpp"
+ #include "oops/inlineKlass.inline.hpp"
#include "oops/instanceKlass.inline.hpp"
#include "oops/instanceMirrorKlass.hpp"
#include "oops/klass.inline.hpp"
#include "oops/klassVtable.hpp"
#include "oops/metadata.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/macros.hpp"
#include "utilities/ostream.hpp"
#include "utilities/resourceHash.hpp"
+ #include "utilities/stringUtils.hpp"
#include "utilities/utf8.hpp"
#if INCLUDE_CDS
#include "classfile/systemDictionaryShared.hpp"
#endif
#if INCLUDE_JFR
#define JAVA_22_VERSION 66
#define JAVA_23_VERSION 67
+ #define CONSTANT_CLASS_DESCRIPTORS 68
+
#define JAVA_24_VERSION 68
void ClassFileParser::set_class_bad_constant_seen(short bad_constant) {
assert((bad_constant == JVM_CONSTANT_Module ||
bad_constant == JVM_CONSTANT_Package) && _major_version >= JAVA_9_VERSION,
// Each of the following case guarantees one more byte in the stream
// for the following tag or the access_flags following constant pool,
// so we don't need bounds-check for reading tag.
const u1 tag = cfs->get_u1_fast();
switch (tag) {
- case JVM_CONSTANT_Class : {
+ case JVM_CONSTANT_Class: {
cfs->guarantee_more(3, CHECK); // name_index, tag/access_flags
const u2 name_index = cfs->get_u2_fast();
cp->klass_index_at_put(index, name_index);
break;
}
case JVM_CONSTANT_ClassIndex: {
const int class_index = cp->klass_index_at(index);
check_property(valid_symbol_at(class_index),
"Invalid constant pool index %u in class file %s",
class_index, CHECK);
+
+ Symbol* const name = cp->symbol_at(class_index);
+ const unsigned int name_len = name->utf8_length();
cp->unresolved_klass_at_put(index, class_index, num_klasses++);
break;
}
case JVM_CONSTANT_StringIndex: {
const int string_index = cp->string_index_at(index);
if (name != vmSymbols::object_initializer_name()) {
classfile_parse_error(
"Bad method name at constant pool index %u in class file %s",
name_ref_index, THREAD);
return;
- } else if (!Signature::is_void_method(signature)) { // must have void signature.
+ } else if (!Signature::is_void_method(signature)) { // must have void signature.
throwIllegalSignature("Method", name, signature, CHECK);
}
}
}
break;
const int name_and_type_ref_index =
cp->uncached_name_and_type_ref_index_at(ref_index);
const int name_ref_index =
cp->name_ref_index_at(name_and_type_ref_index);
const Symbol* const name = cp->symbol_at(name_ref_index);
- if (ref_kind == JVM_REF_newInvokeSpecial) {
- if (name != vmSymbols::object_initializer_name()) {
+
+ if (name != vmSymbols::object_initializer_name()) { // !<init>
+ if (ref_kind == JVM_REF_newInvokeSpecial) {
classfile_parse_error(
"Bad constructor name at constant pool index %u in class file %s",
name_ref_index, THREAD);
return;
}
- } else {
- if (name == vmSymbols::object_initializer_name()) {
+ } else { // <init>
+ // The allowed invocation mode of <init> depends on its signature.
+ // This test corresponds to verify_invoke_instructions in the verifier.
+ const int signature_ref_index =
+ cp->signature_ref_index_at(name_and_type_ref_index);
+ const Symbol* const signature = cp->symbol_at(signature_ref_index);
+ if (signature->is_void_method_signature()
+ && ref_kind == JVM_REF_newInvokeSpecial) {
+ // OK, could be a constructor call
+ } else {
classfile_parse_error(
"Bad method name at constant pool index %u in class file %s",
name_ref_index, THREAD);
return;
}
using NameSigHashtable = ResourceHashtable<NameSigHash, int,
NameSigHash::HASH_ROW_SIZE,
AnyObj::RESOURCE_AREA, mtInternal,
&NameSigHash::hash, &NameSigHash::equals>;
- // Side-effects: populates the _local_interfaces field
- void ClassFileParser::parse_interfaces(const ClassFileStream* const stream,
- const int itfs_len,
- ConstantPool* const cp,
+ static void check_identity_and_value_modifiers(ClassFileParser* current, const InstanceKlass* super_type, TRAPS) {
+ assert(super_type != nullptr,"Method doesn't support null super type");
+ if (super_type->access_flags().is_identity_class() && !current->access_flags().is_identity_class()
+ && super_type->name() != vmSymbols::java_lang_Object()) {
+ THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(),
+ err_msg("Value type %s has an identity type as supertype",
+ current->class_name()->as_klass_external_name()));
+ }
+ }
+
+ void ClassFileParser::parse_interfaces(const ClassFileStream* stream,
+ int itfs_len,
+ ConstantPool* cp,
bool* const has_nonstatic_concrete_methods,
+ // FIXME: lots of these functions
+ // declare their parameters as const,
+ // which adds only noise to the code.
+ // Remove the spurious const modifiers.
+ // Many are of the form "const int x"
+ // or "T* const x".
TRAPS) {
assert(stream != nullptr, "invariant");
assert(cp != nullptr, "invariant");
assert(has_nonstatic_concrete_methods != nullptr, "invariant");
if (itfs_len == 0) {
_local_interfaces = Universe::the_empty_instance_klass_array();
+
} else {
assert(itfs_len > 0, "only called for len>0");
- _local_interfaces = MetadataFactory::new_array<InstanceKlass*>(_loader_data, itfs_len, nullptr, CHECK);
-
- int index;
+ _local_interface_indexes = new GrowableArray<u2>(itfs_len);
+ int index = 0;
for (index = 0; index < itfs_len; index++) {
const u2 interface_index = stream->get_u2(CHECK);
- Klass* interf;
check_property(
valid_klass_reference_at(interface_index),
"Interface name has bad constant pool index %u in class file %s",
interface_index, CHECK);
- if (cp->tag_at(interface_index).is_klass()) {
- interf = cp->resolved_klass_at(interface_index);
- } else {
- Symbol* const unresolved_klass = cp->klass_name_at(interface_index);
-
- // Don't need to check legal name because it's checked when parsing constant pool.
- // But need to make sure it's not an array type.
- guarantee_property(unresolved_klass->char_at(0) != JVM_SIGNATURE_ARRAY,
- "Bad interface name in class file %s", CHECK);
-
- // Call resolve on the interface class name with class circularity checking
- interf = SystemDictionary::resolve_super_or_fail(_class_name,
- unresolved_klass,
- Handle(THREAD, _loader_data->class_loader()),
- _protection_domain,
- false, CHECK);
- }
-
- if (!interf->is_interface()) {
- THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(),
- err_msg("class %s can not implement %s, because it is not an interface (%s)",
- _class_name->as_klass_external_name(),
- interf->external_name(),
- interf->class_in_module_of_loader()));
- }
-
- if (InstanceKlass::cast(interf)->has_nonstatic_concrete_methods()) {
- *has_nonstatic_concrete_methods = true;
- }
- _local_interfaces->at_put(index, InstanceKlass::cast(interf));
+ _local_interface_indexes->at_put_grow(index, interface_index);
}
if (!_need_verify || itfs_len <= 1) {
return;
}
// Check if there's any duplicates in interfaces
ResourceMark rm(THREAD);
// Set containing interface names
ResourceHashtable<Symbol*, int>* interface_names = new ResourceHashtable<Symbol*, int>();
for (index = 0; index < itfs_len; index++) {
- const InstanceKlass* const k = _local_interfaces->at(index);
- Symbol* interface_name = k->name();
+ Symbol* interface_name = cp->klass_name_at(_local_interface_indexes->at(index));
// If no duplicates, add (name, nullptr) in hashtable interface_names.
if (!interface_names->put(interface_name, 0)) {
classfile_parse_error("Duplicate interface name \"%s\" in class file %s",
interface_name->as_C_string(), THREAD);
return;
_method_IntrinsicCandidate,
_jdk_internal_vm_annotation_Contended,
_field_Stable,
_jdk_internal_vm_annotation_ReservedStackAccess,
_jdk_internal_ValueBased,
+ _jdk_internal_ImplicitlyConstructible,
+ _jdk_internal_LooselyConsistentValue,
+ _jdk_internal_NullRestricted,
_java_lang_Deprecated,
_java_lang_Deprecated_for_removal,
_annotation_LIMIT
};
const Location _location;
// Side-effects: populates the _fields, _fields_annotations,
// _fields_type_annotations fields
void ClassFileParser::parse_fields(const ClassFileStream* const cfs,
- bool is_interface,
+ AccessFlags class_access_flags,
ConstantPool* cp,
const int cp_size,
u2* const java_fields_count_ptr,
TRAPS) {
assert(java_fields_count_ptr != nullptr, "invariant");
assert(nullptr == _fields_annotations, "invariant");
assert(nullptr == _fields_type_annotations, "invariant");
+ bool is_inline_type = !class_access_flags.is_identity_class() && !class_access_flags.is_abstract();
cfs->guarantee_more(2, CHECK); // length
const u2 length = cfs->get_u2_fast();
*java_fields_count_ptr = length;
int num_injected = 0;
const InjectedField* const injected = JavaClasses::get_injected(_class_name,
&num_injected);
- const int total_fields = length + num_injected;
+
+ // two more slots are required for inline classes:
+ // one for the static field with a reference to the pre-allocated default value
+ // one for the field the JVM injects when detecting an empty inline class
+ const int total_fields = length + num_injected + (is_inline_type ? 2 : 0);
// Allocate a temporary resource array to collect field data.
// After parsing all fields, data are stored in a UNSIGNED5 compressed stream.
_temp_field_info = new GrowableArray<FieldInfo>(total_fields);
+ int instance_fields_count = 0;
ResourceMark rm(THREAD);
for (int n = 0; n < length; n++) {
// access_flags, name_index, descriptor_index, attributes_count
cfs->guarantee_more(8, CHECK);
+ jint recognized_modifiers = JVM_RECOGNIZED_FIELD_MODIFIERS;
+ if (!supports_inline_types()) {
+ recognized_modifiers &= ~JVM_ACC_STRICT;
+ }
+
+ const jint flags = cfs->get_u2_fast() & recognized_modifiers;
+ verify_legal_field_modifiers(flags, class_access_flags, CHECK);
AccessFlags access_flags;
- const jint flags = cfs->get_u2_fast() & JVM_RECOGNIZED_FIELD_MODIFIERS;
- verify_legal_field_modifiers(flags, is_interface, CHECK);
access_flags.set_flags(flags);
FieldInfo::FieldFlags fieldFlags(0);
const u2 name_index = cfs->get_u2_fast();
check_property(valid_symbol_at(name_index),
check_property(valid_symbol_at(signature_index),
"Invalid constant pool index %u for field signature in class file %s",
signature_index, CHECK);
const Symbol* const sig = cp->symbol_at(signature_index);
verify_legal_field_signature(name, sig, CHECK);
+ if (!access_flags.is_static()) instance_fields_count++;
u2 constantvalue_index = 0;
bool is_synthetic = false;
u2 generic_signature_index = 0;
const bool is_static = access_flags.is_static();
FieldAnnotationCollector parsed_annotations(_loader_data);
+ bool is_null_restricted = false;
+
const u2 attributes_count = cfs->get_u2_fast();
if (attributes_count > 0) {
parse_field_attributes(cfs,
attributes_count,
is_static,
_fields_annotations = MetadataFactory::new_array<AnnotationArray*>(
_loader_data, length, nullptr,
CHECK);
}
_fields_annotations->at_put(n, parsed_annotations.field_annotations());
+ if (parsed_annotations.has_annotation(AnnotationCollector::_jdk_internal_NullRestricted)) {
+ if (!Signature::has_envelope(sig)) {
+ Exceptions::fthrow(
+ THREAD_AND_LOCATION,
+ vmSymbols::java_lang_ClassFormatError(),
+ "Illegal use of @jdk.internal.vm.annotation.NullRestricted annotation on field %s with signature %s (primitive types can never be null)",
+ name->as_C_string(), sig->as_C_string());
+ }
+ is_null_restricted = true;
+ }
parsed_annotations.set_field_annotations(nullptr);
}
if (parsed_annotations.field_type_annotations() != nullptr) {
if (_fields_type_annotations == nullptr) {
_fields_type_annotations =
if (generic_signature_index != 0) {
fieldFlags.update_generic(true);
}
}
+ if (is_null_restricted) {
+ fieldFlags.update_null_free_inline_type(true);
+ }
+
const BasicType type = cp->basic_type_for_signature_at(signature_index);
// Update number of static oop fields.
if (is_static && is_reference_type(type)) {
_static_oop_count++;
}
_temp_field_info->append(fi);
}
assert(_temp_field_info->length() == length, "Must be");
- int index = length;
if (num_injected != 0) {
for (int n = 0; n < num_injected; n++) {
// Check for duplicates
if (injected[n].may_be_java) {
const Symbol* const name = injected[n].name();
// Injected field
FieldInfo::FieldFlags fflags(0);
fflags.update_injected(true);
AccessFlags aflags;
FieldInfo fi(aflags, (u2)(injected[n].name_index), (u2)(injected[n].signature_index), 0, fflags);
- fi.set_index(index);
- _temp_field_info->append(fi);
- index++;
+ int idx = _temp_field_info->append(fi);
+ _temp_field_info->adr_at(idx)->set_index(idx);
}
}
- assert(_temp_field_info->length() == index, "Must be");
+ if (is_inline_type) {
+ // Inject static ".default" field
+ FieldInfo::FieldFlags fflags(0);
+ fflags.update_injected(true);
+ AccessFlags aflags(JVM_ACC_STATIC);
+ FieldInfo fi(aflags,
+ (u2)vmSymbols::as_int(VM_SYMBOL_ENUM_NAME(default_value_name)),
+ (u2)vmSymbols::as_int(VM_SYMBOL_ENUM_NAME(object_signature)),
+ 0,
+ fflags);
+ int idx = _temp_field_info->append(fi);
+ _temp_field_info->adr_at(idx)->set_index(idx);
+ _static_oop_count++;
+ // Inject static ".null_reset" field. This is the same as the .default field but will never have its null-channel set to non-zero.
+ FieldInfo::FieldFlags fflags2(0);
+ fflags2.update_injected(true);
+ AccessFlags aflags2(JVM_ACC_STATIC);
+ FieldInfo fi2(aflags2,
+ (u2)vmSymbols::as_int(VM_SYMBOL_ENUM_NAME(null_reset_value_name)),
+ (u2)vmSymbols::as_int(VM_SYMBOL_ENUM_NAME(object_signature)),
+ 0,
+ fflags2);
+ int idx2 = _temp_field_info->append(fi2);
+ _temp_field_info->adr_at(idx2)->set_index(idx2);
+ _static_oop_count++;
+ }
if (_need_verify && length > 1) {
// Check duplicated fields
ResourceMark rm(THREAD);
// Set containing name-signature pairs
case VM_SYMBOL_ENUM_NAME(jdk_internal_ValueBased_signature): {
if (_location != _in_class) break; // only allow for classes
if (!privileged) break; // only allow in privileged code
return _jdk_internal_ValueBased;
}
+ case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_ImplicitlyConstructible_signature): {
+ if (_location != _in_class) break; // only allow for classes
+ return _jdk_internal_ImplicitlyConstructible;
+ }
+ case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_LooselyConsistentValue_signature): {
+ if (_location != _in_class) break; // only allow for classes
+ return _jdk_internal_LooselyConsistentValue;
+ }
+ case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_NullRestricted_signature): {
+ if (_location != _in_field) break; // only allow for fields
+ return _jdk_internal_NullRestricted;
+ }
case VM_SYMBOL_ENUM_NAME(java_lang_Deprecated): {
return _java_lang_Deprecated;
}
default: {
break;
//
// The has_localvariable_table parameter is used to pass up the value to InstanceKlass.
Method* ClassFileParser::parse_method(const ClassFileStream* const cfs,
bool is_interface,
+ bool is_value_class,
+ bool is_abstract_class,
const ConstantPool* cp,
bool* const has_localvariable_table,
TRAPS) {
assert(cfs != nullptr, "invariant");
assert(cp != nullptr, "invariant");
} else {
classfile_parse_error("Method <clinit> is not static in class file %s", THREAD);
return nullptr;
}
} else {
- verify_legal_method_modifiers(flags, is_interface, name, CHECK_NULL);
+ verify_legal_method_modifiers(flags, access_flags() , name, CHECK_NULL);
}
if (name == vmSymbols::object_initializer_name() && is_interface) {
classfile_parse_error("Interface cannot have a method named <init>, class file %s", THREAD);
return nullptr;
}
+ if (EnableValhalla) {
+ if (((flags & JVM_ACC_SYNCHRONIZED) == JVM_ACC_SYNCHRONIZED)
+ && ((flags & JVM_ACC_STATIC) == 0 )
+ && !_access_flags.is_identity_class()) {
+ classfile_parse_error("Invalid synchronized method in non-identity class %s", THREAD);
+ return nullptr;
+ }
+ }
+
int args_size = -1; // only used when _need_verify is true
if (_need_verify) {
verify_legal_name_with_signature(name, signature, CHECK_NULL);
args_size = ((flags & JVM_ACC_STATIC) ? 0 : 1) +
verify_legal_method_signature(name, signature, CHECK_NULL);
// Side-effects: populates the _methods field in the parser
void ClassFileParser::parse_methods(const ClassFileStream* const cfs,
bool is_interface,
+ bool is_value_class,
+ bool is_abstract_type,
bool* const has_localvariable_table,
bool* has_final_method,
bool* declares_nonstatic_concrete_methods,
TRAPS) {
assert(cfs != nullptr, "invariant");
CHECK);
for (int index = 0; index < length; index++) {
Method* method = parse_method(cfs,
is_interface,
+ is_value_class,
+ is_abstract_type,
_cp,
has_localvariable_table,
CHECK);
if (method->is_final()) {
inner_name_index, CHECK_0);
if (_need_verify) {
guarantee_property(inner_class_info_index != outer_class_info_index,
"Class is both outer and inner class in class file %s", CHECK_0);
}
- // Access flags
- jint flags;
+
+ jint recognized_modifiers = RECOGNIZED_INNER_CLASS_MODIFIERS;
// JVM_ACC_MODULE is defined in JDK-9 and later.
if (_major_version >= JAVA_9_VERSION) {
- flags = cfs->get_u2_fast() & (RECOGNIZED_INNER_CLASS_MODIFIERS | JVM_ACC_MODULE);
- } else {
- flags = cfs->get_u2_fast() & RECOGNIZED_INNER_CLASS_MODIFIERS;
+ recognized_modifiers |= JVM_ACC_MODULE;
}
+
+ // Access flags
+ jint flags = cfs->get_u2_fast() & recognized_modifiers;
+
if ((flags & JVM_ACC_INTERFACE) && _major_version < JAVA_6_VERSION) {
// Set abstract bit for old class files for backward compatibility
flags |= JVM_ACC_ABSTRACT;
}
- verify_legal_class_modifiers(flags, CHECK_0);
+
+ if (!supports_inline_types()) {
+ const bool is_module = (flags & JVM_ACC_MODULE) != 0;
+ const bool is_interface = (flags & JVM_ACC_INTERFACE) != 0;
+ if (!is_module && !is_interface) {
+ flags |= JVM_ACC_IDENTITY;
+ }
+ }
+
+ const char* name = inner_name_index == 0 ? "unnamed" : cp->symbol_at(inner_name_index)->as_utf8();
+ verify_legal_class_modifiers(flags, name, false, CHECK_0);
AccessFlags inner_access_flags(flags);
inner_classes->at_put(index++, inner_class_info_index);
inner_classes->at_put(index++, outer_class_info_index);
inner_classes->at_put(index++, inner_name_index);
cfs->set_current(current_mark);
return length;
}
+ u2 ClassFileParser::parse_classfile_loadable_descriptors_attribute(const ClassFileStream* const cfs,
+ const u1* const loadable_descriptors_attribute_start,
+ TRAPS) {
+ const u1* const current_mark = cfs->current();
+ u2 length = 0;
+ if (loadable_descriptors_attribute_start != nullptr) {
+ cfs->set_current(loadable_descriptors_attribute_start);
+ cfs->guarantee_more(2, CHECK_0); // length
+ length = cfs->get_u2_fast();
+ }
+ const int size = length;
+ Array<u2>* const loadable_descriptors = MetadataFactory::new_array<u2>(_loader_data, size, CHECK_0);
+ _loadable_descriptors = loadable_descriptors;
+ if (length > 0) {
+ int index = 0;
+ cfs->guarantee_more(2 * length, CHECK_0);
+ for (int n = 0; n < length; n++) {
+ const u2 descriptor_index = cfs->get_u2_fast();
+ check_property(
+ valid_symbol_at(descriptor_index),
+ "LoadableDescriptors descriptor_index %u has bad constant type in class file %s",
+ descriptor_index, CHECK_0);
+ Symbol* descriptor = _cp->symbol_at(descriptor_index);
+ bool valid = legal_field_signature(descriptor, CHECK_0);
+ if(!valid) {
+ ResourceMark rm(THREAD);
+ Exceptions::fthrow(THREAD_AND_LOCATION,
+ vmSymbols::java_lang_ClassFormatError(),
+ "Descriptor from LoadableDescriptors attribute at index \"%d\" in class %s has illegal signature \"%s\"",
+ descriptor_index, _class_name->as_C_string(), descriptor->as_C_string());
+ return 0;
+ }
+ loadable_descriptors->at_put(index++, descriptor_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;
// u2 components_count;
// component_info components[components_count];
_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();
+ // Set _loadable_descriptors attribute to default sentinel
+ _loadable_descriptors = 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_loadable_descriptors_attribute = false;
bool parsed_nest_host_attribute = false;
bool parsed_record_attribute = false;
bool parsed_enclosingmethod_attribute = false;
bool parsed_bootstrap_methods_attribute = false;
const u1* runtime_visible_annotations = nullptr;
u4 nest_members_attribute_length = 0;
const u1* record_attribute_start = nullptr;
u4 record_attribute_length = 0;
const u1* permitted_subclasses_attribute_start = nullptr;
u4 permitted_subclasses_attribute_length = 0;
+ const u1* loadable_descriptors_attribute_start = nullptr;
+ u4 loadable_descriptors_attribute_length = 0;
// Iterate over attributes
while (attributes_count--) {
cfs->guarantee_more(6, CHECK); // attribute_name_index, attribute_length
const u2 attribute_name_index = cfs->get_u2_fast();
}
parsed_permitted_subclasses_attribute = true;
permitted_subclasses_attribute_start = cfs->current();
permitted_subclasses_attribute_length = attribute_length;
}
+ if (EnableValhalla && tag == vmSymbols::tag_loadable_descriptors()) {
+ if (parsed_loadable_descriptors_attribute) {
+ classfile_parse_error("Multiple LoadableDescriptors attributes in class file %s", CHECK);
+ return;
+ }
+ parsed_loadable_descriptors_attribute = true;
+ loadable_descriptors_attribute_start = cfs->current();
+ loadable_descriptors_attribute_length = attribute_length;
+ }
}
// Skip attribute_length for any attribute where major_verson >= JAVA_17_VERSION
cfs->skip_u1(attribute_length, CHECK);
} else {
// Unknown attribute
permitted_subclasses_attribute_length == sizeof(num_subclasses) + sizeof(u2) * num_subclasses,
"Wrong PermittedSubclasses attribute length in class file %s", CHECK);
}
}
+ if (parsed_loadable_descriptors_attribute) {
+ const u2 num_classes = parse_classfile_loadable_descriptors_attribute(
+ cfs,
+ loadable_descriptors_attribute_start,
+ CHECK);
+ if (_need_verify) {
+ guarantee_property(
+ loadable_descriptors_attribute_length == sizeof(num_classes) + sizeof(u2) * num_classes,
+ "Wrong LoadableDescriptors 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);
}
}
this_klass->set_fields_status(_fields_status);
this_klass->set_methods(_methods);
this_klass->set_inner_classes(_inner_classes);
this_klass->set_nest_members(_nest_members);
this_klass->set_nest_host_index(_nest_host);
+ this_klass->set_loadable_descriptors(_loadable_descriptors);
this_klass->set_annotations(_combined_annotations);
this_klass->set_permitted_subclasses(_permitted_subclasses);
this_klass->set_record_components(_record_components);
+ this_klass->set_inline_layout_info_array(_inline_layout_info_array);
// Delay the setting of _local_interfaces and _transitive_interfaces until after
// initialize_supers() in fill_instance_klass(). It is because the _local_interfaces could
// be shared with _transitive_interfaces and _transitive_interfaces may be shared with
// its _super. If an OOM occurs while loading the current klass, its _super field
// may not have been set. When GC tries to free the klass, the _transitive_interfaces
assert(cp != nullptr, "invariant");
const InstanceKlass* super_klass = nullptr;
if (super_class_index == 0) {
check_property(_class_name == vmSymbols::java_lang_Object(),
- "Invalid superclass index %u in class file %s",
- super_class_index,
+ "Invalid superclass index 0 in class file %s",
CHECK_NULL);
} else {
check_property(valid_klass_reference_at(super_class_index),
"Invalid superclass index %u in class file %s",
super_class_index,
CHECK_NULL);
// The class name should be legal because it is checked when parsing constant pool.
// However, make sure it is not an array type.
- bool is_array = false;
if (cp->tag_at(super_class_index).is_klass()) {
super_klass = InstanceKlass::cast(cp->resolved_klass_at(super_class_index));
- if (need_verify)
- is_array = super_klass->is_array_klass();
- } else if (need_verify) {
- is_array = (cp->klass_name_at(super_class_index)->char_at(0) == JVM_SIGNATURE_ARRAY);
}
if (need_verify) {
+ bool is_array = (cp->klass_name_at(super_class_index)->char_at(0) == JVM_SIGNATURE_ARRAY);
guarantee_property(!is_array,
"Bad superclass name in class file %s", CHECK_NULL);
}
}
return super_klass;
const jint lh = Klass::instance_layout_helper(ik->size_helper(), true);
ik->set_layout_helper(lh);
}
}
+ bool ClassFileParser::supports_inline_types() const {
+ // Inline types are only supported by class file version 68.65535 and later
+ return _major_version > JAVA_24_VERSION ||
+ (_major_version == JAVA_24_VERSION && _minor_version == JAVA_PREVIEW_MINOR_VERSION);
+ }
+
// utility methods for appending an array with check for duplicates
static void append_interfaces(GrowableArray<InstanceKlass*>* result,
const Array<InstanceKlass*>* const ifs) {
// iterate over new interfaces
// no interfaces, use canonicalized array
return Universe::the_empty_instance_klass_array();
} else if (max_transitive_size == super_size) {
// no new local interfaces added, share superklass' transitive interface array
return super->transitive_interfaces();
- } else if (max_transitive_size == local_size) {
- // only local interfaces added, share local interface array
- return local_ifs;
+ // The three lines below are commented to work around bug JDK-8245487
+ // } else if (max_transitive_size == local_size) {
+ // // only local interfaces added, share local interface array
+ // return local_ifs;
} else {
ResourceMark rm;
GrowableArray<InstanceKlass*>* const result = new GrowableArray<InstanceKlass*>(max_transitive_size);
// Copy down from superclass
append_interfaces(result, local_ifs);
// length will be less than the max_transitive_size if duplicates were removed
const int length = result->length();
assert(length <= max_transitive_size, "just checking");
+
Array<InstanceKlass*>* const new_result =
MetadataFactory::new_array<InstanceKlass*>(loader_data, length, CHECK_NULL);
for (int i = 0; i < length; i++) {
InstanceKlass* const e = result->at(i);
assert(e != nullptr, "just checking");
if (super_ik->is_sealed() && !super_ik->has_as_permitted_subclass(this_klass)) {
classfile_icce_error("class %s cannot inherit from sealed class %s", super_ik, THREAD);
return;
}
+ // The JVMS says that super classes for value types must not have the ACC_IDENTITY
+ // flag set. But, java.lang.Object must still be allowed to be a direct super class
+ // for a value classes. So, it is treated as a special case for now.
+ if (!this_klass->access_flags().is_identity_class() &&
+ super_ik->name() != vmSymbols::java_lang_Object() &&
+ super_ik->is_identity_class()) {
+ classfile_icce_error("value class %s cannot inherit from class %s", super_ik, THREAD);
+ 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()) {
PackageEntry* super_package = super->package();
}
}
// utility methods for format checking
- void ClassFileParser::verify_legal_class_modifiers(jint flags, TRAPS) const {
+ void ClassFileParser::verify_legal_class_modifiers(jint flags, const char* name, bool is_Object, TRAPS) const {
const bool is_module = (flags & JVM_ACC_MODULE) != 0;
+ const bool is_inner_class = name != nullptr;
assert(_major_version >= JAVA_9_VERSION || !is_module, "JVM_ACC_MODULE should not be set");
if (is_module) {
ResourceMark rm(THREAD);
Exceptions::fthrow(
THREAD_AND_LOCATION,
if (!_need_verify) { return; }
const bool is_interface = (flags & JVM_ACC_INTERFACE) != 0;
const bool is_abstract = (flags & JVM_ACC_ABSTRACT) != 0;
const bool is_final = (flags & JVM_ACC_FINAL) != 0;
- const bool is_super = (flags & JVM_ACC_SUPER) != 0;
+ const bool is_identity = (flags & JVM_ACC_IDENTITY) != 0;
const bool is_enum = (flags & JVM_ACC_ENUM) != 0;
const bool is_annotation = (flags & JVM_ACC_ANNOTATION) != 0;
const bool major_gte_1_5 = _major_version >= JAVA_1_5_VERSION;
+ const bool valid_value_class = is_identity || is_interface ||
+ (supports_inline_types() && (!is_identity && (is_abstract || is_final)));
if ((is_abstract && is_final) ||
(is_interface && !is_abstract) ||
- (is_interface && major_gte_1_5 && (is_super || is_enum)) ||
- (!is_interface && major_gte_1_5 && is_annotation)) {
+ (is_interface && major_gte_1_5 && (is_identity || is_enum)) || // ACC_SUPER (now ACC_IDENTITY) was illegal for interfaces
+ (!is_interface && major_gte_1_5 && is_annotation) ||
+ (!valid_value_class)) {
ResourceMark rm(THREAD);
- Exceptions::fthrow(
- THREAD_AND_LOCATION,
- vmSymbols::java_lang_ClassFormatError(),
- "Illegal class modifiers in class %s: 0x%X",
- _class_name->as_C_string(), flags
- );
- return;
+ const char* class_note = "";
+ if (!valid_value_class) {
+ class_note = " (a value class must be final or else abstract)";
+ }
+ if (name == nullptr) { // Not an inner class
+ Exceptions::fthrow(
+ THREAD_AND_LOCATION,
+ vmSymbols::java_lang_ClassFormatError(),
+ "Illegal class modifiers in class %s%s: 0x%X",
+ _class_name->as_C_string(), class_note, flags
+ );
+ return;
+ } else {
+ Exceptions::fthrow(
+ THREAD_AND_LOCATION,
+ vmSymbols::java_lang_ClassFormatError(),
+ "Illegal class modifiers in declaration of inner class %s%s of class %s: 0x%X",
+ name, class_note, _class_name->as_C_string(), flags
+ );
+ return;
+ }
}
}
static bool has_illegal_visibility(jint flags) {
const bool is_public = (flags & JVM_ACC_PUBLIC) != 0;
classfile_ucve_error("%s (class file version %u.%u) was compiled with an invalid non-zero minor version",
class_name, major, minor, THREAD);
}
}
- void ClassFileParser::verify_legal_field_modifiers(jint flags,
- bool is_interface,
+ void ClassFileParser:: verify_legal_field_modifiers(jint flags,
+ AccessFlags class_access_flags,
TRAPS) const {
if (!_need_verify) { return; }
const bool is_public = (flags & JVM_ACC_PUBLIC) != 0;
const bool is_protected = (flags & JVM_ACC_PROTECTED) != 0;
const bool is_static = (flags & JVM_ACC_STATIC) != 0;
const bool is_final = (flags & JVM_ACC_FINAL) != 0;
const bool is_volatile = (flags & JVM_ACC_VOLATILE) != 0;
const bool is_transient = (flags & JVM_ACC_TRANSIENT) != 0;
const bool is_enum = (flags & JVM_ACC_ENUM) != 0;
+ const bool is_strict = (flags & JVM_ACC_STRICT) != 0;
const bool major_gte_1_5 = _major_version >= JAVA_1_5_VERSION;
+ const bool is_interface = class_access_flags.is_interface();
+ const bool is_identity_class = class_access_flags.is_identity_class();
+
bool is_illegal = false;
+ const char* error_msg = "";
- if (is_interface) {
- if (!is_public || !is_static || !is_final || is_private ||
- is_protected || is_volatile || is_transient ||
- (major_gte_1_5 && is_enum)) {
+ // There is some overlap in the checks that apply, for example interface fields
+ // must be static, static fields can't be strict, and therefore interfaces can't
+ // have strict fields. So we don't have to check every possible invalid combination
+ // individually as long as all are covered. Once we have found an illegal combination
+ // we can stop checking.
+
+ if (supports_inline_types()) {
+ if (is_strict && is_static) {
is_illegal = true;
+ error_msg = "field cannot be strict and static";
}
- } else { // not interface
- if (has_illegal_visibility(flags) || (is_final && is_volatile)) {
+ else if (is_strict && !is_final) {
is_illegal = true;
+ error_msg = "strict field must be final";
+ }
+ }
+
+ if (!is_illegal) {
+ if (is_interface) {
+ if (!is_public || !is_static || !is_final || is_private ||
+ is_protected || is_volatile || is_transient ||
+ (major_gte_1_5 && is_enum)) {
+ is_illegal = true;
+ error_msg = "interface fields must be public, static and final, and may be synthetic";
+ }
+ } else { // not interface
+ if (has_illegal_visibility(flags)) {
+ is_illegal = true;
+ error_msg = "invalid visibility flags for class field";
+ } else if (is_final && is_volatile) {
+ is_illegal = true;
+ error_msg = "fields cannot be final and volatile";
+ } else if (supports_inline_types()) {
+ if (!is_identity_class && !is_static && !is_strict) {
+ is_illegal = true;
+ error_msg = "value class fields must be either strict or static";
+ }
+ }
}
}
if (is_illegal) {
ResourceMark rm(THREAD);
Exceptions::fthrow(
THREAD_AND_LOCATION,
vmSymbols::java_lang_ClassFormatError(),
- "Illegal field modifiers in class %s: 0x%X",
- _class_name->as_C_string(), flags);
+ "Illegal field modifiers (%s) in class %s: 0x%X",
+ error_msg, _class_name->as_C_string(), flags);
return;
}
}
void ClassFileParser::verify_legal_method_modifiers(jint flags,
- bool is_interface,
+ AccessFlags class_access_flags,
const Symbol* name,
TRAPS) const {
if (!_need_verify) { return; }
const bool is_public = (flags & JVM_ACC_PUBLIC) != 0;
const bool is_protected = (flags & JVM_ACC_PROTECTED) != 0;
const bool major_gte_1_5 = _major_version >= JAVA_1_5_VERSION;
const bool major_gte_8 = _major_version >= JAVA_8_VERSION;
const bool major_gte_17 = _major_version >= JAVA_17_VERSION;
const bool is_initializer = (name == vmSymbols::object_initializer_name());
+ // LW401 CR required: removal of value factories support
+ const bool is_interface = class_access_flags.is_interface();
+ const bool is_identity_class = class_access_flags.is_identity_class();
+ const bool is_abstract_class = class_access_flags.is_abstract();
bool is_illegal = false;
+ const char* class_note = "";
if (is_interface) {
if (major_gte_8) {
// Class file version is JAVA_8_VERSION or later Methods of
// interfaces may set any of the flags except ACC_PROTECTED,
// ACC_FINAL, ACC_NATIVE, and ACC_SYNCHRONIZED; they must
if (is_static || is_final || is_synchronized || is_native ||
is_abstract || (major_gte_1_5 && is_bridge)) {
is_illegal = true;
}
} else { // not initializer
- if (is_abstract) {
- if ((is_final || is_native || is_private || is_static ||
- (major_gte_1_5 && (is_synchronized || (!major_gte_17 && is_strict))))) {
- is_illegal = true;
+ if (!is_identity_class && is_synchronized && !is_static) {
+ is_illegal = true;
+ class_note = " (not an identity class)";
+ } else {
+ if (is_abstract) {
+ if ((is_final || is_native || is_private || is_static ||
+ (major_gte_1_5 && (is_synchronized || (!major_gte_17 && is_strict))))) {
+ is_illegal = true;
+ }
}
}
}
}
}
if (is_illegal) {
ResourceMark rm(THREAD);
Exceptions::fthrow(
THREAD_AND_LOCATION,
vmSymbols::java_lang_ClassFormatError(),
- "Method %s in class %s has illegal modifiers: 0x%X",
- name->as_C_string(), _class_name->as_C_string(), flags);
+ "Method %s in class %s%s has illegal modifiers: 0x%X",
+ name->as_C_string(), _class_name->as_C_string(),
+ class_note, flags);
return;
}
}
void ClassFileParser::verify_legal_utf8(const unsigned char* buffer,
}
}
return true;
}
+ bool ClassFileParser::is_class_in_loadable_descriptors_attribute(Symbol *klass) {
+ if (_loadable_descriptors == nullptr) return false;
+ for (int i = 0; i < _loadable_descriptors->length(); i++) {
+ Symbol* class_name = _cp->symbol_at(_loadable_descriptors->at(i));
+ if (class_name == klass) return true;
+ }
+ return false;
+ }
+
// Take pointer to a UTF8 byte string (not NUL-terminated).
// Skip over the longest part of the string that could
// be taken as a fieldname. Allow non-trailing '/'s if slash_ok is true.
// Return a pointer to just past the fieldname.
// Return null if no fieldname at all was found, or in the case of slash_ok
case JVM_SIGNATURE_INT:
case JVM_SIGNATURE_FLOAT:
case JVM_SIGNATURE_LONG:
case JVM_SIGNATURE_DOUBLE:
return signature + 1;
- case JVM_SIGNATURE_CLASS: {
+ case JVM_SIGNATURE_CLASS:
+ {
if (_major_version < JAVA_1_5_VERSION) {
// Skip over the class name if one is there
const char* const p = skip_over_field_name(signature + 1, true, --length);
// The next character better be a semicolon
if (p && (p - signature) > 1 && p[0] == JVM_SIGNATURE_ENDCLASS) {
return p + 1;
}
}
else {
- // Skip leading 'L' and ignore first appearance of ';'
+ // Skip leading 'L' or 'Q' and ignore first appearance of ';'
signature++;
const char* c = (const char*) memchr(signature, JVM_SIGNATURE_ENDCLASS, length - 1);
// Format check signature
if (c != nullptr) {
int newlen = pointer_delta_as_int(c, (char*) signature);
} else if (_major_version < JAVA_1_5_VERSION) {
if (bytes[0] != JVM_SIGNATURE_SPECIAL) {
p = skip_over_field_name(bytes, true, length);
legal = (p != nullptr) && ((p - bytes) == (int)length);
}
+ } else if ((_major_version >= CONSTANT_CLASS_DESCRIPTORS || _class_name->starts_with("jdk/internal/reflect/"))
+ && bytes[length - 1] == ';' ) {
+ // Support for L...; descriptors
+ legal = verify_unqualified_name(bytes + 1, length - 2, LegalClass);
} else {
// 4900761: relax the constraints based on JSR202 spec
// Class names may be drawn from the entire Unicode character set.
// Identifiers between '/' must be unqualified names.
// The utf8 string has been verified when parsing cpool entries.
unsigned int length = name->utf8_length();
bool legal = false;
if (length > 0) {
if (bytes[0] == JVM_SIGNATURE_SPECIAL) {
- if (name == vmSymbols::object_initializer_name() || name == vmSymbols::class_initializer_name()) {
+ if (name == vmSymbols::object_initializer_name() ||
+ name == vmSymbols::class_initializer_name()) {
legal = true;
}
} else if (_major_version < JAVA_1_5_VERSION) {
const char* p;
p = skip_over_field_name(bytes, false, length);
);
return;
}
}
+ bool ClassFileParser::legal_field_signature(const Symbol* signature, TRAPS) const {
+ const char* const bytes = (const char*)signature->bytes();
+ const unsigned int length = signature->utf8_length();
+ const char* const p = skip_over_field_signature(bytes, false, length, CHECK_false);
+
+ if (p == nullptr || (p - bytes) != (int)length) {
+ return false;
+ }
+ return true;
+ }
// Checks if signature is a legal field signature.
void ClassFileParser::verify_legal_field_signature(const Symbol* name,
const Symbol* signature,
TRAPS) const {
return;
}
int sig_length = signature->utf8_length();
if (name->utf8_length() > 0 &&
- name->char_at(0) == JVM_SIGNATURE_SPECIAL &&
- sig_length > 0 &&
- signature->char_at(sig_length - 1) != JVM_SIGNATURE_VOID) {
+ name->char_at(0) == JVM_SIGNATURE_SPECIAL &&
+ sig_length > 0 &&
+ signature->char_at(sig_length - 1) != JVM_SIGNATURE_VOID) {
throwIllegalSignature("Method", name, signature, THREAD);
}
}
// Checks if signature is a legal method signature.
throwIllegalSignature("Method", name, signature, THREAD);
return 0;
}
int ClassFileParser::static_field_size() const {
- assert(_field_info != nullptr, "invariant");
- return _field_info->_static_field_size;
+ assert(_layout_info != nullptr, "invariant");
+ return _layout_info->_static_field_size;
}
int ClassFileParser::total_oop_map_count() const {
- assert(_field_info != nullptr, "invariant");
- return _field_info->oop_map_blocks->_nonstatic_oop_map_count;
+ assert(_layout_info != nullptr, "invariant");
+ return _layout_info->oop_map_blocks->_nonstatic_oop_map_count;
}
jint ClassFileParser::layout_size() const {
- assert(_field_info != nullptr, "invariant");
- return _field_info->_instance_size;
+ assert(_layout_info != nullptr, "invariant");
+ return _layout_info->_instance_size;
}
static void check_methods_for_intrinsics(const InstanceKlass* ik,
const Array<Method*>* methods) {
assert(ik != nullptr, "invariant");
}
fill_instance_klass(ik, changed_by_loadhook, cl_inst_info, CHECK_NULL);
assert(_klass == ik, "invariant");
-
return ik;
}
void ClassFileParser::fill_instance_klass(InstanceKlass* ik,
bool changed_by_loadhook,
_loader_data->add_class(ik, publicize);
set_klass_to_deallocate(ik);
- assert(_field_info != nullptr, "invariant");
- assert(ik->static_field_size() == _field_info->_static_field_size, "sanity");
- assert(ik->nonstatic_oop_map_count() == _field_info->oop_map_blocks->_nonstatic_oop_map_count,
+ assert(_layout_info != nullptr, "invariant");
+ assert(ik->static_field_size() == _layout_info->_static_field_size, "sanity");
+ assert(ik->nonstatic_oop_map_count() == _layout_info->oop_map_blocks->_nonstatic_oop_map_count,
"sanity");
assert(ik->is_instance_klass(), "sanity");
- assert(ik->size_helper() == _field_info->_instance_size, "sanity");
+ assert(ik->size_helper() == _layout_info->_instance_size, "sanity");
// Fill in information already parsed
ik->set_should_verify_class(_need_verify);
// Not yet: supers are done below to support the new subtype-checking fields
- ik->set_nonstatic_field_size(_field_info->_nonstatic_field_size);
- ik->set_has_nonstatic_fields(_field_info->_has_nonstatic_fields);
+ ik->set_nonstatic_field_size(_layout_info->_nonstatic_field_size);
+ ik->set_has_nonstatic_fields(_layout_info->_has_nonstatic_fields);
+
+ if (_layout_info->_is_naturally_atomic) {
+ ik->set_is_naturally_atomic();
+ }
+
+ if (_layout_info->_must_be_atomic) {
+ ik->set_must_be_atomic();
+ }
+ if (_is_implicitly_constructible) {
+ ik->set_is_implicitly_constructible();
+ }
+
ik->set_static_oop_field_count(_static_oop_count);
// this transfers ownership of a lot of arrays from
// the parser onto the InstanceKlass*
apply_parsed_class_metadata(ik, _java_fields_count);
+ if (ik->is_inline_klass()) {
+ InlineKlass::cast(ik)->init_fixed_block();
+ }
// can only set dynamic nest-host after static nest information is set
if (cl_inst_info.dynamic_nest_host() != nullptr) {
ik->set_nest_host(cl_inst_info.dynamic_nest_host());
}
assert(nullptr == _fieldinfo_stream, "invariant");
assert(nullptr == _fields_status, "invariant");
assert(nullptr == _methods, "invariant");
assert(nullptr == _inner_classes, "invariant");
assert(nullptr == _nest_members, "invariant");
+ assert(nullptr == _loadable_descriptors, "invariant");
assert(nullptr == _combined_annotations, "invariant");
assert(nullptr == _record_components, "invariant");
assert(nullptr == _permitted_subclasses, "invariant");
+ assert(nullptr == _inline_layout_info_array, "invariant");
if (_has_localvariable_table) {
ik->set_has_localvariable_table(true);
}
// Initialize itable offset tables
klassItable::setup_itable_offset_table(ik);
// Compute transitive closure of interfaces this class implements
// Do final class setup
- OopMapBlocksBuilder* oop_map_blocks = _field_info->oop_map_blocks;
+ OopMapBlocksBuilder* oop_map_blocks = _layout_info->oop_map_blocks;
if (oop_map_blocks->_nonstatic_oop_map_count > 0) {
oop_map_blocks->copy(ik->start_of_nonstatic_oop_maps());
}
if (_has_contended_fields || _parsed_annotations->is_contended() ||
// We won a potential race
JvmtiExport::add_default_read_edges(module_handle, THREAD);
}
}
+ if (is_inline_type()) {
+ InlineKlass* vk = InlineKlass::cast(ik);
+ vk->set_payload_alignment(_layout_info->_payload_alignment);
+ vk->set_first_field_offset(_layout_info->_first_field_offset);
+ vk->set_payload_size_in_bytes(_layout_info->_payload_size_in_bytes);
+ vk->set_non_atomic_size_in_bytes(_layout_info->_non_atomic_size_in_bytes);
+ vk->set_non_atomic_alignment(_layout_info->_non_atomic_alignment);
+ vk->set_atomic_size_in_bytes(_layout_info->_atomic_layout_size_in_bytes);
+ vk->set_nullable_size_in_bytes(_layout_info->_nullable_layout_size_in_bytes);
+ vk->set_null_marker_offset(_layout_info->_null_marker_offset);
+ vk->set_default_value_offset(_layout_info->_default_value_offset);
+ vk->set_null_reset_value_offset(_layout_info->_null_reset_value_offset);
+ if (_layout_info->_is_empty_inline_klass) vk->set_is_empty_inline_type();
+ vk->initialize_calling_convention(CHECK);
+ }
+
ClassLoadingService::notify_class_loaded(ik, false /* not shared class */);
if (!is_internal()) {
ik->print_class_load_logging(_loader_data, module_entry, _stream);
_methods(nullptr),
_inner_classes(nullptr),
_nest_members(nullptr),
_nest_host(0),
_permitted_subclasses(nullptr),
+ _loadable_descriptors(nullptr),
_record_components(nullptr),
_local_interfaces(nullptr),
+ _local_interface_indexes(nullptr),
_transitive_interfaces(nullptr),
_combined_annotations(nullptr),
_class_annotations(nullptr),
_class_type_annotations(nullptr),
_fields_annotations(nullptr),
_fields_type_annotations(nullptr),
_klass(nullptr),
_klass_to_deallocate(nullptr),
_parsed_annotations(nullptr),
- _field_info(nullptr),
+ _layout_info(nullptr),
+ _inline_layout_info_array(nullptr),
_temp_field_info(nullptr),
_method_ordering(nullptr),
_all_mirandas(nullptr),
_vtable_size(0),
_itable_size(0),
_has_nonstatic_concrete_methods(false),
_declares_nonstatic_concrete_methods(false),
_has_localvariable_table(false),
_has_final_method(false),
_has_contended_fields(false),
+ _has_inline_type_fields(false),
+ _is_naturally_atomic(false),
+ _must_be_atomic(true),
+ _is_implicitly_constructible(false),
+ _has_loosely_consistent_annotation(false),
+ _has_implicitly_constructible_annotation(false),
_has_finalizer(false),
_has_empty_finalizer(false),
_max_bootstrap_specifier_index(-1) {
_class_name = name != nullptr ? name : vmSymbols::unknown_class_name();
_fields_status = nullptr;
_methods = nullptr;
_inner_classes = nullptr;
_nest_members = nullptr;
_permitted_subclasses = nullptr;
+ _loadable_descriptors = nullptr;
_combined_annotations = nullptr;
_class_annotations = _class_type_annotations = nullptr;
_fields_annotations = _fields_type_annotations = nullptr;
_record_components = nullptr;
+ _inline_layout_info_array = nullptr;
}
// Destructor to clean up
ClassFileParser::~ClassFileParser() {
_class_name->decrement_refcount();
if (_fields_status != nullptr) {
MetadataFactory::free_array<FieldStatus>(_loader_data, _fields_status);
}
+ if (_inline_layout_info_array != nullptr) {
+ MetadataFactory::free_array<InlineLayoutInfo>(_loader_data, _inline_layout_info_array);
+ }
+
if (_methods != nullptr) {
// Free methods
InstanceKlass::deallocate_methods(_loader_data, _methods);
}
if (_permitted_subclasses != nullptr && _permitted_subclasses != Universe::the_empty_short_array()) {
MetadataFactory::free_array<u2>(_loader_data, _permitted_subclasses);
}
+ if (_loadable_descriptors != nullptr && _loadable_descriptors != Universe::the_empty_short_array()) {
+ MetadataFactory::free_array<u2>(_loader_data, _loadable_descriptors);
+ }
+
// Free interfaces
InstanceKlass::deallocate_interfaces(_loader_data, _super_klass,
_local_interfaces, _transitive_interfaces);
if (_combined_annotations != nullptr) {
assert(cp_size == (u2)cp->length(), "invariant");
// ACCESS FLAGS
stream->guarantee_more(8, CHECK); // flags, this_class, super_class, infs_len
- // Access flags
- jint flags;
+ jint recognized_modifiers = JVM_RECOGNIZED_CLASS_MODIFIERS;
// JVM_ACC_MODULE is defined in JDK-9 and later.
if (_major_version >= JAVA_9_VERSION) {
- flags = stream->get_u2_fast() & (JVM_RECOGNIZED_CLASS_MODIFIERS | JVM_ACC_MODULE);
- } else {
- flags = stream->get_u2_fast() & JVM_RECOGNIZED_CLASS_MODIFIERS;
+ recognized_modifiers |= JVM_ACC_MODULE;
}
+ // Access flags
+ jint flags = stream->get_u2_fast() & recognized_modifiers;
+
if ((flags & JVM_ACC_INTERFACE) && _major_version < JAVA_6_VERSION) {
// Set abstract bit for old class files for backward compatibility
flags |= JVM_ACC_ABSTRACT;
}
- verify_legal_class_modifiers(flags, CHECK);
-
- short bad_constant = class_bad_constant_seen();
- if (bad_constant != 0) {
- // Do not throw CFE until after the access_flags are checked because if
- // ACC_MODULE is set in the access flags, then NCDFE must be thrown, not CFE.
- classfile_parse_error("Unknown constant tag %u in class file %s", bad_constant, THREAD);
- return;
+ // Fixing ACC_SUPER/ACC_IDENTITY for old class files
+ if (!supports_inline_types()) {
+ const bool is_module = (flags & JVM_ACC_MODULE) != 0;
+ const bool is_interface = (flags & JVM_ACC_INTERFACE) != 0;
+ if (!is_module && !is_interface) {
+ flags |= JVM_ACC_IDENTITY;
+ }
}
- _access_flags.set_flags(flags);
// This class and superclass
_this_class_index = stream->get_u2_fast();
check_property(
valid_cp_range(_this_class_index, cp_size) &&
_this_class_index, CHECK);
Symbol* const class_name_in_cp = cp->klass_name_at(_this_class_index);
assert(class_name_in_cp != nullptr, "class_name can't be null");
+ bool is_java_lang_Object = class_name_in_cp == vmSymbols::java_lang_Object();
+
+ verify_legal_class_modifiers(flags, nullptr, is_java_lang_Object, CHECK);
+
+ _access_flags.set_flags(flags);
+
+ short bad_constant = class_bad_constant_seen();
+ if (bad_constant != 0) {
+ // Do not throw CFE until after the access_flags are checked because if
+ // ACC_MODULE is set in the access flags, then NCDFE must be thrown, not CFE.
+ classfile_parse_error("Unknown constant tag %u in class file %s", bad_constant, THREAD);
+ return;
+ }
+
// Don't need to check whether this class name is legal or not.
// It has been checked when constant pool is parsed.
// However, make sure it is not an array type.
if (_need_verify) {
guarantee_property(class_name_in_cp->char_at(0) != JVM_SIGNATURE_ARRAY,
_itfs_len,
cp,
&_has_nonstatic_concrete_methods,
CHECK);
- assert(_local_interfaces != nullptr, "invariant");
-
// Fields (offsets are filled in later)
parse_fields(stream,
- _access_flags.is_interface(),
+ _access_flags,
cp,
cp_size,
&_java_fields_count,
CHECK);
assert(_temp_field_info != nullptr, "invariant");
// Methods
parse_methods(stream,
- _access_flags.is_interface(),
+ is_interface(),
+ !is_identity_class(),
+ is_abstract_class(),
&_has_localvariable_table,
&_has_final_method,
&_declares_nonstatic_concrete_methods,
CHECK);
assert(cp != nullptr, "invariant");
assert(_loader_data != nullptr, "invariant");
if (_class_name == vmSymbols::java_lang_Object()) {
check_property(_local_interfaces == Universe::the_empty_instance_klass_array(),
- "java.lang.Object cannot implement an interface in class file %s",
- CHECK);
+ "java.lang.Object cannot implement an interface in class file %s",
+ CHECK);
}
// We check super class after class file is parsed and format is checked
if (_super_class_index > 0 && nullptr == _super_klass) {
Symbol* const super_class_name = cp->klass_name_at(_super_class_index);
- if (_access_flags.is_interface()) {
+ if (is_interface()) {
// Before attempting to resolve the superclass, check for class format
// errors not checked yet.
guarantee_property(super_class_name == vmSymbols::java_lang_Object(),
"Interfaces must have java.lang.Object as superclass in class file %s",
CHECK);
Handle loader(THREAD, _loader_data->class_loader());
if (loader.is_null() && super_class_name == vmSymbols::java_lang_Object()) {
_super_klass = vmClasses::Object_klass();
} else {
_super_klass = (const InstanceKlass*)
- SystemDictionary::resolve_super_or_fail(_class_name,
+ SystemDictionary::resolve_with_circularity_detection_or_fail(_class_name,
super_class_name,
loader,
_protection_domain,
true,
CHECK);
}
}
if (_super_klass != nullptr) {
+ if (_super_klass->is_interface()) {
+ classfile_icce_error("class %s has interface %s as super class", _super_klass, THREAD);
+ return;
+ }
+
+ if (_super_klass->is_final()) {
+ classfile_icce_error("class %s cannot inherit from final class %s", _super_klass, THREAD);
+ return;
+ }
+
+ if (EnableValhalla) {
+ check_identity_and_value_modifiers(this, _super_klass, CHECK);
+ }
+
if (_super_klass->has_nonstatic_concrete_methods()) {
_has_nonstatic_concrete_methods = true;
}
+ }
- if (_super_klass->is_interface()) {
- classfile_icce_error("class %s has interface %s as super class", _super_klass, THREAD);
- return;
+ if (_parsed_annotations->has_annotation(AnnotationCollector::_jdk_internal_LooselyConsistentValue) && _access_flags.is_identity_class()) {
+ THROW_MSG(vmSymbols::java_lang_ClassFormatError(),
+ err_msg("class %s cannot have annotation jdk.internal.vm.annotation.LooselyConsistentValue, because it is not a value class",
+ _class_name->as_klass_external_name()));
+ }
+ if (_parsed_annotations->has_annotation(AnnotationCollector::_jdk_internal_ImplicitlyConstructible) && _access_flags.is_identity_class()) {
+ THROW_MSG(vmSymbols::java_lang_ClassFormatError(),
+ err_msg("class %s cannot have annotation jdk.internal.vm.annotation.ImplicitlyConstructible, because it is not a value class",
+ _class_name->as_klass_external_name()));
+ }
+
+ // Determining is the class allows tearing or not (default is not)
+ if (EnableValhalla && !_access_flags.is_identity_class()) {
+ if (_parsed_annotations->has_annotation(ClassAnnotationCollector::_jdk_internal_LooselyConsistentValue)
+ && (_super_klass == vmClasses::Object_klass() || !_super_klass->must_be_atomic())) {
+ // Conditions above are not sufficient to determine atomicity requirements,
+ // the presence of fields with atomic requirements could force the current class to have atomicy requirements too
+ // Marking as not needing atomicity for now, can be updated when computing the fields layout
+ // The InstanceKlass must be filled with the value from the FieldLayoutInfo returned by
+ // the FieldLayoutBuilder, not with this _must_be_atomic field.
+ _must_be_atomic = false;
+ }
+ if (_parsed_annotations->has_annotation(ClassAnnotationCollector::_jdk_internal_ImplicitlyConstructible)
+ && (_super_klass == vmClasses::Object_klass() || _super_klass->is_implicitly_constructible())) {
+ _is_implicitly_constructible = true;
+ }
+ // Apply VM options override
+ if (*ForceNonTearable != '\0') {
+ // Allow a command line switch to force the same atomicity property:
+ const char* class_name_str = _class_name->as_C_string();
+ if (StringUtils::class_list_match(ForceNonTearable, class_name_str)) {
+ _must_be_atomic = true;
+ }
+ }
+ }
+
+ int itfs_len = _local_interface_indexes == nullptr ? 0 : _local_interface_indexes->length();
+ _local_interfaces = MetadataFactory::new_array<InstanceKlass*>(_loader_data, itfs_len, nullptr, CHECK);
+ if (_local_interface_indexes != nullptr) {
+ for (int i = 0; i < _local_interface_indexes->length(); i++) {
+ u2 interface_index = _local_interface_indexes->at(i);
+ Klass* interf;
+ if (cp->tag_at(interface_index).is_klass()) {
+ interf = cp->resolved_klass_at(interface_index);
+ } else {
+ Symbol* const unresolved_klass = cp->klass_name_at(interface_index);
+
+ // Don't need to check legal name because it's checked when parsing constant pool.
+ // But need to make sure it's not an array type.
+ guarantee_property(unresolved_klass->char_at(0) != JVM_SIGNATURE_ARRAY,
+ "Bad interface name in class file %s", CHECK);
+
+ // Call resolve on the interface class name with class circularity checking
+ interf = SystemDictionary::resolve_with_circularity_detection_or_fail(
+ _class_name,
+ unresolved_klass,
+ Handle(THREAD, _loader_data->class_loader()),
+ _protection_domain,
+ false,
+ CHECK);
+ }
+
+ if (!interf->is_interface()) {
+ THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(),
+ err_msg("class %s can not implement %s, because it is not an interface (%s)",
+ _class_name->as_klass_external_name(),
+ interf->external_name(),
+ interf->class_in_module_of_loader()));
+ }
+
+ if (EnableValhalla) {
+ // Check modifiers and set carries_identity_modifier/carries_value_modifier flags
+ check_identity_and_value_modifiers(this, InstanceKlass::cast(interf), CHECK);
+ }
+
+ if (InstanceKlass::cast(interf)->has_nonstatic_concrete_methods()) {
+ _has_nonstatic_concrete_methods = true;
+ }
+ _local_interfaces->at_put(i, InstanceKlass::cast(interf));
}
}
+ assert(_local_interfaces != nullptr, "invariant");
// Compute the transitive list of all unique interfaces implemented by this class
_transitive_interfaces =
compute_transitive_interfaces(_super_klass,
_local_interfaces,
loader,
_class_name,
_local_interfaces);
// Size of Java itable (in words)
- _itable_size = _access_flags.is_interface() ? 0 :
+ _itable_size = is_interface() ? 0 :
klassItable::compute_itable_size(_transitive_interfaces);
assert(_parsed_annotations != nullptr, "invariant");
- _field_info = new FieldLayoutInfo();
- FieldLayoutBuilder lb(class_name(), super_klass(), _cp, /*_fields*/ _temp_field_info,
- _parsed_annotations->is_contended(), _field_info);
+ if (EnableValhalla) {
+ _inline_layout_info_array = MetadataFactory::new_array<InlineLayoutInfo>(_loader_data,
+ java_fields_count(),
+ CHECK);
+ for (GrowableArrayIterator<FieldInfo> it = _temp_field_info->begin(); it != _temp_field_info->end(); ++it) {
+ FieldInfo fieldinfo = *it;
+ if (fieldinfo.access_flags().is_static()) continue; // Only non-static fields are processed at load time
+ Symbol* sig = fieldinfo.signature(cp);
+ if (fieldinfo.field_flags().is_null_free_inline_type()) {
+ // Pre-load classes of null-free fields that are candidate for flattening
+ TempNewSymbol s = Signature::strip_envelope(sig);
+ if (s == _class_name) {
+ THROW_MSG(vmSymbols::java_lang_ClassCircularityError(), err_msg("Class %s cannot have a null-free non-static field of its own type", _class_name->as_C_string()));
+ }
+ log_info(class, preload)("Preloading class %s during loading of class %s. Cause: a null-free non-static field is declared with this type", s->as_C_string(), _class_name->as_C_string());
+ Klass* klass = SystemDictionary::resolve_with_circularity_detection_or_fail(_class_name, s, Handle(THREAD, _loader_data->class_loader()), _protection_domain, false, THREAD);
+ if (HAS_PENDING_EXCEPTION) {
+ log_warning(class, preload)("Preloading of class %s during loading of class %s (cause: null-free non-static field) failed: %s",
+ s->as_C_string(), _class_name->as_C_string(), PENDING_EXCEPTION->klass()->name()->as_C_string());
+ return; // Exception is still pending
+ }
+ assert(klass != nullptr, "Sanity check");
+ if (klass->access_flags().is_identity_class()) {
+ assert(klass->is_instance_klass(), "Sanity check");
+ ResourceMark rm(THREAD);
+ THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(),
+ err_msg("Class %s expects class %s to be a value class, but it is an identity class",
+ _class_name->as_C_string(),
+ InstanceKlass::cast(klass)->external_name()));
+ }
+ if (klass->is_abstract()) {
+ assert(klass->is_instance_klass(), "Sanity check");
+ ResourceMark rm(THREAD);
+ THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(),
+ err_msg("Class %s expects class %s to be concrete value type, but it is an abstract class",
+ _class_name->as_C_string(),
+ InstanceKlass::cast(klass)->external_name()));
+ }
+ InlineKlass* vk = InlineKlass::cast(klass);
+ if (!vk->is_implicitly_constructible()) {
+ THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(),
+ err_msg("class %s is not implicitly constructible and it is used in a null restricted non-static field (not supported)",
+ klass->name()->as_C_string()));
+ }
+ _inline_layout_info_array->adr_at(fieldinfo.index())->set_klass(vk);
+ log_info(class, preload)("Preloading of class %s during loading of class %s (cause: null-free non-static field) succeeded", s->as_C_string(), _class_name->as_C_string());
+ } else if (Signature::has_envelope(sig)) {
+ // Preloading classes for nullable fields that are listed in the LoadableDescriptors attribute
+ // Those classes would be required later for the flattening of nullable inline type fields
+ TempNewSymbol name = Signature::strip_envelope(sig);
+ if (name != _class_name && is_class_in_loadable_descriptors_attribute(sig)) {
+ log_info(class, preload)("Preloading class %s during loading of class %s. Cause: field type in LoadableDescriptors attribute", name->as_C_string(), _class_name->as_C_string());
+ oop loader = loader_data()->class_loader();
+ Klass* klass = SystemDictionary::resolve_with_circularity_detection_or_fail(_class_name, name, Handle(THREAD, loader), _protection_domain, false, THREAD);
+ if (klass != nullptr) {
+ if (klass->is_inline_klass()) {
+ _inline_layout_info_array->adr_at(fieldinfo.index())->set_klass(InlineKlass::cast(klass));
+ log_info(class, preload)("Preloading of class %s during loading of class %s (cause: field type in LoadableDescriptors attribute) succeeded", name->as_C_string(), _class_name->as_C_string());
+ } else {
+ // Non value class are allowed by the current spec, but it could be an indication of an issue so let's log a warning
+ log_warning(class, preload)("Preloading class %s during loading of class %s (cause: field type in LoadableDescriptors attribute) but loaded class is not a value class", name->as_C_string(), _class_name->as_C_string());
+ }
+ } else {
+ log_warning(class, preload)("Preloading of class %s during loading of class %s (cause: field type in LoadableDescriptors attribute) failed : %s",
+ name->as_C_string(), _class_name->as_C_string(), PENDING_EXCEPTION->klass()->name()->as_C_string());
+ }
+ // Loads triggered by the LoadableDescriptors attribute are speculative, failures must not impact loading of current class
+ if (HAS_PENDING_EXCEPTION) {
+ CLEAR_PENDING_EXCEPTION;
+ }
+ }
+ }
+ }
+ }
+
+ _layout_info = new FieldLayoutInfo();
+ FieldLayoutBuilder lb(class_name(), loader_data(), super_klass(), _cp, /*_fields*/ _temp_field_info,
+ _parsed_annotations->is_contended(), is_inline_type(),
+ access_flags().is_abstract() && !access_flags().is_identity_class() && !access_flags().is_interface(),
+ _must_be_atomic, _layout_info, _inline_layout_info_array);
lb.build_layout();
+ _has_inline_type_fields = _layout_info->_has_inline_fields;
int injected_fields_count = _temp_field_info->length() - _java_fields_count;
_fieldinfo_stream =
FieldInfoStream::create_FieldInfoStream(_temp_field_info, _java_fields_count,
injected_fields_count, loader_data(), CHECK);
+
_fields_status =
MetadataFactory::new_array<FieldStatus>(_loader_data, _temp_field_info->length(),
FieldStatus(0), CHECK);
}
< prev index next >