--- old/src/hotspot/cpu/x86/macroAssembler_x86.cpp 2018-05-07 15:51:58.104279717 +0200 +++ new/src/hotspot/cpu/x86/macroAssembler_x86.cpp 2018-05-07 15:51:57.836275178 +0200 @@ -3728,6 +3728,20 @@ } } +void MacroAssembler::test_oop_is_not_value(Register oop, Register temp, Label& is_not_value) { + const int mask = Universe::oop_metadata_valuetype_mask(); +#ifdef _LP64 + if (UseCompressedClassPointers) { + movl(temp, Address(oop, oopDesc::klass_offset_in_bytes())); + } else +#endif + movptr(temp, Address(oop, oopDesc::klass_offset_in_bytes())); + + andl(temp, mask); + testl(temp, temp); + jcc(Assembler::zero, is_not_value); +} + void MacroAssembler::os_breakpoint() { // instead of directly emitting a breakpoint, call os:breakpoint for better debugability // (e.g., MSVC can't call ps() otherwise) --- old/src/hotspot/cpu/x86/macroAssembler_x86.hpp 2018-05-07 15:51:58.792291370 +0200 +++ new/src/hotspot/cpu/x86/macroAssembler_x86.hpp 2018-05-07 15:51:58.496286356 +0200 @@ -108,6 +108,8 @@ void test_flat_array_klass(Register klass, Register temp_reg, Label& is_flat_array); void test_flat_array_oop(Register oop, Register temp_reg, Label& is_flat_array); + void test_oop_is_not_value(Register oop, Register temp, Label& is_not_value); + // Required platform-specific helpers for Label::patch_instructions. // They _shadow_ the declarations in AbstractAssembler, which are undefined. void pd_patch_instruction(address branch, address target) { --- old/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp 2018-05-07 15:51:59.408301803 +0200 +++ new/src/hotspot/cpu/x86/templateInterpreterGenerator_x86.cpp 2018-05-07 15:51:59.132297129 +0200 @@ -671,12 +671,12 @@ // get synchronization object { - Label done; + Label check_value_recv, done; __ movl(rax, access_flags); __ testl(rax, JVM_ACC_STATIC); // get receiver (assume this is frequent case) __ movptr(rax, Address(rlocals, Interpreter::local_offset_in_bytes(0))); - __ jcc(Assembler::zero, done); + __ jcc(Assembler::zero, (EnableValhalla) ? check_value_recv : done); __ load_mirror(rax, rbx); #ifdef ASSERT @@ -688,7 +688,14 @@ __ bind(L); } #endif // ASSERT - + __ jmp(done); + if (EnableValhalla) { + __ bind(check_value_recv); + __ test_oop_is_not_value(rax, rbx, done); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, + InterpreterRuntime::throw_illegal_monitor_state_exception)); + __ should_not_reach_here(); + } __ bind(done); } --- old/src/hotspot/cpu/x86/templateTable_x86.cpp 2018-05-07 15:52:00.056312779 +0200 +++ new/src/hotspot/cpu/x86/templateTable_x86.cpp 2018-05-07 15:51:59.760307766 +0200 @@ -4764,6 +4764,17 @@ Register rbot = LP64_ONLY(c_rarg2) NOT_LP64(rbx); Register rmon = LP64_ONLY(c_rarg1) NOT_LP64(rdx); + if (EnableValhalla) { + Label is_not_value; + + __ test_oop_is_not_value(rax, rmon, is_not_value); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, + InterpreterRuntime::throw_illegal_monitor_state_exception)); + __ should_not_reach_here(); + + __ bind(is_not_value); + + } // initialize entry pointer __ xorl(rmon, rmon); // points to free slot or NULL --- old/src/hotspot/share/classfile/classFileParser.cpp 2018-05-07 15:52:00.744324434 +0200 +++ new/src/hotspot/share/classfile/classFileParser.cpp 2018-05-07 15:52:00.412318811 +0200 @@ -4074,6 +4074,7 @@ if (is_value_type() && (!has_nonstatic_fields)) { // There are a number of fixes required throughout the type system and JIT throwValueTypeLimitation(THREAD_AND_LOCATION, "Value Types do not support zero instance size yet"); + return; } // Prepare list of oops for oop map generation. @@ -4531,7 +4532,7 @@ info->has_nonstatic_fields = has_nonstatic_fields; } -void ClassFileParser::set_precomputed_flags(InstanceKlass* ik) { +void ClassFileParser::set_precomputed_flags(InstanceKlass* ik, TRAPS) { assert(ik != NULL, "invariant"); const Klass* const super = ik->super(); @@ -4564,6 +4565,10 @@ // Check if this klass supports the java.lang.Cloneable interface if (SystemDictionary::Cloneable_klass_loaded()) { if (ik->is_subtype_of(SystemDictionary::Cloneable_klass())) { + if (ik->is_value()) { + throwValueTypeLimitation(THREAD_AND_LOCATION, "Value Types do not support Cloneable"); + return; + } ik->set_is_cloneable(); } } @@ -5740,7 +5745,7 @@ } // Fill in has_finalizer, has_vanilla_constructor, and layout_helper - set_precomputed_flags(ik); + set_precomputed_flags(ik, CHECK); // check if this class can access its super class check_super_class_access(ik, CHECK); --- old/src/hotspot/share/classfile/classFileParser.hpp 2018-05-07 15:52:01.416335818 +0200 +++ new/src/hotspot/share/classfile/classFileParser.hpp 2018-05-07 15:52:01.088330262 +0200 @@ -314,7 +314,7 @@ int runtime_invisible_annotations_length, TRAPS); - void set_precomputed_flags(InstanceKlass* k); + void set_precomputed_flags(InstanceKlass* k, TRAPS); // Format checker methods void classfile_parse_error(const char* msg, TRAPS) const; --- old/src/hotspot/share/classfile/systemDictionary.cpp 2018-05-07 15:52:02.000345711 +0200 +++ new/src/hotspot/share/classfile/systemDictionary.cpp 2018-05-07 15:52:01.708340765 +0200 @@ -2297,7 +2297,7 @@ // cleared if revocation occurs too often for this type // NOTE that we must only do this when the class is initally // defined, not each time it is referenced from a new class loader - if (k->class_loader() == class_loader()) { + if (k->class_loader() == class_loader() && (!k->is_value())) { k->set_prototype_header(markOopDesc::biased_locking_prototype()); } } --- old/src/hotspot/share/memory/universe.cpp 2018-05-07 15:52:02.668357028 +0200 +++ new/src/hotspot/share/memory/universe.cpp 2018-05-07 15:52:02.348351607 +0200 @@ -166,6 +166,7 @@ NarrowPtrStruct Universe::_narrow_oop = { NULL, 0, true }; NarrowPtrStruct Universe::_narrow_klass = { NULL, 0, true }; address Universe::_narrow_ptrs_base; +int Universe::_oop_metadata_valuetype_mask = KlassPtrValueTypeMask; void Universe::basic_type_classes_do(void f(Klass*)) { f(boolArrayKlassObj()); --- old/src/hotspot/share/memory/universe.hpp 2018-05-07 15:52:03.272367261 +0200 +++ new/src/hotspot/share/memory/universe.hpp 2018-05-07 15:52:02.988362450 +0200 @@ -195,6 +195,9 @@ static struct NarrowPtrStruct _narrow_klass; static address _narrow_ptrs_base; + // value type using klass alignment encoded as oop metadata + static int _oop_metadata_valuetype_mask; + // array of dummy objects used with +FullGCAlot debug_only(static objArrayOop _fullgc_alot_dummy_array;) // index of next entry to clear @@ -436,8 +439,13 @@ static void set_narrow_klass_shift(int shift) { assert(shift == 0 || shift == LogKlassAlignmentInBytes, "invalid shift for klass ptrs"); _narrow_klass._shift = shift; + if (shift == LogKlassAlignmentInBytes) { + _oop_metadata_valuetype_mask = 1; + } } + static int oop_metadata_valuetype_mask() { return _oop_metadata_valuetype_mask; } + // Reserve Java heap and determine CompressedOops mode static ReservedSpace reserve_heap(size_t heap_size, size_t alignment); --- old/src/hotspot/share/oops/instanceKlass.cpp 2018-05-07 15:52:03.932378444 +0200 +++ new/src/hotspot/share/oops/instanceKlass.cpp 2018-05-07 15:52:03.604372887 +0200 @@ -171,7 +171,7 @@ ik = new (loader_data, size, THREAD) InstanceClassLoaderKlass(parser); } else if (parser.is_value_type()) { // value type - ik = new (loader_data, size, THREAD) ValueKlass(parser); + ik = new (loader_data, size, true, THREAD) ValueKlass(parser); } else { // normal ik = new (loader_data, size, THREAD) InstanceKlass(parser, InstanceKlass::_misc_kind_other); --- old/src/hotspot/share/oops/klass.cpp 2018-05-07 15:52:04.600389762 +0200 +++ new/src/hotspot/share/oops/klass.cpp 2018-05-07 15:52:04.292384544 +0200 @@ -170,8 +170,17 @@ return NULL; } -void* Klass::operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw() { - return Metaspace::allocate(loader_data, word_size, MetaspaceObj::ClassType, THREAD); +void* Klass::operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, bool is_value, TRAPS) throw () { + // Pad size in case need adjust to even/odd klass ptr + uintptr_t addr = (uintptr_t) Metaspace::allocate(loader_data, word_size + (1 << LogKlassAlignment), MetaspaceObj::ClassType, THREAD); + // values are odd, otherwise make even (and vice versa) + if (is_value ^ (((addr & KlassPtrValueTypeMask) >> LogKlassAlignmentInBytes) != 0)) { + addr += (1 << LogKlassAlignmentInBytes); + } + assert(is_aligned(addr, (1 << LogKlassAlignmentInBytes)), "Klass base alignment incorrect"); + assert( is_value || ((addr & KlassPtrValueTypeMask) == 0), "Klass even alignment incorrect"); + assert(!is_value || ((addr & KlassPtrValueTypeMask) != 0), "Klass odd alignment incorrect"); + return (void*) addr; } // "Normal" instantiation is preceeded by a MetaspaceObj allocation --- old/src/hotspot/share/oops/klass.hpp 2018-05-07 15:52:05.276401217 +0200 +++ new/src/hotspot/share/oops/klass.hpp 2018-05-07 15:52:04.948395659 +0200 @@ -162,7 +162,10 @@ // Constructor Klass(); - void* operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw(); + void* operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, bool is_value, TRAPS) throw(); + void* operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw() { + return operator new (size, loader_data, word_size, false, THREAD); + } public: enum DefaultsLookupMode { find_defaults, skip_defaults }; @@ -689,6 +692,9 @@ static Klass* decode_klass_not_null(narrowKlass v); static Klass* decode_klass(narrowKlass v); + + static bool decode_ptr_is_value_type(narrowKlass v); + static bool ptr_is_value_type(Klass* v); }; // Helper to convert the oop iterate macro suffixes into bool values that can be used by template functions. --- old/src/hotspot/share/oops/klass.inline.hpp 2018-05-07 15:52:05.936412401 +0200 +++ new/src/hotspot/share/oops/klass.inline.hpp 2018-05-07 15:52:05.616406978 +0200 @@ -30,7 +30,7 @@ #include "oops/markOop.hpp" inline void Klass::set_prototype_header(markOop header) { - assert(!header->has_bias_pattern() || is_instance_klass(), "biased locking currently only supported for Java instances"); + assert(!header->has_bias_pattern() || (is_instance_klass() && (!is_value())), "biased locking currently only supported for Java instances"); _prototype_header = header; } @@ -71,6 +71,14 @@ return is_null(v) ? (Klass*)NULL : decode_klass_not_null(v); } +inline bool Klass::decode_ptr_is_value_type(narrowKlass v) { + return (v & Universe::oop_metadata_valuetype_mask()) != 0; +} + +inline bool Klass::ptr_is_value_type(Klass* v) { + return ((uintptr_t)v & KlassPtrValueTypeMask) != 0; +} + template bool Klass::is_instanceof_or_null(T element) { if (oopDesc::is_null(element)) { --- old/src/hotspot/share/oops/oop.cpp 2018-05-07 15:52:06.564423043 +0200 +++ new/src/hotspot/share/oops/oop.cpp 2018-05-07 15:52:06.248417689 +0200 @@ -101,7 +101,7 @@ ResetNoHandleMark rnm; // Might be called from LEAF/QUICK ENTRY HandleMark hm(THREAD); Handle object(THREAD, this); - return ObjectSynchronizer::identity_hash_value_for(object); + return ObjectSynchronizer::FastHashCode(THREAD, object()); } // When String table needs to rehash --- old/src/hotspot/share/oops/oop.hpp 2018-05-07 15:52:07.144432873 +0200 +++ new/src/hotspot/share/oops/oop.hpp 2018-05-07 15:52:06.868428195 +0200 @@ -81,6 +81,9 @@ inline Klass** klass_addr(); inline narrowKlass* compressed_klass_addr(); + // oop only test (does not load klass) + inline bool klass_is_value_type(); + inline void set_klass(Klass* k); inline void release_set_klass(Klass* k); --- old/src/hotspot/share/oops/oop.inline.hpp 2018-05-07 15:52:07.736442905 +0200 +++ new/src/hotspot/share/oops/oop.inline.hpp 2018-05-07 15:52:07.480438566 +0200 @@ -96,6 +96,16 @@ return &_metadata._compressed_klass; } +// oop only test (does not load klass) +bool oopDesc::klass_is_value_type() { + if (UseCompressedClassPointers) { + return Klass::decode_ptr_is_value_type(_metadata._compressed_klass); + } else { + return Klass::ptr_is_value_type(_metadata._klass); + } +} + + #define CHECK_SET_KLASS(k) \ do { \ assert(Universe::is_bootstrapping() || k != NULL, "NULL Klass"); \ --- old/src/hotspot/share/oops/valueKlass.hpp 2018-05-07 15:52:08.388453956 +0200 +++ new/src/hotspot/share/oops/valueKlass.hpp 2018-05-07 15:52:08.076448668 +0200 @@ -49,7 +49,7 @@ *((address*)adr_unpack_handler()) = NULL; assert(pack_handler() == NULL, "pack handler not null"); *((int*)adr_default_value_offset()) = 0; - + assert(Klass::ptr_is_value_type(this), "Value type klass ptr encoding"); } ValueKlassFixedBlock* valueklass_static_bloc() const { --- old/src/hotspot/share/prims/jvm.cpp 2018-05-07 15:52:09.016464600 +0200 +++ new/src/hotspot/share/prims/jvm.cpp 2018-05-07 15:52:08.724459650 +0200 @@ -653,6 +653,7 @@ ResourceMark rm(THREAD); THROW_MSG_0(vmSymbols::java_lang_CloneNotSupportedException(), klass->external_name()); } + assert(!obj->klass_is_value_type(), "Disallowed on value type"); // Make shallow object copy const int size = obj->size(); --- old/src/hotspot/share/runtime/biasedLocking.cpp 2018-05-07 15:52:09.684475923 +0200 +++ new/src/hotspot/share/runtime/biasedLocking.cpp 2018-05-07 15:52:09.360470431 +0200 @@ -45,7 +45,9 @@ static GrowableArray* _preserved_mark_stack = NULL; static void enable_biased_locking(InstanceKlass* k) { - k->set_prototype_header(markOopDesc::biased_locking_prototype()); + if (!k->is_value()) { + k->set_prototype_header(markOopDesc::biased_locking_prototype()); + } } class VM_EnableBiasedLocking: public VM_Operation { --- old/src/hotspot/share/runtime/synchronizer.cpp 2018-05-07 15:52:10.292486228 +0200 +++ new/src/hotspot/share/runtime/synchronizer.cpp 2018-05-07 15:52:09.988481075 +0200 @@ -128,6 +128,19 @@ const oop, const ObjectSynchronizer::InflateCause); +#define CHECK_THROW_VALUE_TYPE_IMSE(obj) \ + if ((obj)->klass_is_value_type()) { \ + ResourceMark rm(THREAD); \ + THROW_MSG(vmSymbols::java_lang_IllegalMonitorStateException(), obj->klass()->external_name()); \ + } + +#define CHECK_THROW_VALUE_TYPE_IMSE_0(obj) \ + if ((obj)->klass_is_value_type()) { \ + ResourceMark rm(THREAD); \ + THROW_MSG_0(vmSymbols::java_lang_IllegalMonitorStateException(), obj->klass()->external_name()); \ + } + + #define CHAINMARKER (cast_to_oop(-1)) @@ -159,6 +172,7 @@ assert(((JavaThread *) self)->thread_state() == _thread_in_Java, "invariant"); NoSafepointVerifier nsv; if (obj == NULL) return false; // slow-path for invalid obj + assert(!obj->klass_is_value_type(), "monitor op on value type"); const markOop mark = obj->mark(); if (mark->has_locker() && self->is_lock_owned((address)mark->locker())) { @@ -209,6 +223,7 @@ assert(((JavaThread *) Self)->thread_state() == _thread_in_Java, "invariant"); NoSafepointVerifier nsv; if (obj == NULL) return false; // Need to throw NPE + assert(!obj->klass_is_value_type(), "monitor op on value type"); const markOop mark = obj->mark(); if (mark->has_monitor()) { @@ -264,6 +279,7 @@ void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) { + assert(!obj->klass_is_value_type(), "monitor op on value type"); if (UseBiasedLocking) { if (!SafepointSynchronize::is_at_safepoint()) { BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD); @@ -282,6 +298,7 @@ void ObjectSynchronizer::fast_exit(oop object, BasicLock* lock, TRAPS) { markOop mark = object->mark(); + assert(!object->klass_is_value_type(), "monitor op on value type"); // We cannot check for Biased Locking if we are racing an inflation. assert(mark == markOopDesc::INFLATING() || !mark->has_bias_pattern(), "should not see bias pattern here"); @@ -338,6 +355,7 @@ // We don't need to use fast path here, because it must have been // failed in the interpreter/compiler code. void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) { + CHECK_THROW_VALUE_TYPE_IMSE(obj); markOop mark = obj->mark(); assert(!mark->has_bias_pattern(), "should not see bias pattern here"); @@ -390,6 +408,7 @@ // NOTE: must use heavy weight monitor to handle complete_exit/reenter() intptr_t ObjectSynchronizer::complete_exit(Handle obj, TRAPS) { TEVENT(complete_exit); + assert(!obj->klass_is_value_type(), "monitor op on value type"); if (UseBiasedLocking) { BiasedLocking::revoke_and_rebias(obj, false, THREAD); assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); @@ -405,6 +424,7 @@ // NOTE: must use heavy weight monitor to handle complete_exit/reenter() void ObjectSynchronizer::reenter(Handle obj, intptr_t recursion, TRAPS) { TEVENT(reenter); + assert(!obj->klass_is_value_type(), "monitor op on value type"); if (UseBiasedLocking) { BiasedLocking::revoke_and_rebias(obj, false, THREAD); assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); @@ -422,6 +442,7 @@ void ObjectSynchronizer::jni_enter(Handle obj, TRAPS) { // the current locking is from JNI instead of Java code TEVENT(jni_enter); + CHECK_THROW_VALUE_TYPE_IMSE(obj); if (UseBiasedLocking) { BiasedLocking::revoke_and_rebias(obj, false, THREAD); assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); @@ -434,6 +455,7 @@ // NOTE: must use heavy weight monitor to handle jni monitor exit void ObjectSynchronizer::jni_exit(oop obj, Thread* THREAD) { TEVENT(jni_exit); + CHECK_THROW_VALUE_TYPE_IMSE(obj); if (UseBiasedLocking) { Handle h_obj(THREAD, obj); BiasedLocking::revoke_and_rebias(h_obj, false, THREAD); @@ -478,6 +500,7 @@ // Wait/Notify/NotifyAll // NOTE: must use heavy weight monitor to handle wait() int ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) { + CHECK_THROW_VALUE_TYPE_IMSE_0(obj); if (UseBiasedLocking) { BiasedLocking::revoke_and_rebias(obj, false, THREAD); assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); @@ -501,6 +524,7 @@ } void ObjectSynchronizer::waitUninterruptibly(Handle obj, jlong millis, TRAPS) { + CHECK_THROW_VALUE_TYPE_IMSE(obj); if (UseBiasedLocking) { BiasedLocking::revoke_and_rebias(obj, false, THREAD); assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); @@ -515,6 +539,7 @@ } void ObjectSynchronizer::notify(Handle obj, TRAPS) { + CHECK_THROW_VALUE_TYPE_IMSE(obj); if (UseBiasedLocking) { BiasedLocking::revoke_and_rebias(obj, false, THREAD); assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); @@ -531,6 +556,7 @@ // NOTE: see comment of notify() void ObjectSynchronizer::notifyall(Handle obj, TRAPS) { + CHECK_THROW_VALUE_TYPE_IMSE(obj); if (UseBiasedLocking) { BiasedLocking::revoke_and_rebias(obj, false, THREAD); assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); @@ -709,6 +735,14 @@ } intptr_t ObjectSynchronizer::FastHashCode(Thread * Self, oop obj) { + if (EnableValhalla && obj->klass_is_value_type()) { + // Expected tooling to override hashCode for value type, just don't crash + if (log_is_enabled(Debug, monitorinflation)) { + ResourceMark rm; + log_debug(monitorinflation)("FastHashCode for value type: %s", obj->klass()->external_name()); + } + return 0; + } if (UseBiasedLocking) { // NOTE: many places throughout the JVM do not expect a safepoint // to be taken here, in particular most operations on perm gen @@ -813,12 +847,6 @@ return hash; } -// Deprecated -- use FastHashCode() instead. - -intptr_t ObjectSynchronizer::identity_hash_value_for(Handle obj) { - return FastHashCode(Thread::current(), obj()); -} - bool ObjectSynchronizer::current_thread_holds_lock(JavaThread* thread, Handle h_obj) { @@ -1383,6 +1411,10 @@ assert(Universe::verify_in_progress() || !SafepointSynchronize::is_at_safepoint(), "invariant"); + if (EnableValhalla) { + guarantee(!object->klass_is_value_type(), "Attempt to inflate value type"); + } + EventJavaMonitorInflate event; for (;;) { --- old/src/hotspot/share/runtime/synchronizer.hpp 2018-05-07 15:52:10.908496670 +0200 +++ new/src/hotspot/share/runtime/synchronizer.hpp 2018-05-07 15:52:10.652492330 +0200 @@ -119,7 +119,6 @@ // Returns the identity hash value for an oop // NOTE: It may cause monitor inflation - static intptr_t identity_hash_value_for(Handle obj); static intptr_t FastHashCode(Thread * Self, oop obj); // java.lang.Thread support --- old/src/hotspot/share/utilities/globalDefinitions.hpp 2018-05-07 15:52:11.548507519 +0200 +++ new/src/hotspot/share/utilities/globalDefinitions.hpp 2018-05-07 15:52:11.236502230 +0200 @@ -451,6 +451,7 @@ const int LogKlassAlignment = LogKlassAlignmentInBytes - LogHeapWordSize; const int KlassAlignmentInBytes = 1 << LogKlassAlignmentInBytes; const int KlassAlignment = KlassAlignmentInBytes / HeapWordSize; +const int KlassPtrValueTypeMask = (1 << (LogKlassAlignmentInBytes + 1)) - 1; // Maximal size of heap where unscaled compression can be used. Also upper bound // for heap placement: 4GB. --- old/test/hotspot/jtreg/runtime/valhalla/valuetypes/classfileparser/BadValueTypes.java 2018-05-07 15:52:12.188518368 +0200 +++ new/test/hotspot/jtreg/runtime/valhalla/valuetypes/classfileparser/BadValueTypes.java 2018-05-07 15:52:11.884513216 +0200 @@ -88,5 +88,7 @@ throw new RuntimeException( "Wrong ClassCircularityError: " + e.getMessage()); } } + + runTest("ValueCloneable", "Value Types do not support Cloneable"); } } --- old/test/hotspot/jtreg/runtime/valhalla/valuetypes/classfileparser/cfpTests.jcod 2018-05-07 15:52:12.776528337 +0200 +++ new/test/hotspot/jtreg/runtime/valhalla/valuetypes/classfileparser/cfpTests.jcod 2018-05-07 15:52:12.508523794 +0200 @@ -1395,3 +1395,89 @@ } // end SourceFile } // Attributes } // end class CircStaticB + + +// Test that a value type cannot be Cloneable. +// +// final __ByValue class ValueCloneable implements Cloneable { +// final int field; +// private ValueCloneable() { field = 0; } +//} + +class ValueCloneable { + 0xCAFEBABE; + 0; // minor version + 55; // version + [] { // Constant Pool + ; // first element is empty + Method #4 #14; // #1 + Field #3 #15; // #2 + class #16; // #3 + class #17; // #4 + class #18; // #5 + Utf8 "field"; // #6 + Utf8 "I"; // #7 + Utf8 ""; // #8 + Utf8 "()V"; // #9 + Utf8 "Code"; // #10 + Utf8 "LineNumberTable"; // #11 + Utf8 "SourceFile"; // #12 + Utf8 "ValueCloneable.java"; // #13 + NameAndType #8 #9; // #14 + NameAndType #6 #7; // #15 + Utf8 "ValueCloneable"; // #16 + Utf8 "java/lang/Object"; // #17 + Utf8 "java/lang/Cloneable"; // #18 + } // Constant Pool + + 0x0130; // access + #3;// this_cpx + #4;// super_cpx + + [] { // Interfaces + #5; + } // Interfaces + + [] { // fields + { // Member + 0x0010; // access + #6; // name_cpx + #7; // sig_cpx + [] { // Attributes + } // Attributes + } // Member + } // fields + + [] { // methods + { // Member + 0x0002; // access + #8; // name_cpx + #9; // sig_cpx + [] { // Attributes + Attr(#10) { // Code + 2; // max_stack + 1; // max_locals + Bytes[]{ + 0x2AB700012A03B500; + 0x02B1; + } + [] { // Traps + } // end Traps + [] { // Attributes + Attr(#11) { // LineNumberTable + [] { // LineNumberTable + 0 4; + } + } // end LineNumberTable + } // Attributes + } // end Code + } // Attributes + } // Member + } // methods + + [] { // Attributes + Attr(#12) { // SourceFile + #13; + } // end SourceFile + } // Attributes +} // end class ValueCloneable --- /dev/null 2018-04-25 09:38:36.139592716 +0200 +++ new/test/hotspot/jtreg/runtime/valhalla/valuetypes/ObjectMethods.java 2018-05-07 15:52:13.116534101 +0200 @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * 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. + */ +package runtime.valhalla.valuetypes; + +import java.lang.invoke.*; + +import jdk.experimental.value.MethodHandleBuilder; + +/* + * @test ObjectMethods + * @summary Check object method implemented by the VM behave with value types + * @modules java.base/jdk.experimental.bytecode + * java.base/jdk.experimental.value + * @library /test/lib + * @compile -XDenableValueTypes ObjectMethods.java + * @run main/othervm -Xint -XX:+EnableValhalla -XX:+UseCompressedClassPointers runtime.valhalla.valuetypes.ObjectMethods + * @run main/othervm -Xint -XX:+EnableValhalla -XX:-UseCompressedClassPointers runtime.valhalla.valuetypes.ObjectMethods + * @run main/othervm -Xint -XX:+EnableValhalla -noverify runtime.valhalla.valuetypes.ObjectMethods noverify + */ + +public class ObjectMethods { + + public static void main(String[] args) { + testObjectMethods((args.length > 0 && args[0].equals("noverify"))); + } + + public static void testObjectMethods(boolean verifierDisabled) { + MyInt val = MyInt.create(7); + MyInt sameVal = MyInt.create(7); + + // Exercise all the Object native/VM methods... + + if (verifierDisabled) { // Just noverifier... + checkMonitorExit(val); + return; + } + + // getClass() + checkGetClass(val, MyInt.class); + + //hashCode()/identityHashCode() + checkHashCodes(val, sameVal.hashCode()); + + // clone() + checkNotCloneable(val); + + // synchronized + checkSynchronized(val); + + // wait/notify() + checkWait(val); + checkNotify(val); + } + + + static void checkGetClass(Object val, Class expectedClass) { + Class clazz = val.getClass(); + if (clazz == null) { + throw new RuntimeException("getClass return null"); + } else if (clazz != expectedClass) { + throw new RuntimeException("getClass (" + clazz + ") doesn't match " + expectedClass); + } + } + + // Just check we don't crash the VM + static void checkHashCodes(Object val, int expectedHashCode) { + if (val.hashCode() != expectedHashCode) { + throw new RuntimeException("Hash code mismatch value: " + val.hashCode() + + " expected: " + expectedHashCode); + } + } + + static void checkNotCloneable(MyInt val) { + boolean sawCnse = false; + try { + val.attemptClone(); + } catch (CloneNotSupportedException cnse) { + sawCnse = true; + } + if (!sawCnse) { + throw new RuntimeException("clone() did not fail"); + } + // Cloneable value type checked by "BadValueTypes" CFP tests + } + + static void checkSynchronized(Object val) { + boolean sawImse = false; + try { + synchronized (val) { + throw new IllegalStateException("Unreachable code, reached"); + } + } catch (IllegalMonitorStateException imse) { + sawImse = true; + } + if (!sawImse) { + throw new RuntimeException("monitorenter did not fail"); + } + // synchronized method modifiers tested by "BadValueTypes" CFP tests + // jni monitor ops tested by "ValueWithJni" + } + + // Check we haven't broken the mismatched monitor block check... + static void checkMonitorExit(Object val) { + boolean sawImse = false; + try { + MethodHandleBuilder.loadCode(MethodHandles.lookup(), + "mismatchedMonitorExit", + MethodType.methodType(Void.TYPE, Object.class), + CODE->{ + CODE + .aload(0) + .monitorexit() + .return_(); + }).invokeExact(val); + throw new IllegalStateException("Unreachable code, reached"); + } catch (Throwable t) { + if (t instanceof IllegalMonitorStateException) { + sawImse = true; + } + else { + throw new RuntimeException(t); + } + } + if (!sawImse) { + throw new RuntimeException("monitorexit did not fail"); + } + } + + static void checkWait(Object val) { + boolean sawImse = false; + try { + val.wait(); + } catch (IllegalMonitorStateException imse) { + sawImse = true; + } catch (InterruptedException intExc) { + throw new RuntimeException(intExc); + } + if (!sawImse) { + throw new RuntimeException("wait() did not fail"); + } + + sawImse = false; + try { + val.wait(1l); + } catch (IllegalMonitorStateException imse) { + sawImse = true; + } catch (InterruptedException intExc) { + throw new RuntimeException(intExc); + } + if (!sawImse) { + throw new RuntimeException("wait() did not fail"); + } + + sawImse = false; + try { + val.wait(0l, 100); + } catch (IllegalMonitorStateException imse) { + sawImse = true; + } catch (InterruptedException intExc) { + throw new RuntimeException(intExc); + } + if (!sawImse) { + throw new RuntimeException("wait() did not fail"); + } + } + + static void checkNotify(Object val) { + boolean sawImse = false; + try { + val.notify(); + } catch (IllegalMonitorStateException imse) { + sawImse = true; + } + if (!sawImse) { + throw new RuntimeException("notify() did not fail"); + } + + sawImse = false; + try { + val.notifyAll(); + } catch (IllegalMonitorStateException imse) { + sawImse = true; + } + if (!sawImse) { + throw new RuntimeException("notifyAll() did not fail"); + } + } + + static final __ByValue class MyInt { + final int value; + private MyInt() { value = 0; } + public static MyInt create(int v) { + MyInt mi = __MakeDefault MyInt(); + mi = __WithField(mi.value, v); + return mi; + } + public Object attemptClone() throws CloneNotSupportedException { + try { // Check it is not possible to clone... + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle mh = lookup.findVirtual(getClass(), + "clone", + MethodType.methodType(Object.class)); + return mh.invokeExact(this); + } catch (Throwable t) { + if (t instanceof CloneNotSupportedException) { + throw (CloneNotSupportedException) t; + } + throw new RuntimeException(t); + } + } + } + +} --- /dev/null 2018-04-25 09:38:36.139592716 +0200 +++ new/test/hotspot/jtreg/runtime/valhalla/valuetypes/ValueWithJni.java 2018-05-07 15:52:13.748544817 +0200 @@ -0,0 +1,79 @@ + +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * 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. + */ +package runtime.valhalla.valuetypes; + +/* @test + * @summary test JNI functions with values + * @run main/othervm/native -Xint -XX:+EnableValhalla runtime.valhalla.valuetypes.ValueWithJni + */ +public __ByValue final class ValueWithJni { + + static { + System.loadLibrary("ValueWithJni"); + } + + public static void main(String[] args) { + testJniMonitorOps(); + } + + final int x; + private ValueWithJni() { x = 0; } + + public native void doJniMonitorEnter(); + public native void doJniMonitorExit(); + + public static ValueWithJni createValueWithJni(int x) { + ValueWithJni v = __MakeDefault ValueWithJni(); + v = __WithField(v.x, x); + return v; + } + + public static void testJniMonitorOps() { + boolean sawImse = false; + try { + createValueWithJni(0).doJniMonitorEnter(); + } catch (Throwable t) { + sawImse = checkImse(t); + } + if (!sawImse) { + throw new RuntimeException("JNI MonitorEnter did not fail"); + } + sawImse = false; + try { + createValueWithJni(0).doJniMonitorExit(); + } catch (Throwable t) { + sawImse = checkImse(t); + } + if (!sawImse) { + throw new RuntimeException("JNI MonitorExit did not fail"); + } + } + + static boolean checkImse(Throwable t) { + if (t instanceof IllegalMonitorStateException) { + return true; + } + throw new RuntimeException(t); + } +} --- /dev/null 2018-04-25 09:38:36.139592716 +0200 +++ new/test/hotspot/jtreg/runtime/valhalla/valuetypes/libValueWithJni.c 2018-05-07 15:52:14.368555329 +0200 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * 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 + +JNIEXPORT void JNICALL +Java_runtime_valhalla_valuetypes_ValueWithJni_doJniMonitorEnter(JNIEnv *env, jobject obj) { + (*env)->MonitorEnter(env, obj); +} + +JNIEXPORT void JNICALL +Java_runtime_valhalla_valuetypes_ValueWithJni_doJniMonitorExit(JNIEnv *env, jobject obj) { + (*env)->MonitorExit(env, obj); +}