--- old/src/hotspot/share/oops/instanceKlass.cpp 2019-03-11 14:26:17.778354995 +0100 +++ new/src/hotspot/share/oops/instanceKlass.cpp 2019-03-11 14:26:17.562354998 +0100 @@ -62,6 +62,7 @@ #include "oops/method.hpp" #include "oops/oop.inline.hpp" #include "oops/symbol.hpp" +#include "oops/valueKlass.hpp" #include "prims/jvmtiExport.hpp" #include "prims/jvmtiRedefineClasses.hpp" #include "prims/jvmtiThreadState.hpp" @@ -370,7 +371,9 @@ nonstatic_oop_map_size(parser.total_oop_map_count()), parser.is_interface(), parser.is_unsafe_anonymous(), - should_store_fingerprint(parser.is_unsafe_anonymous())); + should_store_fingerprint(parser.is_unsafe_anonymous()), + parser.has_flattenable_fields() ? parser.java_fields_count() : 0, + parser.is_value_type()); const Symbol* const class_name = parser.class_name(); assert(class_name != NULL, "invariant"); @@ -384,10 +387,12 @@ if (class_name == vmSymbols::java_lang_Class()) { // mirror ik = new (loader_data, size, THREAD) InstanceMirrorKlass(parser); - } - else if (is_class_loader(class_name, parser)) { + } else if (is_class_loader(class_name, parser)) { // class loader ik = new (loader_data, size, THREAD) InstanceClassLoaderKlass(parser); + } else if (parser.is_value_type()) { + // value type + ik = new (loader_data, size, THREAD) ValueKlass(parser); } else { // normal ik = new (loader_data, size, THREAD) InstanceKlass(parser, InstanceKlass::_misc_kind_other); @@ -403,9 +408,39 @@ return NULL; } +#ifdef ASSERT + assert(ik->size() == size, ""); + ik->bounds_check((address) ik->start_of_vtable(), false, size); + ik->bounds_check((address) ik->start_of_itable(), false, size); + ik->bounds_check((address) ik->end_of_itable(), true, size); + ik->bounds_check((address) ik->end_of_nonstatic_oop_maps(), true, size); +#endif //ASSERT return ik; } +#ifndef PRODUCT +bool InstanceKlass::bounds_check(address addr, bool edge_ok, intptr_t size_in_bytes) const { + const char* bad = NULL; + address end = NULL; + if (addr < (address)this) { + bad = "before"; + } else if (addr == (address)this) { + if (edge_ok) return true; + bad = "just before"; + } else if (addr == (end = (address)this + sizeof(intptr_t) * (size_in_bytes < 0 ? size() : size_in_bytes))) { + if (edge_ok) return true; + bad = "just after"; + } else if (addr > end) { + bad = "after"; + } else { + return true; + } + tty->print_cr("%s object bounds: " INTPTR_FORMAT " [" INTPTR_FORMAT ".." INTPTR_FORMAT "]", + bad, (intptr_t)addr, (intptr_t)this, (intptr_t)end); + Verbose = WizardMode = true; this->print(); //@@ + return false; +} +#endif //PRODUCT // copy method ordering from resource area to Metaspace void InstanceKlass::copy_method_ordering(const intArray* m, TRAPS) { @@ -436,22 +471,27 @@ _static_field_size(parser.static_field_size()), _nonstatic_oop_map_size(nonstatic_oop_map_size(parser.total_oop_map_count())), _itable_len(parser.itable_size()), - _reference_type(parser.reference_type()) -{ - set_vtable_length(parser.vtable_size()); - set_kind(kind); - set_access_flags(parser.access_flags()); - set_is_unsafe_anonymous(parser.is_unsafe_anonymous()); - set_layout_helper(Klass::instance_layout_helper(parser.layout_size(), + _extra_flags(0), + _reference_type(parser.reference_type()), + _adr_valueklass_fixed_block(NULL) { + set_vtable_length(parser.vtable_size()); + set_kind(kind); + set_access_flags(parser.access_flags()); + set_is_unsafe_anonymous(parser.is_unsafe_anonymous()); + set_layout_helper(Klass::instance_layout_helper(parser.layout_size(), false)); + if (parser.has_flattenable_fields()) { + set_has_value_fields(); + } + _java_fields_count = parser.java_fields_count(); - assert(NULL == _methods, "underlying memory not zeroed?"); - assert(is_instance_klass(), "is layout incorrect?"); - assert(size_helper() == parser.layout_size(), "incorrect size_helper?"); + assert(NULL == _methods, "underlying memory not zeroed?"); + assert(is_instance_klass(), "is layout incorrect?"); + assert(size_helper() == parser.layout_size(), "incorrect size_helper?"); - if (DumpSharedSpaces) { - SystemDictionaryShared::init_dumptime_info(this); - } + if (DumpSharedSpaces) { + SystemDictionaryShared::init_dumptime_info(this); + } } void InstanceKlass::deallocate_methods(ClassLoaderData* loader_data, @@ -789,6 +829,67 @@ interk->link_class_impl(CHECK_false); } + + // If a class declares a method that uses a value class as an argument + // type or return value type, this value class must be loaded during the + // linking of this class because size and properties of the value class + // must be known in order to be able to perform value type optimizations. + // The implementation below is an approximation of this rule, the code + // iterates over all methods of the current class (including overridden + // methods), not only the methods declared by this class. This + // approximation makes the code simpler, and doesn't change the semantic + // because classes declaring methods overridden by the current class are + // linked (and have performed their own pre-loading) before the linking + // of the current class. + // This is also the moment to detect potential mismatch between the + // ValueTypes attribute and the kind of the class effectively loaded. + + + // Note: + // Value class types used for flattenable fields are loaded during + // the loading phase (see ClassFileParser::post_process_parsed_stream()). + // Value class types used as element types for array creation + // are not pre-loaded. Their loading is triggered by either anewarray + // or multianewarray bytecodes. + + // Could it be possible to do the following processing only if the + // class uses value types? + { + ResourceMark rm(THREAD); + for (int i = 0; i < methods()->length(); i++) { + Method* m = methods()->at(i); + for (SignatureStream ss(m->signature()); !ss.is_done(); ss.next()) { + Symbol* sig = ss.as_symbol(THREAD); + if (ss.is_object()) { + Symbol* symb = sig; + if (ss.is_array()) { + int i=0; + while (sig->char_at(i) == '[') i++; + if (i == sig->utf8_length() - 1 ) continue; // primitive array + symb = SymbolTable::lookup(sig->as_C_string() + i + 1, + sig->utf8_length() - 3, CHECK_false); + } + if (ss.type() == T_VALUETYPE) { + oop loader = class_loader(); + oop protection_domain = this->protection_domain(); + Klass* klass = SystemDictionary::resolve_or_fail(symb, + Handle(THREAD, loader), Handle(THREAD, protection_domain), true, + CHECK_false); + if (symb != sig) { + symb->decrement_refcount(); + } + if (klass == NULL) { + THROW_(vmSymbols::java_lang_LinkageError(), false); + } + if (!klass->is_value()) { + THROW_(vmSymbols::java_lang_IncompatibleClassChangeError(), false); + } + } + } + } + } + } + // in case the class is linked in the process of linking its superclasses if (is_linked()) { return true; @@ -858,6 +959,7 @@ // itable().verify(tty, true); } #endif + set_init_state(linked); if (JvmtiExport::should_post_class_prepare()) { Thread *thread = THREAD; @@ -1011,11 +1113,22 @@ } } + // Step 8 + // Initialize classes of flattenable fields + { + for (AllFieldStream fs(this); !fs.done(); fs.next()) { + if (fs.is_flattenable()) { + InstanceKlass* field_klass = InstanceKlass::cast(this->get_value_field_klass(fs.index())); + field_klass->initialize(CHECK); + } + } + } + // Look for aot compiled methods for this klass, including class initializer. AOTLoader::load_for_klass(this, THREAD); - // Step 8 + // Step 9 { DTRACE_CLASSINIT_PROBE_WAIT(clinit, -1, wait); // Timer includes any side effects of class initialization (resolution, @@ -1029,7 +1142,7 @@ call_class_initializer(THREAD); } - // Step 9 + // Step 10 if (!HAS_PENDING_EXCEPTION) { set_initialization_state_and_notify(fully_initialized, CHECK); { @@ -1037,7 +1150,7 @@ } } else { - // Step 10 and 11 + // Step 11 and 12 Handle e(THREAD, PENDING_EXCEPTION); CLEAR_PENDING_EXCEPTION; // JVMTI has already reported the pending exception @@ -1359,7 +1472,7 @@ // Lock-free access requires load_acquire. OopMapCache* oop_map_cache = OrderAccess::load_acquire(&_oop_map_cache); if (oop_map_cache == NULL) { - MutexLocker x(OopMapCacheAlloc_lock); + MutexLockerEx x(OopMapCacheAlloc_lock, Mutex::_no_safepoint_check_flag); // Check if _oop_map_cache was allocated while we were waiting for this lock if ((oop_map_cache = _oop_map_cache) == NULL) { oop_map_cache = new OopMapCache(); @@ -2521,6 +2634,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); } @@ -2543,6 +2664,10 @@ } const char* InstanceKlass::signature_name() const { + return signature_name_of(is_value() ? 'Q' : 'L'); +} + +const char* InstanceKlass::signature_name_of(char c) const { int hash_len = 0; char hash_buf[40]; @@ -2559,9 +2684,9 @@ char* dest = NEW_RESOURCE_ARRAY(char, src_length + hash_len + 3); - // Add L as type indicator + // Add L or Q as type indicator int dest_index = 0; - dest[dest_index++] = 'L'; + dest[dest_index++] = c; // Add the actual class name for (int src_index = 0; src_index < src_length; ) { @@ -3083,20 +3208,55 @@ "allocated", "loaded", "linked", "being_initialized", "fully_initialized", "initialization_error" }; -static void print_vtable(intptr_t* start, int len, outputStream* st) { +static void print_vtable(address self, intptr_t* start, int len, outputStream* st) { + ResourceMark rm; + int* forward_refs = NEW_RESOURCE_ARRAY(int, len); + for (int i = 0; i < len; i++) forward_refs[i] = 0; for (int i = 0; i < len; i++) { intptr_t e = start[i]; st->print("%d : " INTPTR_FORMAT, i, e); + if (forward_refs[i] != 0) { + int from = forward_refs[i]; + int off = (int) start[from]; + st->print(" (offset %d <= [%d])", off, from); + } if (e != 0 && ((Metadata*)e)->is_metaspace_object()) { st->print(" "); ((Metadata*)e)->print_value_on(st); + } else if (self != NULL && e > 0 && e < 0x10000) { + address location = self + e; + int index = (int)((intptr_t*)location - start); + st->print(" (offset %d => [%d])", (int)e, index); + if (index >= 0 && index < len) + forward_refs[index] = i; } st->cr(); } } static void print_vtable(vtableEntry* start, int len, outputStream* st) { - return print_vtable(reinterpret_cast(start), len, st); + return print_vtable(NULL, reinterpret_cast(start), len, st); +} + +template + static void print_array_on(outputStream* st, Array* array) { + if (array == NULL) { st->print_cr("NULL"); return; } + array->print_value_on(st); st->cr(); + if (Verbose || WizardMode) { + for (int i = 0; i < array->length(); i++) { + st->print("%d : ", i); array->at(i)->print_value_on(st); st->cr(); + } + } + } + +static void print_array_on(outputStream* st, Array* array) { + if (array == NULL) { st->print_cr("NULL"); return; } + array->print_value_on(st); st->cr(); + if (Verbose || WizardMode) { + for (int i = 0; i < array->length(); i++) { + st->print("%d : %d", i, array->at(i)); st->cr(); + } + } } void InstanceKlass::print_on(outputStream* st) const { @@ -3106,6 +3266,7 @@ st->print(BULLET"instance size: %d", size_helper()); st->cr(); st->print(BULLET"klass size: %d", size()); st->cr(); st->print(BULLET"access: "); access_flags().print_on(st); st->cr(); + st->print(BULLET"misc flags: 0x%x", _misc_flags); st->cr(); st->print(BULLET"state: "); st->print_cr("%s", state_names[_init_state]); st->print(BULLET"name: "); name()->print_value_on(st); st->cr(); st->print(BULLET"super: "); Metadata::print_value_on_maybe_null(st, super()); st->cr(); @@ -3132,26 +3293,14 @@ } st->print(BULLET"arrays: "); Metadata::print_value_on_maybe_null(st, array_klasses()); st->cr(); - st->print(BULLET"methods: "); methods()->print_value_on(st); st->cr(); - if (Verbose || WizardMode) { - Array* method_array = methods(); - for (int i = 0; i < method_array->length(); i++) { - st->print("%d : ", i); method_array->at(i)->print_value(); st->cr(); - } - } - st->print(BULLET"method ordering: "); method_ordering()->print_value_on(st); st->cr(); - st->print(BULLET"default_methods: "); default_methods()->print_value_on(st); st->cr(); - if (Verbose && default_methods() != NULL) { - Array* method_array = default_methods(); - for (int i = 0; i < method_array->length(); i++) { - st->print("%d : ", i); method_array->at(i)->print_value(); st->cr(); - } - } + st->print(BULLET"methods: "); print_array_on(st, methods()); + st->print(BULLET"method ordering: "); print_array_on(st, method_ordering()); + st->print(BULLET"default_methods: "); print_array_on(st, default_methods()); if (default_vtable_indices() != NULL) { - st->print(BULLET"default vtable indices: "); default_vtable_indices()->print_value_on(st); st->cr(); + st->print(BULLET"default vtable indices: "); print_array_on(st, default_vtable_indices()); } - st->print(BULLET"local interfaces: "); local_interfaces()->print_value_on(st); st->cr(); - st->print(BULLET"trans. interfaces: "); transitive_interfaces()->print_value_on(st); st->cr(); + st->print(BULLET"local interfaces: "); print_array_on(st, local_interfaces()); + st->print(BULLET"trans. interfaces: "); print_array_on(st, transitive_interfaces()); st->print(BULLET"constants: "); constants()->print_value_on(st); st->cr(); if (class_loader_data() != NULL) { st->print(BULLET"class loader data: "); @@ -3204,7 +3353,7 @@ st->print(BULLET"vtable length %d (start addr: " INTPTR_FORMAT ")", vtable_length(), p2i(start_of_vtable())); st->cr(); if (vtable_length() > 0 && (Verbose || WizardMode)) print_vtable(start_of_vtable(), vtable_length(), st); st->print(BULLET"itable length %d (start addr: " INTPTR_FORMAT ")", itable_length(), p2i(start_of_itable())); st->cr(); - if (itable_length() > 0 && (Verbose || WizardMode)) print_vtable(start_of_itable(), itable_length(), st); + if (itable_length() > 0 && (Verbose || WizardMode)) print_vtable(NULL, start_of_itable(), itable_length(), st); st->print_cr(BULLET"---- static fields (%d words):", static_field_size()); FieldPrinter print_static_field(st); ((InstanceKlass*)this)->do_local_static_fields(&print_static_field); @@ -3978,3 +4127,8 @@ return VM_RedefineClasses::get_cached_class_file_bytes(_cached_class_file); } #endif + +#define THROW_DVT_ERROR(s) \ + Exceptions::fthrow(THREAD_AND_LOCATION, vmSymbols::java_lang_IncompatibleClassChangeError(), \ + "ValueCapableClass class '%s' %s", external_name(),(s)); \ + return