< 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 "precompiled.hpp"
#include "jvm.h"
#include "classfile/classFileParser.hpp"
#include "classfile/classFileStream.hpp"
#include "classfile/classLoader.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "oops/annotations.hpp"
#include "oops/constantPool.inline.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
#define JAVA_18_VERSION 62
#define JAVA_19_VERSION 63
+ #define CONSTANT_CLASS_DESCRIPTORS 63
+
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,
"Unexpected bad constant pool entry");
if (_bad_constant_seen == 0) _bad_constant_seen = bad_constant;
// 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);
- cp->unresolved_klass_at_put(index, class_index, num_klasses++);
+
+ Symbol* const name = cp->symbol_at(class_index);
+ const unsigned int name_len = name->utf8_length();
+ if (name->is_Q_signature()) {
+ cp->unresolved_qdescriptor_at_put(index, class_index, num_klasses++);
+ } else {
+ cp->unresolved_klass_at_put(index, class_index, num_klasses++);
+ }
break;
}
case JVM_CONSTANT_StringIndex: {
const int string_index = cp->string_index_at(index);
check_property(valid_symbol_at(string_index),
// the right type.
if (!Signature::is_method(signature)) {
throwIllegalSignature("Method", name, signature, CHECK);
}
}
- // If a class method name begins with '<', it must be "<init>" and have void signature.
+ // If a class method name begins with '<', it must be "<init>" and have void signature
+ // unless it's an inline type.
const unsigned int name_len = name->utf8_length();
if (tag == JVM_CONSTANT_Methodref && name_len != 0 &&
name->char_at(0) == JVM_SIGNATURE_SPECIAL) {
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.
- throwIllegalSignature("Method", name, signature, CHECK);
+ } else if (!Signature::is_void_method(signature)) {
+ // if return type is non-void then it cannot be a basic primitive
+ // and primitve types must be supported.
+ if (!signature->ends_with(JVM_SIGNATURE_ENDCLASS) || !EnableValhalla) {
+ throwIllegalSignature("Method", name, signature, CHECK);
+ }
}
}
}
break;
}
const int name_and_type_ref_index =
cp->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()) {
+ 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()) {
+ // 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 if (!signature->is_void_method_signature()
+ && ref_kind == JVM_REF_invokeStatic) {
+ // also OK, could be a static factory call
+ } else {
classfile_parse_error(
"Bad method name at constant pool index %u in class file %s",
name_ref_index, THREAD);
return;
}
return true;
}
// Side-effects: populates the _local_interfaces field
- void ClassFileParser::parse_interfaces(const ClassFileStream* const stream,
- const int itfs_len,
- ConstantPool* const cp,
+ 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".
+ bool* const is_declared_atomic,
TRAPS) {
assert(stream != NULL, "invariant");
assert(cp != NULL, "invariant");
assert(has_nonstatic_concrete_methods != NULL, "invariant");
if (itfs_len == 0) {
- _local_interfaces = Universe::the_empty_instance_klass_array();
+ _temp_local_interfaces = new GrowableArray<InstanceKlass*>(0);
} else {
assert(itfs_len > 0, "only called for len>0");
- _local_interfaces = MetadataFactory::new_array<InstanceKlass*>(_loader_data, itfs_len, NULL, CHECK);
-
- int index;
+ _temp_local_interfaces = new GrowableArray<InstanceKlass*>(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),
_class_name->as_klass_external_name(),
interf->external_name(),
interf->class_in_module_of_loader()));
}
- if (InstanceKlass::cast(interf)->has_nonstatic_concrete_methods()) {
+ InstanceKlass* ik = InstanceKlass::cast(interf);
+ if (is_inline_type() && ik->invalid_inline_super()) {
+ ResourceMark rm(THREAD);
+ Exceptions::fthrow(
+ THREAD_AND_LOCATION,
+ vmSymbols::java_lang_IncompatibleClassChangeError(),
+ "Inline type %s has an identity type as supertype",
+ _class_name->as_klass_external_name());
+ return;
+ }
+ if (ik->invalid_inline_super()) {
+ set_invalid_inline_super();
+ }
+ if (ik->has_nonstatic_concrete_methods()) {
*has_nonstatic_concrete_methods = true;
}
- _local_interfaces->at_put(index, InstanceKlass::cast(interf));
+ if (ik->is_declared_atomic()) {
+ *is_declared_atomic = true;
+ }
+ _temp_local_interfaces->append(ik);
}
if (!_need_verify || itfs_len <= 1) {
return;
}
bool dup = false;
const Symbol* name = NULL;
{
debug_only(NoSafepointVerifier nsv;)
for (index = 0; index < itfs_len; index++) {
- const InstanceKlass* const k = _local_interfaces->at(index);
+ const InstanceKlass* const k = _temp_local_interfaces->at(index);
name = k->name();
// If no duplicates, add (name, NULL) in hashtable interface_names.
if (!put_after_lookup(name, NULL, interface_names)) {
dup = true;
break;
STATIC_OOP, // Oops
STATIC_BYTE, // Boolean, Byte, char
STATIC_SHORT, // shorts
STATIC_WORD, // ints
STATIC_DOUBLE, // aligned long or double
+ STATIC_INLINE, // inline type field
NONSTATIC_OOP,
NONSTATIC_BYTE,
NONSTATIC_SHORT,
NONSTATIC_WORD,
NONSTATIC_DOUBLE,
+ NONSTATIC_INLINE,
MAX_FIELD_ALLOCATION_TYPE,
BAD_ALLOCATION_TYPE = -1
};
static FieldAllocationType _basic_type_to_atype[2 * (T_CONFLICT + 1)] = {
NONSTATIC_SHORT, // T_SHORT = 9,
NONSTATIC_WORD, // T_INT = 10,
NONSTATIC_DOUBLE, // T_LONG = 11,
NONSTATIC_OOP, // T_OBJECT = 12,
NONSTATIC_OOP, // T_ARRAY = 13,
- BAD_ALLOCATION_TYPE, // T_VOID = 14,
- BAD_ALLOCATION_TYPE, // T_ADDRESS = 15,
- BAD_ALLOCATION_TYPE, // T_NARROWOOP = 16,
- BAD_ALLOCATION_TYPE, // T_METADATA = 17,
- BAD_ALLOCATION_TYPE, // T_NARROWKLASS = 18,
- BAD_ALLOCATION_TYPE, // T_CONFLICT = 19,
+ NONSTATIC_OOP, // T_PRIMITIVE_OBJECT = 14,
+ BAD_ALLOCATION_TYPE, // T_VOID = 15,
+ BAD_ALLOCATION_TYPE, // T_ADDRESS = 16,
+ BAD_ALLOCATION_TYPE, // T_NARROWOOP = 17,
+ BAD_ALLOCATION_TYPE, // T_METADATA = 18,
+ BAD_ALLOCATION_TYPE, // T_NARROWKLASS = 19,
+ BAD_ALLOCATION_TYPE, // T_CONFLICT = 20,
BAD_ALLOCATION_TYPE, // 0
BAD_ALLOCATION_TYPE, // 1
BAD_ALLOCATION_TYPE, // 2
BAD_ALLOCATION_TYPE, // 3
STATIC_BYTE , // T_BOOLEAN = 4,
STATIC_SHORT, // T_SHORT = 9,
STATIC_WORD, // T_INT = 10,
STATIC_DOUBLE, // T_LONG = 11,
STATIC_OOP, // T_OBJECT = 12,
STATIC_OOP, // T_ARRAY = 13,
- BAD_ALLOCATION_TYPE, // T_VOID = 14,
- BAD_ALLOCATION_TYPE, // T_ADDRESS = 15,
- BAD_ALLOCATION_TYPE, // T_NARROWOOP = 16,
- BAD_ALLOCATION_TYPE, // T_METADATA = 17,
- BAD_ALLOCATION_TYPE, // T_NARROWKLASS = 18,
- BAD_ALLOCATION_TYPE, // T_CONFLICT = 19,
+ STATIC_OOP, // T_PRIMITIVE_OBJECT = 14,
+ BAD_ALLOCATION_TYPE, // T_VOID = 15,
+ BAD_ALLOCATION_TYPE, // T_ADDRESS = 16,
+ BAD_ALLOCATION_TYPE, // T_NARROWOOP = 17,
+ BAD_ALLOCATION_TYPE, // T_METADATA = 18,
+ BAD_ALLOCATION_TYPE, // T_NARROWKLASS = 19,
+ BAD_ALLOCATION_TYPE, // T_CONFLICT = 20
};
- static FieldAllocationType basic_type_to_atype(bool is_static, BasicType type) {
+ static FieldAllocationType basic_type_to_atype(bool is_static, BasicType type, bool is_inline_type) {
assert(type >= T_BOOLEAN && type < T_VOID, "only allowable values");
FieldAllocationType result = _basic_type_to_atype[type + (is_static ? (T_CONFLICT + 1) : 0)];
assert(result != BAD_ALLOCATION_TYPE, "bad type");
+ if (is_inline_type) {
+ result = is_static ? STATIC_INLINE : NONSTATIC_INLINE;
+ }
return result;
}
class ClassFileParser::FieldAllocationCount : public ResourceObj {
public:
for (int i = 0; i < MAX_FIELD_ALLOCATION_TYPE; i++) {
count[i] = 0;
}
}
- void update(bool is_static, BasicType type) {
- FieldAllocationType atype = basic_type_to_atype(is_static, type);
+ void update(bool is_static, BasicType type, bool is_inline_type) {
+ FieldAllocationType atype = basic_type_to_atype(is_static, type, is_inline_type);
if (atype != BAD_ALLOCATION_TYPE) {
// Make sure there is no overflow with injected fields.
assert(count[atype] < 0xFFFF, "More than 65535 fields");
count[atype]++;
}
// Side-effects: populates the _fields, _fields_annotations,
// _fields_type_annotations fields
void ClassFileParser::parse_fields(const ClassFileStream* const cfs,
bool is_interface,
+ bool is_inline_type,
+ bool is_permits_value_class,
FieldAllocationCount* const fac,
ConstantPool* cp,
const int cp_size,
u2* const java_fields_count_ptr,
TRAPS) {
*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);
// The field array starts with tuples of shorts
// [access, name index, sig index, initial value index, byte offset].
// A generic signature slot only exists for field with generic
// signature attribute. And the access flag is set with
total_fields * (FieldInfo::field_slots + 1));
// The generic signature slots start after all other fields' data.
int generic_signature_slot = total_fields * FieldInfo::field_slots;
int num_generic_signature = 0;
+ int instance_fields_count = 0;
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;
+
+ const jint flags = cfs->get_u2_fast() & recognized_modifiers;
+ verify_legal_field_modifiers(flags, is_interface, is_inline_type, is_permits_value_class, 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);
const u2 name_index = cfs->get_u2_fast();
check_property(valid_symbol_at(name_index),
"Invalid constant pool index %u for field name in class file %s",
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();
signature_index,
constantvalue_index);
const BasicType type = cp->basic_type_for_signature_at(signature_index);
// Update FieldAllocationCount for this kind of field
- fac->update(is_static, type);
+ fac->update(is_static, type, type == T_PRIMITIVE_OBJECT);
// After field is initialized with type, we can augment it with aux info
if (parsed_annotations.has_any_annotations()) {
parsed_annotations.apply_to(field);
if (field->is_contended()) {
0);
const BasicType type = Signature::basic_type(injected[n].signature());
// Update FieldAllocationCount for this kind of field
- fac->update(false, type);
+ fac->update(false, type, false);
index++;
}
}
+ if (is_inline_type) {
+ FieldInfo* const field = FieldInfo::from_field_array(fa, index);
+ field->initialize(JVM_ACC_FIELD_INTERNAL | JVM_ACC_STATIC,
+ (u2)vmSymbols::as_int(VM_SYMBOL_ENUM_NAME(default_value_name)),
+ (u2)vmSymbols::as_int(VM_SYMBOL_ENUM_NAME(object_signature)),
+ 0);
+ const BasicType type = Signature::basic_type(vmSymbols::object_signature());
+ fac->update(true, type, false);
+ index++;
+ }
+
+ if (is_inline_type && instance_fields_count == 0) {
+ _is_empty_inline_type = true;
+ FieldInfo* const field = FieldInfo::from_field_array(fa, index);
+ field->initialize(JVM_ACC_FIELD_INTERNAL,
+ (u2)vmSymbols::as_int(VM_SYMBOL_ENUM_NAME(empty_marker_name)),
+ (u2)vmSymbols::as_int(VM_SYMBOL_ENUM_NAME(byte_signature)),
+ 0);
+ const BasicType type = Signature::basic_type(vmSymbols::byte_signature());
+ fac->update(false, type, false);
+ index++;
+ }
+
+ if (instance_fields_count > 0) {
+ _has_nonstatic_fields = true;
+ }
+
assert(NULL == _fields, "invariant");
_fields =
MetadataFactory::new_array<u2>(_loader_data,
index * FieldInfo::field_slots + num_generic_signature,
const Symbol* sig,
TRAPS) const {
assert(name != NULL, "invariant");
assert(sig != NULL, "invariant");
+ const char* class_note = "";
+ if (is_inline_type() && name == vmSymbols::object_initializer_name()) {
+ class_note = " (an inline class)";
+ }
+
ResourceMark rm(THREAD);
Exceptions::fthrow(THREAD_AND_LOCATION,
vmSymbols::java_lang_ClassFormatError(),
- "%s \"%s\" in class %s has illegal signature \"%s\"", type,
- name->as_C_string(), _class_name->as_C_string(), sig->as_C_string());
+ "%s \"%s\" in class %s%s has illegal signature \"%s\"", type,
+ name->as_C_string(), _class_name->as_C_string(), class_note, sig->as_C_string());
}
AnnotationCollector::ID
AnnotationCollector::annotation_index(const ClassLoaderData* loader_data,
const Symbol* name,
// from the method back up to the containing klass. These flag values
// are added to klass's access_flags.
Method* ClassFileParser::parse_method(const ClassFileStream* const cfs,
bool is_interface,
+ bool is_inline_type,
+ bool is_permits_value_class,
const ConstantPool* cp,
AccessFlags* const promoted_flags,
TRAPS) {
assert(cfs != NULL, "invariant");
assert(cp != NULL, "invariant");
} else {
classfile_parse_error("Method <clinit> is not static in class file %s", THREAD);
return NULL;
}
} else {
- verify_legal_method_modifiers(flags, is_interface, name, CHECK_NULL);
+ verify_legal_method_modifiers(flags, is_interface, is_inline_type, is_permits_value_class, 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 NULL;
+ if (name == vmSymbols::object_initializer_name()) {
+ if (is_interface) {
+ classfile_parse_error("Interface cannot have a method named <init>, class file %s", THREAD);
+ return NULL;
+ } else if (!is_inline_type && signature->is_void_method_signature()) {
+ // OK, a constructor
+ } else if (is_inline_type && !signature->is_void_method_signature()) {
+ // also OK, a static factory, as long as the return value is good
+ bool ok = false;
+ SignatureStream ss((Symbol*) signature, true);
+ while (!ss.at_return_type()) ss.next();
+ if (ss.is_reference()) {
+ Symbol* ret = ss.as_symbol();
+ const Symbol* required = class_name();
+ if (is_hidden()) {
+ // The original class name in hidden classes gets changed. So using
+ // the original name in the return type is no longer valid.
+ // Note that expecting the return type for inline hidden class factory
+ // methods to be java.lang.Object works around a JVM Spec issue for
+ // hidden classes.
+ required = vmSymbols::java_lang_Object();
+ }
+ ok = (ret == required);
+ }
+ if (!ok) {
+ throwIllegalSignature("Method", name, signature, CHECK_0);
+ }
+ } else {
+ // not OK, so throw the same error as in verify_legal_method_signature.
+ throwIllegalSignature("Method", name, signature, CHECK_0);
+ }
+ // A declared <init> method must always be either a non-static
+ // object constructor, with a void return, or else it must be a
+ // static factory method, with a non-void return. No other
+ // definition of <init> is possible.
+ //
+ // The verifier (in verify_invoke_instructions) will inspect the
+ // signature of any attempt to invoke <init>, and ensures that it
+ // returns non-void if and only if it is being invoked by
+ // invokestatic, and void if and only if it is being invoked by
+ // invokespecial.
+ //
+ // When a symbolic reference to <init> is resolved for a
+ // particular invocation mode (special or static), the mode is
+ // matched to the JVM_ACC_STATIC modifier of the <init> method.
+ // Thus, it is impossible to statically invoke a constructor, and
+ // impossible to "new + invokespecial" a static factory, either
+ // through bytecode or through reflection.
}
int args_size = -1; // only used when _need_verify is true
if (_need_verify) {
verify_legal_name_with_signature(name, signature, CHECK_NULL);
// from the methods back up to the containing klass. These flag values
// are added to klass's access_flags.
// Side-effects: populates the _methods field in the parser
void ClassFileParser::parse_methods(const ClassFileStream* const cfs,
bool is_interface,
+ bool is_inline_type,
+ bool is_permits_value_class,
AccessFlags* promoted_flags,
bool* has_final_method,
bool* declares_nonstatic_concrete_methods,
TRAPS) {
assert(cfs != NULL, "invariant");
CHECK);
for (int index = 0; index < length; index++) {
Method* method = parse_method(cfs,
is_interface,
+ is_inline_type,
+ is_permits_value_class,
_cp,
promoted_flags,
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;
+ }
+ // JVM_ACC_VALUE, JVM_ACC_PRIMITIVE, and JVM_ACC_PERMITS_VALUE are defined for class file version 62 and later
+ if (supports_inline_types()) {
+ recognized_modifiers |= JVM_ACC_PRIMITIVE | JVM_ACC_VALUE | JVM_ACC_PERMITS_VALUE;
}
+
+ // 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);
cfs->set_current(current_mark);
return length;
}
+ u2 ClassFileParser::parse_classfile_preload_attribute(const ClassFileStream* const cfs,
+ const u1* const preload_attribute_start,
+ TRAPS) {
+ const u1* const current_mark = cfs->current();
+ u2 length = 0;
+ if (preload_attribute_start != NULL) {
+ cfs->set_current(preload_attribute_start);
+ cfs->guarantee_more(2, CHECK_0); // length
+ length = cfs->get_u2_fast();
+ }
+ const int size = length;
+ Array<u2>* const preload_classes = MetadataFactory::new_array<u2>(_loader_data, size, CHECK_0);
+ _preload_classes = preload_classes;
+ if (length > 0) {
+ 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),
+ "Preload class_info_index %u has bad constant type in class file %s",
+ class_info_index, CHECK_0);
+ preload_classes->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;
// 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 _preload_classes attribute to default sentinel
+ _preload_classes = 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_preload_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 = NULL;
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;
+ const u1* preload_attribute_start = NULL;
+ u4 preload_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_preload()) {
+ if (parsed_preload_attribute) {
+ classfile_parse_error("Multiple Preload attributes in class file %s", CHECK);
+ return;
+ }
+ parsed_preload_attribute = true;
+ preload_attribute_start = cfs->current();
+ preload_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_preload_attribute) {
+ const u2 num_classes = parse_classfile_preload_attribute(
+ cfs,
+ preload_attribute_start,
+ CHECK);
+ if (_need_verify) {
+ guarantee_property(
+ preload_attribute_length == sizeof(num_classes) + sizeof(u2) * num_classes,
+ "Wrong Preload 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(_fields, java_fields_count);
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_preload_classes(_preload_classes);
this_klass->set_annotations(_combined_annotations);
this_klass->set_permitted_subclasses(_permitted_subclasses);
this_klass->set_record_components(_record_components);
// Delay the setting of _local_interfaces and _transitive_interfaces until after
// initialize_supers() in fill_instance_klass(). It is because the _local_interfaces could
assert(cp != NULL, "invariant");
const InstanceKlass* super_klass = NULL;
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,
void OopMapBlocksBuilder::print_value_on(outputStream* st) const {
print_on(st);
}
+ void ClassFileParser::throwInlineTypeLimitation(THREAD_AND_LOCATION_DECL,
+ const char* msg,
+ const Symbol* name,
+ const Symbol* sig) const {
+
+ ResourceMark rm(THREAD);
+ if (name == NULL || sig == NULL) {
+ Exceptions::fthrow(THREAD_AND_LOCATION_ARGS,
+ vmSymbols::java_lang_ClassFormatError(),
+ "class: %s - %s", _class_name->as_C_string(), msg);
+ }
+ else {
+ Exceptions::fthrow(THREAD_AND_LOCATION_ARGS,
+ vmSymbols::java_lang_ClassFormatError(),
+ "\"%s\" sig: \"%s\" class: %s - %s", name->as_C_string(), sig->as_C_string(),
+ _class_name->as_C_string(), msg);
+ }
+ }
+
void ClassFileParser::set_precomputed_flags(InstanceKlass* ik) {
assert(ik != NULL, "invariant");
const Klass* const super = ik->super();
#endif
// Check if this klass supports the java.lang.Cloneable interface
if (vmClasses::Cloneable_klass_loaded()) {
if (ik->is_subtype_of(vmClasses::Cloneable_klass())) {
+ if (ik->is_inline_klass()) {
+ JavaThread *THREAD = JavaThread::current();
+ throwInlineTypeLimitation(THREAD_AND_LOCATION, "Inline Types do not support Cloneable");
+ return;
+ }
ik->set_is_cloneable();
}
}
// Check if this klass has a vanilla default constructor
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 55 and later
+ return _major_version >= JAVA_11_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 != NULL, "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 have the ACC_PERMITS_VALUE
+ // flag set. However, since java.lang.Object has not yet been changed into an abstract
+ // class, it cannot have its ACC_PERMITS_VALUE 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_value_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 {
const bool is_module = (flags & JVM_ACC_MODULE) != 0;
+ const bool is_value_class = (flags & JVM_ACC_VALUE) != 0;
+ const bool is_primitive_class = (flags & JVM_ACC_PRIMITIVE) != 0;
+ const bool is_permits_value_class = (flags & JVM_ACC_PERMITS_VALUE) != 0;
assert(_major_version >= JAVA_9_VERSION || !is_module, "JVM_ACC_MODULE should not be set");
+ assert(supports_inline_types() || !is_value_class, "JVM_ACC_VALUE should not be set");
+ assert(supports_inline_types() || !is_primitive_class, "JVM_ACC_PRIMITIVE should not be set");
+ assert(supports_inline_types() || !is_permits_value_class, "JVM_ACC_PERMITS_VALUE should not be set");
if (is_module) {
ResourceMark rm(THREAD);
Exceptions::fthrow(
THREAD_AND_LOCATION,
vmSymbols::java_lang_NoClassDefFoundError(),
"%s is not a class because access_flag ACC_MODULE is set",
_class_name->as_C_string());
return;
}
+ if (!EnableValhalla) {
+ if (is_value_class || is_primitive_class) {
+ const char* bad_flag = is_primitive_class ? "ACC_PRIMITIVE" : "ACC_VALUE";
+ ResourceMark rm(THREAD);
+ Exceptions::fthrow(
+ THREAD_AND_LOCATION,
+ vmSymbols::java_lang_ClassFormatError(),
+ "Class modifier %s in class %s requires option -XX:+EnableValhalla",
+ bad_flag, _class_name->as_C_string()
+ );
+ }
+ return;
+ }
+
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 major_gte_1_5 = _major_version >= JAVA_1_5_VERSION;
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_annotation) ||
+ (is_value_class && (is_enum || is_permits_value_class)) ||
+ (is_permits_value_class && (is_interface || is_final || !is_abstract)) ||
+ (is_primitive_class && (!is_value_class || !is_final || is_interface || is_abstract))) {
ResourceMark rm(THREAD);
+ const char* class_note = "";
+ if (is_value_class) class_note = " (a value class)";
+ if (is_primitive_class) class_note = " (a primitive class)";
+ if (is_permits_value_class) class_note = " (a permits_value class)";
Exceptions::fthrow(
THREAD_AND_LOCATION,
vmSymbols::java_lang_ClassFormatError(),
- "Illegal class modifiers in class %s: 0x%X",
- _class_name->as_C_string(), flags
+ "Illegal class modifiers in class %s%s: 0x%X",
+ _class_name->as_C_string(), class_note, flags
);
return;
}
}
}
}
void ClassFileParser::verify_legal_field_modifiers(jint flags,
bool is_interface,
+ bool is_inline_type,
+ bool is_permits_value_class,
TRAPS) const {
if (!_need_verify) { return; }
const bool is_public = (flags & JVM_ACC_PUBLIC) != 0;
const bool is_protected = (flags & JVM_ACC_PROTECTED) != 0;
is_illegal = true;
}
} else { // not interface
if (has_illegal_visibility(flags) || (is_final && is_volatile)) {
is_illegal = true;
+ } else {
+ if (is_inline_type && !is_static && !is_final) {
+ is_illegal = true;
+ } else if (is_permits_value_class && !is_static) {
+ is_illegal = true;
+ }
}
}
if (is_illegal) {
ResourceMark rm(THREAD);
}
}
void ClassFileParser::verify_legal_method_modifiers(jint flags,
bool is_interface,
+ bool is_inline_type,
+ bool is_permits_value_class,
const Symbol* name,
TRAPS) const {
if (!_need_verify) { return; }
const bool is_public = (flags & JVM_ACC_PUBLIC) != 0;
const bool major_gte_17 = _major_version >= JAVA_17_VERSION;
const bool is_initializer = (name == vmSymbols::object_initializer_name());
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
} else { // not interface
if (has_illegal_visibility(flags)) {
is_illegal = true;
} else {
if (is_initializer) {
- if (is_static || is_final || is_synchronized || is_native ||
+ if (is_final || is_synchronized || is_native ||
is_abstract || (major_gte_1_5 && is_bridge)) {
is_illegal = true;
}
+ if (!is_static && !is_inline_type) {
+ // OK, an object constructor in a regular class
+ } else if (is_static && is_inline_type) {
+ // OK, a static init factory in an inline class
+ } else {
+ // but no other combinations are allowed
+ is_illegal = true;
+ class_note = (is_inline_type ? " (an inline class)" : " (not an inline class)");
+ }
} 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_inline_type || is_permits_value_class) && is_synchronized && !is_static) {
+ is_illegal = true;
+ class_note = " (an inline 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,
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_PRIMITIVE_OBJECT:
+ // Can't enable this check fully until JDK upgrades the bytecode generators.
+ // For now, compare to class file version 51 so old verifier doesn't see Q signatures.
+ if (_major_version < 51 /* CONSTANT_CLASS_DESCRIPTORS */ ) {
+ classfile_parse_error("Class name contains illegal Q-signature "
+ "in descriptor in class file %s",
+ CHECK_0);
+ return NULL;
+ }
+ // fall through
+ 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 != NULL) {
int newlen = 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 != NULL) && ((p - bytes) == (int)length);
}
+ } else if ((_major_version >= CONSTANT_CLASS_DESCRIPTORS || _class_name->starts_with("jdk/internal/reflect/"))
+ && bytes[length - 1] == ';' ) {
+ // Support for L...; and Q...; 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.
// Checks if signature is a legal field signature.
void ClassFileParser::verify_legal_field_signature(const Symbol* name,
const Symbol* signature,
TRAPS) const {
if (!_need_verify) { return; }
+ if (!supports_inline_types() && (signature->is_Q_signature() || signature->is_Q_array_signature())) {
+ throwIllegalSignature("Field", name, signature, CHECK);
+ }
const char* const bytes = (const char* const)signature->bytes();
const unsigned int length = signature->utf8_length();
const char* const p = skip_over_field_signature(bytes, false, length, CHECK);
_major_version >= JAVA_7_VERSION) {
throwIllegalSignature("Method", name, signature, THREAD);
return;
}
- int sig_length = signature->utf8_length();
- if (name->utf8_length() > 0 &&
+ if (!is_inline_type()) {
+ 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) {
- throwIllegalSignature("Method", name, signature, THREAD);
+ throwIllegalSignature("Method", name, signature, THREAD);
+ }
}
}
// Checks if signature is a legal method signature.
// Returns number of parameters
}
fill_instance_klass(ik, changed_by_loadhook, cl_inst_info, CHECK_NULL);
assert(_klass == ik, "invariant");
-
return ik;
}
+ // Return true if the specified class is not a valid super class for an inline type.
+ // A valid super class for an inline type is abstract, has no instance fields,
+ // is not declared with the identity modifier (checked elsewhere), has
+ // an empty body-less no-arg constructor, and no synchronized instance methods.
+ // This function doesn't check if the class's super types are invalid. Those checks
+ // are done elsewhere. The final determination of whether or not a class is an
+ // invalid super type for an inline class is done in fill_instance_klass().
+ bool ClassFileParser::is_invalid_super_for_inline_type() {
+ if (is_interface() || class_name() == vmSymbols::java_lang_Object()) {
+ return false;
+ }
+ if (!access_flags().is_abstract() || _has_nonstatic_fields) {
+ return true;
+ } else {
+ // Look at each method
+ for (int x = 0; x < _methods->length(); x++) {
+ const Method* const method = _methods->at(x);
+ if (method->is_synchronized() && !method->is_static()) {
+ return true;
+
+ } else if (method->name() == vmSymbols::object_initializer_name()) {
+ if (method->signature() != vmSymbols::void_method_signature() ||
+ !method->is_vanilla_constructor()) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
void ClassFileParser::fill_instance_klass(InstanceKlass* ik,
bool changed_by_loadhook,
const ClassInstanceInfo& cl_inst_info,
TRAPS) {
assert(ik != NULL, "invariant");
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);
+ if (_field_info->_is_naturally_atomic && ik->is_inline_klass()) {
+ ik->set_is_naturally_atomic();
+ }
+
+ if (this->_invalid_inline_super) {
+ ik->set_invalid_inline_super();
+ }
+
assert(_fac != NULL, "invariant");
- ik->set_static_oop_field_count(_fac->count[STATIC_OOP]);
+ ik->set_static_oop_field_count(_fac->count[STATIC_OOP] + _fac->count[STATIC_INLINE]);
// this transfers ownership of a lot of arrays from
// the parser onto the InstanceKlass*
apply_parsed_class_metadata(ik, _java_fields_count);
assert(NULL == _cp, "invariant");
assert(NULL == _fields, "invariant");
assert(NULL == _methods, "invariant");
assert(NULL == _inner_classes, "invariant");
assert(NULL == _nest_members, "invariant");
+ assert(NULL == _preload_classes, "invariant");
assert(NULL == _combined_annotations, "invariant");
assert(NULL == _record_components, "invariant");
assert(NULL == _permitted_subclasses, "invariant");
if (_has_final_method) {
ik->set_minor_version(_minor_version);
ik->set_major_version(_major_version);
ik->set_has_nonstatic_concrete_methods(_has_nonstatic_concrete_methods);
ik->set_declares_nonstatic_concrete_methods(_declares_nonstatic_concrete_methods);
+ if (_is_declared_atomic) {
+ ik->set_is_declared_atomic();
+ }
if (_is_hidden) {
ik->set_is_hidden();
}
// We won a potential race
JvmtiExport::add_default_read_edges(module_handle, THREAD);
}
}
+ bool all_fields_empty = true;
+ for (AllFieldStream fs(ik->fields(), ik->constants()); !fs.done(); fs.next()) {
+ if (!fs.access_flags().is_static()) {
+ if (fs.field_descriptor().is_inline_type()) {
+ Klass* k = _inline_type_field_klasses->at(fs.index());
+ ik->set_inline_type_field_klass(fs.index(), k);
+ if (!InlineKlass::cast(k)->is_empty_inline_type()) { all_fields_empty = false; }
+ } else {
+ all_fields_empty = false;
+ }
+ } else if (is_inline_type() && (fs.name() == vmSymbols::default_value_name())) {
+ InlineKlass::cast(ik)->set_default_value_offset(ik->field_offset(fs.index()));
+ }
+ }
+
+ if (_is_empty_inline_type || (is_inline_type() && all_fields_empty)) {
+ ik->set_is_empty_inline_type();
+ }
+
+ if (is_inline_type()) {
+ InlineKlass* vk = InlineKlass::cast(ik);
+ vk->set_alignment(_alignment);
+ vk->set_first_field_offset(_first_field_offset);
+ vk->set_exact_size_in_bytes(_exact_size_in_bytes);
+ InlineKlass::cast(ik)->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(NULL),
_inner_classes(NULL),
_nest_members(NULL),
_nest_host(0),
_permitted_subclasses(NULL),
+ _preload_classes(NULL),
_record_components(NULL),
+ _temp_local_interfaces(NULL),
_local_interfaces(NULL),
_transitive_interfaces(NULL),
_combined_annotations(NULL),
_class_annotations(NULL),
_class_type_annotations(NULL),
_klass(NULL),
_klass_to_deallocate(NULL),
_parsed_annotations(NULL),
_fac(NULL),
_field_info(NULL),
+ _inline_type_field_klasses(NULL),
_method_ordering(NULL),
_all_mirandas(NULL),
_vtable_size(0),
_itable_size(0),
_num_miranda_methods(0),
_relax_verify(false),
_has_nonstatic_concrete_methods(false),
_declares_nonstatic_concrete_methods(false),
_has_final_method(false),
_has_contended_fields(false),
+ _has_inline_type_fields(false),
+ _has_nonstatic_fields(false),
+ _is_empty_inline_type(false),
+ _is_naturally_atomic(false),
+ _is_declared_atomic(false),
+ _invalid_inline_super(false),
+ _invalid_identity_super(false),
_has_finalizer(false),
_has_empty_finalizer(false),
_has_vanilla_constructor(false),
_max_bootstrap_specifier_index(-1) {
_fields = NULL;
_methods = NULL;
_inner_classes = NULL;
_nest_members = NULL;
_permitted_subclasses = NULL;
+ _preload_classes = NULL;
_combined_annotations = NULL;
_class_annotations = _class_type_annotations = NULL;
_fields_annotations = _fields_type_annotations = NULL;
_record_components = NULL;
}
}
if (_fields != NULL) {
MetadataFactory::free_array<u2>(_loader_data, _fields);
}
+ if (_inline_type_field_klasses != NULL) {
+ MetadataFactory::free_array<InlineKlass*>(_loader_data, _inline_type_field_klasses);
+ }
+
if (_methods != NULL) {
// Free methods
InstanceKlass::deallocate_methods(_loader_data, _methods);
}
if (_permitted_subclasses != NULL && _permitted_subclasses != Universe::the_empty_short_array()) {
MetadataFactory::free_array<u2>(_loader_data, _permitted_subclasses);
}
+ if (_preload_classes != NULL && _preload_classes != Universe::the_empty_short_array()) {
+ MetadataFactory::free_array<u2>(_loader_data, _preload_classes);
+ }
+
// Free interfaces
InstanceKlass::deallocate_interfaces(_loader_data, _super_klass,
_local_interfaces, _transitive_interfaces);
if (_combined_annotations != NULL) {
assert(cp_size == (const 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;
}
+ // JVM_ACC_VALUE and JVM_ACC_PRIMITIVE are defined for class file version 55 and later
+ if (supports_inline_types()) {
+ recognized_modifiers |= JVM_ACC_PRIMITIVE | JVM_ACC_VALUE | JVM_ACC_PERMITS_VALUE;
+ }
+
+ // 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;
}
_itfs_len = stream->get_u2_fast();
parse_interfaces(stream,
_itfs_len,
cp,
&_has_nonstatic_concrete_methods,
+ &_is_declared_atomic,
CHECK);
- assert(_local_interfaces != NULL, "invariant");
+ assert(_temp_local_interfaces != NULL, "invariant");
// Fields (offsets are filled in later)
_fac = new FieldAllocationCount();
parse_fields(stream,
- _access_flags.is_interface(),
+ is_interface(),
+ is_inline_type(),
+ is_permits_value_class(),
_fac,
cp,
cp_size,
&_java_fields_count,
CHECK);
assert(_fields != NULL, "invariant");
// Methods
AccessFlags promoted_flags;
parse_methods(stream,
- _access_flags.is_interface(),
+ is_interface(),
+ is_inline_type(),
+ is_permits_value_class(),
&promoted_flags,
&_has_final_method,
&_declares_nonstatic_concrete_methods,
CHECK);
assert(stream->at_eos(), "invariant");
assert(cp != NULL, "invariant");
assert(_loader_data != NULL, "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);
+ check_property(_temp_local_interfaces->length() == 0,
+ "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 && NULL == _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);
if (_super_klass != NULL) {
if (_super_klass->has_nonstatic_concrete_methods()) {
_has_nonstatic_concrete_methods = true;
}
+ if (_super_klass->is_declared_atomic()) {
+ _is_declared_atomic = true;
+ }
if (_super_klass->is_interface()) {
classfile_icce_error("class %s has interface %s as super class", _super_klass, THREAD);
return;
}
+
+ // For an inline class, only java/lang/Object or special abstract classes
+ // are acceptable super classes.
+ if (is_inline_type()) {
+ const InstanceKlass* super_ik = _super_klass;
+ if (super_ik->invalid_inline_super()) {
+ classfile_icce_error("inline class %s has an invalid super class %s", _super_klass, THREAD);
+ return;
+ }
+ }
}
+ if (_class_name == vmSymbols::java_lang_NonTearable() && _loader_data->class_loader() == NULL) {
+ // This is the original source of this condition.
+ // It propagates by inheritance, as if testing "instanceof NonTearable".
+ _is_declared_atomic = true;
+ } else 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)) {
+ _is_declared_atomic = true;
+ }
+ }
+
+ // Set ik->invalid_inline_super field to TRUE if already marked as invalid,
+ // if super is marked invalid, or if is_invalid_super_for_inline_type()
+ // returns true
+ if (invalid_inline_super() ||
+ (_super_klass != NULL && _super_klass->invalid_inline_super()) ||
+ is_invalid_super_for_inline_type()) {
+ set_invalid_inline_super();
+ }
+
+ int itfs_len = _temp_local_interfaces->length();
+ if (itfs_len == 0) {
+ _local_interfaces = Universe::the_empty_instance_klass_array();
+ } else {
+ _local_interfaces = MetadataFactory::new_array<InstanceKlass*>(_loader_data, itfs_len, NULL, CHECK);
+ for (int i = 0; i < itfs_len; i++) {
+ _local_interfaces->at_put(i, _temp_local_interfaces->at(i));
+ }
+ }
+ _temp_local_interfaces = NULL;
+ assert(_local_interfaces != NULL, "invariant");
+
// Compute the transitive list of all unique interfaces implemented by this class
_transitive_interfaces =
compute_transitive_interfaces(_super_klass,
_local_interfaces,
_loader_data,
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(_fac != NULL, "invariant");
assert(_parsed_annotations != NULL, "invariant");
+
+ if (EnableValhalla) {
+ _inline_type_field_klasses = MetadataFactory::new_array<InlineKlass*>(_loader_data,
+ java_fields_count(),
+ NULL,
+ CHECK);
+ for (AllFieldStream fs(_fields, cp); !fs.done(); fs.next()) {
+ if (Signature::basic_type(fs.signature()) == T_PRIMITIVE_OBJECT && !fs.access_flags().is_static()) {
+ // Pre-load inline class
+ Klass* klass = SystemDictionary::resolve_inline_type_field_or_fail(&fs,
+ Handle(THREAD, _loader_data->class_loader()),
+ _protection_domain, true, CHECK);
+ assert(klass != NULL, "Sanity check");
+ if (!klass->access_flags().is_value_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 an inline type, but it is not",
+ _class_name->as_C_string(),
+ InstanceKlass::cast(klass)->external_name()));
+ }
+ _inline_type_field_klasses->at_put(fs.index(), InlineKlass::cast(klass));
+ }
+ }
+ }
+
_field_info = new FieldLayoutInfo();
FieldLayoutBuilder lb(class_name(), super_klass(), _cp, _fields,
- _parsed_annotations->is_contended(), _field_info);
- lb.build_layout();
+ _parsed_annotations->is_contended(), is_inline_type(),
+ _field_info, _inline_type_field_klasses);
+ lb.build_layout(CHECK);
+ if (is_inline_type()) {
+ _alignment = lb.get_alignment();
+ _first_field_offset = lb.get_first_field_offset();
+ _exact_size_in_bytes = lb.get_exact_size_in_byte();
+ }
+ _has_inline_type_fields = _field_info->_has_inline_fields;
- // Compute reference typ
+ // Compute reference type
_rt = (NULL ==_super_klass) ? REF_NONE : _super_klass->reference_type();
-
}
void ClassFileParser::set_klass(InstanceKlass* klass) {
#ifdef ASSERT
const ClassFileStream* ClassFileParser::clone_stream() const {
assert(_stream != NULL, "invariant");
return _stream->clone();
}
+
// ----------------------------------------------------------------------------
// debugging
#ifdef ASSERT
< prev index next >