--- old/src/hotspot/share/classfile/classFileParser.cpp 2018-05-17 15:55:39.000000000 -0400 +++ new/src/hotspot/share/classfile/classFileParser.cpp 2018-05-17 15:55:38.000000000 -0400 @@ -3159,6 +3159,40 @@ JVM_ACC_STATIC \ ) +u2 ClassFileParser::parse_value_types_attribute(const ClassFileStream* const cfs, + const u1* const value_types_attribute_start, + TRAPS) { + const u1* const current_mark = cfs->current(); + u2 length = 0; + if (value_types_attribute_start != NULL) { + cfs->set_current(value_types_attribute_start); + cfs->guarantee_more(2, CHECK_0); // length + length = cfs->get_u2_fast(); + } + Array* const value_types = MetadataFactory::new_array(_loader_data, length, CHECK_0); + + int index = 0; + const int cp_size = _cp->length(); + cfs->guarantee_more(2 *length, CHECK_0); + for (int n = 0; n < length; n++) { + // Value types class index + const u2 value_types_info_index = cfs->get_u2_fast(); + check_property( + valid_klass_reference_at(value_types_info_index), + "value_types_info_index %u has bad constant type in class file %s", + value_types_info_index, CHECK_0); + ValueTypes vt; + vt._class_info_index = value_types_info_index; + vt._class_name = NULL; + value_types->at_put(index++, vt); + } + _value_types = value_types; + // Restore buffer's current position. + cfs->set_current(current_mark); + + return length; +} + // Return number of classes in the inner classes attribute table u2 ClassFileParser::parse_classfile_inner_classes_attribute(const ClassFileStream* const cfs, const u1* const inner_classes_attribute_start, @@ -3379,6 +3413,7 @@ u2 attributes_count = cfs->get_u2_fast(); bool parsed_sourcefile_attribute = false; bool parsed_innerclasses_attribute = false; + bool parsed_value_types_attribute = false; bool parsed_enclosingmethod_attribute = false; bool parsed_bootstrap_methods_attribute = false; const u1* runtime_visible_annotations = NULL; @@ -3394,6 +3429,8 @@ bool parsed_source_debug_ext_annotations_exist = false; const u1* inner_classes_attribute_start = NULL; u4 inner_classes_attribute_length = 0; + const u1* value_types_attribute_start = NULL; + u4 value_types_attribute_length = 0; u2 enclosing_method_class_index = 0; u2 enclosing_method_method_index = 0; // Iterate over attributes @@ -3435,6 +3472,16 @@ inner_classes_attribute_start = cfs->current(); inner_classes_attribute_length = attribute_length; cfs->skip_u1(inner_classes_attribute_length, CHECK); + } else if (tag == vmSymbols::tag_value_types()) { + // Check for ValueTypes tag + if (parsed_value_types_attribute) { + classfile_parse_error("Multiple ValueTypes attributes in class file %s", CHECK); + } else { + parsed_value_types_attribute = true; + } + value_types_attribute_start = cfs->current(); + value_types_attribute_length = attribute_length; + cfs->skip_u1(value_types_attribute_length, CHECK); } else if (tag == vmSymbols::tag_synthetic()) { // Check for Synthetic tag // Shouldn't we check that the synthetic flags wasn't already set? - not required in spec @@ -3579,6 +3626,10 @@ } } + if (parsed_value_types_attribute) { + parse_value_types_attribute(cfs, value_types_attribute_start, CHECK); + } + if (_max_bootstrap_specifier_index >= 0) { guarantee_property(parsed_bootstrap_methods_attribute, "Missing BootstrapMethods attribute in class file %s", CHECK); @@ -3641,6 +3692,7 @@ this_klass->set_fields(_fields, java_fields_count); this_klass->set_methods(_methods); this_klass->set_inner_classes(_inner_classes); + this_klass->set_value_types(_value_types); this_klass->set_local_interfaces(_local_interfaces); this_klass->set_transitive_interfaces(_transitive_interfaces); this_klass->set_annotations(_combined_annotations); @@ -4029,6 +4081,8 @@ _protection_domain, true, CHECK); assert(klass != NULL, "Sanity check"); assert(klass->access_flags().is_value_type(), "Value type expected"); + assert(InstanceKlass::is_declared_value_type(_cp, _value_types, klass->name()), + "Flattenable fields' type must be listed in the ValueTypes attribute"); static_value_type_count++; } else if (fs.allocation_type() == NONSTATIC_FLATTENABLE) { // Pre-resolve the flattenable field and check for value type circularity issues. @@ -4038,6 +4092,8 @@ _protection_domain, true, CHECK); assert(klass != NULL, "Sanity check"); assert(klass->access_flags().is_value_type(), "Value type expected"); + assert(InstanceKlass::is_declared_value_type(_cp, _value_types, klass->name()), + "Flattenable fields' type must be listed in the ValueTypes attribute"); ValueKlass* vk = ValueKlass::cast(klass); // Conditions to apply flattening or not should be defined in a single place if ((ValueFieldMaxFlatSize < 0) || (vk->size_helper() * HeapWordSize) <= ValueFieldMaxFlatSize) { @@ -5942,6 +5998,7 @@ _fields(NULL), _methods(NULL), _inner_classes(NULL), + _value_types(NULL), _local_interfaces(NULL), _transitive_interfaces(NULL), _combined_annotations(NULL), --- old/src/hotspot/share/classfile/classFileParser.hpp 2018-05-17 15:55:41.000000000 -0400 +++ new/src/hotspot/share/classfile/classFileParser.hpp 2018-05-17 15:55:40.000000000 -0400 @@ -98,6 +98,7 @@ Array* _fields; Array* _methods; Array* _inner_classes; + Array* _value_types; Array* _local_interfaces; Array* _transitive_interfaces; Annotations* _combined_annotations; @@ -288,6 +289,10 @@ int length, TRAPS); + u2 parse_value_types_attribute(const ClassFileStream* const cfs, + const u1* const value_types_attribute_start, + TRAPS); + u2 parse_classfile_inner_classes_attribute(const ClassFileStream* const cfs, const u1* const inner_classes_attribute_start, bool parsed_enclosingmethod_attribute, --- old/src/hotspot/share/classfile/vmSymbols.hpp 2018-05-17 15:55:42.000000000 -0400 +++ new/src/hotspot/share/classfile/vmSymbols.hpp 2018-05-17 15:55:42.000000000 -0400 @@ -140,6 +140,7 @@ /* class file format tags */ \ template(tag_source_file, "SourceFile") \ template(tag_inner_classes, "InnerClasses") \ + template(tag_value_types, "ValueTypes") \ template(tag_constant_value, "ConstantValue") \ template(tag_code, "Code") \ template(tag_exceptions, "Exceptions") \ --- old/src/hotspot/share/oops/instanceKlass.cpp 2018-05-17 15:55:44.000000000 -0400 +++ new/src/hotspot/share/oops/instanceKlass.cpp 2018-05-17 15:55:43.000000000 -0400 @@ -403,6 +403,11 @@ } set_inner_classes(NULL); + if (value_types() != NULL && !value_types()->is_shared()) { + MetadataFactory::free_array(loader_data, value_types()); + } + set_value_types(NULL); + // We should deallocate the Annotations instance if it's not in shared spaces. if (annotations() != NULL && !annotations()->is_shared()) { MetadataFactory::free_metadata(loader_data, annotations()); @@ -626,40 +631,46 @@ ResourceMark rm(THREAD); if (fs.field_descriptor().access_flags().is_flattenable()) { Symbol* signature = fs.field_descriptor().signature(); + ResourceMark rm; + Symbol* name = SymbolTable::lookup(signature->as_C_string() + 1, + signature->utf8_length() - 2, CHECK_false); + assert(this->is_declared_value_type(name), "Verifying consistency with ValueTypes attribute"); + name->decrement_refcount(); + name = NULL; // Get current loader and protection domain first. oop loader = class_loader(); oop prot_domain = protection_domain(); Klass* klass = SystemDictionary::resolve_or_fail(signature, Handle(THREAD, loader), Handle(THREAD, prot_domain), true, - THREAD); + CHECK_false); if (klass == NULL) { THROW_(vmSymbols::java_lang_LinkageError(), false); } } } - // Second step: methods arguments and return types - // for (int i = 0; i < this_k->constants()->length(); i++) { - // if (this_k->constants()->tag_at(i).is_method()) { - // Symbol* signature = this_k->constants()->signature_ref_at(i); - // ResourceMark rm(THREAD); - // for (SignatureStream ss(signature); !ss.is_done(); ss.next()) { - // if (ss.type() == T_VALUETYPE) { - // Symbol* sig = ss.as_symbol(THREAD); - // // Get current loader and protection domain first. - // oop loader = this_k->class_loader(); - // oop protection_domain = this_k->protection_domain(); - // - // bool ok = SystemDictionary::resolve_or_fail(sig, - // Handle(THREAD, loader), Handle(THREAD, protection_domain), true, - // THREAD); - // if (!ok) { - // THROW_(vmSymbols::java_lang_LinkageError(), false); - // } - // } - // } - // } - // } + // Second step: methods arguments and return types + for (int i = 0; i < constants()->length(); i++) { + if (constants()->tag_at(i).is_method()) { + Symbol* signature = constants()->uncached_signature_ref_at(i); + ResourceMark rm(THREAD); + for (SignatureStream ss(signature); !ss.is_done(); ss.next()) { + Symbol* sig = ss.as_symbol(THREAD); + if (is_declared_value_type(sig)) { + // Get current loader and protection domain first. + oop loader = class_loader(); + oop protection_domain = this->protection_domain(); + + bool ok = SystemDictionary::resolve_or_fail(sig, + Handle(THREAD, loader), Handle(THREAD, protection_domain), true, + CHECK_false); + if (!ok) { + THROW_(vmSymbols::java_lang_LinkageError(), false); + } + } + } + } + } // in case the class is linked in the process of linking its superclasses if (is_linked()) { @@ -2342,6 +2353,14 @@ // unreference array name derived from this class name (arrays of an unloaded // class can't be referenced anymore). if (_array_name != NULL) _array_name->decrement_refcount(); + if (_value_types != NULL) { + for (int i = 0; i < _value_types->length(); i++) { + Symbol* s = _value_types->at(i)._class_name; + if (s != NULL) { + s->decrement_refcount(); + } + } + } if (_source_debug_extension != NULL) FREE_C_HEAP_ARRAY(char, _source_debug_extension); } @@ -3219,6 +3238,48 @@ return external_name(); } +bool InstanceKlass::is_declared_value_type(int index) { + assert(constants()->is_within_bounds(index) && + constants()->tag_at(index).is_klass_or_reference(), "Invalid index"); + if (value_types() == NULL) return false; // No ValueType attribute in this class file + return InstanceKlass::is_declared_value_type(value_types(), index); +} + +bool InstanceKlass::is_declared_value_type(Array* value_types, int index) { + assert(value_types != NULL, "Sanity check"); + for(int i = 0; i < value_types->length(); i++) { + if (value_types->at(i)._class_info_index == index) { + return true; + } + } + return false; +} + +bool InstanceKlass::is_declared_value_type(Symbol* symbol) { + if (value_types() == NULL) return false; // No ValueType attribute in this class file + return InstanceKlass::is_declared_value_type(constants(), value_types(), symbol); +} + +bool InstanceKlass::is_declared_value_type(ConstantPool* constants, Array* value_types, Symbol* symbol) { + assert(symbol != NULL, "Sanity check"); + assert(value_types != NULL, "Sanity check"); + for(int i = 0; i < value_types->length(); i++) { + if (value_types->at(i)._class_name == symbol) { + return true; + } + } + // symbol not found, class name symbol might not have been + // updated yet + for(int i = 0; i < value_types->length(); i++) { + if (constants->klass_at_noresolve((int)value_types->at(i)._class_info_index) == symbol) { + value_types->adr_at(i)->_class_name = symbol; + symbol->increment_refcount(); + return true; + } + } + return false; +} + void InstanceKlass::print_class_load_logging(ClassLoaderData* loader_data, const char* module_name, const ClassFileStream* cfs) const { --- old/src/hotspot/share/oops/instanceKlass.hpp 2018-05-17 15:55:46.000000000 -0400 +++ new/src/hotspot/share/oops/instanceKlass.hpp 2018-05-17 15:55:45.000000000 -0400 @@ -143,6 +143,12 @@ friend class ValueKlass; }; +class ValueTypes VALUE_OBJ_CLASS_SPEC { +public: + u2 _class_info_index; + Symbol* _class_name; +}; + class InstanceKlass: public Klass { friend class VMStructs; friend class JVMCIVMStructs; @@ -194,6 +200,8 @@ // number_of_inner_classes * 4 + enclosing_method_attribute_size. Array* _inner_classes; + Array* _value_types; + // the source debug extension for this klass, NULL if not specified. // Specified as UTF-8 string without terminating zero byte in the classfile, // it is stored in the instanceklass as a NULL-terminated UTF-8 string @@ -480,6 +488,15 @@ Array* inner_classes() const { return _inner_classes; } void set_inner_classes(Array* f) { _inner_classes = f; } + Array* value_types() const { return _value_types; } + void set_value_types(Array* f) { _value_types = f; } + + bool is_declared_value_type(int index); + bool is_declared_value_type(Symbol* symbol); + + static bool is_declared_value_type(Array* value_types, int index); + static bool is_declared_value_type(ConstantPool* constants, Array* value_types, Symbol* symbol); + enum InnerClassAttributeOffset { // From http://mirror.eng/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc10.html#18814 inner_class_inner_class_info_offset = 0, --- old/test/hotspot/jtreg/runtime/valhalla/valuetypes/Empty.java 2018-05-17 15:55:47.000000000 -0400 +++ new/test/hotspot/jtreg/runtime/valhalla/valuetypes/Empty.java 2018-05-17 15:55:47.000000000 -0400 @@ -34,6 +34,13 @@ } } +class EmptyTest { + public void run() { + EmptyValue.createEmptyValue(); + throw new RuntimeException("Excepted class file parse error"); + } +} + /** * @test Empty * @summary Test empty value type @@ -43,9 +50,9 @@ */ public class Empty { public static void main(String[] args) { - try { - EmptyValue.createEmptyValue(); - throw new RuntimeException("Excepted class file parse error"); - } catch (ClassFormatError cfe) {} + try { + EmptyTest test = new EmptyTest(); + test.run(); + } catch (ClassFormatError cfe) {} } }