From 5d3c63cac688aaf841ced93588e400736f838521 Mon Sep 17 00:00:00 2001 From: Martin Buchholz Date: Tue, 1 Jul 2014 13:29:24 -0700 Subject: [PATCH 001/118] 8043780: Use open(O_CLOEXEC) instead of fcntl(FD_CLOEXEC) Use open(O_CLOEXEC) where available; fall back to FD_CLOEXEC when necessary Reviewed-by: rasbold, dholmes --- hotspot/src/os/linux/vm/os_linux.cpp | 64 +++++++++++++++++----------- 1 file changed, 39 insertions(+), 25 deletions(-) diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp index 3e5dc4066d5..186d5a811a6 100644 --- a/hotspot/src/os/linux/vm/os_linux.cpp +++ b/hotspot/src/os/linux/vm/os_linux.cpp @@ -5103,9 +5103,38 @@ int os::open(const char *path, int oflag, int mode) { errno = ENAMETOOLONG; return -1; } - int fd; - fd = ::open64(path, oflag, mode); + // All file descriptors that are opened in the Java process and not + // specifically destined for a subprocess should have the close-on-exec + // flag set. If we don't set it, then careless 3rd party native code + // might fork and exec without closing all appropriate file descriptors + // (e.g. as we do in closeDescriptors in UNIXProcess.c), and this in + // turn might: + // + // - cause end-of-file to fail to be detected on some file + // descriptors, resulting in mysterious hangs, or + // + // - might cause an fopen in the subprocess to fail on a system + // suffering from bug 1085341. + // + // (Yes, the default setting of the close-on-exec flag is a Unix + // design flaw) + // + // See: + // 1085341: 32-bit stdio routines should support file descriptors >255 + // 4843136: (process) pipe file descriptor from Runtime.exec not being closed + // 6339493: (process) Runtime.exec does not close all file descriptors on Solaris 9 + // + // Modern Linux kernels (after 2.6.23 2007) support O_CLOEXEC with open(). + // O_CLOEXEC is preferable to using FD_CLOEXEC on an open file descriptor + // because it saves a system call and removes a small window where the flag + // is unset. On ancient Linux kernels the O_CLOEXEC flag will be ignored + // and we fall back to using FD_CLOEXEC (see below). +#ifdef O_CLOEXEC + oflag |= O_CLOEXEC; +#endif + + int fd = ::open64(path, oflag, mode); if (fd == -1) return -1; //If the open succeeded, the file might still be a directory @@ -5126,32 +5155,17 @@ int os::open(const char *path, int oflag, int mode) { } } - // All file descriptors that are opened in the JVM and not - // specifically destined for a subprocess should have the - // close-on-exec flag set. If we don't set it, then careless 3rd - // party native code might fork and exec without closing all - // appropriate file descriptors (e.g. as we do in closeDescriptors in - // UNIXProcess.c), and this in turn might: - // - // - cause end-of-file to fail to be detected on some file - // descriptors, resulting in mysterious hangs, or - // - // - might cause an fopen in the subprocess to fail on a system - // suffering from bug 1085341. - // - // (Yes, the default setting of the close-on-exec flag is a Unix - // design flaw) - // - // See: - // 1085341: 32-bit stdio routines should support file descriptors >255 - // 4843136: (process) pipe file descriptor from Runtime.exec not being closed - // 6339493: (process) Runtime.exec does not close all file descriptors on Solaris 9 - // #ifdef FD_CLOEXEC - { + // Validate that the use of the O_CLOEXEC flag on open above worked. + // With recent kernels, we will perform this check exactly once. + static sig_atomic_t O_CLOEXEC_is_known_to_work = 0; + if (!O_CLOEXEC_is_known_to_work) { int flags = ::fcntl(fd, F_GETFD); if (flags != -1) { - ::fcntl(fd, F_SETFD, flags | FD_CLOEXEC); + if ((flags & FD_CLOEXEC) != 0) + O_CLOEXEC_is_known_to_work = 1; + else + ::fcntl(fd, F_SETFD, flags | FD_CLOEXEC); } } #endif From a87b3d13df46de7b812e13deb2f01723f1007b1b Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Thu, 16 Oct 2014 12:57:04 +0200 Subject: [PATCH 002/118] 8059846: InstanceKlass should use MutexLockerEx to acquire OsrList_lock Replace explicit locking of OsrList_lock by a MutexLockerEx instantiation. Reviewed-by: kvn, anoll, drchase, dholmes, dlong, coleenp --- hotspot/src/share/vm/oops/instanceKlass.cpp | 32 +++++++++------------ 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp index 8ea08cd54c7..38c2c8727d6 100644 --- a/hotspot/src/share/vm/oops/instanceKlass.cpp +++ b/hotspot/src/share/vm/oops/instanceKlass.cpp @@ -2779,19 +2779,18 @@ void InstanceKlass::adjust_default_methods(Method** old_methods, Method** new_me // On-stack replacement stuff void InstanceKlass::add_osr_nmethod(nmethod* n) { // only one compilation can be active - NEEDS_CLEANUP - // This is a short non-blocking critical region, so the no safepoint check is ok. - OsrList_lock->lock_without_safepoint_check(); - assert(n->is_osr_method(), "wrong kind of nmethod"); - n->set_osr_link(osr_nmethods_head()); - set_osr_nmethods_head(n); - // Raise the highest osr level if necessary - if (TieredCompilation) { - Method* m = n->method(); - m->set_highest_osr_comp_level(MAX2(m->highest_osr_comp_level(), n->comp_level())); + { + // This is a short non-blocking critical region, so the no safepoint check is ok. + MutexLockerEx ml(OsrList_lock, Mutex::_no_safepoint_check_flag); + assert(n->is_osr_method(), "wrong kind of nmethod"); + n->set_osr_link(osr_nmethods_head()); + set_osr_nmethods_head(n); + // Raise the highest osr level if necessary + if (TieredCompilation) { + Method* m = n->method(); + m->set_highest_osr_comp_level(MAX2(m->highest_osr_comp_level(), n->comp_level())); + } } - // Remember to unlock again - OsrList_lock->unlock(); // Get rid of the osr methods for the same bci that have lower levels. if (TieredCompilation) { @@ -2807,7 +2806,7 @@ void InstanceKlass::add_osr_nmethod(nmethod* n) { void InstanceKlass::remove_osr_nmethod(nmethod* n) { // This is a short non-blocking critical region, so the no safepoint check is ok. - OsrList_lock->lock_without_safepoint_check(); + MutexLockerEx ml(OsrList_lock, Mutex::_no_safepoint_check_flag); assert(n->is_osr_method(), "wrong kind of nmethod"); nmethod* last = NULL; nmethod* cur = osr_nmethods_head(); @@ -2844,13 +2843,11 @@ void InstanceKlass::remove_osr_nmethod(nmethod* n) { } m->set_highest_osr_comp_level(max_level); } - // Remember to unlock again - OsrList_lock->unlock(); } nmethod* InstanceKlass::lookup_osr_nmethod(const Method* m, int bci, int comp_level, bool match_level) const { // This is a short non-blocking critical region, so the no safepoint check is ok. - OsrList_lock->lock_without_safepoint_check(); + MutexLockerEx ml(OsrList_lock, Mutex::_no_safepoint_check_flag); nmethod* osr = osr_nmethods_head(); nmethod* best = NULL; while (osr != NULL) { @@ -2866,14 +2863,12 @@ nmethod* InstanceKlass::lookup_osr_nmethod(const Method* m, int bci, int comp_le if (match_level) { if (osr->comp_level() == comp_level) { // Found a match - return it. - OsrList_lock->unlock(); return osr; } } else { if (best == NULL || (osr->comp_level() > best->comp_level())) { if (osr->comp_level() == CompLevel_highest_tier) { // Found the best possible - return it. - OsrList_lock->unlock(); return osr; } best = osr; @@ -2882,7 +2877,6 @@ nmethod* InstanceKlass::lookup_osr_nmethod(const Method* m, int bci, int comp_le } osr = osr->osr_link(); } - OsrList_lock->unlock(); if (best != NULL && best->comp_level() >= comp_level && match_level == false) { return best; } From bdd3c3df3a7ab4c1683392771fadb307d74d97ba Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Fri, 17 Oct 2014 08:56:07 +0200 Subject: [PATCH 003/118] 8060196: 'CodeHeap is full' warning suggests to increase wrong code heap size Fixed 'CodeHeap is full' warning to output the right CodeHeapSize flag. Reviewed-by: kvn, anoll --- hotspot/src/share/vm/code/codeCache.cpp | 21 ++++++++++++++++++--- hotspot/src/share/vm/code/codeCache.hpp | 2 ++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/hotspot/src/share/vm/code/codeCache.cpp b/hotspot/src/share/vm/code/codeCache.cpp index b21604f8549..2b8845a6d65 100644 --- a/hotspot/src/share/vm/code/codeCache.cpp +++ b/hotspot/src/share/vm/code/codeCache.cpp @@ -267,6 +267,22 @@ bool CodeCache::heap_available(int code_blob_type) { } } +const char* CodeCache::get_code_heap_flag_name(int code_blob_type) { + switch(code_blob_type) { + case CodeBlobType::NonNMethod: + return "NonNMethodCodeHeapSize"; + break; + case CodeBlobType::MethodNonProfiled: + return "NonProfiledCodeHeapSize"; + break; + case CodeBlobType::MethodProfiled: + return "ProfiledCodeHeapSize"; + break; + } + ShouldNotReachHere(); + return NULL; +} + void CodeCache::add_heap(ReservedSpace rs, const char* name, size_t size_initial, int code_blob_type) { // Check if heap is needed if (!heap_available(code_blob_type)) { @@ -1011,9 +1027,8 @@ void CodeCache::report_codemem_full(int code_blob_type, bool print) { // Not yet reported for this heap, report heap->report_full(); if (SegmentedCodeCache) { - warning("%s is full. Compiler has been disabled.", CodeCache::get_code_heap_name(code_blob_type)); - warning("Try increasing the code heap size using -XX:%s=", - (code_blob_type == CodeBlobType::MethodNonProfiled) ? "NonProfiledCodeHeapSize" : "ProfiledCodeHeapSize"); + warning("%s is full. Compiler has been disabled.", get_code_heap_name(code_blob_type)); + warning("Try increasing the code heap size using -XX:%s=", get_code_heap_flag_name(code_blob_type)); } else { warning("CodeCache is full. Compiler has been disabled."); warning("Try increasing the code cache size using -XX:ReservedCodeCacheSize="); diff --git a/hotspot/src/share/vm/code/codeCache.hpp b/hotspot/src/share/vm/code/codeCache.hpp index 87fc1e8e839..b8b119c3b86 100644 --- a/hotspot/src/share/vm/code/codeCache.hpp +++ b/hotspot/src/share/vm/code/codeCache.hpp @@ -100,6 +100,8 @@ class CodeCache : AllStatic { static void add_heap(ReservedSpace rs, const char* name, size_t size_initial, int code_blob_type); static CodeHeap* get_code_heap(CodeBlob* cb); // Returns the CodeHeap for the given CodeBlob static CodeHeap* get_code_heap(int code_blob_type); // Returns the CodeHeap for the given CodeBlobType + // Returns the name of the VM option to set the size of the corresponding CodeHeap + static const char* get_code_heap_flag_name(int code_blob_type); static bool heap_available(int code_blob_type); // Returns true if an own CodeHeap for the given CodeBlobType is available static ReservedCodeSpace reserve_heap_memory(size_t size); // Reserves one continuous chunk of memory for the CodeHeaps From 415f2cd0750f703d9533e821b1a0cebf51709511 Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Fri, 17 Oct 2014 10:04:45 +0200 Subject: [PATCH 004/118] 8060454: [TESTBUG] Whitebox tests fail with -XX:CompileThreshold=100 Move the call to 'waitAndDeoptimize' from the warmup methods to the osr triggering methods to make sure that no non-osr compilation is in the queue after warmup. Reviewed-by: kvn --- .../whitebox/CompilerWhiteBoxTest.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/hotspot/test/compiler/whitebox/CompilerWhiteBoxTest.java b/hotspot/test/compiler/whitebox/CompilerWhiteBoxTest.java index f1a23314703..a39a079e355 100644 --- a/hotspot/test/compiler/whitebox/CompilerWhiteBoxTest.java +++ b/hotspot/test/compiler/whitebox/CompilerWhiteBoxTest.java @@ -530,7 +530,7 @@ enum SimpleTestCase implements CompilerWhiteBoxTest.TestCase { * @param e Executable * @throws Exception */ - private static void waitAndDeoptimize(Executable e) throws Exception { + private static void waitAndDeoptimize(Executable e) { CompilerWhiteBoxTest.waitBackgroundCompilation(e); if (WhiteBox.getWhiteBox().isMethodQueuedForCompilation(e)) { throw new RuntimeException(e + " must not be in queue"); @@ -554,8 +554,6 @@ enum SimpleTestCase implements CompilerWhiteBoxTest.TestCase { for (long i = 0; i < CompilerWhiteBoxTest.OSR_WARMUP; ++i) { result += (int)m.invoke(helper, 1); } - // Deoptimize non-osr versions - waitAndDeoptimize(m); return result; } @@ -573,8 +571,6 @@ enum SimpleTestCase implements CompilerWhiteBoxTest.TestCase { for (long i = 0; i < CompilerWhiteBoxTest.OSR_WARMUP; ++i) { result += c.newInstance(null, 1).hashCode(); } - // Deoptimize non-osr versions - waitAndDeoptimize(c); return result; } @@ -623,6 +619,11 @@ enum SimpleTestCase implements CompilerWhiteBoxTest.TestCase { } private static int osrStaticMethod(long limit) { + if (limit != 1) { + // Make sure there is no compiled version after warmup + waitAndDeoptimize(OSR_STATIC); + } + // Trigger osr compilation int result = 0; for (long i = 0; i < limit; ++i) { result += staticMethod(); @@ -631,6 +632,11 @@ enum SimpleTestCase implements CompilerWhiteBoxTest.TestCase { } private int osrMethod(long limit) { + if (limit != 1) { + // Make sure there is no compiled version after warmup + waitAndDeoptimize(OSR_METHOD); + } + // Trigger osr compilation int result = 0; for (long i = 0; i < limit; ++i) { result += method(); @@ -647,6 +653,11 @@ enum SimpleTestCase implements CompilerWhiteBoxTest.TestCase { // for OSR constructor test case private Helper(Object o, long limit) { + if (limit != 1) { + // Make sure there is no compiled version after warmup + waitAndDeoptimize(OSR_CONSTRUCTOR); + } + // Trigger osr compilation int result = 0; for (long i = 0; i < limit; ++i) { result += method(); From 44d1787a26bcaab1a85d7a4c54f4dd6c341e72de Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Fri, 17 Oct 2014 15:35:25 -0700 Subject: [PATCH 005/118] 8059139: It should be possible to explicitly disable usage of TZCNT instr w/ -XX:-UseBMI1Instructions Reviewed-by: iveresov --- hotspot/src/cpu/x86/vm/vm_version_x86.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp index e73f93c705f..8d575c2d4bd 100644 --- a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp +++ b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp @@ -865,14 +865,19 @@ void VM_Version::get_processor_features() { if (supports_bmi1()) { // tzcnt does not require VEX prefix if (FLAG_IS_DEFAULT(UseCountTrailingZerosInstruction)) { - UseCountTrailingZerosInstruction = true; + if (!UseBMI1Instructions && !FLAG_IS_DEFAULT(UseBMI1Instructions)) { + // Don't use tzcnt if BMI1 is switched off on command line. + UseCountTrailingZerosInstruction = false; + } else { + UseCountTrailingZerosInstruction = true; + } } } else if (UseCountTrailingZerosInstruction) { warning("tzcnt instruction is not available on this CPU"); FLAG_SET_DEFAULT(UseCountTrailingZerosInstruction, false); } - // BMI instructions use an encoding with VEX prefix. + // BMI instructions (except tzcnt) use an encoding with VEX prefix. // VEX prefix is generated only when AVX > 0. if (supports_bmi1() && supports_avx()) { if (FLAG_IS_DEFAULT(UseBMI1Instructions)) { From da37592acbe89eac39ebb5af1e25f2a80bee629c Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Tue, 21 Oct 2014 12:37:48 -0700 Subject: [PATCH 006/118] 8061563: Typo in test/compiler/exceptions/CatchInlineExceptions.java Fix typo Reviewed-by: iveresov --- hotspot/test/compiler/exceptions/CatchInlineExceptions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hotspot/test/compiler/exceptions/CatchInlineExceptions.java b/hotspot/test/compiler/exceptions/CatchInlineExceptions.java index 01be927cf7b..64e986f55c0 100644 --- a/hotspot/test/compiler/exceptions/CatchInlineExceptions.java +++ b/hotspot/test/compiler/exceptions/CatchInlineExceptions.java @@ -70,7 +70,7 @@ public class CatchInlineExceptions { if (counter1 != 0) { throw new RuntimeException("Failed: counter1(" + counter1 + ") != 0"); } - if (counter2 != counter) { + if (counter2 != counter0) { throw new RuntimeException("Failed: counter2(" + counter2 + ") != counter0(" + counter0 + ")"); } if (counter2 != counter) { From 7b42cc8306d1b52212341cbe06b02e5a15c783bf Mon Sep 17 00:00:00 2001 From: Igor Veresov Date: Thu, 23 Oct 2014 09:41:59 -1000 Subject: [PATCH 007/118] 8047383: SIGBUS in C2 compiled method weblogic.wsee.jaxws.framework.jaxrpc.EnvironmentFactory$SimulatedWsdlDefinitions. Do not rematerialize constant table loads in PhaseAggressiveCoalesce::insert_copies() Reviewed-by: kvn --- hotspot/src/share/vm/opto/coalesce.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/hotspot/src/share/vm/opto/coalesce.cpp b/hotspot/src/share/vm/opto/coalesce.cpp index 57a20bcb638..1e8af4e27ee 100644 --- a/hotspot/src/share/vm/opto/coalesce.cpp +++ b/hotspot/src/share/vm/opto/coalesce.cpp @@ -281,9 +281,11 @@ void PhaseAggressiveCoalesce::insert_copies( Matcher &matcher ) { Block *pred = _phc._cfg.get_block_for_node(b->pred(j)); Node *copy; assert(!m->is_Con() || m->is_Mach(), "all Con must be Mach"); - // Rematerialize constants instead of copying them - if( m->is_Mach() && m->as_Mach()->is_Con() && - m->as_Mach()->rematerialize() ) { + // Rematerialize constants instead of copying them. + // We do this only for immediate constants, we avoid constant table loads + // because that will unsafely extend the live range of the constant table base. + if (m->is_Mach() && m->as_Mach()->is_Con() && !m->as_Mach()->is_MachConstant() && + m->as_Mach()->rematerialize()) { copy = m->clone(); // Insert the copy in the predecessor basic block pred->add_inst(copy); @@ -317,8 +319,8 @@ void PhaseAggressiveCoalesce::insert_copies( Matcher &matcher ) { assert(!m->is_Con() || m->is_Mach(), "all Con must be Mach"); // At this point it is unsafe to extend live ranges (6550579). // Rematerialize only constants as we do for Phi above. - if(m->is_Mach() && m->as_Mach()->is_Con() && - m->as_Mach()->rematerialize()) { + if (m->is_Mach() && m->as_Mach()->is_Con() && !m->as_Mach()->is_MachConstant() && + m->as_Mach()->rematerialize()) { copy = m->clone(); // Insert the copy in the basic block, just before us b->insert_node(copy, l++); From 009e923e04efaafb7b8183914f3d66a67958189e Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Fri, 24 Oct 2014 08:22:33 +0200 Subject: [PATCH 008/118] 8061443: Whitebox get*VMFlag() methods fail with develop flags in product builds Changed 'get*VMFlag' to return all flags. Added methods 'isLockedVMFlag' and 'isConstantVMFlag' and adapted tests. Reviewed-by: kvn, dholmes, sla --- hotspot/src/share/vm/prims/whitebox.cpp | 24 ++++++++- hotspot/src/share/vm/runtime/globals.cpp | 28 +++++----- hotspot/src/share/vm/runtime/globals.hpp | 28 +++++----- .../whitebox/sun/hotspot/WhiteBox.java | 2 + .../whitebox/vm_flags/BooleanTest.java | 2 + .../whitebox/vm_flags/IntxTest.java | 2 + .../whitebox/vm_flags/StringTest.java | 2 + .../whitebox/vm_flags/UintxTest.java | 2 + .../whitebox/vm_flags/VmFlagTest.java | 53 +++++++++++++------ 9 files changed, 97 insertions(+), 46 deletions(-) diff --git a/hotspot/src/share/vm/prims/whitebox.cpp b/hotspot/src/share/vm/prims/whitebox.cpp index 89b9807c4ca..5ea1b11e28a 100644 --- a/hotspot/src/share/vm/prims/whitebox.cpp +++ b/hotspot/src/share/vm/prims/whitebox.cpp @@ -566,13 +566,13 @@ WB_ENTRY(void, WB_ClearMethodState(JNIEnv* env, jobject o, jobject method)) WB_END template -static bool GetVMFlag(JavaThread* thread, JNIEnv* env, jstring name, T* value, bool (*TAt)(const char*, T*)) { +static bool GetVMFlag(JavaThread* thread, JNIEnv* env, jstring name, T* value, bool (*TAt)(const char*, T*, bool, bool)) { if (name == NULL) { return false; } ThreadToNativeFromVM ttnfv(thread); // can't be in VM when we call JNI const char* flag_name = env->GetStringUTFChars(name, NULL); - bool result = (*TAt)(flag_name, value); + bool result = (*TAt)(flag_name, value, true, true); env->ReleaseStringUTFChars(name, flag_name); return result; } @@ -619,6 +619,24 @@ static jobject doubleBox(JavaThread* thread, JNIEnv* env, jdouble value) { return box(thread, env, vmSymbols::java_lang_Double(), vmSymbols::Double_valueOf_signature(), value); } +static Flag* getVMFlag(JavaThread* thread, JNIEnv* env, jstring name) { + ThreadToNativeFromVM ttnfv(thread); // can't be in VM when we call JNI + const char* flag_name = env->GetStringUTFChars(name, NULL); + Flag* result = Flag::find_flag(flag_name, strlen(flag_name), true, true); + env->ReleaseStringUTFChars(name, flag_name); + return result; +} + +WB_ENTRY(jboolean, WB_IsConstantVMFlag(JNIEnv* env, jobject o, jstring name)) + Flag* flag = getVMFlag(thread, env, name); + return (flag != NULL) && flag->is_constant_in_binary(); +WB_END + +WB_ENTRY(jboolean, WB_IsLockedVMFlag(JNIEnv* env, jobject o, jstring name)) + Flag* flag = getVMFlag(thread, env, name); + return (flag != NULL) && !(flag->is_unlocked() || flag->is_unlocker()); +WB_END + WB_ENTRY(jobject, WB_GetBooleanVMFlag(JNIEnv* env, jobject o, jstring name)) bool result; if (GetVMFlag (thread, env, name, &result, &CommandLineFlags::boolAt)) { @@ -1018,6 +1036,8 @@ static JNINativeMethod methods[] = { CC"(Ljava/lang/reflect/Executable;II)Z", (void*)&WB_EnqueueMethodForCompilation}, {CC"clearMethodState", CC"(Ljava/lang/reflect/Executable;)V", (void*)&WB_ClearMethodState}, + {CC"isConstantVMFlag", CC"(Ljava/lang/String;)Z", (void*)&WB_IsConstantVMFlag}, + {CC"isLockedVMFlag", CC"(Ljava/lang/String;)Z", (void*)&WB_IsLockedVMFlag}, {CC"setBooleanVMFlag", CC"(Ljava/lang/String;Z)V",(void*)&WB_SetBooleanVMFlag}, {CC"setIntxVMFlag", CC"(Ljava/lang/String;J)V",(void*)&WB_SetIntxVMFlag}, {CC"setUintxVMFlag", CC"(Ljava/lang/String;J)V",(void*)&WB_SetUintxVMFlag}, diff --git a/hotspot/src/share/vm/runtime/globals.cpp b/hotspot/src/share/vm/runtime/globals.cpp index c5564c913c9..81bf15d0f0c 100644 --- a/hotspot/src/share/vm/runtime/globals.cpp +++ b/hotspot/src/share/vm/runtime/globals.cpp @@ -634,8 +634,8 @@ static void trace_flag_changed(const char* name, const T old_value, const T new_ e.commit(); } -bool CommandLineFlags::boolAt(const char* name, size_t len, bool* value) { - Flag* result = Flag::find_flag(name, len); +bool CommandLineFlags::boolAt(const char* name, size_t len, bool* value, bool allow_locked, bool return_flag) { + Flag* result = Flag::find_flag(name, len, allow_locked, return_flag); if (result == NULL) return false; if (!result->is_bool()) return false; *value = result->get_bool(); @@ -662,8 +662,8 @@ void CommandLineFlagsEx::boolAtPut(CommandLineFlagWithType flag, bool value, Fla faddr->set_origin(origin); } -bool CommandLineFlags::intxAt(const char* name, size_t len, intx* value) { - Flag* result = Flag::find_flag(name, len); +bool CommandLineFlags::intxAt(const char* name, size_t len, intx* value, bool allow_locked, bool return_flag) { + Flag* result = Flag::find_flag(name, len, allow_locked, return_flag); if (result == NULL) return false; if (!result->is_intx()) return false; *value = result->get_intx(); @@ -690,8 +690,8 @@ void CommandLineFlagsEx::intxAtPut(CommandLineFlagWithType flag, intx value, Fla faddr->set_origin(origin); } -bool CommandLineFlags::uintxAt(const char* name, size_t len, uintx* value) { - Flag* result = Flag::find_flag(name, len); +bool CommandLineFlags::uintxAt(const char* name, size_t len, uintx* value, bool allow_locked, bool return_flag) { + Flag* result = Flag::find_flag(name, len, allow_locked, return_flag); if (result == NULL) return false; if (!result->is_uintx()) return false; *value = result->get_uintx(); @@ -718,8 +718,8 @@ void CommandLineFlagsEx::uintxAtPut(CommandLineFlagWithType flag, uintx value, F faddr->set_origin(origin); } -bool CommandLineFlags::uint64_tAt(const char* name, size_t len, uint64_t* value) { - Flag* result = Flag::find_flag(name, len); +bool CommandLineFlags::uint64_tAt(const char* name, size_t len, uint64_t* value, bool allow_locked, bool return_flag) { + Flag* result = Flag::find_flag(name, len, allow_locked, return_flag); if (result == NULL) return false; if (!result->is_uint64_t()) return false; *value = result->get_uint64_t(); @@ -746,8 +746,8 @@ void CommandLineFlagsEx::uint64_tAtPut(CommandLineFlagWithType flag, uint64_t va faddr->set_origin(origin); } -bool CommandLineFlags::size_tAt(const char* name, size_t len, size_t* value) { - Flag* result = Flag::find_flag(name, len); +bool CommandLineFlags::size_tAt(const char* name, size_t len, size_t* value, bool allow_locked, bool return_flag) { + Flag* result = Flag::find_flag(name, len, allow_locked, return_flag); if (result == NULL) return false; if (!result->is_size_t()) return false; *value = result->get_size_t(); @@ -774,8 +774,8 @@ void CommandLineFlagsEx::size_tAtPut(CommandLineFlagWithType flag, size_t value, faddr->set_origin(origin); } -bool CommandLineFlags::doubleAt(const char* name, size_t len, double* value) { - Flag* result = Flag::find_flag(name, len); +bool CommandLineFlags::doubleAt(const char* name, size_t len, double* value, bool allow_locked, bool return_flag) { + Flag* result = Flag::find_flag(name, len, allow_locked, return_flag); if (result == NULL) return false; if (!result->is_double()) return false; *value = result->get_double(); @@ -802,8 +802,8 @@ void CommandLineFlagsEx::doubleAtPut(CommandLineFlagWithType flag, double value, faddr->set_origin(origin); } -bool CommandLineFlags::ccstrAt(const char* name, size_t len, ccstr* value) { - Flag* result = Flag::find_flag(name, len); +bool CommandLineFlags::ccstrAt(const char* name, size_t len, ccstr* value, bool allow_locked, bool return_flag) { + Flag* result = Flag::find_flag(name, len, allow_locked, return_flag); if (result == NULL) return false; if (!result->is_ccstr()) return false; *value = result->get_ccstr(); diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp index bbab0184627..f219767d9ab 100644 --- a/hotspot/src/share/vm/runtime/globals.hpp +++ b/hotspot/src/share/vm/runtime/globals.hpp @@ -379,38 +379,38 @@ class SizeTFlagSetting { class CommandLineFlags { public: - static bool boolAt(const char* name, size_t len, bool* value); - static bool boolAt(const char* name, bool* value) { return boolAt(name, strlen(name), value); } + static bool boolAt(const char* name, size_t len, bool* value, bool allow_locked = false, bool return_flag = false); + static bool boolAt(const char* name, bool* value, bool allow_locked = false, bool return_flag = false) { return boolAt(name, strlen(name), value, allow_locked, return_flag); } static bool boolAtPut(const char* name, size_t len, bool* value, Flag::Flags origin); static bool boolAtPut(const char* name, bool* value, Flag::Flags origin) { return boolAtPut(name, strlen(name), value, origin); } - static bool intxAt(const char* name, size_t len, intx* value); - static bool intxAt(const char* name, intx* value) { return intxAt(name, strlen(name), value); } + static bool intxAt(const char* name, size_t len, intx* value, bool allow_locked = false, bool return_flag = false); + static bool intxAt(const char* name, intx* value, bool allow_locked = false, bool return_flag = false) { return intxAt(name, strlen(name), value, allow_locked, return_flag); } static bool intxAtPut(const char* name, size_t len, intx* value, Flag::Flags origin); static bool intxAtPut(const char* name, intx* value, Flag::Flags origin) { return intxAtPut(name, strlen(name), value, origin); } - static bool uintxAt(const char* name, size_t len, uintx* value); - static bool uintxAt(const char* name, uintx* value) { return uintxAt(name, strlen(name), value); } + static bool uintxAt(const char* name, size_t len, uintx* value, bool allow_locked = false, bool return_flag = false); + static bool uintxAt(const char* name, uintx* value, bool allow_locked = false, bool return_flag = false) { return uintxAt(name, strlen(name), value, allow_locked, return_flag); } static bool uintxAtPut(const char* name, size_t len, uintx* value, Flag::Flags origin); static bool uintxAtPut(const char* name, uintx* value, Flag::Flags origin) { return uintxAtPut(name, strlen(name), value, origin); } - static bool size_tAt(const char* name, size_t len, size_t* value); - static bool size_tAt(const char* name, size_t* value) { return size_tAt(name, strlen(name), value); } + static bool size_tAt(const char* name, size_t len, size_t* value, bool allow_locked = false, bool return_flag = false); + static bool size_tAt(const char* name, size_t* value, bool allow_locked = false, bool return_flag = false) { return size_tAt(name, strlen(name), value, allow_locked, return_flag); } static bool size_tAtPut(const char* name, size_t len, size_t* value, Flag::Flags origin); static bool size_tAtPut(const char* name, size_t* value, Flag::Flags origin) { return size_tAtPut(name, strlen(name), value, origin); } - static bool uint64_tAt(const char* name, size_t len, uint64_t* value); - static bool uint64_tAt(const char* name, uint64_t* value) { return uint64_tAt(name, strlen(name), value); } + static bool uint64_tAt(const char* name, size_t len, uint64_t* value, bool allow_locked = false, bool return_flag = false); + static bool uint64_tAt(const char* name, uint64_t* value, bool allow_locked = false, bool return_flag = false) { return uint64_tAt(name, strlen(name), value, allow_locked, return_flag); } static bool uint64_tAtPut(const char* name, size_t len, uint64_t* value, Flag::Flags origin); static bool uint64_tAtPut(const char* name, uint64_t* value, Flag::Flags origin) { return uint64_tAtPut(name, strlen(name), value, origin); } - static bool doubleAt(const char* name, size_t len, double* value); - static bool doubleAt(const char* name, double* value) { return doubleAt(name, strlen(name), value); } + static bool doubleAt(const char* name, size_t len, double* value, bool allow_locked = false, bool return_flag = false); + static bool doubleAt(const char* name, double* value, bool allow_locked = false, bool return_flag = false) { return doubleAt(name, strlen(name), value, allow_locked, return_flag); } static bool doubleAtPut(const char* name, size_t len, double* value, Flag::Flags origin); static bool doubleAtPut(const char* name, double* value, Flag::Flags origin) { return doubleAtPut(name, strlen(name), value, origin); } - static bool ccstrAt(const char* name, size_t len, ccstr* value); - static bool ccstrAt(const char* name, ccstr* value) { return ccstrAt(name, strlen(name), value); } + static bool ccstrAt(const char* name, size_t len, ccstr* value, bool allow_locked = false, bool return_flag = false); + static bool ccstrAt(const char* name, ccstr* value, bool allow_locked = false, bool return_flag = false) { return ccstrAt(name, strlen(name), value, allow_locked, return_flag); } // Contract: Flag will make private copy of the incoming value. // Outgoing value is always malloc-ed, and caller MUST call free. static bool ccstrAtPut(const char* name, size_t len, ccstr* value, Flag::Flags origin); diff --git a/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java b/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java index 228d1ddb319..0d4975d6dae 100644 --- a/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java +++ b/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java @@ -179,6 +179,8 @@ public class WhiteBox { public native void printRegionInfo(int context); // VM flags + public native boolean isConstantVMFlag(String name); + public native boolean isLockedVMFlag(String name); public native void setBooleanVMFlag(String name, boolean value); public native void setIntxVMFlag(String name, long value); public native void setUintxVMFlag(String name, long value); diff --git a/hotspot/test/testlibrary_tests/whitebox/vm_flags/BooleanTest.java b/hotspot/test/testlibrary_tests/whitebox/vm_flags/BooleanTest.java index ae068f8867a..1da1728877d 100644 --- a/hotspot/test/testlibrary_tests/whitebox/vm_flags/BooleanTest.java +++ b/hotspot/test/testlibrary_tests/whitebox/vm_flags/BooleanTest.java @@ -43,6 +43,7 @@ public class BooleanTest { private static final Boolean[] TESTS = {true, false, true, true, false}; private static final String TEST_NAME = "BooleanTest"; private static final String FLAG_NAME = "PrintCompilation"; + private static final String FLAG_DEBUG_NAME = "SafepointALot"; private static final String METHOD = TEST_NAME + "::method"; private static final String METHOD1 = METHOD + "1"; private static final String METHOD2 = METHOD + "2"; @@ -54,6 +55,7 @@ public class BooleanTest { VmFlagTest.WHITE_BOX::getBooleanVMFlag); testFunctional(false); testFunctional(true); + VmFlagTest.runTest(FLAG_DEBUG_NAME, VmFlagTest.WHITE_BOX::getBooleanVMFlag); } else { boolean value = Boolean.valueOf(args[0]); method1(); diff --git a/hotspot/test/testlibrary_tests/whitebox/vm_flags/IntxTest.java b/hotspot/test/testlibrary_tests/whitebox/vm_flags/IntxTest.java index 3b957eff084..9acdd31dd8b 100644 --- a/hotspot/test/testlibrary_tests/whitebox/vm_flags/IntxTest.java +++ b/hotspot/test/testlibrary_tests/whitebox/vm_flags/IntxTest.java @@ -35,6 +35,7 @@ public class IntxTest { private static final String FLAG_NAME = "OnStackReplacePercentage"; + private static final String FLAG_DEBUG_NAME = "InlineFrequencyCount"; private static final Long[] TESTS = {0L, 100L, -1L, (long) Integer.MAX_VALUE, (long) Integer.MIN_VALUE}; @@ -42,6 +43,7 @@ public class IntxTest { VmFlagTest.runTest(FLAG_NAME, TESTS, VmFlagTest.WHITE_BOX::setIntxVMFlag, VmFlagTest.WHITE_BOX::getIntxVMFlag); + VmFlagTest.runTest(FLAG_DEBUG_NAME, VmFlagTest.WHITE_BOX::getIntxVMFlag); } } diff --git a/hotspot/test/testlibrary_tests/whitebox/vm_flags/StringTest.java b/hotspot/test/testlibrary_tests/whitebox/vm_flags/StringTest.java index 77a02d3d2d4..e613df85b78 100644 --- a/hotspot/test/testlibrary_tests/whitebox/vm_flags/StringTest.java +++ b/hotspot/test/testlibrary_tests/whitebox/vm_flags/StringTest.java @@ -35,12 +35,14 @@ public class StringTest { private static final String FLAG_NAME = "CompileOnly"; + private static final String FLAG_DEBUG_NAME = "SuppressErrorAt"; private static final String[] TESTS = {"StringTest::*", ""}; public static void main(String[] args) throws Exception { VmFlagTest.runTest(FLAG_NAME, TESTS, VmFlagTest.WHITE_BOX::setStringVMFlag, VmFlagTest.WHITE_BOX::getStringVMFlag); + VmFlagTest.runTest(FLAG_DEBUG_NAME, VmFlagTest.WHITE_BOX::getStringVMFlag); } } diff --git a/hotspot/test/testlibrary_tests/whitebox/vm_flags/UintxTest.java b/hotspot/test/testlibrary_tests/whitebox/vm_flags/UintxTest.java index 40eb85f868f..bdb1f647209 100644 --- a/hotspot/test/testlibrary_tests/whitebox/vm_flags/UintxTest.java +++ b/hotspot/test/testlibrary_tests/whitebox/vm_flags/UintxTest.java @@ -36,6 +36,7 @@ import com.oracle.java.testlibrary.Platform; public class UintxTest { private static final String FLAG_NAME = "VerifyGCStartAt"; + private static final String FLAG_DEBUG_NAME = "CodeCacheMinimumUseSpace"; private static final Long[] TESTS = {0L, 100L, (long) Integer.MAX_VALUE, (1L << 32L) - 1L, 1L << 32L}; private static final Long[] EXPECTED_64 = TESTS; @@ -47,6 +48,7 @@ public class UintxTest { Platform.is64bit() ? EXPECTED_64 : EXPECTED_32, VmFlagTest.WHITE_BOX::setUintxVMFlag, VmFlagTest.WHITE_BOX::getUintxVMFlag); + VmFlagTest.runTest(FLAG_DEBUG_NAME, VmFlagTest.WHITE_BOX::getUintxVMFlag); } } diff --git a/hotspot/test/testlibrary_tests/whitebox/vm_flags/VmFlagTest.java b/hotspot/test/testlibrary_tests/whitebox/vm_flags/VmFlagTest.java index 6c98889e6ae..e2d15630c04 100644 --- a/hotspot/test/testlibrary_tests/whitebox/vm_flags/VmFlagTest.java +++ b/hotspot/test/testlibrary_tests/whitebox/vm_flags/VmFlagTest.java @@ -37,16 +37,18 @@ public final class VmFlagTest { private final BiConsumer test; private final BiConsumer set; private final Function get; + private final boolean isPositive; protected VmFlagTest(String flagName, BiConsumer set, Function get, boolean isPositive) { this.flagName = flagName; this.set = set; this.get = get; + this.isPositive = isPositive; if (isPositive) { - test = this::testPositive; + test = this::testWritePositive; } else { - test = this::testNegative; + test = this::testWriteNegative; } } @@ -63,6 +65,10 @@ public final class VmFlagTest { runTest(existentFlag, tests, tests, set, get); } + protected static void runTest(String existentFlag, Function get) { + runTest(existentFlag, null, null, null, get); + } + protected static void runTest(String existentFlag, T[] tests, T[] results, BiConsumer set, Function get) { if (existentFlag != null) { @@ -72,13 +78,23 @@ public final class VmFlagTest { } public final void test(T[] tests, T[] results) { - Asserts.assertEQ(tests.length, results.length, "[TESTBUG] tests.length != results.length"); - for (int i = 0, n = tests.length ; i < n; ++i) { - test.accept(tests[i], results[i]); + if (isPositive) { + testRead(); + } + if (tests != null) { + Asserts.assertEQ(tests.length, results.length, "[TESTBUG] tests.length != results.length"); + for (int i = 0, n = tests.length ; i < n; ++i) { + test.accept(tests[i], results[i]); + } } } protected String getVMOptionAsString() { + if (WHITE_BOX.isConstantVMFlag(flagName) || WHITE_BOX.isLockedVMFlag(flagName)) { + // JMM cannot access debug flags in product builds or locked flags, + // use whitebox methods to get such flags value. + return asString(getValue()); + } HotSpotDiagnosticMXBean diagnostic = ManagementFactoryHelper.getDiagnosticMXBean(); VMOption tmp; @@ -90,18 +106,24 @@ public final class VmFlagTest { return tmp == null ? null : tmp.getValue(); } - private void testPositive(T value, T expected) { - String oldValue = getVMOptionAsString(); - Asserts.assertEQ(oldValue, asString(getValue())); - Asserts.assertEQ(oldValue, asString(WHITE_BOX.getVMFlag(flagName))); - setNewValue(value); - String newValue = getVMOptionAsString(); - Asserts.assertEQ(newValue, asString(expected)); - Asserts.assertEQ(newValue, asString(getValue())); - Asserts.assertEQ(newValue, asString(WHITE_BOX.getVMFlag(flagName))); + private String testRead() { + String value = getVMOptionAsString(); + Asserts.assertNotNull(value); + Asserts.assertEQ(value, asString(getValue())); + Asserts.assertEQ(value, asString(WHITE_BOX.getVMFlag(flagName))); + return value; } - private void testNegative(T value, T expected) { + private void testWritePositive(T value, T expected) { + setNewValue(value); + String newValue = testRead(); + Asserts.assertEQ(newValue, asString(expected)); + } + + private void testWriteNegative(T value, T expected) { + // Should always return false for non-existing flags + Asserts.assertFalse(WHITE_BOX.isConstantVMFlag(flagName)); + Asserts.assertFalse(WHITE_BOX.isLockedVMFlag(flagName)); String oldValue = getVMOptionAsString(); Asserts.assertEQ(oldValue, asString(getValue())); Asserts.assertEQ(oldValue, asString(WHITE_BOX.getVMFlag(flagName))); @@ -114,4 +136,3 @@ public final class VmFlagTest { return value == null ? null : "" + value; } } - From 7d0337349b15fb66a599f552368a1bbebcd5c8e9 Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Fri, 24 Oct 2014 08:27:39 +0200 Subject: [PATCH 009/118] 8060479: [TESTBUG] compiler/codecache/CheckSegmentedCodeCache.java test fails with product build Added check for product build and compute minimum code cache size accordingly. Reviewed-by: kvn, iignatyev --- .../codecache/CheckSegmentedCodeCache.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/hotspot/test/compiler/codecache/CheckSegmentedCodeCache.java b/hotspot/test/compiler/codecache/CheckSegmentedCodeCache.java index 06330d840b6..9784b4835a3 100644 --- a/hotspot/test/compiler/codecache/CheckSegmentedCodeCache.java +++ b/hotspot/test/compiler/codecache/CheckSegmentedCodeCache.java @@ -22,15 +22,20 @@ */ import com.oracle.java.testlibrary.*; +import sun.hotspot.WhiteBox; /* * @test CheckSegmentedCodeCache * @bug 8015774 + * @library /testlibrary /testlibrary/whitebox * @summary "Checks VM options related to the segmented code cache" - * @library /testlibrary - * @run main/othervm CheckSegmentedCodeCache + * @build CheckSegmentedCodeCache + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI CheckSegmentedCodeCache */ public class CheckSegmentedCodeCache { + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); // Code heap names private static final String NON_METHOD = "CodeHeap 'non-nmethods'"; private static final String PROFILED = "CodeHeap 'profiled nmethods'"; @@ -133,8 +138,12 @@ public class CheckSegmentedCodeCache { failsWith(pb, "Invalid code heap sizes"); // Fails if not enough space for VM internal code + long minUseSpace = WHITE_BOX.getUintxVMFlag("CodeCacheMinimumUseSpace"); + long minFreeSpace = WHITE_BOX.getUintxVMFlag("CodeCacheMinimumFreeSpace"); + // minimum size: (CodeCacheMinimumUseSpace DEBUG_ONLY(* 3)) + CodeCacheMinimumFreeSpace + long minSize = (Platform.isDebugBuild() ? 3 : 1) * minUseSpace + minFreeSpace; pb = ProcessTools.createJavaProcessBuilder("-XX:+SegmentedCodeCache", - "-XX:ReservedCodeCacheSize=1700K", + "-XX:ReservedCodeCacheSize=" + minSize, "-XX:InitialCodeCacheSize=100K"); failsWith(pb, "Not enough space in non-nmethod code heap to run VM"); } From 2597d484c63e321a58ceb6a369f7881308b00570 Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Fri, 24 Oct 2014 08:35:29 +0200 Subject: [PATCH 010/118] 8061486: [TESTBUG] compiler/whitebox/ tests fail : must be osr_compiled (reappeared in nightlies) Call warmup code from OSR triggering method to make sure no non-OSR compilation is triggered in the loop. Reviewed-by: kvn --- .../whitebox/CompilerWhiteBoxTest.java | 142 +++++++++--------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/hotspot/test/compiler/whitebox/CompilerWhiteBoxTest.java b/hotspot/test/compiler/whitebox/CompilerWhiteBoxTest.java index a39a079e355..86eb174b6d8 100644 --- a/hotspot/test/compiler/whitebox/CompilerWhiteBoxTest.java +++ b/hotspot/test/compiler/whitebox/CompilerWhiteBoxTest.java @@ -73,8 +73,6 @@ public abstract class CompilerWhiteBoxTest { protected static final int THRESHOLD; /** invocation count to trigger OSR compilation */ protected static final long BACKEDGE_THRESHOLD; - /** invocation count to warm up method before triggering OSR compilation */ - protected static final long OSR_WARMUP = 2000; /** Value of {@code java.vm.info} (interpreted|mixed|comp mode) */ protected static final String MODE = System.getProperty("java.vm.info"); @@ -498,8 +496,7 @@ enum SimpleTestCase implements CompilerWhiteBoxTest.TestCase { = new Callable() { @Override public Integer call() throws Exception { - int result = warmup(OSR_CONSTRUCTOR); - return result + new Helper(null, CompilerWhiteBoxTest.BACKEDGE_THRESHOLD).hashCode(); + return new Helper(null, CompilerWhiteBoxTest.BACKEDGE_THRESHOLD).hashCode(); } }; @@ -509,8 +506,7 @@ enum SimpleTestCase implements CompilerWhiteBoxTest.TestCase { @Override public Integer call() throws Exception { - int result = warmup(OSR_METHOD); - return result + helper.osrMethod(CompilerWhiteBoxTest.BACKEDGE_THRESHOLD); + return helper.osrMethod(CompilerWhiteBoxTest.BACKEDGE_THRESHOLD); } }; @@ -518,62 +514,10 @@ enum SimpleTestCase implements CompilerWhiteBoxTest.TestCase { = new Callable() { @Override public Integer call() throws Exception { - int result = warmup(OSR_STATIC); - return result + osrStaticMethod(CompilerWhiteBoxTest.BACKEDGE_THRESHOLD); + return osrStaticMethod(CompilerWhiteBoxTest.BACKEDGE_THRESHOLD); } }; - /** - * Deoptimizes all non-osr versions of the given executable after - * compilation finished. - * - * @param e Executable - * @throws Exception - */ - private static void waitAndDeoptimize(Executable e) { - CompilerWhiteBoxTest.waitBackgroundCompilation(e); - if (WhiteBox.getWhiteBox().isMethodQueuedForCompilation(e)) { - throw new RuntimeException(e + " must not be in queue"); - } - // Deoptimize non-osr versions of executable - WhiteBox.getWhiteBox().deoptimizeMethod(e, false); - } - - /** - * Executes the method multiple times to make sure we have - * enough profiling information before triggering an OSR - * compilation. Otherwise the C2 compiler may add uncommon traps. - * - * @param m Method to be executed - * @return Number of times the method was executed - * @throws Exception - */ - private static int warmup(Method m) throws Exception { - Helper helper = new Helper(); - int result = 0; - for (long i = 0; i < CompilerWhiteBoxTest.OSR_WARMUP; ++i) { - result += (int)m.invoke(helper, 1); - } - return result; - } - - /** - * Executes the constructor multiple times to make sure we - * have enough profiling information before triggering an OSR - * compilation. Otherwise the C2 compiler may add uncommon traps. - * - * @param c Constructor to be executed - * @return Number of times the constructor was executed - * @throws Exception - */ - private static int warmup(Constructor c) throws Exception { - int result = 0; - for (long i = 0; i < CompilerWhiteBoxTest.OSR_WARMUP; ++i) { - result += c.newInstance(null, 1).hashCode(); - } - return result; - } - private static final Constructor CONSTRUCTOR; private static final Constructor OSR_CONSTRUCTOR; private static final Method METHOD; @@ -618,26 +562,83 @@ enum SimpleTestCase implements CompilerWhiteBoxTest.TestCase { return 42; } - private static int osrStaticMethod(long limit) { + /** + * Deoptimizes all non-osr versions of the given executable after + * compilation finished. + * + * @param e Executable + * @throws Exception + */ + private static void waitAndDeoptimize(Executable e) { + CompilerWhiteBoxTest.waitBackgroundCompilation(e); + if (WhiteBox.getWhiteBox().isMethodQueuedForCompilation(e)) { + throw new RuntimeException(e + " must not be in queue"); + } + // Deoptimize non-osr versions of executable + WhiteBox.getWhiteBox().deoptimizeMethod(e, false); + } + + /** + * Executes the method multiple times to make sure we have + * enough profiling information before triggering an OSR + * compilation. Otherwise the C2 compiler may add uncommon traps. + * + * @param m Method to be executed + * @return Number of times the method was executed + * @throws Exception + */ + private static int warmup(Method m) throws Exception { + waitAndDeoptimize(m); + Helper helper = new Helper(); + int result = 0; + for (long i = 0; i < CompilerWhiteBoxTest.THRESHOLD; ++i) { + result += (int)m.invoke(helper, 1); + } + // Wait to make sure OSR compilation is not blocked by + // non-OSR compilation in the compile queue + CompilerWhiteBoxTest.waitBackgroundCompilation(m); + return result; + } + + /** + * Executes the constructor multiple times to make sure we + * have enough profiling information before triggering an OSR + * compilation. Otherwise the C2 compiler may add uncommon traps. + * + * @param c Constructor to be executed + * @return Number of times the constructor was executed + * @throws Exception + */ + private static int warmup(Constructor c) throws Exception { + waitAndDeoptimize(c); + int result = 0; + for (long i = 0; i < CompilerWhiteBoxTest.THRESHOLD; ++i) { + result += c.newInstance(null, 1).hashCode(); + } + // Wait to make sure OSR compilation is not blocked by + // non-OSR compilation in the compile queue + CompilerWhiteBoxTest.waitBackgroundCompilation(c); + return result; + } + + private static int osrStaticMethod(long limit) throws Exception { + int result = 0; if (limit != 1) { - // Make sure there is no compiled version after warmup - waitAndDeoptimize(OSR_STATIC); + result = warmup(OSR_STATIC); } // Trigger osr compilation - int result = 0; for (long i = 0; i < limit; ++i) { result += staticMethod(); } return result; } - private int osrMethod(long limit) { + private int osrMethod(long limit) throws Exception { + int result = 0; if (limit != 1) { - // Make sure there is no compiled version after warmup - waitAndDeoptimize(OSR_METHOD); + result = warmup(OSR_METHOD); } // Trigger osr compilation - int result = 0; for (long i = 0; i < limit; ++i) { result += method(); } @@ -652,13 +653,12 @@ enum SimpleTestCase implements CompilerWhiteBoxTest.TestCase { } // for OSR constructor test case - private Helper(Object o, long limit) { + private Helper(Object o, long limit) throws Exception { + int result = 0; if (limit != 1) { - // Make sure there is no compiled version after warmup - waitAndDeoptimize(OSR_CONSTRUCTOR); + result = warmup(OSR_CONSTRUCTOR); } // Trigger osr compilation - int result = 0; for (long i = 0; i < limit; ++i) { result += method(); } From 6520320d1aaf6d1052cda95dd6783dec51262bc9 Mon Sep 17 00:00:00 2001 From: Albert Noll Date: Fri, 24 Oct 2014 14:25:46 +0200 Subject: [PATCH 011/118] 8046809: vm/mlvm/meth/stress/compiler/deoptimize CodeCache is full Use separate sweeper thread; enables more aggressive sweeping. Reviewed-by: kvn, jrose --- hotspot/src/share/vm/ci/ciEnv.cpp | 3 +- hotspot/src/share/vm/code/codeBlob.cpp | 18 +- hotspot/src/share/vm/code/codeBlob.hpp | 2 +- hotspot/src/share/vm/code/codeCache.cpp | 58 ++--- hotspot/src/share/vm/code/codeCache.hpp | 21 +- hotspot/src/share/vm/code/nmethod.cpp | 5 +- hotspot/src/share/vm/code/vtableStubs.cpp | 1 - .../src/share/vm/compiler/compileBroker.cpp | 126 +++++------ .../src/share/vm/compiler/compileBroker.hpp | 6 +- .../vm/interpreter/interpreterRuntime.cpp | 1 - hotspot/src/share/vm/memory/heap.cpp | 22 +- hotspot/src/share/vm/memory/heap.hpp | 6 +- hotspot/src/share/vm/opto/compile.cpp | 1 - hotspot/src/share/vm/opto/output.cpp | 3 - hotspot/src/share/vm/prims/methodHandles.cpp | 35 +-- hotspot/src/share/vm/prims/methodHandles.hpp | 2 +- hotspot/src/share/vm/runtime/arguments.cpp | 21 +- hotspot/src/share/vm/runtime/globals.hpp | 14 +- hotspot/src/share/vm/runtime/mutexLocker.cpp | 4 +- hotspot/src/share/vm/runtime/mutexLocker.hpp | 2 +- .../src/share/vm/runtime/sharedRuntime.cpp | 5 - hotspot/src/share/vm/runtime/sweeper.cpp | 205 ++++++++++-------- hotspot/src/share/vm/runtime/sweeper.hpp | 14 +- hotspot/src/share/vm/runtime/thread.cpp | 14 +- hotspot/src/share/vm/runtime/thread.hpp | 39 ++-- .../src/share/vm/runtime/vm_operations.cpp | 3 + .../src/share/vm/runtime/vm_operations.hpp | 8 + hotspot/src/share/vm/trace/trace.xml | 1 - .../startup/SmallCodeCacheStartup.java | 12 +- .../gc/g1/TestHumongousCodeCacheRoots.java | 1 - 30 files changed, 319 insertions(+), 334 deletions(-) diff --git a/hotspot/src/share/vm/ci/ciEnv.cpp b/hotspot/src/share/vm/ci/ciEnv.cpp index 98179f46fdf..ec16b8da1ee 100644 --- a/hotspot/src/share/vm/ci/ciEnv.cpp +++ b/hotspot/src/share/vm/ci/ciEnv.cpp @@ -1093,9 +1093,8 @@ void ciEnv::register_method(ciMethod* target, // JVMTI -- compiled method notification (must be done outside lock) nm->post_compiled_method_load_event(); } else { - // The CodeCache is full. Print out warning and disable compilation. + // The CodeCache is full. record_failure("code cache is full"); - CompileBroker::handle_full_code_cache(CodeCache::get_code_blob_type(comp_level)); } } diff --git a/hotspot/src/share/vm/code/codeBlob.cpp b/hotspot/src/share/vm/code/codeBlob.cpp index 780111b0aa7..98b7ec0f23c 100644 --- a/hotspot/src/share/vm/code/codeBlob.cpp +++ b/hotspot/src/share/vm/code/codeBlob.cpp @@ -229,8 +229,8 @@ BufferBlob* BufferBlob::create(const char* name, CodeBuffer* cb) { return blob; } -void* BufferBlob::operator new(size_t s, unsigned size, bool is_critical) throw() { - return CodeCache::allocate(size, CodeBlobType::NonNMethod, is_critical); +void* BufferBlob::operator new(size_t s, unsigned size) throw() { + return CodeCache::allocate(size, CodeBlobType::NonNMethod); } void BufferBlob::free(BufferBlob *blob) { @@ -260,10 +260,7 @@ AdapterBlob* AdapterBlob::create(CodeBuffer* cb) { unsigned int size = allocation_size(cb, sizeof(AdapterBlob)); { MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - // The parameter 'true' indicates a critical memory allocation. - // This means that CodeCacheMinimumFreeSpace is used, if necessary - const bool is_critical = true; - blob = new (size, is_critical) AdapterBlob(size, cb); + blob = new (size) AdapterBlob(size, cb); } // Track memory usage statistic after releasing CodeCache_lock MemoryService::track_code_cache_memory_usage(); @@ -285,10 +282,7 @@ MethodHandlesAdapterBlob* MethodHandlesAdapterBlob::create(int buffer_size) { size += round_to(buffer_size, oopSize); { MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - // The parameter 'true' indicates a critical memory allocation. - // This means that CodeCacheMinimumFreeSpace is used, if necessary - const bool is_critical = true; - blob = new (size, is_critical) MethodHandlesAdapterBlob(size); + blob = new (size) MethodHandlesAdapterBlob(size); } // Track memory usage statistic after releasing CodeCache_lock MemoryService::track_code_cache_memory_usage(); @@ -336,14 +330,14 @@ RuntimeStub* RuntimeStub::new_runtime_stub(const char* stub_name, void* RuntimeStub::operator new(size_t s, unsigned size) throw() { - void* p = CodeCache::allocate(size, CodeBlobType::NonNMethod, true); + void* p = CodeCache::allocate(size, CodeBlobType::NonNMethod); if (!p) fatal("Initial size of CodeCache is too small"); return p; } // operator new shared by all singletons: void* SingletonBlob::operator new(size_t s, unsigned size) throw() { - void* p = CodeCache::allocate(size, CodeBlobType::NonNMethod, true); + void* p = CodeCache::allocate(size, CodeBlobType::NonNMethod); if (!p) fatal("Initial size of CodeCache is too small"); return p; } diff --git a/hotspot/src/share/vm/code/codeBlob.hpp b/hotspot/src/share/vm/code/codeBlob.hpp index 88d86eb5995..2c066788657 100644 --- a/hotspot/src/share/vm/code/codeBlob.hpp +++ b/hotspot/src/share/vm/code/codeBlob.hpp @@ -221,7 +221,7 @@ class BufferBlob: public CodeBlob { BufferBlob(const char* name, int size); BufferBlob(const char* name, int size, CodeBuffer* cb); - void* operator new(size_t s, unsigned size, bool is_critical = false) throw(); + void* operator new(size_t s, unsigned size) throw(); public: // Creation diff --git a/hotspot/src/share/vm/code/codeCache.cpp b/hotspot/src/share/vm/code/codeCache.cpp index 2b8845a6d65..4876a40111d 100644 --- a/hotspot/src/share/vm/code/codeCache.cpp +++ b/hotspot/src/share/vm/code/codeCache.cpp @@ -44,6 +44,7 @@ #include "runtime/icache.hpp" #include "runtime/java.hpp" #include "runtime/mutexLocker.hpp" +#include "runtime/sweeper.hpp" #include "runtime/compilationPolicy.hpp" #include "services/memoryService.hpp" #include "trace/tracing.hpp" @@ -192,16 +193,16 @@ void CodeCache::initialize_heaps() { } // Make sure we have enough space for VM internal code - uint min_code_cache_size = (CodeCacheMinimumUseSpace DEBUG_ONLY(* 3)) + CodeCacheMinimumFreeSpace; + uint min_code_cache_size = CodeCacheMinimumUseSpace DEBUG_ONLY(* 3); if (NonNMethodCodeHeapSize < (min_code_cache_size + code_buffers_size)) { vm_exit_during_initialization("Not enough space in non-nmethod code heap to run VM."); } guarantee(NonProfiledCodeHeapSize + ProfiledCodeHeapSize + NonNMethodCodeHeapSize <= ReservedCodeCacheSize, "Size check"); // Align reserved sizes of CodeHeaps - size_t non_method_size = ReservedCodeSpace::allocation_align_size_up(NonNMethodCodeHeapSize); - size_t profiled_size = ReservedCodeSpace::allocation_align_size_up(ProfiledCodeHeapSize); - size_t non_profiled_size = ReservedCodeSpace::allocation_align_size_up(NonProfiledCodeHeapSize); + size_t non_method_size = ReservedCodeSpace::allocation_align_size_up(NonNMethodCodeHeapSize); + size_t profiled_size = ReservedCodeSpace::allocation_align_size_up(ProfiledCodeHeapSize); + size_t non_profiled_size = ReservedCodeSpace::allocation_align_size_up(NonProfiledCodeHeapSize); // Compute initial sizes of CodeHeaps size_t init_non_method_size = MIN2(InitialCodeCacheSize, non_method_size); @@ -348,14 +349,18 @@ CodeBlob* CodeCache::next_blob(CodeBlob* cb) { return next_blob(get_code_heap(cb), cb); } -CodeBlob* CodeCache::allocate(int size, int code_blob_type, bool is_critical) { - // Do not seize the CodeCache lock here--if the caller has not - // already done so, we are going to lose bigtime, since the code - // cache will contain a garbage CodeBlob until the caller can - // run the constructor for the CodeBlob subclass he is busy - // instantiating. +/** + * Do not seize the CodeCache lock here--if the caller has not + * already done so, we are going to lose bigtime, since the code + * cache will contain a garbage CodeBlob until the caller can + * run the constructor for the CodeBlob subclass he is busy + * instantiating. + */ +CodeBlob* CodeCache::allocate(int size, int code_blob_type) { + // Possibly wakes up the sweeper thread. + NMethodSweeper::notify(code_blob_type); assert_locked_or_safepoint(CodeCache_lock); - assert(size > 0, "allocation request must be reasonable"); + assert(size > 0, err_msg_res("Code cache allocation request must be > 0 but is %d", size)); if (size <= 0) { return NULL; } @@ -366,14 +371,18 @@ CodeBlob* CodeCache::allocate(int size, int code_blob_type, bool is_critical) { assert(heap != NULL, "heap is null"); while (true) { - cb = (CodeBlob*)heap->allocate(size, is_critical); + cb = (CodeBlob*)heap->allocate(size); if (cb != NULL) break; if (!heap->expand_by(CodeCacheExpansionSize)) { // Expansion failed if (SegmentedCodeCache && (code_blob_type == CodeBlobType::NonNMethod)) { - // Fallback solution: Store non-nmethod code in the non-profiled code heap - return allocate(size, CodeBlobType::MethodNonProfiled, is_critical); + // Fallback solution: Store non-nmethod code in the non-profiled code heap. + // Note that at in the sweeper, we check the reverse_free_ratio of the non-profiled + // code heap and force stack scanning if less than 10% if the code heap are free. + return allocate(size, CodeBlobType::MethodNonProfiled); } + MutexUnlockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + CompileBroker::handle_full_code_cache(code_blob_type); return NULL; } if (PrintCodeCacheExtension) { @@ -770,19 +779,6 @@ size_t CodeCache::max_capacity() { return max_cap; } -/** - * Returns true if a CodeHeap is full and sets code_blob_type accordingly. - */ -bool CodeCache::is_full(int* code_blob_type) { - FOR_ALL_HEAPS(heap) { - if ((*heap)->unallocated_capacity() < CodeCacheMinimumFreeSpace) { - *code_blob_type = (*heap)->code_blob_type(); - return true; - } - } - return false; -} - /** * Returns the reverse free ratio. E.g., if 25% (1/4) of the code heap * is free, reverse_free_ratio() returns 4. @@ -792,9 +788,13 @@ double CodeCache::reverse_free_ratio(int code_blob_type) { if (heap == NULL) { return 0; } - double unallocated_capacity = (double)(heap->unallocated_capacity() - CodeCacheMinimumFreeSpace); + + double unallocated_capacity = MAX2((double)heap->unallocated_capacity(), 1.0); // Avoid division by 0; double max_capacity = (double)heap->max_capacity(); - return max_capacity / unallocated_capacity; + double result = max_capacity / unallocated_capacity; + assert (max_capacity >= unallocated_capacity, "Must be"); + assert (result >= 1.0, err_msg_res("reverse_free_ratio must be at least 1. It is %f", result)); + return result; } size_t CodeCache::bytes_allocated_in_freelists() { diff --git a/hotspot/src/share/vm/code/codeCache.hpp b/hotspot/src/share/vm/code/codeCache.hpp index b8b119c3b86..fd7fb286c31 100644 --- a/hotspot/src/share/vm/code/codeCache.hpp +++ b/hotspot/src/share/vm/code/codeCache.hpp @@ -120,16 +120,16 @@ class CodeCache : AllStatic { static void initialize(); // Allocation/administration - static CodeBlob* allocate(int size, int code_blob_type, bool is_critical = false); // allocates a new CodeBlob - static void commit(CodeBlob* cb); // called when the allocated CodeBlob has been filled - static int alignment_unit(); // guaranteed alignment of all CodeBlobs - static int alignment_offset(); // guaranteed offset of first CodeBlob byte within alignment unit (i.e., allocation header) - static void free(CodeBlob* cb); // frees a CodeBlob - static bool contains(void *p); // returns whether p is included - static void blobs_do(void f(CodeBlob* cb)); // iterates over all CodeBlobs - static void blobs_do(CodeBlobClosure* f); // iterates over all CodeBlobs - static void nmethods_do(void f(nmethod* nm)); // iterates over all nmethods - static void alive_nmethods_do(void f(nmethod* nm)); // iterates over all alive nmethods + static CodeBlob* allocate(int size, int code_blob_type); // allocates a new CodeBlob + static void commit(CodeBlob* cb); // called when the allocated CodeBlob has been filled + static int alignment_unit(); // guaranteed alignment of all CodeBlobs + static int alignment_offset(); // guaranteed offset of first CodeBlob byte within alignment unit (i.e., allocation header) + static void free(CodeBlob* cb); // frees a CodeBlob + static bool contains(void *p); // returns whether p is included + static void blobs_do(void f(CodeBlob* cb)); // iterates over all CodeBlobs + static void blobs_do(CodeBlobClosure* f); // iterates over all CodeBlobs + static void nmethods_do(void f(nmethod* nm)); // iterates over all nmethods + static void alive_nmethods_do(void f(nmethod* nm)); // iterates over all alive nmethods // Lookup static CodeBlob* find_blob(void* start); // Returns the CodeBlob containing the given address @@ -182,7 +182,6 @@ class CodeCache : AllStatic { static size_t unallocated_capacity(); static size_t max_capacity(); - static bool is_full(int* code_blob_type); static double reverse_free_ratio(int code_blob_type); static bool needs_cache_clean() { return _needs_cache_clean; } diff --git a/hotspot/src/share/vm/code/nmethod.cpp b/hotspot/src/share/vm/code/nmethod.cpp index 7804712fb87..03456870130 100644 --- a/hotspot/src/share/vm/code/nmethod.cpp +++ b/hotspot/src/share/vm/code/nmethod.cpp @@ -804,10 +804,7 @@ nmethod::nmethod( #endif // def HAVE_DTRACE_H void* nmethod::operator new(size_t size, int nmethod_size, int comp_level) throw () { - // With a SegmentedCodeCache, nmethods are allocated on separate heaps and therefore do not share memory - // with critical CodeBlobs. We define the allocation as critical to make sure all code heap memory is used. - bool is_critical = SegmentedCodeCache; - return CodeCache::allocate(nmethod_size, CodeCache::get_code_blob_type(comp_level), is_critical); + return CodeCache::allocate(nmethod_size, CodeCache::get_code_blob_type(comp_level)); } nmethod::nmethod( diff --git a/hotspot/src/share/vm/code/vtableStubs.cpp b/hotspot/src/share/vm/code/vtableStubs.cpp index f4b0f20652c..c6262988265 100644 --- a/hotspot/src/share/vm/code/vtableStubs.cpp +++ b/hotspot/src/share/vm/code/vtableStubs.cpp @@ -63,7 +63,6 @@ void* VtableStub::operator new(size_t size, int code_size) throw() { // If changing the name, update the other file accordingly. BufferBlob* blob = BufferBlob::create("vtable chunks", bytes); if (blob == NULL) { - CompileBroker::handle_full_code_cache(CodeBlobType::NonNMethod); return NULL; } _chunk = blob->content_begin(); diff --git a/hotspot/src/share/vm/compiler/compileBroker.cpp b/hotspot/src/share/vm/compiler/compileBroker.cpp index 1eccfde6fd0..1c26cbb8f77 100644 --- a/hotspot/src/share/vm/compiler/compileBroker.cpp +++ b/hotspot/src/share/vm/compiler/compileBroker.cpp @@ -156,8 +156,6 @@ long CompileBroker::_peak_compilation_time = 0; CompileQueue* CompileBroker::_c2_compile_queue = NULL; CompileQueue* CompileBroker::_c1_compile_queue = NULL; -GrowableArray* CompileBroker::_compiler_threads = NULL; - class CompilationLog : public StringEventLog { public: @@ -649,13 +647,10 @@ void CompileQueue::free_all() { lock()->notify_all(); } -// ------------------------------------------------------------------ -// CompileQueue::get -// -// Get the next CompileTask from a CompileQueue +/** + * Get the next CompileTask from a CompileQueue + */ CompileTask* CompileQueue::get() { - NMethodSweeper::possibly_sweep(); - MutexLocker locker(lock()); // If _first is NULL we have no more compile jobs. There are two reasons for // having no compile jobs: First, we compiled everything we wanted. Second, @@ -668,35 +663,16 @@ CompileTask* CompileQueue::get() { return NULL; } - if (UseCodeCacheFlushing && !CompileBroker::should_compile_new_jobs()) { - // Wait a certain amount of time to possibly do another sweep. - // We must wait until stack scanning has happened so that we can - // transition a method's state from 'not_entrant' to 'zombie'. - long wait_time = NmethodSweepCheckInterval * 1000; - if (FLAG_IS_DEFAULT(NmethodSweepCheckInterval)) { - // Only one thread at a time can do sweeping. Scale the - // wait time according to the number of compiler threads. - // As a result, the next sweep is likely to happen every 100ms - // with an arbitrary number of threads that do sweeping. - wait_time = 100 * CICompilerCount; - } - bool timeout = lock()->wait(!Mutex::_no_safepoint_check_flag, wait_time); - if (timeout) { - MutexUnlocker ul(lock()); - NMethodSweeper::possibly_sweep(); - } - } else { - // If there are no compilation tasks and we can compile new jobs - // (i.e., there is enough free space in the code cache) there is - // no need to invoke the sweeper. As a result, the hotness of methods - // remains unchanged. This behavior is desired, since we want to keep - // the stable state, i.e., we do not want to evict methods from the - // code cache if it is unnecessary. - // We need a timed wait here, since compiler threads can exit if compilation - // is disabled forever. We use 5 seconds wait time; the exiting of compiler threads - // is not critical and we do not want idle compiler threads to wake up too often. - lock()->wait(!Mutex::_no_safepoint_check_flag, 5*1000); - } + // If there are no compilation tasks and we can compile new jobs + // (i.e., there is enough free space in the code cache) there is + // no need to invoke the sweeper. As a result, the hotness of methods + // remains unchanged. This behavior is desired, since we want to keep + // the stable state, i.e., we do not want to evict methods from the + // code cache if it is unnecessary. + // We need a timed wait here, since compiler threads can exit if compilation + // is disabled forever. We use 5 seconds wait time; the exiting of compiler threads + // is not critical and we do not want idle compiler threads to wake up too often. + lock()->wait(!Mutex::_no_safepoint_check_flag, 5*1000); } if (CompileBroker::is_compilation_disabled_forever()) { @@ -886,8 +862,8 @@ void CompileBroker::compilation_init() { _compilers[1] = new SharkCompiler(); #endif // SHARK - // Start the CompilerThreads - init_compiler_threads(c1_count, c2_count); + // Start the compiler thread(s) and the sweeper thread + init_compiler_sweeper_threads(c1_count, c2_count); // totalTime performance counter is always created as it is required // by the implementation of java.lang.management.CompilationMBean. { @@ -991,13 +967,10 @@ void CompileBroker::compilation_init() { } -CompilerThread* CompileBroker::make_compiler_thread(const char* name, CompileQueue* queue, CompilerCounters* counters, - AbstractCompiler* comp, TRAPS) { - CompilerThread* compiler_thread = NULL; - - Klass* k = - SystemDictionary::resolve_or_fail(vmSymbols::java_lang_Thread(), - true, CHECK_0); +JavaThread* CompileBroker::make_thread(const char* name, CompileQueue* queue, CompilerCounters* counters, + AbstractCompiler* comp, bool compiler_thread, TRAPS) { + JavaThread* thread = NULL; + Klass* k = SystemDictionary::resolve_or_fail(vmSymbols::java_lang_Thread(), true, CHECK_0); instanceKlassHandle klass (THREAD, k); instanceHandle thread_oop = klass->allocate_instance_handle(CHECK_0); Handle string = java_lang_String::create_from_str(name, CHECK_0); @@ -1015,7 +988,11 @@ CompilerThread* CompileBroker::make_compiler_thread(const char* name, CompileQue { MutexLocker mu(Threads_lock, THREAD); - compiler_thread = new CompilerThread(queue, counters); + if (compiler_thread) { + thread = new CompilerThread(queue, counters); + } else { + thread = new CodeCacheSweeperThread(); + } // At this point the new CompilerThread data-races with this startup // thread (which I believe is the primoridal thread and NOT the VM // thread). This means Java bytecodes being executed at startup can @@ -1028,12 +1005,12 @@ CompilerThread* CompileBroker::make_compiler_thread(const char* name, CompileQue // in that case. However, since this must work and we do not allow // exceptions anyway, check and abort if this fails. - if (compiler_thread == NULL || compiler_thread->osthread() == NULL){ + if (thread == NULL || thread->osthread() == NULL) { vm_exit_during_initialization("java.lang.OutOfMemoryError", os::native_thread_creation_failed_msg()); } - java_lang_Thread::set_thread(thread_oop(), compiler_thread); + java_lang_Thread::set_thread(thread_oop(), thread); // Note that this only sets the JavaThread _priority field, which by // definition is limited to Java priorities and not OS priorities. @@ -1054,24 +1031,26 @@ CompilerThread* CompileBroker::make_compiler_thread(const char* name, CompileQue native_prio = os::java_to_os_priority[NearMaxPriority]; } } - os::set_native_priority(compiler_thread, native_prio); + os::set_native_priority(thread, native_prio); java_lang_Thread::set_daemon(thread_oop()); - compiler_thread->set_threadObj(thread_oop()); - compiler_thread->set_compiler(comp); - Threads::add(compiler_thread); - Thread::start(compiler_thread); + thread->set_threadObj(thread_oop()); + if (compiler_thread) { + thread->as_CompilerThread()->set_compiler(comp); + } + Threads::add(thread); + Thread::start(thread); } // Let go of Threads_lock before yielding os::naked_yield(); // make sure that the compiler thread is started early (especially helpful on SOLARIS) - return compiler_thread; + return thread; } -void CompileBroker::init_compiler_threads(int c1_compiler_count, int c2_compiler_count) { +void CompileBroker::init_compiler_sweeper_threads(int c1_compiler_count, int c2_compiler_count) { EXCEPTION_MARK; #if !defined(ZERO) && !defined(SHARK) assert(c2_compiler_count > 0 || c1_compiler_count > 0, "No compilers?"); @@ -1088,17 +1067,14 @@ void CompileBroker::init_compiler_threads(int c1_compiler_count, int c2_compiler int compiler_count = c1_compiler_count + c2_compiler_count; - _compiler_threads = - new (ResourceObj::C_HEAP, mtCompiler) GrowableArray(compiler_count, true); - char name_buffer[256]; + const bool compiler_thread = true; for (int i = 0; i < c2_compiler_count; i++) { // Create a name for our thread. sprintf(name_buffer, "C2 CompilerThread%d", i); CompilerCounters* counters = new CompilerCounters("compilerThread", i, CHECK); // Shark and C2 - CompilerThread* new_thread = make_compiler_thread(name_buffer, _c2_compile_queue, counters, _compilers[1], CHECK); - _compiler_threads->append(new_thread); + make_thread(name_buffer, _c2_compile_queue, counters, _compilers[1], compiler_thread, CHECK); } for (int i = c2_compiler_count; i < compiler_count; i++) { @@ -1106,13 +1082,17 @@ void CompileBroker::init_compiler_threads(int c1_compiler_count, int c2_compiler sprintf(name_buffer, "C1 CompilerThread%d", i); CompilerCounters* counters = new CompilerCounters("compilerThread", i, CHECK); // C1 - CompilerThread* new_thread = make_compiler_thread(name_buffer, _c1_compile_queue, counters, _compilers[0], CHECK); - _compiler_threads->append(new_thread); + make_thread(name_buffer, _c1_compile_queue, counters, _compilers[0], compiler_thread, CHECK); } if (UsePerfData) { PerfDataManager::create_constant(SUN_CI, "threads", PerfData::U_Bytes, compiler_count, CHECK); } + + if (MethodFlushing) { + // Initialize the sweeper thread + make_thread("Sweeper thread", NULL, NULL, NULL, false, CHECK); + } } @@ -1759,13 +1739,6 @@ void CompileBroker::compiler_thread_loop() { // We need this HandleMark to avoid leaking VM handles. HandleMark hm(thread); - // Check if the CodeCache is full - int code_blob_type = 0; - if (CodeCache::is_full(&code_blob_type)) { - // The CodeHeap for code_blob_type is really full - handle_full_code_cache(code_blob_type); - } - CompileTask* task = queue->get(); if (task == NULL) { continue; @@ -1773,8 +1746,9 @@ void CompileBroker::compiler_thread_loop() { // Give compiler threads an extra quanta. They tend to be bursty and // this helps the compiler to finish up the job. - if( CompilerThreadHintNoPreempt ) + if (CompilerThreadHintNoPreempt) { os::hint_no_preempt(); + } // trace per thread time and compile statistics CompilerCounters* counters = ((CompilerThread*)thread)->counters(); @@ -2074,8 +2048,10 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) { } /** - * The CodeCache is full. Print out warning and disable compilation - * or try code cache cleaning so compilation can continue later. + * The CodeCache is full. Print warning and disable compilation. + * Schedule code cache cleaning so compilation can continue later. + * This function needs to be called only from CodeCache::allocate(), + * since we currently handle a full code cache uniformly. */ void CompileBroker::handle_full_code_cache(int code_blob_type) { UseInterpreter = true; @@ -2107,10 +2083,6 @@ void CompileBroker::handle_full_code_cache(int code_blob_type) { if (CompileBroker::set_should_compile_new_jobs(CompileBroker::stop_compilation)) { NMethodSweeper::log_sweep("disable_compiler"); } - // Switch to 'vm_state'. This ensures that possibly_sweep() can be called - // without having to consider the state in which the current thread is. - ThreadInVMfromUnknown in_vm; - NMethodSweeper::possibly_sweep(); } else { disable_compilation_forever(); } diff --git a/hotspot/src/share/vm/compiler/compileBroker.hpp b/hotspot/src/share/vm/compiler/compileBroker.hpp index c199cb1480d..e5d3d6e7629 100644 --- a/hotspot/src/share/vm/compiler/compileBroker.hpp +++ b/hotspot/src/share/vm/compiler/compileBroker.hpp @@ -290,8 +290,6 @@ class CompileBroker: AllStatic { static CompileQueue* _c2_compile_queue; static CompileQueue* _c1_compile_queue; - static GrowableArray* _compiler_threads; - // performance counters static PerfCounter* _perf_total_compilation; static PerfCounter* _perf_native_compilation; @@ -339,8 +337,8 @@ class CompileBroker: AllStatic { static volatile jint _print_compilation_warning; - static CompilerThread* make_compiler_thread(const char* name, CompileQueue* queue, CompilerCounters* counters, AbstractCompiler* comp, TRAPS); - static void init_compiler_threads(int c1_compiler_count, int c2_compiler_count); + static JavaThread* make_thread(const char* name, CompileQueue* queue, CompilerCounters* counters, AbstractCompiler* comp, bool compiler_thread, TRAPS); + static void init_compiler_sweeper_threads(int c1_compiler_count, int c2_compiler_count); static bool compilation_is_complete (methodHandle method, int osr_bci, int comp_level); static bool compilation_is_prohibited(methodHandle method, int osr_bci, int comp_level); static bool is_compile_blocking(); diff --git a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp index 124b1d1e3fc..53be23d848b 100644 --- a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp +++ b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp @@ -1077,7 +1077,6 @@ IRT_END address SignatureHandlerLibrary::set_handler_blob() { BufferBlob* handler_blob = BufferBlob::create("native signature handlers", blob_size); if (handler_blob == NULL) { - CompileBroker::handle_full_code_cache(CodeBlobType::NonNMethod); return NULL; } address handler = handler_blob->code_begin(); diff --git a/hotspot/src/share/vm/memory/heap.cpp b/hotspot/src/share/vm/memory/heap.cpp index 096c212a703..5ba099e78e8 100644 --- a/hotspot/src/share/vm/memory/heap.cpp +++ b/hotspot/src/share/vm/memory/heap.cpp @@ -171,13 +171,13 @@ void CodeHeap::clear() { } -void* CodeHeap::allocate(size_t instance_size, bool is_critical) { +void* CodeHeap::allocate(size_t instance_size) { size_t number_of_segments = size_to_segments(instance_size + header_size()); assert(segments_to_size(number_of_segments) >= sizeof(FreeBlock), "not enough room for FreeList"); // First check if we can satisfy request from freelist NOT_PRODUCT(verify()); - HeapBlock* block = search_freelist(number_of_segments, is_critical); + HeapBlock* block = search_freelist(number_of_segments); NOT_PRODUCT(verify()); if (block != NULL) { @@ -191,15 +191,6 @@ void* CodeHeap::allocate(size_t instance_size, bool is_critical) { // Ensure minimum size for allocation to the heap. number_of_segments = MAX2((int)CodeCacheMinBlockLength, (int)number_of_segments); - if (!is_critical) { - // Make sure the allocation fits in the unallocated heap without using - // the CodeCacheMimimumFreeSpace that is reserved for critical allocations. - if (segments_to_size(number_of_segments) > (heap_unallocated_capacity() - CodeCacheMinimumFreeSpace)) { - // Fail allocation - return NULL; - } - } - if (_next_segment + number_of_segments <= _number_of_committed_segments) { mark_segmap_as_used(_next_segment, _next_segment + number_of_segments); HeapBlock* b = block_at(_next_segment); @@ -427,24 +418,17 @@ void CodeHeap::add_to_freelist(HeapBlock* a) { * Search freelist for an entry on the list with the best fit. * @return NULL, if no one was found */ -FreeBlock* CodeHeap::search_freelist(size_t length, bool is_critical) { +FreeBlock* CodeHeap::search_freelist(size_t length) { FreeBlock* found_block = NULL; FreeBlock* found_prev = NULL; size_t found_length = 0; FreeBlock* prev = NULL; FreeBlock* cur = _freelist; - const size_t critical_boundary = (size_t)high_boundary() - CodeCacheMinimumFreeSpace; // Search for first block that fits while(cur != NULL) { if (cur->length() >= length) { - // Non critical allocations are not allowed to use the last part of the code heap. - // Make sure the end of the allocation doesn't cross into the last part of the code heap. - if (!is_critical && (((size_t)cur + length) > critical_boundary)) { - // The freelist is sorted by address - if one fails, all consecutive will also fail. - break; - } // Remember block, its previous element, and its length found_block = cur; found_prev = prev; diff --git a/hotspot/src/share/vm/memory/heap.hpp b/hotspot/src/share/vm/memory/heap.hpp index c4150787768..4fdbeabbfc3 100644 --- a/hotspot/src/share/vm/memory/heap.hpp +++ b/hotspot/src/share/vm/memory/heap.hpp @@ -120,7 +120,7 @@ class CodeHeap : public CHeapObj { // Toplevel freelist management void add_to_freelist(HeapBlock* b); - FreeBlock* search_freelist(size_t length, bool is_critical); + FreeBlock* search_freelist(size_t length); // Iteration helpers void* next_free(HeapBlock* b) const; @@ -140,8 +140,8 @@ class CodeHeap : public CHeapObj { bool expand_by(size_t size); // expands committed memory by size // Memory allocation - void* allocate (size_t size, bool is_critical); // allocates a block of size or returns NULL - void deallocate(void* p); // deallocates a block + void* allocate (size_t size); // Allocate 'size' bytes in the code cache or return NULL + void deallocate(void* p); // Deallocate memory // Attributes char* low_boundary() const { return _memory.low_boundary (); } diff --git a/hotspot/src/share/vm/opto/compile.cpp b/hotspot/src/share/vm/opto/compile.cpp index 6304ff2686b..ec74d5ef4a1 100644 --- a/hotspot/src/share/vm/opto/compile.cpp +++ b/hotspot/src/share/vm/opto/compile.cpp @@ -535,7 +535,6 @@ void Compile::init_scratch_buffer_blob(int const_size) { if (scratch_buffer_blob() == NULL) { // Let CompilerBroker disable further compilations. record_failure("Not enough space for scratch buffer in CodeCache"); - CompileBroker::handle_full_code_cache(CodeBlobType::NonNMethod); return; } } diff --git a/hotspot/src/share/vm/opto/output.cpp b/hotspot/src/share/vm/opto/output.cpp index 165e187b964..6284abde6e2 100644 --- a/hotspot/src/share/vm/opto/output.cpp +++ b/hotspot/src/share/vm/opto/output.cpp @@ -1166,7 +1166,6 @@ CodeBuffer* Compile::init_buffer(uint* blk_starts) { // Have we run out of code space? if ((cb->blob() == NULL) || (!CompileBroker::should_compile_new_jobs())) { C->record_failure("CodeCache is full"); - CompileBroker::handle_full_code_cache(CodeBlobType::NonNMethod); return NULL; } // Configure the code buffer. @@ -1491,7 +1490,6 @@ void Compile::fill_buffer(CodeBuffer* cb, uint* blk_starts) { cb->insts()->maybe_expand_to_ensure_remaining(MAX_inst_size); if ((cb->blob() == NULL) || (!CompileBroker::should_compile_new_jobs())) { C->record_failure("CodeCache is full"); - CompileBroker::handle_full_code_cache(CodeBlobType::NonNMethod); return; } @@ -1648,7 +1646,6 @@ void Compile::fill_buffer(CodeBuffer* cb, uint* blk_starts) { // One last check for failed CodeBuffer::expand: if ((cb->blob() == NULL) || (!CompileBroker::should_compile_new_jobs())) { C->record_failure("CodeCache is full"); - CompileBroker::handle_full_code_cache(CodeBlobType::NonNMethod); return; } diff --git a/hotspot/src/share/vm/prims/methodHandles.cpp b/hotspot/src/share/vm/prims/methodHandles.cpp index 8053a3c9763..8013a1329a8 100644 --- a/hotspot/src/share/vm/prims/methodHandles.cpp +++ b/hotspot/src/share/vm/prims/methodHandles.cpp @@ -36,6 +36,7 @@ #include "runtime/reflection.hpp" #include "runtime/signature.hpp" #include "runtime/stubRoutines.hpp" +#include "utilities/exceptions.hpp" /* @@ -55,26 +56,30 @@ bool MethodHandles::_enabled = false; // set true after successful native linkage MethodHandlesAdapterBlob* MethodHandles::_adapter_code = NULL; -//------------------------------------------------------------------------------ -// MethodHandles::generate_adapters -// -void MethodHandles::generate_adapters() { - if (SystemDictionary::MethodHandle_klass() == NULL) return; + +/** + * Generates method handle adapters. Returns 'false' if memory allocation + * failed and true otherwise. + */ +bool MethodHandles::generate_adapters() { + if (SystemDictionary::MethodHandle_klass() == NULL) { + return true; + } assert(_adapter_code == NULL, "generate only once"); ResourceMark rm; TraceTime timer("MethodHandles adapters generation", TraceStartupTime); _adapter_code = MethodHandlesAdapterBlob::create(adapter_code_size); - if (_adapter_code == NULL) - vm_exit_out_of_memory(adapter_code_size, OOM_MALLOC_ERROR, - "CodeCache: no room for MethodHandles adapters"); - { - CodeBuffer code(_adapter_code); - MethodHandlesAdapterGenerator g(&code); - g.generate(); - code.log_section_sizes("MethodHandlesAdapterBlob"); + if (_adapter_code == NULL) { + return false; } + + CodeBuffer code(_adapter_code); + MethodHandlesAdapterGenerator g(&code); + g.generate(); + code.log_section_sizes("MethodHandlesAdapterBlob"); + return true; } //------------------------------------------------------------------------------ @@ -1401,7 +1406,9 @@ JVM_ENTRY(void, JVM_RegisterMethodHandleMethods(JNIEnv *env, jclass MHN_class)) } if (enable_MH) { - MethodHandles::generate_adapters(); + if (MethodHandles::generate_adapters() == false) { + THROW_MSG(vmSymbols::java_lang_VirtualMachineError(), "Out of space in CodeCache for method handle adapters"); + } MethodHandles::set_enabled(true); } } diff --git a/hotspot/src/share/vm/prims/methodHandles.hpp b/hotspot/src/share/vm/prims/methodHandles.hpp index 323cd823664..4084d6c7d8b 100644 --- a/hotspot/src/share/vm/prims/methodHandles.hpp +++ b/hotspot/src/share/vm/prims/methodHandles.hpp @@ -69,7 +69,7 @@ class MethodHandles: AllStatic { enum { _suppress_defc = 1, _suppress_name = 2, _suppress_type = 4 }; // Generate MethodHandles adapters. - static void generate_adapters(); + static bool generate_adapters(); // Called from MethodHandlesAdapterGenerator. static address generate_method_handle_interpreter_entry(MacroAssembler* _masm, vmIntrinsics::ID iid); diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp index d62d286e18c..1907f54d8fc 100644 --- a/hotspot/src/share/vm/runtime/arguments.cpp +++ b/hotspot/src/share/vm/runtime/arguments.cpp @@ -306,6 +306,9 @@ static ObsoleteFlag obsolete_jvm_flags[] = { { "ReflectionWrapResolutionErrors",JDK_Version::jdk(9), JDK_Version::jdk(10) }, { "VerifyReflectionBytecodes", JDK_Version::jdk(9), JDK_Version::jdk(10) }, { "AutoShutdownNMT", JDK_Version::jdk(9), JDK_Version::jdk(10) }, + { "NmethodSweepFraction", JDK_Version::jdk(9), JDK_Version::jdk(10) }, + { "NmethodSweepCheckInterval", JDK_Version::jdk(9), JDK_Version::jdk(10) }, + { "CodeCacheMinimumFreeSpace", JDK_Version::jdk(9), JDK_Version::jdk(10) }, #ifndef ZERO { "UseFastAccessorMethods", JDK_Version::jdk(9), JDK_Version::jdk(10) }, { "UseFastEmptyMethods", JDK_Version::jdk(9), JDK_Version::jdk(10) }, @@ -2528,7 +2531,7 @@ bool Arguments::check_vm_args_consistency() { // Check lower bounds of the code cache // Template Interpreter code is approximately 3X larger in debug builds. - uint min_code_cache_size = (CodeCacheMinimumUseSpace DEBUG_ONLY(* 3)) + CodeCacheMinimumFreeSpace; + uint min_code_cache_size = CodeCacheMinimumUseSpace DEBUG_ONLY(* 3); if (InitialCodeCacheSize < (uintx)os::vm_page_size()) { jio_fprintf(defaultStream::error_stream(), "Invalid InitialCodeCacheSize=%dK. Must be at least %dK.\n", InitialCodeCacheSize/K, @@ -2564,10 +2567,11 @@ bool Arguments::check_vm_args_consistency() { status = false; } - status &= verify_interval(NmethodSweepFraction, 1, ReservedCodeCacheSize/K, "NmethodSweepFraction"); status &= verify_interval(NmethodSweepActivity, 0, 2000, "NmethodSweepActivity"); status &= verify_interval(CodeCacheMinBlockLength, 1, 100, "CodeCacheMinBlockLength"); status &= verify_interval(CodeCacheSegmentSize, 1, 1024, "CodeCacheSegmentSize"); + status &= verify_interval(StartAggressiveSweepingAt, 0, 100, "StartAggressiveSweepingAt"); + int min_number_of_compiler_threads = get_min_number_of_compiler_threads(); // The default CICompilerCount's value is CI_COMPILER_COUNT. @@ -3985,12 +3989,6 @@ jint Arguments::apply_ergo() { #endif #endif - // Set NmethodSweepFraction after the size of the code cache is adapted (in case of tiered) - if (FLAG_IS_DEFAULT(NmethodSweepFraction)) { - FLAG_SET_DEFAULT(NmethodSweepFraction, 1 + ReservedCodeCacheSize / (16 * M)); - } - - // Set heap size based on available physical memory set_heap_size(); @@ -4058,13 +4056,6 @@ jint Arguments::apply_ergo() { } #ifndef PRODUCT - if (CompileTheWorld) { - // Force NmethodSweeper to sweep whole CodeCache each time. - if (FLAG_IS_DEFAULT(NmethodSweepFraction)) { - NmethodSweepFraction = 1; - } - } - if (!LogVMOutput && FLAG_IS_DEFAULT(LogVMOutput)) { if (use_vm_log()) { LogVMOutput = true; diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp index f219767d9ab..e14b51baa1f 100644 --- a/hotspot/src/share/vm/runtime/globals.hpp +++ b/hotspot/src/share/vm/runtime/globals.hpp @@ -2984,12 +2984,6 @@ class CommandLineFlags { product(intx, SafepointTimeoutDelay, 10000, \ "Delay in milliseconds for option SafepointTimeout") \ \ - product(intx, NmethodSweepFraction, 16, \ - "Number of invocations of sweeper to cover all nmethods") \ - \ - product(intx, NmethodSweepCheckInterval, 5, \ - "Compilers wake up every n seconds to possibly sweep nmethods") \ - \ product(intx, NmethodSweepActivity, 10, \ "Removes cold nmethods from code cache if > 0. Higher values " \ "result in more aggressive sweeping") \ @@ -3378,9 +3372,6 @@ class CommandLineFlags { product_pd(uintx, NonNMethodCodeHeapSize, \ "Size of code heap with non-nmethods (in bytes)") \ \ - product(uintx, CodeCacheMinimumFreeSpace, 500*K, \ - "When less than X space left, we stop compiling") \ - \ product_pd(uintx, CodeCacheExpansionSize, \ "Code cache expansion size (in bytes)") \ \ @@ -3393,6 +3384,11 @@ class CommandLineFlags { product(bool, UseCodeCacheFlushing, true, \ "Remove cold/old nmethods from the code cache") \ \ + product(uintx, StartAggressiveSweepingAt, 10, \ + "Start aggressive sweeping if X[%] of the code cache is free." \ + "Segmented code cache: X[%] of the non-profiled heap." \ + "Non-segmented code cache: X[%] of the total code cache") \ + \ /* interpreter debugging */ \ develop(intx, BinarySwitchThreshold, 5, \ "Minimal number of lookupswitch entries for rewriting to binary " \ diff --git a/hotspot/src/share/vm/runtime/mutexLocker.cpp b/hotspot/src/share/vm/runtime/mutexLocker.cpp index 5cbbbeb3828..25a897a142f 100644 --- a/hotspot/src/share/vm/runtime/mutexLocker.cpp +++ b/hotspot/src/share/vm/runtime/mutexLocker.cpp @@ -61,7 +61,7 @@ Mutex* SymbolTable_lock = NULL; Mutex* StringTable_lock = NULL; Monitor* StringDedupQueue_lock = NULL; Mutex* StringDedupTable_lock = NULL; -Mutex* CodeCache_lock = NULL; +Monitor* CodeCache_lock = NULL; Mutex* MethodData_lock = NULL; Mutex* RetData_lock = NULL; Monitor* VMOperationQueue_lock = NULL; @@ -205,7 +205,7 @@ void mutex_init() { } def(ParGCRareEvent_lock , Mutex , leaf , true ); def(DerivedPointerTableGC_lock , Mutex, leaf, true ); - def(CodeCache_lock , Mutex , special, true ); + def(CodeCache_lock , Monitor, special, true ); def(Interrupt_lock , Monitor, special, true ); // used for interrupt processing def(RawMonitor_lock , Mutex, special, true ); def(OopMapCacheAlloc_lock , Mutex, leaf, true ); // used for oop_map_cache allocation. diff --git a/hotspot/src/share/vm/runtime/mutexLocker.hpp b/hotspot/src/share/vm/runtime/mutexLocker.hpp index ab027291d30..94d8adec813 100644 --- a/hotspot/src/share/vm/runtime/mutexLocker.hpp +++ b/hotspot/src/share/vm/runtime/mutexLocker.hpp @@ -53,7 +53,7 @@ extern Mutex* SymbolTable_lock; // a lock on the symbol table extern Mutex* StringTable_lock; // a lock on the interned string table extern Monitor* StringDedupQueue_lock; // a lock on the string deduplication queue extern Mutex* StringDedupTable_lock; // a lock on the string deduplication table -extern Mutex* CodeCache_lock; // a lock on the CodeCache, rank is special, use MutexLockerEx +extern Monitor* CodeCache_lock; // a lock on the CodeCache, rank is special, use MutexLockerEx extern Mutex* MethodData_lock; // a lock on installation of method data extern Mutex* RetData_lock; // a lock on installation of RetData inside method data extern Mutex* DerivedPointerTableGC_lock; // a lock to protect the derived pointer table diff --git a/hotspot/src/share/vm/runtime/sharedRuntime.cpp b/hotspot/src/share/vm/runtime/sharedRuntime.cpp index 7c47c6f2656..344c2a61f3e 100644 --- a/hotspot/src/share/vm/runtime/sharedRuntime.cpp +++ b/hotspot/src/share/vm/runtime/sharedRuntime.cpp @@ -2421,8 +2421,6 @@ AdapterHandlerEntry* AdapterHandlerLibrary::get_adapter(methodHandle method) { // CodeCache is full, disable compilation // Ought to log this but compile log is only per compile thread // and we're some non descript Java thread. - MutexUnlocker mu(AdapterHandlerLibrary_lock); - CompileBroker::handle_full_code_cache(CodeBlobType::NonNMethod); return NULL; // Out of CodeCache space } entry->relocate(new_adapter->content_begin()); @@ -2594,9 +2592,6 @@ void AdapterHandlerLibrary::create_native_wrapper(methodHandle method) { CompileTask::print_compilation(tty, nm, method->is_static() ? "(static)" : ""); } nm->post_compiled_method_load_event(); - } else { - // CodeCache is full, disable compilation - CompileBroker::handle_full_code_cache(CodeBlobType::MethodNonProfiled); } } diff --git a/hotspot/src/share/vm/runtime/sweeper.cpp b/hotspot/src/share/vm/runtime/sweeper.cpp index cecb8ff08a1..1f70b6edc20 100644 --- a/hotspot/src/share/vm/runtime/sweeper.cpp +++ b/hotspot/src/share/vm/runtime/sweeper.cpp @@ -52,7 +52,6 @@ PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC class SweeperRecord { public: int traversal; - int invocation; int compile_id; long traversal_mark; int state; @@ -62,10 +61,9 @@ class SweeperRecord { int line; void print() { - tty->print_cr("traversal = %d invocation = %d compile_id = %d %s uep = " PTR_FORMAT " vep = " + tty->print_cr("traversal = %d compile_id = %d %s uep = " PTR_FORMAT " vep = " PTR_FORMAT " state = %d traversal_mark %d line = %d", traversal, - invocation, compile_id, kind == NULL ? "" : kind, uep, @@ -117,7 +115,6 @@ void NMethodSweeper::record_sweep(nmethod* nm, int line) { if (_records != NULL) { _records[_sweep_index].traversal = _traversals; _records[_sweep_index].traversal_mark = nm->_stack_traversal_mark; - _records[_sweep_index].invocation = _sweep_fractions_left; _records[_sweep_index].compile_id = nm->compile_id(); _records[_sweep_index].kind = nm->compile_kind(); _records[_sweep_index].state = nm->_state; @@ -127,6 +124,14 @@ void NMethodSweeper::record_sweep(nmethod* nm, int line) { _sweep_index = (_sweep_index + 1) % SweeperLogEntries; } } + +void NMethodSweeper::init_sweeper_log() { + if (LogSweeper && _records == NULL) { + // Create the ring buffer for the logging code + _records = NEW_C_HEAP_ARRAY(SweeperRecord, SweeperLogEntries, mtGC); + memset(_records, 0, sizeof(SweeperRecord) * SweeperLogEntries); + } +} #else #define SWEEP(nm) #endif @@ -142,8 +147,6 @@ int NMethodSweeper::_zombified_count = 0; // Nof. nmethods int NMethodSweeper::_marked_for_reclamation_count = 0; // Nof. nmethods marked for reclaim in current sweep volatile bool NMethodSweeper::_should_sweep = true; // Indicates if we should invoke the sweeper -volatile int NMethodSweeper::_sweep_fractions_left = 0; // Nof. invocations left until we are completed with this pass -volatile int NMethodSweeper::_sweep_started = 0; // Flag to control conc sweeper volatile int NMethodSweeper::_bytes_changed = 0; // Counts the total nmethod size if the nmethod changed from: // 1) alive -> not_entrant // 2) not_entrant -> zombie @@ -190,13 +193,15 @@ int NMethodSweeper::hotness_counter_reset_val() { } return _hotness_counter_reset_val; } -bool NMethodSweeper::sweep_in_progress() { - return !_current.end(); +bool NMethodSweeper::wait_for_stack_scanning() { + return _current.end(); } -// Scans the stacks of all Java threads and marks activations of not-entrant methods. -// No need to synchronize access, since 'mark_active_nmethods' is always executed at a -// safepoint. +/** + * Scans the stacks of all Java threads and marks activations of not-entrant methods. + * No need to synchronize access, since 'mark_active_nmethods' is always executed at a + * safepoint. + */ void NMethodSweeper::mark_active_nmethods() { assert(SafepointSynchronize::is_at_safepoint(), "must be executed at a safepoint"); // If we do not want to reclaim not-entrant or zombie methods there is no need @@ -210,9 +215,8 @@ void NMethodSweeper::mark_active_nmethods() { // Check for restart assert(CodeCache::find_blob_unsafe(_current.method()) == _current.method(), "Sweeper nmethod cached state invalid"); - if (!sweep_in_progress()) { + if (wait_for_stack_scanning()) { _seen = 0; - _sweep_fractions_left = NmethodSweepFraction; _current = NMethodIterator(); // Initialize to first nmethod _current.next(); @@ -231,6 +235,64 @@ void NMethodSweeper::mark_active_nmethods() { OrderAccess::storestore(); } + +/** + * This function triggers a VM operation that does stack scanning of active + * methods. Stack scanning is mandatory for the sweeper to make progress. + */ +void NMethodSweeper::do_stack_scanning() { + assert(!CodeCache_lock->owned_by_self(), "just checking"); + if (wait_for_stack_scanning()) { + VM_MarkActiveNMethods op; + VMThread::execute(&op); + _should_sweep = true; + } +} + +void NMethodSweeper::sweeper_loop() { + bool timeout; + while (true) { + { + ThreadBlockInVM tbivm(JavaThread::current()); + MutexLockerEx waiter(CodeCache_lock, Mutex::_no_safepoint_check_flag); + const long wait_time = 60*60*24 * 1000; + timeout = CodeCache_lock->wait(Mutex::_no_safepoint_check_flag, wait_time); + } + if (!timeout) { + possibly_sweep(); + } + } +} + +/** + * Wakes up the sweeper thread to possibly sweep. + */ +void NMethodSweeper::notify(int code_blob_type) { + // Makes sure that we do not invoke the sweeper too often during startup. + double start_threshold = 100.0 / (double)StartAggressiveSweepingAt; + double aggressive_sweep_threshold = MIN2(start_threshold, 1.1); + if (CodeCache::reverse_free_ratio(code_blob_type) >= aggressive_sweep_threshold) { + assert_locked_or_safepoint(CodeCache_lock); + CodeCache_lock->notify(); + } +} + +/** + * Handle a safepoint request + */ +void NMethodSweeper::handle_safepoint_request() { + if (SafepointSynchronize::is_synchronizing()) { + if (PrintMethodFlushing && Verbose) { + tty->print_cr("### Sweep at %d out of %d, yielding to safepoint", _seen, CodeCache::nof_nmethods()); + } + MutexUnlockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + + JavaThread* thread = JavaThread::current(); + ThreadBlockInVM tbivm(thread); + thread->java_suspend_self(); + } +} + /** * This function invokes the sweeper if at least one of the three conditions is met: * (1) The code cache is getting full @@ -239,11 +301,6 @@ void NMethodSweeper::mark_active_nmethods() { */ void NMethodSweeper::possibly_sweep() { assert(JavaThread::current()->thread_state() == _thread_in_vm, "must run in vm mode"); - // Only compiler threads are allowed to sweep - if (!MethodFlushing || !sweep_in_progress() || !Thread::current()->is_Compiler_thread()) { - return; - } - // If there was no state change while nmethod sweeping, 'should_sweep' will be false. // This is one of the two places where should_sweep can be set to true. The general // idea is as follows: If there is enough free space in the code cache, there is no @@ -280,46 +337,37 @@ void NMethodSweeper::possibly_sweep() { } } - if (_should_sweep && _sweep_fractions_left > 0) { - // Only one thread at a time will sweep - jint old = Atomic::cmpxchg( 1, &_sweep_started, 0 ); - if (old != 0) { - return; - } -#ifdef ASSERT - if (LogSweeper && _records == NULL) { - // Create the ring buffer for the logging code - _records = NEW_C_HEAP_ARRAY(SweeperRecord, SweeperLogEntries, mtGC); - memset(_records, 0, sizeof(SweeperRecord) * SweeperLogEntries); - } -#endif + // Force stack scanning if there is only 10% free space in the code cache. + // We force stack scanning only non-profiled code heap gets full, since critical + // allocation go to the non-profiled heap and we must be make sure that there is + // enough space. + double free_percent = 1 / CodeCache::reverse_free_ratio(CodeBlobType::MethodNonProfiled) * 100; + if (free_percent <= StartAggressiveSweepingAt) { + do_stack_scanning(); + } - if (_sweep_fractions_left > 0) { - sweep_code_cache(); - _sweep_fractions_left--; - } + if (_should_sweep) { + init_sweeper_log(); + sweep_code_cache(); + } - // We are done with sweeping the code cache once. - if (_sweep_fractions_left == 0) { - _total_nof_code_cache_sweeps++; - _last_sweep = _time_counter; - // Reset flag; temporarily disables sweeper - _should_sweep = false; - // If there was enough state change, 'possibly_enable_sweeper()' - // sets '_should_sweep' to true - possibly_enable_sweeper(); - // Reset _bytes_changed only if there was enough state change. _bytes_changed - // can further increase by calls to 'report_state_change'. - if (_should_sweep) { - _bytes_changed = 0; - } - } - // Release work, because another compiler thread could continue. - OrderAccess::release_store((int*)&_sweep_started, 0); + // We are done with sweeping the code cache once. + _total_nof_code_cache_sweeps++; + _last_sweep = _time_counter; + // Reset flag; temporarily disables sweeper + _should_sweep = false; + // If there was enough state change, 'possibly_enable_sweeper()' + // sets '_should_sweep' to true + possibly_enable_sweeper(); + // Reset _bytes_changed only if there was enough state change. _bytes_changed + // can further increase by calls to 'report_state_change'. + if (_should_sweep) { + _bytes_changed = 0; } } void NMethodSweeper::sweep_code_cache() { + ResourceMark rm; Ticks sweep_start_counter = Ticks::now(); _flushed_count = 0; @@ -327,25 +375,10 @@ void NMethodSweeper::sweep_code_cache() { _marked_for_reclamation_count = 0; if (PrintMethodFlushing && Verbose) { - tty->print_cr("### Sweep at %d out of %d. Invocations left: %d", _seen, CodeCache::nof_nmethods(), _sweep_fractions_left); + tty->print_cr("### Sweep at %d out of %d", _seen, CodeCache::nof_nmethods()); } - if (!CompileBroker::should_compile_new_jobs()) { - // If we have turned off compilations we might as well do full sweeps - // in order to reach the clean state faster. Otherwise the sleeping compiler - // threads will slow down sweeping. - _sweep_fractions_left = 1; - } - - // We want to visit all nmethods after NmethodSweepFraction - // invocations so divide the remaining number of nmethods by the - // remaining number of invocations. This is only an estimate since - // the number of nmethods changes during the sweep so the final - // stage must iterate until it there are no more nmethods. - int todo = (CodeCache::nof_nmethods() - _seen) / _sweep_fractions_left; int swept_count = 0; - - assert(!SafepointSynchronize::is_at_safepoint(), "should not be in safepoint when we get here"); assert(!CodeCache_lock->owned_by_self(), "just checking"); @@ -354,19 +387,9 @@ void NMethodSweeper::sweep_code_cache() { MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); // The last invocation iterates until there are no more nmethods - while ((swept_count < todo || _sweep_fractions_left == 1) && !_current.end()) { + while (!_current.end()) { swept_count++; - if (SafepointSynchronize::is_synchronizing()) { // Safepoint request - if (PrintMethodFlushing && Verbose) { - tty->print_cr("### Sweep at %d out of %d, invocation: %d, yielding to safepoint", _seen, CodeCache::nof_nmethods(), _sweep_fractions_left); - } - MutexUnlockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - - assert(Thread::current()->is_Java_thread(), "should be java thread"); - JavaThread* thread = (JavaThread*)Thread::current(); - ThreadBlockInVM tbivm(thread); - thread->java_suspend_self(); - } + handle_safepoint_request(); // Since we will give up the CodeCache_lock, always skip ahead // to the next nmethod. Other blobs can be deleted by other // threads but nmethods are only reclaimed by the sweeper. @@ -382,7 +405,7 @@ void NMethodSweeper::sweep_code_cache() { } } - assert(_sweep_fractions_left > 1 || _current.end(), "must have scanned the whole cache"); + assert(_current.end(), "must have scanned the whole cache"); const Ticks sweep_end_counter = Ticks::now(); const Tickspan sweep_time = sweep_end_counter - sweep_start_counter; @@ -397,7 +420,6 @@ void NMethodSweeper::sweep_code_cache() { event.set_starttime(sweep_start_counter); event.set_endtime(sweep_end_counter); event.set_sweepIndex(_traversals); - event.set_sweepFractionIndex(NmethodSweepFraction - _sweep_fractions_left + 1); event.set_sweptCount(swept_count); event.set_flushedCount(_flushed_count); event.set_markedCount(_marked_for_reclamation_count); @@ -407,15 +429,12 @@ void NMethodSweeper::sweep_code_cache() { #ifdef ASSERT if(PrintMethodFlushing) { - tty->print_cr("### sweeper: sweep time(%d): " - INT64_FORMAT, _sweep_fractions_left, (jlong)sweep_time.value()); + tty->print_cr("### sweeper: sweep time(%d): ", (jlong)sweep_time.value()); } #endif - if (_sweep_fractions_left == 1) { - _peak_sweep_time = MAX2(_peak_sweep_time, _total_time_this_sweep); - log_sweep("finished"); - } + _peak_sweep_time = MAX2(_peak_sweep_time, _total_time_this_sweep); + log_sweep("finished"); // Sweeper is the only case where memory is released, check here if it // is time to restart the compiler. Only checking if there is a certain @@ -459,10 +478,12 @@ void NMethodSweeper::possibly_enable_sweeper() { class NMethodMarker: public StackObj { private: - CompilerThread* _thread; + CodeCacheSweeperThread* _thread; public: NMethodMarker(nmethod* nm) { - _thread = CompilerThread::current(); + JavaThread* current = JavaThread::current(); + assert (current->is_Code_cache_sweeper_thread(), "Must be"); + _thread = (CodeCacheSweeperThread*)JavaThread::current(); if (!nm->is_zombie() && !nm->is_unloaded()) { // Only expose live nmethods for scanning _thread->set_scanned_nmethod(nm); @@ -473,7 +494,7 @@ class NMethodMarker: public StackObj { } }; -void NMethodSweeper::release_nmethod(nmethod *nm) { +void NMethodSweeper::release_nmethod(nmethod* nm) { // Clean up any CompiledICHolders { ResourceMark rm; @@ -490,7 +511,7 @@ void NMethodSweeper::release_nmethod(nmethod *nm) { nm->flush(); } -int NMethodSweeper::process_nmethod(nmethod *nm) { +int NMethodSweeper::process_nmethod(nmethod* nm) { assert(!CodeCache_lock->owned_by_self(), "just checking"); int freed_memory = 0; diff --git a/hotspot/src/share/vm/runtime/sweeper.hpp b/hotspot/src/share/vm/runtime/sweeper.hpp index 82b1a279c2a..2da9425390e 100644 --- a/hotspot/src/share/vm/runtime/sweeper.hpp +++ b/hotspot/src/share/vm/runtime/sweeper.hpp @@ -49,9 +49,7 @@ // remove the nmethod, all inline caches (IC) that point to the the nmethod must be // cleared. After that, the nmethod can be evicted from the code cache. Each nmethod's // state change happens during separate sweeps. It may take at least 3 sweeps before an -// nmethod's space is freed. Sweeping is currently done by compiler threads between -// compilations or at least each 5 sec (NmethodSweepCheckInterval) when the code cache -// is full. +// nmethod's space is freed. class NMethodSweeper : public AllStatic { static long _traversals; // Stack scan count, also sweep ID. @@ -64,7 +62,6 @@ class NMethodSweeper : public AllStatic { static int _zombified_count; // Nof. nmethods made zombie in current sweep static int _marked_for_reclamation_count; // Nof. nmethods marked for reclaim in current sweep - static volatile int _sweep_fractions_left; // Nof. invocations left until we are completed with this pass static volatile int _sweep_started; // Flag to control conc sweeper static volatile bool _should_sweep; // Indicates if we should invoke the sweeper static volatile int _bytes_changed; // Counts the total nmethod size if the nmethod changed from: @@ -85,8 +82,12 @@ class NMethodSweeper : public AllStatic { static int process_nmethod(nmethod *nm); static void release_nmethod(nmethod* nm); - static bool sweep_in_progress(); + static void init_sweeper_log() NOT_DEBUG_RETURN; + static bool wait_for_stack_scanning(); static void sweep_code_cache(); + static void handle_safepoint_request(); + static void do_stack_scanning(); + static void possibly_sweep(); public: static long traversal_count() { return _traversals; } @@ -106,7 +107,8 @@ class NMethodSweeper : public AllStatic { #endif static void mark_active_nmethods(); // Invoked at the end of each safepoint - static void possibly_sweep(); // Compiler threads call this to sweep + static void sweeper_loop(); + static void notify(int code_blob_type); // Possibly start the sweeper thread. static int hotness_counter_reset_val(); static void report_state_change(nmethod* nm); diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp index a522867da21..471f9e1eeda 100644 --- a/hotspot/src/share/vm/runtime/thread.cpp +++ b/hotspot/src/share/vm/runtime/thread.cpp @@ -66,6 +66,7 @@ #include "runtime/sharedRuntime.hpp" #include "runtime/statSampler.hpp" #include "runtime/stubRoutines.hpp" +#include "runtime/sweeper.hpp" #include "runtime/task.hpp" #include "runtime/thread.inline.hpp" #include "runtime/threadCritical.hpp" @@ -1551,6 +1552,7 @@ void JavaThread::block_if_vm_exited() { // Remove this ifdef when C1 is ported to the compiler interface. static void compiler_thread_entry(JavaThread* thread, TRAPS); +static void sweeper_thread_entry(JavaThread* thread, TRAPS); JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) : Thread() @@ -3170,6 +3172,10 @@ static void compiler_thread_entry(JavaThread* thread, TRAPS) { CompileBroker::compiler_thread_loop(); } +static void sweeper_thread_entry(JavaThread* thread, TRAPS) { + NMethodSweeper::sweeper_loop(); +} + // Create a CompilerThread CompilerThread::CompilerThread(CompileQueue* queue, CompilerCounters* counters) @@ -3180,7 +3186,6 @@ CompilerThread::CompilerThread(CompileQueue* queue, _queue = queue; _counters = counters; _buffer_blob = NULL; - _scanned_nmethod = NULL; _compiler = NULL; #ifndef PRODUCT @@ -3188,7 +3193,12 @@ CompilerThread::CompilerThread(CompileQueue* queue, #endif } -void CompilerThread::oops_do(OopClosure* f, CLDClosure* cld_f, CodeBlobClosure* cf) { +// Create sweeper thread +CodeCacheSweeperThread::CodeCacheSweeperThread() +: JavaThread(&sweeper_thread_entry) { + _scanned_nmethod = NULL; +} +void CodeCacheSweeperThread::oops_do(OopClosure* f, CLDClosure* cld_f, CodeBlobClosure* cf) { JavaThread::oops_do(f, cld_f, cf); if (_scanned_nmethod != NULL && cf != NULL) { // Safepoints can occur when the sweeper is scanning an nmethod so diff --git a/hotspot/src/share/vm/runtime/thread.hpp b/hotspot/src/share/vm/runtime/thread.hpp index 79f25e889ee..7bec298b95d 100644 --- a/hotspot/src/share/vm/runtime/thread.hpp +++ b/hotspot/src/share/vm/runtime/thread.hpp @@ -305,6 +305,7 @@ class Thread: public ThreadShadow { virtual bool is_VM_thread() const { return false; } virtual bool is_Java_thread() const { return false; } virtual bool is_Compiler_thread() const { return false; } + virtual bool is_Code_cache_sweeper_thread() const { return false; } virtual bool is_hidden_from_external_view() const { return false; } virtual bool is_jvmti_agent_thread() const { return false; } // True iff the thread can perform GC operations at a safepoint. @@ -1746,6 +1747,24 @@ inline CompilerThread* JavaThread::as_CompilerThread() { return (CompilerThread*)this; } +// Dedicated thread to sweep the code cache +class CodeCacheSweeperThread : public JavaThread { + nmethod* _scanned_nmethod; // nmethod being scanned by the sweeper + public: + CodeCacheSweeperThread(); + // Track the nmethod currently being scanned by the sweeper + void set_scanned_nmethod(nmethod* nm) { + assert(_scanned_nmethod == NULL || nm == NULL, "should reset to NULL before writing a new value"); + _scanned_nmethod = nm; + } + + bool is_Code_cache_sweeper_thread() const { return true; } + // GC support + // Apply "f->do_oop" to all root oops in "this". + // Apply "cf->do_code_blob" (if !NULL) to all code blobs active in frames + void oops_do(OopClosure* f, CLDClosure* cld_f, CodeBlobClosure* cf); +}; + // A thread used for Compilation. class CompilerThread : public JavaThread { friend class VMStructs; @@ -1758,7 +1777,6 @@ class CompilerThread : public JavaThread { CompileQueue* _queue; BufferBlob* _buffer_blob; - nmethod* _scanned_nmethod; // nmethod being scanned by the sweeper AbstractCompiler* _compiler; public: @@ -1792,28 +1810,17 @@ class CompilerThread : public JavaThread { _log = log; } - // GC support - // Apply "f->do_oop" to all root oops in "this". - // Apply "cf->do_code_blob" (if !NULL) to all code blobs active in frames - void oops_do(OopClosure* f, CLDClosure* cld_f, CodeBlobClosure* cf); - #ifndef PRODUCT private: IdealGraphPrinter *_ideal_graph_printer; public: - IdealGraphPrinter *ideal_graph_printer() { return _ideal_graph_printer; } - void set_ideal_graph_printer(IdealGraphPrinter *n) { _ideal_graph_printer = n; } + IdealGraphPrinter *ideal_graph_printer() { return _ideal_graph_printer; } + void set_ideal_graph_printer(IdealGraphPrinter *n) { _ideal_graph_printer = n; } #endif // Get/set the thread's current task - CompileTask* task() { return _task; } - void set_task(CompileTask* task) { _task = task; } - - // Track the nmethod currently being scanned by the sweeper - void set_scanned_nmethod(nmethod* nm) { - assert(_scanned_nmethod == NULL || nm == NULL, "should reset to NULL before writing a new value"); - _scanned_nmethod = nm; - } + CompileTask* task() { return _task; } + void set_task(CompileTask* task) { _task = task; } }; inline CompilerThread* CompilerThread::current() { diff --git a/hotspot/src/share/vm/runtime/vm_operations.cpp b/hotspot/src/share/vm/runtime/vm_operations.cpp index da07423db81..efacdc7c610 100644 --- a/hotspot/src/share/vm/runtime/vm_operations.cpp +++ b/hotspot/src/share/vm/runtime/vm_operations.cpp @@ -111,6 +111,9 @@ void VM_Deoptimize::doit() { CodeCache::make_marked_nmethods_zombies(); } +void VM_MarkActiveNMethods::doit() { + NMethodSweeper::mark_active_nmethods(); +} VM_DeoptimizeFrame::VM_DeoptimizeFrame(JavaThread* thread, intptr_t* id) { _thread = thread; diff --git a/hotspot/src/share/vm/runtime/vm_operations.hpp b/hotspot/src/share/vm/runtime/vm_operations.hpp index b5a1532338e..c8b4a3855fc 100644 --- a/hotspot/src/share/vm/runtime/vm_operations.hpp +++ b/hotspot/src/share/vm/runtime/vm_operations.hpp @@ -100,6 +100,7 @@ template(RotateGCLog) \ template(WhiteBoxOperation) \ template(ClassLoaderStatsOperation) \ + template(MarkActiveNMethods) \ template(PrintCompileQueue) \ template(PrintCodeList) \ template(PrintCodeCache) \ @@ -252,6 +253,13 @@ class VM_Deoptimize: public VM_Operation { bool allow_nested_vm_operations() const { return true; } }; +class VM_MarkActiveNMethods: public VM_Operation { + public: + VM_MarkActiveNMethods() {} + VMOp_Type type() const { return VMOp_MarkActiveNMethods; } + void doit(); + bool allow_nested_vm_operations() const { return true; } +}; // Deopt helper that can deoptimize frames in threads other than the // current thread. Only used through Deoptimization::deoptimize_frame. diff --git a/hotspot/src/share/vm/trace/trace.xml b/hotspot/src/share/vm/trace/trace.xml index 9ffc764599b..120d27f4d4d 100644 --- a/hotspot/src/share/vm/trace/trace.xml +++ b/hotspot/src/share/vm/trace/trace.xml @@ -383,7 +383,6 @@ Declares a structure type that can be used in other events. - diff --git a/hotspot/test/compiler/startup/SmallCodeCacheStartup.java b/hotspot/test/compiler/startup/SmallCodeCacheStartup.java index 5db43673fa8..72583df8502 100644 --- a/hotspot/test/compiler/startup/SmallCodeCacheStartup.java +++ b/hotspot/test/compiler/startup/SmallCodeCacheStartup.java @@ -27,10 +27,20 @@ * @summary Test ensures that there is no crash if there is not enough ReservedCodeacacheSize * to initialize all compiler threads. The option -Xcomp gives the VM more time to * to trigger the old bug. - * @run main/othervm -XX:ReservedCodeCacheSize=3m -XX:CICompilerCount=64 -Xcomp SmallCodeCacheStartup + * @library /testlibrary */ +import com.oracle.java.testlibrary.*; + public class SmallCodeCacheStartup { public static void main(String[] args) throws Exception { + try { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-XX:ReservedCodeCacheSize=3m", + "-XX:CICompilerCount=64", + "-Xcomp", + "SmallCodeCacheStartup"); + pb.start(); + } catch (VirtualMachineError e) {} + System.out.println("TEST PASSED"); } } diff --git a/hotspot/test/gc/g1/TestHumongousCodeCacheRoots.java b/hotspot/test/gc/g1/TestHumongousCodeCacheRoots.java index 122597aea64..d234cf81dfb 100644 --- a/hotspot/test/gc/g1/TestHumongousCodeCacheRoots.java +++ b/hotspot/test/gc/g1/TestHumongousCodeCacheRoots.java @@ -135,7 +135,6 @@ public class TestHumongousCodeCacheRoots { "-XX:+UnlockDiagnosticVMOptions", "-XX:InitiatingHeapOccupancyPercent=1", // strong code root marking "-XX:+G1VerifyHeapRegionCodeRoots", "-XX:+VerifyAfterGC", // make sure that verification is run - "-XX:NmethodSweepFraction=1", "-XX:NmethodSweepCheckInterval=1", // make the code cache sweep more predictable }; runTest("-client", baseArguments); runTest("-server", baseArguments); From 83181efbeb075e51846f911b46bbdb0353a720c4 Mon Sep 17 00:00:00 2001 From: Vladimir Ivanov Date: Fri, 24 Oct 2014 09:13:12 -0700 Subject: [PATCH 012/118] 8058847: C2: EliminateAutoBox regression after 8042786 Reviewed-by: kvn, roland --- hotspot/src/share/vm/opto/memnode.cpp | 10 +++ .../EliminateAutoBox/UnsignedLoads.java | 63 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 hotspot/test/compiler/EliminateAutoBox/UnsignedLoads.java diff --git a/hotspot/src/share/vm/opto/memnode.cpp b/hotspot/src/share/vm/opto/memnode.cpp index 3d8271dc92a..8ed5154f7be 100644 --- a/hotspot/src/share/vm/opto/memnode.cpp +++ b/hotspot/src/share/vm/opto/memnode.cpp @@ -1257,6 +1257,16 @@ Node* LoadNode::eliminate_autobox(PhaseGVN* phase) { result = new ConvI2LNode(phase->transform(result)); } #endif + // Boxing/unboxing can be done from signed & unsigned loads (e.g. LoadUB -> ... -> LoadB pair). + // Need to preserve unboxing load type if it is unsigned. + switch(this->Opcode()) { + case Op_LoadUB: + result = new AndINode(phase->transform(result), phase->intcon(0xFF)); + break; + case Op_LoadUS: + result = new AndINode(phase->transform(result), phase->intcon(0xFFFF)); + break; + } return result; } } diff --git a/hotspot/test/compiler/EliminateAutoBox/UnsignedLoads.java b/hotspot/test/compiler/EliminateAutoBox/UnsignedLoads.java new file mode 100644 index 00000000000..982c5f7beb1 --- /dev/null +++ b/hotspot/test/compiler/EliminateAutoBox/UnsignedLoads.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +/* + * @test + * @library /testlibrary + * @run main/othervm -Xbatch -XX:+EliminateAutoBox + * -XX:CompileOnly=::valueOf,::byteValue,::shortValue,::testUnsignedByte,::testUnsignedShort + * UnsignedLoads + */ +import static com.oracle.java.testlibrary.Asserts.assertEQ; + +public class UnsignedLoads { + public static int testUnsignedByte() { + byte[] bytes = new byte[] {-1}; + int res = 0; + for (int i = 0; i < 100000; i++) { + for (Byte b : bytes) { + res = b & 0xff; + } + } + return res; + } + + public static int testUnsignedShort() { + int res = 0; + short[] shorts = new short[] {-1}; + for (int i = 0; i < 100000; i++) { + for (Short s : shorts) { + res = s & 0xffff; + } + } + return res; + } + + public static void main(String[] args) { + assertEQ(testUnsignedByte(), 255); + assertEQ(testUnsignedShort(), 65535); + System.out.println("TEST PASSED"); + } +} From 916697af2c7d3b67e7769831ed7bba29211c4c67 Mon Sep 17 00:00:00 2001 From: Vladimir Ivanov Date: Fri, 24 Oct 2014 09:14:41 -0700 Subject: [PATCH 013/118] 8036748: assert(_base == Int) failed: Not an Int w/ -XX:+TraceIterativeGVN Reviewed-by: kvn, roland --- hotspot/src/share/vm/opto/callnode.cpp | 3 +- .../compiler/debug/TraceIterativeGVN.java | 39 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 hotspot/test/compiler/debug/TraceIterativeGVN.java diff --git a/hotspot/src/share/vm/opto/callnode.cpp b/hotspot/src/share/vm/opto/callnode.cpp index ccf23118bb3..619b37260b5 100644 --- a/hotspot/src/share/vm/opto/callnode.cpp +++ b/hotspot/src/share/vm/opto/callnode.cpp @@ -939,7 +939,8 @@ int CallStaticJavaNode::extract_uncommon_trap_request(const Node* call) { #ifndef PRODUCT if (!(call->req() > TypeFunc::Parms && call->in(TypeFunc::Parms) != NULL && - call->in(TypeFunc::Parms)->is_Con())) { + call->in(TypeFunc::Parms)->is_Con() && + call->in(TypeFunc::Parms)->bottom_type()->isa_int())) { assert(in_dump() != 0, "OK if dumping"); tty->print("[bad uncommon trap]"); return 0; diff --git a/hotspot/test/compiler/debug/TraceIterativeGVN.java b/hotspot/test/compiler/debug/TraceIterativeGVN.java new file mode 100644 index 00000000000..7d98dd660b9 --- /dev/null +++ b/hotspot/test/compiler/debug/TraceIterativeGVN.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2014, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +/* + * @test + * @run main/othervm -Xbatch -XX:-TieredCompilation + * -XX:+IgnoreUnrecognizedVMOptions -XX:+TraceIterativeGVN + * TraceIterativeGVN + */ +public class TraceIterativeGVN { + public static void main(String[] args) { + for (int i = 0; i < 100_000; i++) { + Byte.valueOf((byte)0); + } + System.out.println("TEST PASSED"); + } +} From db334facfc30b18884a323f1af6908bb52fa2f71 Mon Sep 17 00:00:00 2001 From: Sergei Kovalev Date: Fri, 24 Oct 2014 09:17:32 -0700 Subject: [PATCH 014/118] 8028481: [TESTBUG] compiler/jsr292/CreatesInterfaceDotEqualsCallInfo.java should be in needs_nashorn test group Reviewed-by: vlivanov, kvn --- hotspot/test/TEST.groups | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/hotspot/test/TEST.groups b/hotspot/test/TEST.groups index 0b65e906df7..01cd4dbcecb 100644 --- a/hotspot/test/TEST.groups +++ b/hotspot/test/TEST.groups @@ -198,7 +198,8 @@ compact2_minimal = \ # Tests that require compact2 API's # -needs_compact2 = +needs_compact2 = \ + compiler/jsr292/CreatesInterfaceDotEqualsCallInfo.java # All tests that run on the most minimal configuration: Minimal VM on Compact 1 compact1_minimal = \ @@ -479,7 +480,6 @@ hotspot_compiler_3 = \ compiler/intrinsics/unsafe/UnsafeGetAddressTest.java \ compiler/jsr292/ConcurrentClassLoadingTest.java \ compiler/jsr292/CreatesInterfaceDotEqualsCallInfo.java \ - compiler/jsr292/CreatesInterfaceDotEqualsCallInfo.java \ compiler/loopopts/TestLogSum.java \ compiler/macronodes/TestEliminateAllocationPhi.java \ compiler/membars/TestMemBarAcquire.java \ @@ -602,3 +602,14 @@ hotspot_all = \ :hotspot_gc \ :hotspot_runtime \ :hotspot_serviceability + +#All tests that depends on nashorn extension. +# +needs_nashorn = \ + compiler/jsr292/CreatesInterfaceDotEqualsCallInfo.java + +#All tests that do not depends on nashorn extension +# +not_needs_nashorn = \ + :jdk \ + -:needs_nashorh From c620214d183fb41a97ab9261df856a41ff60bb2c Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Fri, 24 Oct 2014 10:28:19 -0700 Subject: [PATCH 015/118] 8041984: CompilerThread seems to occupy all CPU in a very rare situation Add new timeout checks to EA. Reviewed-by: iveresov, drchase --- hotspot/src/share/vm/opto/c2_globals.hpp | 3 + hotspot/src/share/vm/opto/escape.cpp | 74 +++++++++++++++------- hotspot/src/share/vm/opto/escape.hpp | 78 ++++++++++++++++-------- 3 files changed, 106 insertions(+), 49 deletions(-) diff --git a/hotspot/src/share/vm/opto/c2_globals.hpp b/hotspot/src/share/vm/opto/c2_globals.hpp index 4c7c92a7809..a49f31641dc 100644 --- a/hotspot/src/share/vm/opto/c2_globals.hpp +++ b/hotspot/src/share/vm/opto/c2_globals.hpp @@ -476,6 +476,9 @@ product(bool, DoEscapeAnalysis, true, \ "Perform escape analysis") \ \ + product(double, EscapeAnalysisTimeout, 20. DEBUG_ONLY(+40.), \ + "Abort EA when it reaches time limit (in sec)") \ + \ develop(bool, ExitEscapeAnalysisOnTimeout, true, \ "Exit or throw assert in EA when it reaches time limit") \ \ diff --git a/hotspot/src/share/vm/opto/escape.cpp b/hotspot/src/share/vm/opto/escape.cpp index 2e454a70f88..9f09b62d761 100644 --- a/hotspot/src/share/vm/opto/escape.cpp +++ b/hotspot/src/share/vm/opto/escape.cpp @@ -38,6 +38,8 @@ ConnectionGraph::ConnectionGraph(Compile * C, PhaseIterGVN *igvn) : _nodes(C->comp_arena(), C->unique(), C->unique(), NULL), + _in_worklist(C->comp_arena()), + _next_pidx(0), _collecting(true), _verify(false), _compile(C), @@ -125,13 +127,19 @@ bool ConnectionGraph::compute_escape() { if (C->root() != NULL) { ideal_nodes.push(C->root()); } + // Processed ideal nodes are unique on ideal_nodes list + // but several ideal nodes are mapped to the phantom_obj. + // To avoid duplicated entries on the following worklists + // add the phantom_obj only once to them. + ptnodes_worklist.append(phantom_obj); + java_objects_worklist.append(phantom_obj); for( uint next = 0; next < ideal_nodes.size(); ++next ) { Node* n = ideal_nodes.at(next); // Create PointsTo nodes and add them to Connection Graph. Called // only once per ideal node since ideal_nodes is Unique_Node list. add_node_to_connection_graph(n, &delayed_worklist); PointsToNode* ptn = ptnode_adr(n->_idx); - if (ptn != NULL) { + if (ptn != NULL && ptn != phantom_obj) { ptnodes_worklist.append(ptn); if (ptn->is_JavaObject()) { java_objects_worklist.append(ptn->as_JavaObject()); @@ -415,7 +423,7 @@ void ConnectionGraph::add_node_to_connection_graph(Node *n, Unique_Node_List *de } case Op_CreateEx: { // assume that all exception objects globally escape - add_java_object(n, PointsToNode::GlobalEscape); + map_ideal_node(n, phantom_obj); break; } case Op_LoadKlass: @@ -1074,13 +1082,8 @@ bool ConnectionGraph::complete_connection_graph( // on graph complexity. Observed 8 passes in jvm2008 compiler.compiler. // Set limit to 20 to catch situation when something did go wrong and // bailout Escape Analysis. - // Also limit build time to 30 sec (60 in debug VM). + // Also limit build time to 20 sec (60 in debug VM), EscapeAnalysisTimeout flag. #define CG_BUILD_ITER_LIMIT 20 -#ifdef ASSERT -#define CG_BUILD_TIME_LIMIT 60.0 -#else -#define CG_BUILD_TIME_LIMIT 30.0 -#endif // Propagate GlobalEscape and ArgEscape escape states and check that // we still have non-escaping objects. The method pushs on _worklist @@ -1091,12 +1094,13 @@ bool ConnectionGraph::complete_connection_graph( // Now propagate references to all JavaObject nodes. int java_objects_length = java_objects_worklist.length(); elapsedTimer time; + bool timeout = false; int new_edges = 1; int iterations = 0; do { while ((new_edges > 0) && - (iterations++ < CG_BUILD_ITER_LIMIT) && - (time.seconds() < CG_BUILD_TIME_LIMIT)) { + (iterations++ < CG_BUILD_ITER_LIMIT)) { + double start_time = time.seconds(); time.start(); new_edges = 0; // Propagate references to phantom_object for nodes pushed on _worklist @@ -1105,7 +1109,26 @@ bool ConnectionGraph::complete_connection_graph( for (int next = 0; next < java_objects_length; ++next) { JavaObjectNode* ptn = java_objects_worklist.at(next); new_edges += add_java_object_edges(ptn, true); + +#define SAMPLE_SIZE 4 + if ((next % SAMPLE_SIZE) == 0) { + // Each 4 iterations calculate how much time it will take + // to complete graph construction. + time.stop(); + double stop_time = time.seconds(); + double time_per_iter = (stop_time - start_time) / (double)SAMPLE_SIZE; + double time_until_end = time_per_iter * (double)(java_objects_length - next); + if ((start_time + time_until_end) >= EscapeAnalysisTimeout) { + timeout = true; + break; // Timeout + } + start_time = stop_time; + time.start(); + } +#undef SAMPLE_SIZE + } + if (timeout) break; if (new_edges > 0) { // Update escape states on each iteration if graph was updated. if (!find_non_escaped_objects(ptnodes_worklist, non_escaped_worklist)) { @@ -1113,9 +1136,12 @@ bool ConnectionGraph::complete_connection_graph( } } time.stop(); + if (time.seconds() >= EscapeAnalysisTimeout) { + timeout = true; + break; + } } - if ((iterations < CG_BUILD_ITER_LIMIT) && - (time.seconds() < CG_BUILD_TIME_LIMIT)) { + if ((iterations < CG_BUILD_ITER_LIMIT) && !timeout) { time.start(); // Find fields which have unknown value. int fields_length = oop_fields_worklist.length(); @@ -1128,18 +1154,21 @@ bool ConnectionGraph::complete_connection_graph( } } time.stop(); + if (time.seconds() >= EscapeAnalysisTimeout) { + timeout = true; + break; + } } else { new_edges = 0; // Bailout } } while (new_edges > 0); // Bailout if passed limits. - if ((iterations >= CG_BUILD_ITER_LIMIT) || - (time.seconds() >= CG_BUILD_TIME_LIMIT)) { + if ((iterations >= CG_BUILD_ITER_LIMIT) || timeout) { Compile* C = _compile; if (C->log() != NULL) { C->log()->begin_elem("connectionGraph_bailout reason='reached "); - C->log()->text("%s", (iterations >= CG_BUILD_ITER_LIMIT) ? "iterations" : "time"); + C->log()->text("%s", timeout ? "time" : "iterations"); C->log()->end_elem(" limit'"); } assert(ExitEscapeAnalysisOnTimeout, err_msg_res("infinite EA connection graph build (%f sec, %d iterations) with %d nodes and worklist size %d", @@ -1156,7 +1185,6 @@ bool ConnectionGraph::complete_connection_graph( #endif #undef CG_BUILD_ITER_LIMIT -#undef CG_BUILD_TIME_LIMIT // Find fields initialized by NULL for non-escaping Allocations. int non_escaped_length = non_escaped_worklist.length(); @@ -1280,8 +1308,8 @@ int ConnectionGraph::add_java_object_edges(JavaObjectNode* jobj, bool populate_w } } } - while(_worklist.length() > 0) { - PointsToNode* use = _worklist.pop(); + for (int l = 0; l < _worklist.length(); l++) { + PointsToNode* use = _worklist.at(l); if (PointsToNode::is_base_use(use)) { // Add reference from jobj to field and from field to jobj (field's base). use = PointsToNode::get_use_node(use)->as_Field(); @@ -1328,6 +1356,8 @@ int ConnectionGraph::add_java_object_edges(JavaObjectNode* jobj, bool populate_w add_field_uses_to_worklist(use->as_Field()); } } + _worklist.clear(); + _in_worklist.Reset(); return new_edges; } @@ -1906,7 +1936,7 @@ void ConnectionGraph::add_local_var(Node *n, PointsToNode::EscapeState es) { return; } Compile* C = _compile; - ptadr = new (C->comp_arena()) LocalVarNode(C, n, es); + ptadr = new (C->comp_arena()) LocalVarNode(this, n, es); _nodes.at_put(n->_idx, ptadr); } @@ -1917,7 +1947,7 @@ void ConnectionGraph::add_java_object(Node *n, PointsToNode::EscapeState es) { return; } Compile* C = _compile; - ptadr = new (C->comp_arena()) JavaObjectNode(C, n, es); + ptadr = new (C->comp_arena()) JavaObjectNode(this, n, es); _nodes.at_put(n->_idx, ptadr); } @@ -1933,7 +1963,7 @@ void ConnectionGraph::add_field(Node *n, PointsToNode::EscapeState es, int offse es = PointsToNode::GlobalEscape; } Compile* C = _compile; - FieldNode* field = new (C->comp_arena()) FieldNode(C, n, es, offset, is_oop); + FieldNode* field = new (C->comp_arena()) FieldNode(this, n, es, offset, is_oop); _nodes.at_put(n->_idx, field); } @@ -1947,7 +1977,7 @@ void ConnectionGraph::add_arraycopy(Node *n, PointsToNode::EscapeState es, return; } Compile* C = _compile; - ptadr = new (C->comp_arena()) ArraycopyNode(C, n, es); + ptadr = new (C->comp_arena()) ArraycopyNode(this, n, es); _nodes.at_put(n->_idx, ptadr); // Add edge from arraycopy node to source object. (void)add_edge(ptadr, src); diff --git a/hotspot/src/share/vm/opto/escape.hpp b/hotspot/src/share/vm/opto/escape.hpp index 78447f03062..89a0d44a358 100644 --- a/hotspot/src/share/vm/opto/escape.hpp +++ b/hotspot/src/share/vm/opto/escape.hpp @@ -125,6 +125,8 @@ class LocalVarNode; class FieldNode; class ArraycopyNode; +class ConnectionGraph; + // ConnectionGraph nodes class PointsToNode : public ResourceObj { GrowableArray _edges; // List of nodes this node points to @@ -137,6 +139,7 @@ class PointsToNode : public ResourceObj { Node* const _node; // Ideal node corresponding to this PointsTo node. const int _idx; // Cached ideal node's _idx + const uint _pidx; // Index of this node public: typedef enum { @@ -165,17 +168,9 @@ public: } NodeFlags; - PointsToNode(Compile *C, Node* n, EscapeState es, NodeType type): - _edges(C->comp_arena(), 2, 0, NULL), - _uses (C->comp_arena(), 2, 0, NULL), - _node(n), - _idx(n->_idx), - _type((u1)type), - _escape((u1)es), - _fields_escape((u1)es), - _flags(ScalarReplaceable) { - assert(n != NULL && es != UnknownEscape, "sanity"); - } + inline PointsToNode(ConnectionGraph* CG, Node* n, EscapeState es, NodeType type); + + uint pidx() const { return _pidx; } Node* ideal_node() const { return _node; } int idx() const { return _idx; } @@ -243,14 +238,14 @@ public: class LocalVarNode: public PointsToNode { public: - LocalVarNode(Compile *C, Node* n, EscapeState es): - PointsToNode(C, n, es, LocalVar) {} + LocalVarNode(ConnectionGraph *CG, Node* n, EscapeState es): + PointsToNode(CG, n, es, LocalVar) {} }; class JavaObjectNode: public PointsToNode { public: - JavaObjectNode(Compile *C, Node* n, EscapeState es): - PointsToNode(C, n, es, JavaObject) { + JavaObjectNode(ConnectionGraph *CG, Node* n, EscapeState es): + PointsToNode(CG, n, es, JavaObject) { if (es > NoEscape) set_scalar_replaceable(false); } @@ -262,8 +257,8 @@ class FieldNode: public PointsToNode { const bool _is_oop; // Field points to object bool _has_unknown_base; // Has phantom_object base public: - FieldNode(Compile *C, Node* n, EscapeState es, int offs, bool is_oop): - PointsToNode(C, n, es, Field), + FieldNode(ConnectionGraph *CG, Node* n, EscapeState es, int offs, bool is_oop): + PointsToNode(CG, n, es, Field), _offset(offs), _is_oop(is_oop), _has_unknown_base(false) {} @@ -284,8 +279,8 @@ public: class ArraycopyNode: public PointsToNode { public: - ArraycopyNode(Compile *C, Node* n, EscapeState es): - PointsToNode(C, n, es, Arraycopy) {} + ArraycopyNode(ConnectionGraph *CG, Node* n, EscapeState es): + PointsToNode(CG, n, es, Arraycopy) {} }; // Iterators for PointsTo node's edges: @@ -323,11 +318,14 @@ public: class ConnectionGraph: public ResourceObj { + friend class PointsToNode; private: GrowableArray _nodes; // Map from ideal nodes to // ConnectionGraph nodes. GrowableArray _worklist; // Nodes to be processed + VectorSet _in_worklist; + uint _next_pidx; bool _collecting; // Indicates whether escape information // is still being collected. If false, @@ -353,6 +351,8 @@ private: } uint nodes_size() const { return _nodes.length(); } + uint next_pidx() { return _next_pidx++; } + // Add nodes to ConnectionGraph. void add_local_var(Node* n, PointsToNode::EscapeState es); void add_java_object(Node* n, PointsToNode::EscapeState es); @@ -396,15 +396,26 @@ private: int add_java_object_edges(JavaObjectNode* jobj, bool populate_worklist); // Put node on worklist if it is (or was) not there. - void add_to_worklist(PointsToNode* pt) { - _worklist.push(pt); - return; + inline void add_to_worklist(PointsToNode* pt) { + PointsToNode* ptf = pt; + uint pidx_bias = 0; + if (PointsToNode::is_base_use(pt)) { + // Create a separate entry in _in_worklist for a marked base edge + // because _worklist may have an entry for a normal edge pointing + // to the same node. To separate them use _next_pidx as bias. + ptf = PointsToNode::get_use_node(pt)->as_Field(); + pidx_bias = _next_pidx; + } + if (!_in_worklist.test_set(ptf->pidx() + pidx_bias)) { + _worklist.append(pt); + } } // Put on worklist all uses of this node. - void add_uses_to_worklist(PointsToNode* pt) { - for (UseIterator i(pt); i.has_next(); i.next()) - _worklist.push(i.get()); + inline void add_uses_to_worklist(PointsToNode* pt) { + for (UseIterator i(pt); i.has_next(); i.next()) { + add_to_worklist(i.get()); + } } // Put on worklist all field's uses and related field nodes. @@ -517,8 +528,8 @@ private: } // Helper functions bool is_oop_field(Node* n, int offset, bool* unsafe); - static Node* get_addp_base(Node *addp); - static Node* find_second_addp(Node* addp, Node* n); + static Node* get_addp_base(Node *addp); + static Node* find_second_addp(Node* addp, Node* n); // offset of a field reference int address_offset(Node* adr, PhaseTransform *phase); @@ -587,4 +598,17 @@ public: #endif }; +inline PointsToNode::PointsToNode(ConnectionGraph *CG, Node* n, EscapeState es, NodeType type): + _edges(CG->_compile->comp_arena(), 2, 0, NULL), + _uses (CG->_compile->comp_arena(), 2, 0, NULL), + _node(n), + _idx(n->_idx), + _pidx(CG->next_pidx()), + _type((u1)type), + _escape((u1)es), + _fields_escape((u1)es), + _flags(ScalarReplaceable) { + assert(n != NULL && es != UnknownEscape, "sanity"); +} + #endif // SHARE_VM_OPTO_ESCAPE_HPP From f1db529ce7b3f02c1043b31381b040405a8b2ca6 Mon Sep 17 00:00:00 2001 From: Dmitrij Pochepko Date: Sat, 25 Oct 2014 19:06:23 +0400 Subject: [PATCH 016/118] 8043674: Update compiler/intrinsic/bmi tests to run it on all platforms Reviewed-by: kvn, iignatyev, fzhinkin --- hotspot/test/compiler/intrinsics/bmi/TestAndnI.java | 6 +++--- hotspot/test/compiler/intrinsics/bmi/TestAndnL.java | 6 +++--- hotspot/test/compiler/intrinsics/bmi/TestBlsiI.java | 6 +++--- hotspot/test/compiler/intrinsics/bmi/TestBlsiL.java | 6 +++--- hotspot/test/compiler/intrinsics/bmi/TestBlsmskI.java | 6 +++--- hotspot/test/compiler/intrinsics/bmi/TestBlsmskL.java | 6 +++--- hotspot/test/compiler/intrinsics/bmi/TestBlsrI.java | 6 +++--- hotspot/test/compiler/intrinsics/bmi/TestBlsrL.java | 6 +++--- hotspot/test/compiler/intrinsics/bmi/TestLzcntI.java | 5 ++--- hotspot/test/compiler/intrinsics/bmi/TestLzcntL.java | 5 ++--- hotspot/test/compiler/intrinsics/bmi/TestTzcntI.java | 5 ++--- hotspot/test/compiler/intrinsics/bmi/TestTzcntL.java | 5 ++--- 12 files changed, 32 insertions(+), 36 deletions(-) diff --git a/hotspot/test/compiler/intrinsics/bmi/TestAndnI.java b/hotspot/test/compiler/intrinsics/bmi/TestAndnI.java index 45a79b0df37..fa7b842380e 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestAndnI.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestAndnI.java @@ -41,14 +41,14 @@ public class TestAndnI { public static void main(String args[]) throws Throwable { if (!CPUInfo.hasFeature("bmi1")) { - System.out.println("CPU does not support bmi1 feature. "+ - "Test skipped."); - return; + System.out.println("INFO: CPU does not support bmi1 feature."); } BMITestRunner.runTests(AndnIExpr.class, args, + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseBMI1Instructions"); BMITestRunner.runTests(AndnICommutativeExpr.class, args, + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseBMI1Instructions"); } diff --git a/hotspot/test/compiler/intrinsics/bmi/TestAndnL.java b/hotspot/test/compiler/intrinsics/bmi/TestAndnL.java index 8746fefa483..6e7fbe4fbf8 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestAndnL.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestAndnL.java @@ -41,14 +41,14 @@ public class TestAndnL { public static void main(String args[]) throws Throwable { if (!CPUInfo.hasFeature("bmi1")) { - System.out.println("CPU does not support bmi1 feature. " + - "Test skipped."); - return; + System.out.println("INFO: CPU does not support bmi1 feature."); } BMITestRunner.runTests(AndnLExpr.class, args, + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseBMI1Instructions"); BMITestRunner.runTests(AndnLCommutativeExpr.class, args, + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseBMI1Instructions"); } diff --git a/hotspot/test/compiler/intrinsics/bmi/TestBlsiI.java b/hotspot/test/compiler/intrinsics/bmi/TestBlsiI.java index 7ced5242692..794676252a0 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestBlsiI.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestBlsiI.java @@ -41,14 +41,14 @@ public class TestBlsiI { public static void main(String args[]) throws Throwable { if (!CPUInfo.hasFeature("bmi1")) { - System.out.println("CPU does not support bmi1 feature. " + - "Test skipped."); - return; + System.out.println("INFO: CPU does not support bmi1 feature."); } BMITestRunner.runTests(BlsiIExpr.class, args, + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseBMI1Instructions"); BMITestRunner.runTests(BlsiICommutativeExpr.class, args, + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseBMI1Instructions"); } diff --git a/hotspot/test/compiler/intrinsics/bmi/TestBlsiL.java b/hotspot/test/compiler/intrinsics/bmi/TestBlsiL.java index d1fe59c204b..046351222e9 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestBlsiL.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestBlsiL.java @@ -41,14 +41,14 @@ public class TestBlsiL { public static void main(String args[]) throws Throwable { if (!CPUInfo.hasFeature("bmi1")) { - System.out.println("CPU does not support bmi1 feature. " + - "Test skipped."); - return; + System.out.println("INFO: CPU does not support bmi1 feature."); } BMITestRunner.runTests(BlsiLExpr.class, args, + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseBMI1Instructions"); BMITestRunner.runTests(BlsiLCommutativeExpr.class, args, + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseBMI1Instructions"); } diff --git a/hotspot/test/compiler/intrinsics/bmi/TestBlsmskI.java b/hotspot/test/compiler/intrinsics/bmi/TestBlsmskI.java index 7cba5d88e36..3f1b95ffec2 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestBlsmskI.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestBlsmskI.java @@ -41,14 +41,14 @@ public class TestBlsmskI { public static void main(String args[]) throws Throwable { if (!CPUInfo.hasFeature("bmi1")) { - System.out.println("CPU does not support bmi1 feature. " + - "Test skipped."); - return; + System.out.println("INFO: CPU does not support bmi1 feature."); } BMITestRunner.runTests(BlsmskIExpr.class, args, + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseBMI1Instructions"); BMITestRunner.runTests(BlsmskICommutativeExpr.class, args, + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseBMI1Instructions"); } diff --git a/hotspot/test/compiler/intrinsics/bmi/TestBlsmskL.java b/hotspot/test/compiler/intrinsics/bmi/TestBlsmskL.java index d2f3a3f6aea..aca3981f779 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestBlsmskL.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestBlsmskL.java @@ -41,14 +41,14 @@ public class TestBlsmskL { public static void main(String args[]) throws Throwable { if (!CPUInfo.hasFeature("bmi1")) { - System.out.println("CPU does not support bmi1 feature. " + - "Test skipped."); - return; + System.out.println("INFO: CPU does not support bmi1 feature."); } BMITestRunner.runTests(BlsmskLExpr.class, args, + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseBMI1Instructions"); BMITestRunner.runTests(BlsmskLCommutativeExpr.class, args, + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseBMI1Instructions"); } diff --git a/hotspot/test/compiler/intrinsics/bmi/TestBlsrI.java b/hotspot/test/compiler/intrinsics/bmi/TestBlsrI.java index 53e7251143b..90121c88942 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestBlsrI.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestBlsrI.java @@ -41,14 +41,14 @@ public class TestBlsrI { public static void main(String args[]) throws Throwable { if (!CPUInfo.hasFeature("bmi1")) { - System.out.println("CPU does not support bmi1 feature. " + - "Test skipped."); - return; + System.out.println("INFO: CPU does not support bmi1 feature."); } BMITestRunner.runTests(BlsrIExpr.class, args, + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseBMI1Instructions"); BMITestRunner.runTests(BlsrICommutativeExpr.class, args, + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseBMI1Instructions"); } diff --git a/hotspot/test/compiler/intrinsics/bmi/TestBlsrL.java b/hotspot/test/compiler/intrinsics/bmi/TestBlsrL.java index 5e3c885542b..ad267769dac 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestBlsrL.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestBlsrL.java @@ -41,14 +41,14 @@ public class TestBlsrL { public static void main(String args[]) throws Throwable { if (!CPUInfo.hasFeature("bmi1")) { - System.out.println("CPU does not support bmi1 feature. " + - "Test skipped."); - return; + System.out.println("INFO: CPU does not support bmi1 feature."); } BMITestRunner.runTests(BlsrLExpr.class, args, + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseBMI1Instructions"); BMITestRunner.runTests(BlsrLCommutativeExpr.class, args, + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseBMI1Instructions"); } diff --git a/hotspot/test/compiler/intrinsics/bmi/TestLzcntI.java b/hotspot/test/compiler/intrinsics/bmi/TestLzcntI.java index d13b89c0246..5cb08e0a381 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestLzcntI.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestLzcntI.java @@ -41,12 +41,11 @@ public class TestLzcntI { public static void main(String args[]) throws Throwable { if (!CPUInfo.hasFeature("lzcnt")) { - System.out.println("CPU does not support lzcnt feature. " + - "Test skipped."); - return; + System.out.println("INFO: CPU does not support lzcnt feature."); } BMITestRunner.runTests(LzcntIExpr.class, args, + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseCountLeadingZerosInstruction"); } diff --git a/hotspot/test/compiler/intrinsics/bmi/TestLzcntL.java b/hotspot/test/compiler/intrinsics/bmi/TestLzcntL.java index b75830df18a..fbcadf49dad 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestLzcntL.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestLzcntL.java @@ -41,12 +41,11 @@ public class TestLzcntL { public static void main(String args[]) throws Throwable { if (!CPUInfo.hasFeature("lzcnt")) { - System.out.println("CPU does not support lzcnt feature. " + - "Test skipped."); - return; + System.out.println("INFO: CPU does not support lzcnt feature."); } BMITestRunner.runTests(LzcntLExpr.class, args, + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseCountLeadingZerosInstruction"); } diff --git a/hotspot/test/compiler/intrinsics/bmi/TestTzcntI.java b/hotspot/test/compiler/intrinsics/bmi/TestTzcntI.java index 285519537aa..bd8c8764617 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestTzcntI.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestTzcntI.java @@ -41,12 +41,11 @@ public class TestTzcntI { public static void main(String args[]) throws Throwable { if (!CPUInfo.hasFeature("bmi1")) { - System.out.println("CPU does not support bmi1 feature. " + - "Test skipped."); - return; + System.out.println("INFO: CPU does not support bmi1 feature."); } BMITestRunner.runTests(TzcntIExpr.class, args, + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseCountTrailingZerosInstruction"); } diff --git a/hotspot/test/compiler/intrinsics/bmi/TestTzcntL.java b/hotspot/test/compiler/intrinsics/bmi/TestTzcntL.java index 36683fe7283..0a008aa4d58 100644 --- a/hotspot/test/compiler/intrinsics/bmi/TestTzcntL.java +++ b/hotspot/test/compiler/intrinsics/bmi/TestTzcntL.java @@ -41,12 +41,11 @@ public class TestTzcntL { public static void main(String args[]) throws Throwable { if (!CPUInfo.hasFeature("bmi1")) { - System.out.println("CPU does not support bmi1 feature. " + - "Test skipped."); - return; + System.out.println("INFO: CPU does not support bmi1 feature."); } BMITestRunner.runTests(TzcntLExpr.class, args, + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UseCountTrailingZerosInstruction"); } From c86e8f5a2c460512227c83ea8129b368b0b6ded9 Mon Sep 17 00:00:00 2001 From: Igor Veresov Date: Sat, 25 Oct 2014 21:02:29 -1000 Subject: [PATCH 017/118] 8059200: Promoted JDK9 b31 for Solaris-amd64 fails (Error: dl failure on line 744, no picl library) on Solaris 11.1 Manually load libpicl.so (used on SPARC only) Reviewed-by: kvn --- hotspot/make/solaris/makefiles/vm.make | 2 +- .../vm/vm_version_solaris_sparc.cpp | 110 +++++++++++++++--- 2 files changed, 94 insertions(+), 18 deletions(-) diff --git a/hotspot/make/solaris/makefiles/vm.make b/hotspot/make/solaris/makefiles/vm.make index 59f2b575642..da906bcfad8 100644 --- a/hotspot/make/solaris/makefiles/vm.make +++ b/hotspot/make/solaris/makefiles/vm.make @@ -143,7 +143,7 @@ else LIBS += -lsocket -lsched -ldl $(LIBM) -lthread -lc -ldemangle endif # sparcWorks -LIBS += -lkstat -lpicl +LIBS += -lkstat # By default, link the *.o into the library, not the executable. LINK_INTO$(LINK_INTO) = LIBJVM diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp b/hotspot/src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp index 20bac5061ee..b023ee4e0f3 100644 --- a/hotspot/src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp +++ b/hotspot/src/os_cpu/solaris_sparc/vm/vm_version_solaris_sparc.cpp @@ -33,18 +33,51 @@ #include #include #include +#include +#include extern "C" static int PICL_get_l1_data_cache_line_size_helper(picl_nodehdl_t nodeh, void *result); extern "C" static int PICL_get_l2_cache_line_size_helper(picl_nodehdl_t nodeh, void *result); +// Functions from the library we need (signatures should match those in picl.h) +extern "C" { + typedef int (*picl_initialize_func_t)(void); + typedef int (*picl_shutdown_func_t)(void); + typedef int (*picl_get_root_func_t)(picl_nodehdl_t *nodehandle); + typedef int (*picl_walk_tree_by_class_func_t)(picl_nodehdl_t rooth, + const char *classname, void *c_args, + int (*callback_fn)(picl_nodehdl_t hdl, void *args)); + typedef int (*picl_get_prop_by_name_func_t)(picl_nodehdl_t nodeh, const char *nm, + picl_prophdl_t *ph); + typedef int (*picl_get_propval_func_t)(picl_prophdl_t proph, void *valbuf, size_t sz); + typedef int (*picl_get_propinfo_func_t)(picl_prophdl_t proph, picl_propinfo_t *pi); +} + class PICL { + // Pointers to functions in the library + picl_initialize_func_t _picl_initialize; + picl_shutdown_func_t _picl_shutdown; + picl_get_root_func_t _picl_get_root; + picl_walk_tree_by_class_func_t _picl_walk_tree_by_class; + picl_get_prop_by_name_func_t _picl_get_prop_by_name; + picl_get_propval_func_t _picl_get_propval; + picl_get_propinfo_func_t _picl_get_propinfo; + // Handle to the library that is returned by dlopen + void *_dl_handle; + + bool open_library(); + void close_library(); + + template bool bind(FuncType& func, const char* name); + bool bind_library_functions(); + // Get a value of the integer property. The value in the tree can be either 32 or 64 bit // depending on the platform. The result is converted to int. - static int get_int_property(picl_nodehdl_t nodeh, const char* name, int* result) { + int get_int_property(picl_nodehdl_t nodeh, const char* name, int* result) { picl_propinfo_t pinfo; picl_prophdl_t proph; - if (picl_get_prop_by_name(nodeh, name, &proph) != PICL_SUCCESS || - picl_get_propinfo(proph, &pinfo) != PICL_SUCCESS) { + if (_picl_get_prop_by_name(nodeh, name, &proph) != PICL_SUCCESS || + _picl_get_propinfo(proph, &pinfo) != PICL_SUCCESS) { return PICL_FAILURE; } @@ -54,13 +87,13 @@ class PICL { } if (pinfo.size == sizeof(int64_t)) { int64_t val; - if (picl_get_propval(proph, &val, sizeof(int64_t)) != PICL_SUCCESS) { + if (_picl_get_propval(proph, &val, sizeof(int64_t)) != PICL_SUCCESS) { return PICL_FAILURE; } *result = static_cast(val); } else if (pinfo.size == sizeof(int32_t)) { int32_t val; - if (picl_get_propval(proph, &val, sizeof(int32_t)) != PICL_SUCCESS) { + if (_picl_get_propval(proph, &val, sizeof(int32_t)) != PICL_SUCCESS) { return PICL_FAILURE; } *result = static_cast(val); @@ -74,6 +107,7 @@ class PICL { // Visitor and a state machine that visits integer properties and verifies that the // values are the same. Stores the unique value observed. class UniqueValueVisitor { + PICL *_picl; enum { INITIAL, // Start state, no assignments happened ASSIGNED, // Assigned a value @@ -81,7 +115,7 @@ class PICL { } _state; int _value; public: - UniqueValueVisitor() : _state(INITIAL) { } + UniqueValueVisitor(PICL* picl) : _picl(picl), _state(INITIAL) { } int value() { assert(_state == ASSIGNED, "Precondition"); return _value; @@ -98,9 +132,10 @@ class PICL { static int visit(picl_nodehdl_t nodeh, const char* name, void *arg) { UniqueValueVisitor *state = static_cast(arg); + PICL* picl = state->_picl; assert(!state->is_inconsistent(), "Precondition"); int curr; - if (PICL::get_int_property(nodeh, name, &curr) == PICL_SUCCESS) { + if (picl->get_int_property(nodeh, name, &curr) == PICL_SUCCESS) { if (!state->is_assigned()) { // first iteration state->set_value(curr); } else if (curr != state->value()) { // following iterations @@ -124,32 +159,36 @@ public: return UniqueValueVisitor::visit(nodeh, "l2-cache-line-size", state); } - PICL() : _L1_data_cache_line_size(0), _L2_cache_line_size(0) { - if (picl_initialize() == PICL_SUCCESS) { + PICL() : _L1_data_cache_line_size(0), _L2_cache_line_size(0), _dl_handle(NULL) { + if (!open_library()) { + return; + } + if (_picl_initialize() == PICL_SUCCESS) { picl_nodehdl_t rooth; - if (picl_get_root(&rooth) == PICL_SUCCESS) { - UniqueValueVisitor L1_state; + if (_picl_get_root(&rooth) == PICL_SUCCESS) { + UniqueValueVisitor L1_state(this); // Visit all "cpu" class instances - picl_walk_tree_by_class(rooth, "cpu", &L1_state, PICL_get_l1_data_cache_line_size_helper); + _picl_walk_tree_by_class(rooth, "cpu", &L1_state, PICL_get_l1_data_cache_line_size_helper); if (L1_state.is_initial()) { // Still initial, iteration found no values // Try walk all "core" class instances, it might be a Fujitsu machine - picl_walk_tree_by_class(rooth, "core", &L1_state, PICL_get_l1_data_cache_line_size_helper); + _picl_walk_tree_by_class(rooth, "core", &L1_state, PICL_get_l1_data_cache_line_size_helper); } if (L1_state.is_assigned()) { // Is there a value? _L1_data_cache_line_size = L1_state.value(); } - UniqueValueVisitor L2_state; - picl_walk_tree_by_class(rooth, "cpu", &L2_state, PICL_get_l2_cache_line_size_helper); + UniqueValueVisitor L2_state(this); + _picl_walk_tree_by_class(rooth, "cpu", &L2_state, PICL_get_l2_cache_line_size_helper); if (L2_state.is_initial()) { - picl_walk_tree_by_class(rooth, "core", &L2_state, PICL_get_l2_cache_line_size_helper); + _picl_walk_tree_by_class(rooth, "core", &L2_state, PICL_get_l2_cache_line_size_helper); } if (L2_state.is_assigned()) { _L2_cache_line_size = L2_state.value(); } } - picl_shutdown(); + _picl_shutdown(); } + close_library(); } unsigned int L1_data_cache_line_size() const { return _L1_data_cache_line_size; } @@ -163,6 +202,43 @@ extern "C" static int PICL_get_l2_cache_line_size_helper(picl_nodehdl_t nodeh, v return PICL::get_l2_cache_line_size(nodeh, result); } +template +bool PICL::bind(FuncType& func, const char* name) { + func = reinterpret_cast(dlsym(_dl_handle, name)); + return func != NULL; +} + +bool PICL::bind_library_functions() { + assert(_dl_handle != NULL, "library should be open"); + return bind(_picl_initialize, "picl_initialize" ) && + bind(_picl_shutdown, "picl_shutdown" ) && + bind(_picl_get_root, "picl_get_root" ) && + bind(_picl_walk_tree_by_class, "picl_walk_tree_by_class") && + bind(_picl_get_prop_by_name, "picl_get_prop_by_name" ) && + bind(_picl_get_propval, "picl_get_propval" ) && + bind(_picl_get_propinfo, "picl_get_propinfo" ); +} + +bool PICL::open_library() { + _dl_handle = dlopen("libpicl.so.1", RTLD_LAZY); + if (_dl_handle == NULL) { + warning("PICL (libpicl.so.1) is missing. Performance will not be optimal."); + return false; + } + if (!bind_library_functions()) { + assert(false, "unexpected PICL API change"); + close_library(); + return false; + } + return true; +} + +void PICL::close_library() { + assert(_dl_handle != NULL, "library should be open"); + dlclose(_dl_handle); + _dl_handle = NULL; +} + // We need to keep these here as long as we have to build on Solaris // versions before 10. #ifndef SI_ARCHITECTURE_32 From b3f78699f78c8ec11bd23bd2eacf3af96796706e Mon Sep 17 00:00:00 2001 From: Morris Meyer Date: Wed, 13 Aug 2014 13:00:53 -0700 Subject: [PATCH 018/118] 8054530: C2: assert(res == old_res) failed: Inconsistency between old and new Fixed signedness problem with assertion. Reviewed-by: kvn --- hotspot/src/share/vm/oops/objArrayOop.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hotspot/src/share/vm/oops/objArrayOop.hpp b/hotspot/src/share/vm/oops/objArrayOop.hpp index 897452a6624..bfb70564758 100644 --- a/hotspot/src/share/vm/oops/objArrayOop.hpp +++ b/hotspot/src/share/vm/oops/objArrayOop.hpp @@ -45,9 +45,10 @@ class objArrayOopDesc : public arrayOopDesc { private: // Give size of objArrayOop in HeapWords minus the header static int array_size(int length) { - const int OopsPerHeapWord = HeapWordSize/heapOopSize; + const uint OopsPerHeapWord = HeapWordSize/heapOopSize; assert(OopsPerHeapWord >= 1 && (HeapWordSize % heapOopSize == 0), "Else the following (new) computation would be in error"); + uint res = ((uint)length + OopsPerHeapWord - 1)/OopsPerHeapWord; #ifdef ASSERT // The old code is left in for sanity-checking; it'll // go away pretty soon. XXX @@ -55,16 +56,15 @@ private: // oop->length() * HeapWordsPerOop; // With narrowOops, HeapWordsPerOop is 1/2 or equal 0 as an integer. // The oop elements are aligned up to wordSize - const int HeapWordsPerOop = heapOopSize/HeapWordSize; - int old_res; + const uint HeapWordsPerOop = heapOopSize/HeapWordSize; + uint old_res; if (HeapWordsPerOop > 0) { old_res = length * HeapWordsPerOop; } else { - old_res = align_size_up(length, OopsPerHeapWord)/OopsPerHeapWord; + old_res = align_size_up((uint)length, OopsPerHeapWord)/OopsPerHeapWord; } -#endif // ASSERT - int res = ((uint)length + OopsPerHeapWord - 1)/OopsPerHeapWord; assert(res == old_res, "Inconsistency between old and new."); +#endif // ASSERT return res; } From 6c237d9d0f52888d8cafa1a94b599f32948b067b Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Tue, 5 Aug 2014 14:44:18 -0700 Subject: [PATCH 019/118] 8049542: C2: assert(size_in_words <= (julong)max_jint) failed: no overflow Added juint cast to avoid gcc problem we have on one of our platforms. Reviewed-by: dholmes, roland --- hotspot/src/share/vm/oops/typeArrayOop.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hotspot/src/share/vm/oops/typeArrayOop.hpp b/hotspot/src/share/vm/oops/typeArrayOop.hpp index 20f30dc996d..7d61e8946a2 100644 --- a/hotspot/src/share/vm/oops/typeArrayOop.hpp +++ b/hotspot/src/share/vm/oops/typeArrayOop.hpp @@ -150,7 +150,7 @@ class typeArrayOopDesc : public arrayOopDesc { DEBUG_ONLY(BasicType etype = Klass::layout_helper_element_type(lh)); assert(length <= arrayOopDesc::max_array_length(etype), "no overflow"); - julong size_in_bytes = length; + julong size_in_bytes = (juint)length; size_in_bytes <<= element_shift; size_in_bytes += instance_header_size; julong size_in_words = ((size_in_bytes + (HeapWordSize-1)) >> LogHeapWordSize); From 62d33442b8b2756b0ccbd1aa44df3090579f6bd0 Mon Sep 17 00:00:00 2001 From: Tobias Hartmann Date: Fri, 31 Oct 2014 12:16:20 +0100 Subject: [PATCH 020/118] 8062169: Multiple OSR compilations issued for same bci Fixed 'SimpleThresholdPolicy::event' to always perform OSR if an OSR nmethod is available. Reviewed-by: kvn, iveresov --- .../src/share/vm/runtime/simpleThresholdPolicy.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp b/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp index d70e8523a26..11803f1f6a4 100644 --- a/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp +++ b/hotspot/src/share/vm/runtime/simpleThresholdPolicy.cpp @@ -196,7 +196,6 @@ nmethod* SimpleThresholdPolicy::event(methodHandle method, methodHandle inlinee, // Don't trigger other compiles in testing mode return NULL; } - nmethod *osr_nm = NULL; handle_counter_overflow(method()); if (method() != inlinee()) { @@ -210,14 +209,16 @@ nmethod* SimpleThresholdPolicy::event(methodHandle method, methodHandle inlinee, if (bci == InvocationEntryBci) { method_invocation_event(method, inlinee, comp_level, nm, thread); } else { - method_back_branch_event(method, inlinee, bci, comp_level, nm, thread); // method == inlinee if the event originated in the main method - int highest_level = inlinee->highest_osr_comp_level(); - if (highest_level > comp_level) { - osr_nm = inlinee->lookup_osr_nmethod_for(bci, highest_level, false); + method_back_branch_event(method, inlinee, bci, comp_level, nm, thread); + // Check if event led to a higher level OSR compilation + nmethod* osr_nm = inlinee->lookup_osr_nmethod_for(bci, comp_level, false); + if (osr_nm != NULL && osr_nm->comp_level() > comp_level) { + // Perform OSR with new nmethod + return osr_nm; } } - return osr_nm; + return NULL; } // Check if the method can be compiled, change level if necessary From 74def215cdc4ded6ce44a17ad45571f993389064 Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Fri, 31 Oct 2014 16:51:57 -0700 Subject: [PATCH 021/118] 8054492: Casting can result in redundant null checks in generated code Add C2 intrinsic for Class.cast() method and force inline it too. Reviewed-by: jrose, roland, drchase, iignatyev --- hotspot/src/share/vm/classfile/vmSymbols.hpp | 3 + hotspot/src/share/vm/oops/method.cpp | 4 + hotspot/src/share/vm/opto/library_call.cpp | 86 +++++ hotspot/src/share/vm/prims/whitebox.cpp | 10 +- hotspot/test/TEST.groups | 1 + .../classcast/NullCheckDroppingsTest.java | 346 ++++++++++++++++++ .../whitebox/sun/hotspot/code/NMethod.java | 5 +- 7 files changed, 451 insertions(+), 4 deletions(-) create mode 100644 hotspot/test/compiler/intrinsics/classcast/NullCheckDroppingsTest.java diff --git a/hotspot/src/share/vm/classfile/vmSymbols.hpp b/hotspot/src/share/vm/classfile/vmSymbols.hpp index a4cf130d5c9..d6a84b75af0 100644 --- a/hotspot/src/share/vm/classfile/vmSymbols.hpp +++ b/hotspot/src/share/vm/classfile/vmSymbols.hpp @@ -455,6 +455,7 @@ template(object_void_signature, "(Ljava/lang/Object;)V") \ template(object_int_signature, "(Ljava/lang/Object;)I") \ template(object_boolean_signature, "(Ljava/lang/Object;)Z") \ + template(object_object_signature, "(Ljava/lang/Object;)Ljava/lang/Object;") \ template(string_void_signature, "(Ljava/lang/String;)V") \ template(string_int_signature, "(Ljava/lang/String;)I") \ template(throwable_void_signature, "(Ljava/lang/Throwable;)V") \ @@ -746,6 +747,8 @@ do_name( isPrimitive_name, "isPrimitive") \ do_intrinsic(_getSuperclass, java_lang_Class, getSuperclass_name, void_class_signature, F_RN) \ do_name( getSuperclass_name, "getSuperclass") \ + do_intrinsic(_Class_cast, java_lang_Class, Class_cast_name, object_object_signature, F_R) \ + do_name( Class_cast_name, "cast") \ \ do_intrinsic(_getClassAccessFlags, sun_reflect_Reflection, getClassAccessFlags_name, class_int_signature, F_SN) \ do_name( getClassAccessFlags_name, "getClassAccessFlags") \ diff --git a/hotspot/src/share/vm/oops/method.cpp b/hotspot/src/share/vm/oops/method.cpp index ef0349eeb26..a907ac44427 100644 --- a/hotspot/src/share/vm/oops/method.cpp +++ b/hotspot/src/share/vm/oops/method.cpp @@ -1295,6 +1295,10 @@ void Method::init_intrinsic_id() { vmIntrinsics::ID id = vmIntrinsics::find_id(klass_id, name_id, sig_id, flags); if (id != vmIntrinsics::_none) { set_intrinsic_id(id); + if (id == vmIntrinsics::_Class_cast) { + // Even if the intrinsic is rejected, we want to inline this simple method. + set_force_inline(true); + } return; } diff --git a/hotspot/src/share/vm/opto/library_call.cpp b/hotspot/src/share/vm/opto/library_call.cpp index a59007b7ccc..91d82d30f5f 100644 --- a/hotspot/src/share/vm/opto/library_call.cpp +++ b/hotspot/src/share/vm/opto/library_call.cpp @@ -268,6 +268,7 @@ class LibraryCallKit : public GraphKit { bool inline_fp_conversions(vmIntrinsics::ID id); bool inline_number_methods(vmIntrinsics::ID id); bool inline_reference_get(); + bool inline_Class_cast(); bool inline_aescrypt_Block(vmIntrinsics::ID id); bool inline_cipherBlockChaining_AESCrypt(vmIntrinsics::ID id); Node* inline_cipherBlockChaining_AESCrypt_predicate(bool decrypting); @@ -869,6 +870,8 @@ bool LibraryCallKit::try_to_inline(int predicate) { case vmIntrinsics::_Reference_get: return inline_reference_get(); + case vmIntrinsics::_Class_cast: return inline_Class_cast(); + case vmIntrinsics::_aescrypt_encryptBlock: case vmIntrinsics::_aescrypt_decryptBlock: return inline_aescrypt_Block(intrinsic_id()); @@ -3546,6 +3549,89 @@ bool LibraryCallKit::inline_native_Class_query(vmIntrinsics::ID id) { return true; } +//-------------------------inline_Class_cast------------------- +bool LibraryCallKit::inline_Class_cast() { + Node* mirror = argument(0); // Class + Node* obj = argument(1); + const TypeInstPtr* mirror_con = _gvn.type(mirror)->isa_instptr(); + if (mirror_con == NULL) { + return false; // dead path (mirror->is_top()). + } + if (obj == NULL || obj->is_top()) { + return false; // dead path + } + const TypeOopPtr* tp = _gvn.type(obj)->isa_oopptr(); + + // First, see if Class.cast() can be folded statically. + // java_mirror_type() returns non-null for compile-time Class constants. + ciType* tm = mirror_con->java_mirror_type(); + if (tm != NULL && tm->is_klass() && + tp != NULL && tp->klass() != NULL) { + if (!tp->klass()->is_loaded()) { + // Don't use intrinsic when class is not loaded. + return false; + } else { + int static_res = C->static_subtype_check(tm->as_klass(), tp->klass()); + if (static_res == Compile::SSC_always_true) { + // isInstance() is true - fold the code. + set_result(obj); + return true; + } else if (static_res == Compile::SSC_always_false) { + // Don't use intrinsic, have to throw ClassCastException. + // If the reference is null, the non-intrinsic bytecode will + // be optimized appropriately. + return false; + } + } + } + + // Bailout intrinsic and do normal inlining if exception path is frequent. + if (too_many_traps(Deoptimization::Reason_intrinsic)) { + return false; + } + + // Generate dynamic checks. + // Class.cast() is java implementation of _checkcast bytecode. + // Do checkcast (Parse::do_checkcast()) optimizations here. + + mirror = null_check(mirror); + // If mirror is dead, only null-path is taken. + if (stopped()) { + return true; + } + + // Not-subtype or the mirror's klass ptr is NULL (in case it is a primitive). + enum { _bad_type_path = 1, _prim_path = 2, PATH_LIMIT }; + RegionNode* region = new RegionNode(PATH_LIMIT); + record_for_igvn(region); + + // Now load the mirror's klass metaobject, and null-check it. + // If kls is null, we have a primitive mirror and + // nothing is an instance of a primitive type. + Node* kls = load_klass_from_mirror(mirror, false, region, _prim_path); + + Node* res = top(); + if (!stopped()) { + Node* bad_type_ctrl = top(); + // Do checkcast optimizations. + res = gen_checkcast(obj, kls, &bad_type_ctrl); + region->init_req(_bad_type_path, bad_type_ctrl); + } + if (region->in(_prim_path) != top() || + region->in(_bad_type_path) != top()) { + // Let Interpreter throw ClassCastException. + PreserveJVMState pjvms(this); + set_control(_gvn.transform(region)); + uncommon_trap(Deoptimization::Reason_intrinsic, + Deoptimization::Action_maybe_recompile); + } + if (!stopped()) { + set_result(res); + } + return true; +} + + //--------------------------inline_native_subtype_check------------------------ // This intrinsic takes the JNI calls out of the heart of // UnsafeFieldAccessorImpl.set, which improves Field.set, readObject, etc. diff --git a/hotspot/src/share/vm/prims/whitebox.cpp b/hotspot/src/share/vm/prims/whitebox.cpp index c4cf7088e69..ac497910f8c 100644 --- a/hotspot/src/share/vm/prims/whitebox.cpp +++ b/hotspot/src/share/vm/prims/whitebox.cpp @@ -803,20 +803,24 @@ WB_ENTRY(jobjectArray, WB_GetNMethod(JNIEnv* env, jobject o, jobject method, jbo ThreadToNativeFromVM ttn(thread); jclass clazz = env->FindClass(vmSymbols::java_lang_Object()->as_C_string()); CHECK_JNI_EXCEPTION_(env, NULL); - result = env->NewObjectArray(2, clazz, NULL); + result = env->NewObjectArray(3, clazz, NULL); if (result == NULL) { return result; } - jobject obj = integerBox(thread, env, code->comp_level()); + jobject level = integerBox(thread, env, code->comp_level()); CHECK_JNI_EXCEPTION_(env, NULL); - env->SetObjectArrayElement(result, 0, obj); + env->SetObjectArrayElement(result, 0, level); jbyteArray insts = env->NewByteArray(insts_size); CHECK_JNI_EXCEPTION_(env, NULL); env->SetByteArrayRegion(insts, 0, insts_size, (jbyte*) code->insts_begin()); env->SetObjectArrayElement(result, 1, insts); + jobject id = integerBox(thread, env, code->compile_id()); + CHECK_JNI_EXCEPTION_(env, NULL); + env->SetObjectArrayElement(result, 2, id); + return result; WB_END diff --git a/hotspot/test/TEST.groups b/hotspot/test/TEST.groups index 051e2e3fa3b..5b2f9d3a280 100644 --- a/hotspot/test/TEST.groups +++ b/hotspot/test/TEST.groups @@ -479,6 +479,7 @@ hotspot_compiler_3 = \ compiler/intrinsics/mathexact/SubExactILoopDependentTest.java \ compiler/intrinsics/stringequals/TestStringEqualsBadLength.java \ compiler/intrinsics/unsafe/UnsafeGetAddressTest.java \ + compiler/intrinsics/classcast/NullCheckDroppingsTest.java \ compiler/jsr292/ConcurrentClassLoadingTest.java \ compiler/jsr292/CreatesInterfaceDotEqualsCallInfo.java \ compiler/loopopts/TestLogSum.java \ diff --git a/hotspot/test/compiler/intrinsics/classcast/NullCheckDroppingsTest.java b/hotspot/test/compiler/intrinsics/classcast/NullCheckDroppingsTest.java new file mode 100644 index 00000000000..9f88fe44f70 --- /dev/null +++ b/hotspot/test/compiler/intrinsics/classcast/NullCheckDroppingsTest.java @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test NullCheckDroppingsTest + * @bug 8054492 + * @summary "Casting can result in redundant null checks in generated code" + * @library /testlibrary /testlibrary/whitebox /testlibrary/com/oracle/java/testlibrary + * @build NullCheckDroppingsTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main ClassFileInstaller com.oracle.java.testlibrary.Platform + * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -Xmixed -XX:-BackgroundCompilation -XX:-TieredCompilation -XX:CompileThreshold=1000 + * -XX:CompileCommand=exclude,NullCheckDroppingsTest::runTest NullCheckDroppingsTest + */ + +import sun.hotspot.WhiteBox; +import sun.hotspot.code.NMethod; +import com.oracle.java.testlibrary.Platform; + +import java.lang.reflect.Method; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.function.BiFunction; + +public class NullCheckDroppingsTest { + + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + + static final BiFunction fCast = (c, o) -> c.cast(o); + + static final MethodHandle SET_SSINK; + static final MethodHandle MH_CAST; + + static { + try { + SET_SSINK = MethodHandles.lookup().findSetter(NullCheckDroppingsTest.class, "ssink", String.class); + MH_CAST = MethodHandles.lookup().findVirtual(Class.class, + "cast", + MethodType.methodType(Object.class, Object.class)); + } + catch (Exception e) { + throw new Error(e); + } + } + + static volatile String svalue = "A"; + static volatile String snull = null; + static volatile Integer iobj = new Integer(0); + static volatile int[] arr = new int[2]; + static volatile Class objClass = String.class; + static volatile Class nullClass = null; + + String ssink; + Integer isink; + int[] asink; + + public static void main(String[] args) throws Exception { + + // Only test C2 in Server VM + if (!Platform.isServer()) { + return; + } + // Make sure background compilation is disabled + if (WHITE_BOX.getBooleanVMFlag("BackgroundCompilation")) { + throw new AssertionError("Background compilation enabled"); + } + // Make sure Tiered compilation is disabled + if (WHITE_BOX.getBooleanVMFlag("TieredCompilation")) { + throw new AssertionError("Tiered compilation enabled"); + } + + Method methodClassCast = NullCheckDroppingsTest.class.getDeclaredMethod("testClassCast", String.class); + Method methodMHCast = NullCheckDroppingsTest.class.getDeclaredMethod("testMHCast", String.class); + Method methodMHSetter = NullCheckDroppingsTest.class.getDeclaredMethod("testMHSetter", String.class); + Method methodFunction = NullCheckDroppingsTest.class.getDeclaredMethod("testFunction", String.class); + + NullCheckDroppingsTest t = new NullCheckDroppingsTest(); + t.runTest(methodClassCast, false); + t.runTest(methodMHCast, false); + t.runTest(methodMHSetter, false); + t.runTest(methodFunction, false); + + // Edge cases + Method methodClassCastNull = NullCheckDroppingsTest.class.getDeclaredMethod("testClassCastNull", String.class); + Method methodNullClassCast = NullCheckDroppingsTest.class.getDeclaredMethod("testNullClassCast", String.class); + Method methodClassCastObj = NullCheckDroppingsTest.class.getDeclaredMethod("testClassCastObj", Object.class); + Method methodObjClassCast = NullCheckDroppingsTest.class.getDeclaredMethod("testObjClassCast", String.class); + Method methodVarClassCast = NullCheckDroppingsTest.class.getDeclaredMethod("testVarClassCast", String.class); + Method methodClassCastInt = NullCheckDroppingsTest.class.getDeclaredMethod("testClassCastInt", Object.class); + Method methodIntClassCast = NullCheckDroppingsTest.class.getDeclaredMethod("testIntClassCast", Object.class); + Method methodClassCastint = NullCheckDroppingsTest.class.getDeclaredMethod("testClassCastint", Object.class); + Method methodintClassCast = NullCheckDroppingsTest.class.getDeclaredMethod("testintClassCast", Object.class); + Method methodClassCastPrim = NullCheckDroppingsTest.class.getDeclaredMethod("testClassCastPrim", Object.class); + Method methodPrimClassCast = NullCheckDroppingsTest.class.getDeclaredMethod("testPrimClassCast", Object.class); + + t.runTest(methodClassCastNull, false); + t.runTest(methodNullClassCast, false); + t.runTest(methodClassCastObj, false); + t.runTest(methodObjClassCast, true); + t.runTest(methodVarClassCast, true); + t.runTest(methodClassCastInt, false); + t.runTest(methodIntClassCast, true); + t.runTest(methodClassCastint, false); + t.runTest(methodintClassCast, false); + t.runTest(methodClassCastPrim, false); + t.runTest(methodPrimClassCast, true); + } + + void testClassCast(String s) { + try { + ssink = String.class.cast(s); + } catch (Throwable t) { + throw new Error(t); + } + } + + void testClassCastNull(String s) { + try { + ssink = String.class.cast(null); + } catch (Throwable t) { + throw new Error(t); + } + } + + void testNullClassCast(String s) { + try { + ssink = (String)nullClass.cast(s); + throw new AssertionError("NullPointerException is not thrown"); + } catch (NullPointerException t) { + // Ignore NullPointerException + } catch (Throwable t) { + throw new Error(t); + } + } + + void testClassCastObj(Object s) { + try { + ssink = String.class.cast(s); + } catch (Throwable t) { + throw new Error(t); + } + } + + void testObjClassCast(String s) { + try { + ssink = (String)objClass.cast(s); + } catch (Throwable t) { + throw new Error(t); + } + } + + void testVarClassCast(String s) { + Class cl = (s == null) ? null : String.class; + try { + ssink = (String)cl.cast(svalue); + if (s == null) { + throw new AssertionError("NullPointerException is not thrown"); + } + } catch (NullPointerException t) { + // Ignore NullPointerException + } catch (Throwable t) { + throw new Error(t); + } + } + + void testClassCastInt(Object s) { + try { + ssink = String.class.cast(iobj); + throw new AssertionError("ClassCastException is not thrown"); + } catch (ClassCastException t) { + // Ignore ClassCastException: Cannot cast java.lang.Integer to java.lang.String + } catch (Throwable t) { + throw new Error(t); + } + } + + void testIntClassCast(Object s) { + try { + isink = Integer.class.cast(s); + if (s != null) { + throw new AssertionError("ClassCastException is not thrown"); + } + } catch (ClassCastException t) { + // Ignore ClassCastException: Cannot cast java.lang.String to java.lang.Integer + } catch (Throwable t) { + throw new Error(t); + } + } + + void testClassCastint(Object s) { + try { + ssink = String.class.cast(45); + throw new AssertionError("ClassCastException is not thrown"); + } catch (ClassCastException t) { + // Ignore ClassCastException: Cannot cast java.lang.Integer to java.lang.String + } catch (Throwable t) { + throw new Error(t); + } + } + + void testintClassCast(Object s) { + try { + isink = int.class.cast(s); + if (s != null) { + throw new AssertionError("ClassCastException is not thrown"); + } + } catch (ClassCastException t) { + // Ignore ClassCastException: Cannot cast java.lang.String to java.lang.Integer + } catch (Throwable t) { + throw new Error(t); + } + } + + void testClassCastPrim(Object s) { + try { + ssink = String.class.cast(arr); + throw new AssertionError("ClassCastException is not thrown"); + } catch (ClassCastException t) { + // Ignore ClassCastException: Cannot cast [I to java.lang.String + } catch (Throwable t) { + throw new Error(t); + } + } + + void testPrimClassCast(Object s) { + try { + asink = int[].class.cast(s); + if (s != null) { + throw new AssertionError("ClassCastException is not thrown"); + } + } catch (ClassCastException t) { + // Ignore ClassCastException: Cannot cast java.lang.String to [I + } catch (Throwable t) { + throw new Error(t); + } + } + + void testMHCast(String s) { + try { + ssink = (String) (Object) MH_CAST.invokeExact(String.class, (Object) s); + } catch (Throwable t) { + throw new Error(t); + } + } + + void testMHSetter(String s) { + try { + SET_SSINK.invokeExact(this, s); + } catch (Throwable t) { + throw new Error(t); + } + } + + void testFunction(String s) { + try { + ssink = (String) fCast.apply(String.class, s); + } catch (Throwable t) { + throw new Error(t); + } + } + + void runTest(Method method, boolean deopt) { + if (method == null) { + throw new AssertionError("method was not found"); + } + // Ensure method is compiled + WHITE_BOX.testSetDontInlineMethod(method, true); + for (int i = 0; i < 3000; i++) { + try { + method.invoke(this, svalue); + } catch (Exception e) { + throw new Error("Unexpected exception: ", e); + } + } + NMethod nm = getNMethod(method); + + // Passing null should cause a de-optimization + // if method is compiled with a null-check. + try { + method.invoke(this, snull); + } catch (Exception e) { + throw new Error("Unexpected exception: ", e); + } + checkDeoptimization(method, nm, deopt); + } + + static NMethod getNMethod(Method test) { + // Because background compilation is disabled, method should now be compiled + if (!WHITE_BOX.isMethodCompiled(test)) { + throw new AssertionError(test + " not compiled"); + } + + NMethod nm = NMethod.get(test, false); // not OSR nmethod + if (nm == null) { + throw new AssertionError(test + " missing nmethod?"); + } + if (nm.comp_level != 4) { + throw new AssertionError(test + " compiled by not C2: " + nm); + } + return nm; + } + + static void checkDeoptimization(Method method, NMethod nmOrig, boolean deopt) { + // Check deoptimization event (intrinsic Class.cast() works). + if (WHITE_BOX.isMethodCompiled(method) == deopt) { + throw new AssertionError(method + " was" + (deopt ? " not" : "") + " deoptimized"); + } + if (deopt) { + return; + } + // Ensure no recompilation when no deoptimization is expected. + NMethod nm = NMethod.get(method, false); // not OSR nmethod + if (nm == null) { + throw new AssertionError(method + " missing nmethod?"); + } + if (nm.comp_level != 4) { + throw new AssertionError(method + " compiled by not C2: " + nm); + } + if (nm.compile_id != nmOrig.compile_id) { + throw new AssertionError(method + " was recompiled: old nmethod=" + nmOrig + ", new nmethod=" + nm); + } + } +} diff --git a/hotspot/test/testlibrary/whitebox/sun/hotspot/code/NMethod.java b/hotspot/test/testlibrary/whitebox/sun/hotspot/code/NMethod.java index 4bdb49d0b3e..9ac182df343 100644 --- a/hotspot/test/testlibrary/whitebox/sun/hotspot/code/NMethod.java +++ b/hotspot/test/testlibrary/whitebox/sun/hotspot/code/NMethod.java @@ -34,18 +34,21 @@ public class NMethod { return obj == null ? null : new NMethod(obj); } private NMethod(Object[] obj) { - assert obj.length == 2; + assert obj.length == 3; comp_level = (Integer) obj[0]; insts = (byte[]) obj[1]; + compile_id = (Integer) obj[2]; } public byte[] insts; public int comp_level; + public int compile_id; @Override public String toString() { return "NMethod{" + "insts=" + insts + ", comp_level=" + comp_level + + ", compile_id=" + compile_id + '}'; } } From e5a126fe00f017ef0b5b5a650cc98bf21af923a1 Mon Sep 17 00:00:00 2001 From: Igor Ignatyev Date: Sun, 2 Nov 2014 18:42:30 +0300 Subject: [PATCH 022/118] 8036913: make DeoptimizeALot dependent on number of threads Reviewed-by: kvn, shade --- .../src/share/vm/runtime/interfaceSupport.cpp | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/hotspot/src/share/vm/runtime/interfaceSupport.cpp b/hotspot/src/share/vm/runtime/interfaceSupport.cpp index 78a58200f5a..6a30aba6f8e 100644 --- a/hotspot/src/share/vm/runtime/interfaceSupport.cpp +++ b/hotspot/src/share/vm/runtime/interfaceSupport.cpp @@ -187,19 +187,22 @@ void InterfaceSupport::zap_dead_locals_old() { # endif - +// invocation counter for InterfaceSupport::deoptimizeAll/zombieAll functions int deoptimizeAllCounter = 0; int zombieAllCounter = 0; - void InterfaceSupport::zombieAll() { - if (is_init_completed() && zombieAllCounter > ZombieALotInterval) { + // This method is called by all threads when a thread make + // transition to VM state (for example, runtime calls). + // Divide number of calls by number of threads to avoid + // dependence of ZombieAll events frequency on number of threads. + int value = zombieAllCounter / Threads::number_of_threads(); + if (is_init_completed() && value > ZombieALotInterval) { zombieAllCounter = 0; VM_ZombieAll op; VMThread::execute(&op); - } else { - zombieAllCounter++; } + zombieAllCounter++; } void InterfaceSupport::unlinkSymbols() { @@ -208,12 +211,17 @@ void InterfaceSupport::unlinkSymbols() { } void InterfaceSupport::deoptimizeAll() { - if (is_init_completed() ) { - if (DeoptimizeALot && deoptimizeAllCounter > DeoptimizeALotInterval) { + // This method is called by all threads when a thread make + // transition to VM state (for example, runtime calls). + // Divide number of calls by number of threads to avoid + // dependence of DeoptimizeAll events frequency on number of threads. + int value = deoptimizeAllCounter / Threads::number_of_threads(); + if (is_init_completed()) { + if (DeoptimizeALot && value > DeoptimizeALotInterval) { deoptimizeAllCounter = 0; VM_DeoptimizeAll op; VMThread::execute(&op); - } else if (DeoptimizeRandom && (deoptimizeAllCounter & 0x1f) == (os::random() & 0x1f)) { + } else if (DeoptimizeRandom && (value & 0x1F) == (os::random() & 0x1F)) { VM_DeoptimizeAll op; VMThread::execute(&op); } From e429e497cec3ade23a43ca0b79a0ffa23bc86f5d Mon Sep 17 00:00:00 2001 From: Igor Ignatyev Date: Sun, 2 Nov 2014 18:43:00 +0300 Subject: [PATCH 023/118] 8043125: compiler/types/correctness/CorrectnessTest.java: assert(layout->tag() == DataLayout::speculative_trap_data_tag) failed: wrong type Reviewed-by: kvn --- hotspot/src/share/vm/prims/whitebox.cpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/hotspot/src/share/vm/prims/whitebox.cpp b/hotspot/src/share/vm/prims/whitebox.cpp index ac497910f8c..1ed1413727f 100644 --- a/hotspot/src/share/vm/prims/whitebox.cpp +++ b/hotspot/src/share/vm/prims/whitebox.cpp @@ -509,16 +509,6 @@ class AlwaysFalseClosure : public BoolObjectClosure { static AlwaysFalseClosure always_false; -class VM_WhiteBoxCleanMethodData : public VM_WhiteBoxOperation { - public: - VM_WhiteBoxCleanMethodData(MethodData* mdo) : _mdo(mdo) { } - void doit() { - _mdo->clean_method_data(&always_false); - } - private: - MethodData* _mdo; -}; - WB_ENTRY(void, WB_ClearMethodState(JNIEnv* env, jobject o, jobject method)) jmethodID jmid = reflected_method_to_jmid(thread, env, method); CHECK_JNI_EXCEPTION(env); @@ -534,8 +524,8 @@ WB_ENTRY(void, WB_ClearMethodState(JNIEnv* env, jobject o, jobject method)) for (int i = 0; i < arg_count; i++) { mdo->set_arg_modified(i, 0); } - VM_WhiteBoxCleanMethodData op(mdo); - VMThread::execute(&op); + MutexLockerEx mu(mdo->extra_data_lock()); + mdo->clean_method_data(&always_false); } mh->clear_not_c1_compilable(); From 8152a3ea35bce218ad74fdb810dfc627d7e917ce Mon Sep 17 00:00:00 2001 From: Sergei Kovalev Date: Sun, 2 Nov 2014 18:43:38 +0300 Subject: [PATCH 024/118] 8044186: Introduce a reproducible random generator Reviewed-by: kvn, iveresov, iignatyev --- .../test/compiler/6896617/Test6896617.java | 17 +- .../test/compiler/7100757/Test7100757.java | 10 +- .../test/compiler/7177917/Test7177917.java | 15 +- .../test/compiler/7184394/TestAESBase.java | 10 +- .../test/compiler/7184394/TestAESMain.java | 1 + .../test/compiler/8005956/PolynomialRoot.java | 15 +- .../intrinsics/bmi/BMITestRunner.java | 25 +-- .../mathexact/AddExactIConstantTest.java | 3 +- .../mathexact/AddExactILoadTest.java | 3 +- .../mathexact/AddExactILoopDependentTest.java | 3 +- .../mathexact/AddExactINonConstantTest.java | 3 +- .../mathexact/AddExactIRepeatTest.java | 8 +- .../mathexact/AddExactLConstantTest.java | 3 +- .../mathexact/AddExactLNonConstantTest.java | 3 +- .../intrinsics/mathexact/DecExactITest.java | 3 +- .../intrinsics/mathexact/DecExactLTest.java | 3 +- .../intrinsics/mathexact/IncExactITest.java | 3 +- .../intrinsics/mathexact/IncExactLTest.java | 3 +- .../mathexact/MulExactIConstantTest.java | 3 +- .../mathexact/MulExactILoadTest.java | 3 +- .../mathexact/MulExactILoopDependentTest.java | 3 +- .../mathexact/MulExactINonConstantTest.java | 3 +- .../mathexact/MulExactIRepeatTest.java | 8 +- .../mathexact/MulExactLConstantTest.java | 3 +- .../mathexact/MulExactLNonConstantTest.java | 3 +- .../mathexact/NegExactIConstantTest.java | 3 +- .../mathexact/NegExactILoadTest.java | 3 +- .../mathexact/NegExactILoopDependentTest.java | 3 +- .../mathexact/NegExactINonConstantTest.java | 3 +- .../mathexact/NegExactLConstantTest.java | 3 +- .../mathexact/NegExactLNonConstantTest.java | 3 +- .../mathexact/SubExactICondTest.java | 3 +- .../mathexact/SubExactIConstantTest.java | 3 +- .../mathexact/SubExactILoadTest.java | 3 +- .../mathexact/SubExactILoopDependentTest.java | 3 +- .../mathexact/SubExactINonConstantTest.java | 3 +- .../mathexact/SubExactIRepeatTest.java | 8 +- .../mathexact/SubExactLConstantTest.java | 3 +- .../mathexact/SubExactLNonConstantTest.java | 3 +- .../compiler/intrinsics/mathexact/Verify.java | 17 +- .../jsr292/ConcurrentClassLoadingTest.java | 26 ++- .../compiler/types/correctness/OffTest.java | 13 +- hotspot/test/compiler/unsafe/UnsafeRaw.java | 2 +- .../threads/TestFalseDeadLock.java | 6 +- .../com/oracle/java/testlibrary/Utils.java | 43 ++++- .../RandomGeneratorTest.java | 154 ++++++++++++++++++ 46 files changed, 352 insertions(+), 113 deletions(-) create mode 100644 hotspot/test/testlibrary_tests/RandomGeneratorTest.java diff --git a/hotspot/test/compiler/6896617/Test6896617.java b/hotspot/test/compiler/6896617/Test6896617.java index e28d3d7d57b..38835e0e420 100644 --- a/hotspot/test/compiler/6896617/Test6896617.java +++ b/hotspot/test/compiler/6896617/Test6896617.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,13 +25,20 @@ * @test * @bug 6896617 * @summary Optimize sun.nio.cs.ISO_8859_1$Encode.encodeArrayLoop() with SSE instructions on x86 + * @library /testlibrary * @run main/othervm/timeout=1200 -Xbatch -Xmx256m Test6896617 * */ -import java.util.*; -import java.nio.*; -import java.nio.charset.*; +import com.oracle.java.testlibrary.Utils; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CodingErrorAction; +import java.util.Arrays; +import java.util.Random; public class Test6896617 { final static int SIZE = 256; @@ -54,7 +61,7 @@ public class Test6896617 { sun.nio.cs.ArrayDecoder arrdec = (sun.nio.cs.ArrayDecoder)dec; // Populate char[] with chars which can be encoded by ISO_8859_1 (<= 0xFF) - Random rnd = new Random(0); + Random rnd = Utils.getRandomInstance(); int maxchar = 0xFF; char[] a = new char[SIZE]; byte[] b = new byte[SIZE]; diff --git a/hotspot/test/compiler/7100757/Test7100757.java b/hotspot/test/compiler/7100757/Test7100757.java index daa0bfeb22e..50ee9e11550 100644 --- a/hotspot/test/compiler/7100757/Test7100757.java +++ b/hotspot/test/compiler/7100757/Test7100757.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2014, 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 @@ -26,11 +26,13 @@ * @test * @bug 7100757 * @summary The BitSet.nextSetBit() produces incorrect result in 32bit VM on Sparc - * + * @library /testlibrary * @run main/timeout=300 Test7100757 */ -import java.util.*; +import com.oracle.java.testlibrary.Utils; +import java.util.BitSet; +import java.util.Random; public class Test7100757 { @@ -39,7 +41,7 @@ public class Test7100757 { public static void main(String[] args) { BitSet bs = new BitSet(NBITS); - Random rnd = new Random(); + Random rnd = Utils.getRandomInstance(); long[] ra = new long[(NBITS+63)/64]; for(int l=0; l < 5000000; l++) { diff --git a/hotspot/test/compiler/7177917/Test7177917.java b/hotspot/test/compiler/7177917/Test7177917.java index 7d2c76490a9..708f8c3f424 100644 --- a/hotspot/test/compiler/7177917/Test7177917.java +++ b/hotspot/test/compiler/7177917/Test7177917.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2014, 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 @@ -26,13 +26,14 @@ * Micro-benchmark for Math.pow() and Math.exp() */ -import java.util.*; +import com.oracle.java.testlibrary.Utils; +import java.util.Random; public class Test7177917 { static double d; - static Random r = new Random(0); + static final Random R = Utils.getRandomInstance(); static long m_pow(double[][] values) { double res = 0; @@ -59,10 +60,10 @@ public class Test7177917 { static double[][] pow_values(int nb) { double[][] res = new double[nb][2]; for (int i = 0; i < nb; i++) { - double ylogx = (1 + (r.nextDouble() * 2045)) - 1023; // 2045 rather than 2046 as a safety margin - double x = Math.abs(Double.longBitsToDouble(r.nextLong())); + double ylogx = (1 + (R.nextDouble() * 2045)) - 1023; // 2045 rather than 2046 as a safety margin + double x = Math.abs(Double.longBitsToDouble(R.nextLong())); while (x != x) { - x = Math.abs(Double.longBitsToDouble(r.nextLong())); + x = Math.abs(Double.longBitsToDouble(R.nextLong())); } double logx = Math.log(x) / Math.log(2); double y = ylogx / logx; @@ -76,7 +77,7 @@ public class Test7177917 { static double[] exp_values(int nb) { double[] res = new double[nb]; for (int i = 0; i < nb; i++) { - double ylogx = (1 + (r.nextDouble() * 2045)) - 1023; // 2045 rather than 2046 as a safety margin + double ylogx = (1 + (R.nextDouble() * 2045)) - 1023; // 2045 rather than 2046 as a safety margin double x = Math.E; double logx = Math.log(x) / Math.log(2); double y = ylogx / logx; diff --git a/hotspot/test/compiler/7184394/TestAESBase.java b/hotspot/test/compiler/7184394/TestAESBase.java index 4d3204880bc..73a4994e662 100644 --- a/hotspot/test/compiler/7184394/TestAESBase.java +++ b/hotspot/test/compiler/7184394/TestAESBase.java @@ -26,15 +26,13 @@ * @author Tom Deneau */ +import com.oracle.java.testlibrary.Utils; +import java.security.AlgorithmParameters; +import java.util.Random; import javax.crypto.Cipher; -import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; -import java.security.AlgorithmParameters; - -import java.util.Random; -import java.util.Arrays; abstract public class TestAESBase { int msgSize = Integer.getInteger("msgSize", 646); @@ -59,7 +57,7 @@ abstract public class TestAESBase { byte[] expectedEncode; byte[] decode; byte[] expectedDecode; - Random random = new Random(0); + final Random random = Utils.getRandomInstance(); Cipher cipher; Cipher dCipher; AlgorithmParameters algParams; diff --git a/hotspot/test/compiler/7184394/TestAESMain.java b/hotspot/test/compiler/7184394/TestAESMain.java index 20929e8ba68..d09a305a3b4 100644 --- a/hotspot/test/compiler/7184394/TestAESMain.java +++ b/hotspot/test/compiler/7184394/TestAESMain.java @@ -26,6 +26,7 @@ * @test * @bug 7184394 * @summary add intrinsics to use AES instructions + * @library /testlibrary * * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=CBC TestAESMain * @run main/othervm/timeout=600 -Xbatch -DcheckOutput=true -Dmode=CBC -DencInputOffset=1 TestAESMain diff --git a/hotspot/test/compiler/8005956/PolynomialRoot.java b/hotspot/test/compiler/8005956/PolynomialRoot.java index 1ec220955a5..54cc8f3d5c5 100644 --- a/hotspot/test/compiler/8005956/PolynomialRoot.java +++ b/hotspot/test/compiler/8005956/PolynomialRoot.java @@ -1,4 +1,3 @@ -//package com.polytechnik.utils; /* * (C) Vladislav Malyshkin 2010 * This file is under GPL version 3. @@ -14,10 +13,14 @@ * @test * @bug 8005956 * @summary C2: assert(!def_outside->member(r)) failed: Use of external LRG overlaps the same LRG defined in this block -* +* @library /testlibrary * @run main/timeout=300 PolynomialRoot */ +import com.oracle.java.testlibrary.Utils; +import java.util.Arrays; +import java.util.Random; + public class PolynomialRoot { @@ -57,7 +60,7 @@ private static final boolean PRINT_DEBUG=false; public static int root4(final double [] p,final double [] re_root,final double [] im_root) { - if(PRINT_DEBUG) System.err.println("=====================root4:p="+java.util.Arrays.toString(p)); + if (PRINT_DEBUG) { System.err.println("=====================root4:p=" + Arrays.toString(p)); } final double vs=p[4]; if(PRINT_DEBUG) System.err.println("p[4]="+p[4]); if(!(Math.abs(vs)>EPS)) @@ -367,7 +370,7 @@ public static int root4(final double [] p,final double [] re_root,final double [ - static void setRandomP(final double [] p,final int n,java.util.Random r) + static void setRandomP(final double [] p, final int n, Random r) { if(r.nextDouble()<0.1) { @@ -465,7 +468,7 @@ public static int root4(final double [] p,final double [] re_root,final double [ static void testRoots(final int n, final int n_tests, - final java.util.Random rn, + final Random rn, final double eps) { final double [] p=new double [n+1]; @@ -763,7 +766,7 @@ public static int root4(final double [] p,final double [] re_root,final double [ final long t0=System.currentTimeMillis(); final double eps=1e-6; //checkRoots(); - final java.util.Random r=new java.util.Random(-1381923); + final Random r = Utils.getRandomInstance(); printSpecialValues(); final int n_tests=100000; diff --git a/hotspot/test/compiler/intrinsics/bmi/BMITestRunner.java b/hotspot/test/compiler/intrinsics/bmi/BMITestRunner.java index ea7fd821f33..31a65115c22 100644 --- a/hotspot/test/compiler/intrinsics/bmi/BMITestRunner.java +++ b/hotspot/test/compiler/intrinsics/bmi/BMITestRunner.java @@ -22,13 +22,17 @@ * */ -import java.util.*; +import com.oracle.java.testlibrary.Asserts; +import com.oracle.java.testlibrary.OutputAnalyzer; +import com.oracle.java.testlibrary.ProcessTools; +import com.oracle.java.testlibrary.Utils; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; -import java.nio.charset.StandardCharsets; - -import com.oracle.java.testlibrary.*; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; /** * Test runner that invokes all methods implemented by particular Expr @@ -69,7 +73,7 @@ public class BMITestRunner { String... additionalVMOpts) throws Throwable { - int seed = new Random().nextInt(); + int seed = Utils.getRandomInstance().nextInt(); int iterations = DEFAULT_ITERATIONS_COUNT; for (String testOption : testOpts) { @@ -81,8 +85,6 @@ public class BMITestRunner { } } - System.out.println("Running test with seed: " + seed); - OutputAnalyzer intOutput = runTest(expr, VMMode.INT, additionalVMOpts, seed, iterations); @@ -139,9 +141,9 @@ public class BMITestRunner { Collections.addAll(vmOpts, new String[] { "-XX:+DisplayVMOutputToStderr", + "-D" + Utils.SEED_PROPERTY_NAME + "=" + seed, Executor.class.getName(), expr.getName(), - new Integer(seed).toString(), new Integer(iterations).toString() }); @@ -179,16 +181,15 @@ public class BMITestRunner { public static class Executor { /** - * Usage: BMITestRunner$Executor + * Usage: BMITestRunner$Executor <ExprClassName> <iterations> */ public static void main(String args[]) throws Exception { @SuppressWarnings("unchecked") Class exprClass = (Class)Class.forName(args[0]); Expr expr = exprClass.getConstructor().newInstance(); - Random rng = new Random(Integer.valueOf(args[1])); - int iterations = Integer.valueOf(args[2]); - runTests(expr, iterations, rng); + int iterations = Integer.valueOf(args[1]); + runTests(expr, iterations, Utils.getRandomInstance()); } diff --git a/hotspot/test/compiler/intrinsics/mathexact/AddExactIConstantTest.java b/hotspot/test/compiler/intrinsics/mathexact/AddExactIConstantTest.java index 77000a1d958..71e1a9762fa 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/AddExactIConstantTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/AddExactIConstantTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8024924 * @summary Test constant addExact + * @library /testlibrary * @compile AddExactIConstantTest.java Verify.java * @run main AddExactIConstantTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/AddExactILoadTest.java b/hotspot/test/compiler/intrinsics/mathexact/AddExactILoadTest.java index 2d96bb8b8a6..81bcc240ad5 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/AddExactILoadTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/AddExactILoadTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8024924 * @summary Test non constant addExact + * @library /testlibrary * @compile AddExactILoadTest.java Verify.java * @run main AddExactILoadTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/AddExactILoopDependentTest.java b/hotspot/test/compiler/intrinsics/mathexact/AddExactILoopDependentTest.java index 99aae0d7b21..f0adb45018e 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/AddExactILoopDependentTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/AddExactILoopDependentTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8024924 * @summary Test non constant addExact + * @library /testlibrary * @compile AddExactILoopDependentTest.java Verify.java * @run main AddExactILoopDependentTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/AddExactINonConstantTest.java b/hotspot/test/compiler/intrinsics/mathexact/AddExactINonConstantTest.java index b3a24758569..96c0b6014df 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/AddExactINonConstantTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/AddExactINonConstantTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8024924 * @summary Test non constant addExact + * @library /testlibrary * @compile AddExactINonConstantTest.java Verify.java * @run main AddExactINonConstantTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/AddExactIRepeatTest.java b/hotspot/test/compiler/intrinsics/mathexact/AddExactIRepeatTest.java index d111b66ce82..2ba7268838d 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/AddExactIRepeatTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/AddExactIRepeatTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,11 +25,15 @@ * @test * @bug 8025657 * @summary Test repeating addExact + * @library /testlibrary * @compile AddExactIRepeatTest.java Verify.java * @run main AddExactIRepeatTest * */ +import com.oracle.java.testlibrary.Utils; +import java.util.Random; + public class AddExactIRepeatTest { public static void main(String[] args) { runTest(new Verify.AddExactI()); @@ -44,7 +48,7 @@ public class AddExactIRepeatTest { } public static void runTest(Verify.BinaryMethod method) { - java.util.Random rnd = new java.util.Random(); + Random rnd = Utils.getRandomInstance(); for (int i = 0; i < 50000; ++i) { int x = Integer.MAX_VALUE - 10; int y = Integer.MAX_VALUE - 10 + rnd.nextInt(5); diff --git a/hotspot/test/compiler/intrinsics/mathexact/AddExactLConstantTest.java b/hotspot/test/compiler/intrinsics/mathexact/AddExactLConstantTest.java index dc751406192..f36c925f7fd 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/AddExactLConstantTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/AddExactLConstantTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test constant addExact + * @library /testlibrary * @compile AddExactLConstantTest.java Verify.java * @run main AddExactLConstantTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/AddExactLNonConstantTest.java b/hotspot/test/compiler/intrinsics/mathexact/AddExactLNonConstantTest.java index efd5fd7c92b..01860ba94a7 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/AddExactLNonConstantTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/AddExactLNonConstantTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test non constant addExact + * @library /testlibrary * @compile AddExactLNonConstantTest.java Verify.java * @run main AddExactLNonConstantTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/DecExactITest.java b/hotspot/test/compiler/intrinsics/mathexact/DecExactITest.java index 7e6e1ca3bde..256aa916563 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/DecExactITest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/DecExactITest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test decrementExact + * @library /testlibrary * @compile DecExactITest.java Verify.java * @run main DecExactITest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/DecExactLTest.java b/hotspot/test/compiler/intrinsics/mathexact/DecExactLTest.java index 53a16596e3c..1a9f0c86aa1 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/DecExactLTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/DecExactLTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test decrementExact + * @library /testlibrary * @compile DecExactLTest.java Verify.java * @run main DecExactLTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/IncExactITest.java b/hotspot/test/compiler/intrinsics/mathexact/IncExactITest.java index 9f7ddbd3211..14ab592a885 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/IncExactITest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/IncExactITest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test incrementExact + * @library /testlibrary * @compile IncExactITest.java Verify.java * @run main IncExactITest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/IncExactLTest.java b/hotspot/test/compiler/intrinsics/mathexact/IncExactLTest.java index 755d81908ce..5665b8080c1 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/IncExactLTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/IncExactLTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test incrementExact + * @library /testlibrary * @compile IncExactLTest.java Verify.java * @run main IncExactLTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/MulExactIConstantTest.java b/hotspot/test/compiler/intrinsics/mathexact/MulExactIConstantTest.java index 120bef5e9b9..3d3f23ea90e 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/MulExactIConstantTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/MulExactIConstantTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test constant multiplyExact + * @library /testlibrary * @compile MulExactIConstantTest.java Verify.java * @run main MulExactIConstantTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/MulExactILoadTest.java b/hotspot/test/compiler/intrinsics/mathexact/MulExactILoadTest.java index 36aa3d46230..4551e9d4d62 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/MulExactILoadTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/MulExactILoadTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test multiplyExact + * @library /testlibrary * @compile MulExactILoadTest.java Verify.java * @run main MulExactILoadTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/MulExactILoopDependentTest.java b/hotspot/test/compiler/intrinsics/mathexact/MulExactILoopDependentTest.java index 5ba4ad3cfc3..b324135ab53 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/MulExactILoopDependentTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/MulExactILoopDependentTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test loop dependent multiplyExact + * @library /testlibrary * @compile MulExactILoopDependentTest.java Verify.java * @run main MulExactILoopDependentTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/MulExactINonConstantTest.java b/hotspot/test/compiler/intrinsics/mathexact/MulExactINonConstantTest.java index e108059885b..5f60c66c611 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/MulExactINonConstantTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/MulExactINonConstantTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test non constant multiplyExact + * @library /testlibrary * @compile MulExactINonConstantTest.java Verify.java * @run main MulExactINonConstantTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/MulExactIRepeatTest.java b/hotspot/test/compiler/intrinsics/mathexact/MulExactIRepeatTest.java index dd14ce21ed6..5d1784e8560 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/MulExactIRepeatTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/MulExactIRepeatTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,11 +25,15 @@ * @test * @bug 8026844 * @summary Test repeating multiplyExact + * @library /testlibrary * @compile MulExactIRepeatTest.java Verify.java * @run main MulExactIRepeatTest * */ +import com.oracle.java.testlibrary.Utils; +import java.util.Random; + public class MulExactIRepeatTest { public static void main(String[] args) { runTest(new Verify.MulExactI()); @@ -44,7 +48,7 @@ public class MulExactIRepeatTest { } public static void runTest(Verify.BinaryMethod method) { - java.util.Random rnd = new java.util.Random(); + Random rnd = Utils.getRandomInstance(); for (int i = 0; i < 50000; ++i) { int x = Integer.MAX_VALUE - 10; int y = Integer.MAX_VALUE - 10 + rnd.nextInt(5); diff --git a/hotspot/test/compiler/intrinsics/mathexact/MulExactLConstantTest.java b/hotspot/test/compiler/intrinsics/mathexact/MulExactLConstantTest.java index c687cc276f2..d830cf54812 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/MulExactLConstantTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/MulExactLConstantTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test constant mulExact + * @library /testlibrary * @compile MulExactLConstantTest.java Verify.java * @run main MulExactLConstantTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/MulExactLNonConstantTest.java b/hotspot/test/compiler/intrinsics/mathexact/MulExactLNonConstantTest.java index f9d82ed0876..c0f97a9c143 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/MulExactLNonConstantTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/MulExactLNonConstantTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test non constant mulExact + * @library /testlibrary * @compile MulExactLNonConstantTest.java Verify.java * @run main MulExactLNonConstantTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/NegExactIConstantTest.java b/hotspot/test/compiler/intrinsics/mathexact/NegExactIConstantTest.java index ba49d778762..a8e6c7c9984 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/NegExactIConstantTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/NegExactIConstantTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test constant negExact + * @library /testlibrary * @compile NegExactIConstantTest.java Verify.java * @run main NegExactIConstantTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/NegExactILoadTest.java b/hotspot/test/compiler/intrinsics/mathexact/NegExactILoadTest.java index d7e8215600a..dfa8ba21d11 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/NegExactILoadTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/NegExactILoadTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test negExact + * @library /testlibrary * @compile NegExactILoadTest.java Verify.java * @run main NegExactILoadTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/NegExactILoopDependentTest.java b/hotspot/test/compiler/intrinsics/mathexact/NegExactILoopDependentTest.java index 882f80b91a1..17e49739e71 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/NegExactILoopDependentTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/NegExactILoopDependentTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test negExact loop dependent + * @library /testlibrary * @compile NegExactILoopDependentTest.java Verify.java * @run main NegExactILoopDependentTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/NegExactINonConstantTest.java b/hotspot/test/compiler/intrinsics/mathexact/NegExactINonConstantTest.java index 6f044f0d969..883dc4f82b7 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/NegExactINonConstantTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/NegExactINonConstantTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test non constant negExact + * @library /testlibrary * @compile NegExactINonConstantTest.java Verify.java * @run main NegExactINonConstantTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/NegExactLConstantTest.java b/hotspot/test/compiler/intrinsics/mathexact/NegExactLConstantTest.java index 382cd5c5f9e..6425f81b4b2 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/NegExactLConstantTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/NegExactLConstantTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test constant negExact + * @library /testlibrary * @compile NegExactLConstantTest.java Verify.java * @run main NegExactLConstantTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/NegExactLNonConstantTest.java b/hotspot/test/compiler/intrinsics/mathexact/NegExactLNonConstantTest.java index 0bcad8b2b78..105b80d539e 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/NegExactLNonConstantTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/NegExactLNonConstantTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test constant negExact + * @library /testlibrary * @compile NegExactLNonConstantTest.java Verify.java * @run main NegExactLNonConstantTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/SubExactICondTest.java b/hotspot/test/compiler/intrinsics/mathexact/SubExactICondTest.java index f539bdc7cbe..213aa02c29b 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/SubExactICondTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/SubExactICondTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test subtractExact as condition + * @library /testlibrary * @compile SubExactICondTest.java Verify.java * @run main SubExactICondTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/SubExactIConstantTest.java b/hotspot/test/compiler/intrinsics/mathexact/SubExactIConstantTest.java index b450bd90b11..094b47497ac 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/SubExactIConstantTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/SubExactIConstantTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test constant subtractExact + * @library /testlibrary * @compile SubExactIConstantTest.java Verify.java * @run main SubExactIConstantTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/SubExactILoadTest.java b/hotspot/test/compiler/intrinsics/mathexact/SubExactILoadTest.java index af2ed018258..976bf9babc4 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/SubExactILoadTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/SubExactILoadTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test non constant subtractExact + * @library /testlibrary * @compile SubExactILoadTest.java Verify.java * @run main SubExactILoadTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/SubExactILoopDependentTest.java b/hotspot/test/compiler/intrinsics/mathexact/SubExactILoopDependentTest.java index 67ebcbca321..959bbb9eb92 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/SubExactILoopDependentTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/SubExactILoopDependentTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test non constant subtractExact + * @library /testlibrary * @compile SubExactILoopDependentTest.java Verify.java * @run main SubExactILoopDependentTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/SubExactINonConstantTest.java b/hotspot/test/compiler/intrinsics/mathexact/SubExactINonConstantTest.java index b8153810892..74f41f38a0e 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/SubExactINonConstantTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/SubExactINonConstantTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,6 +25,7 @@ * @test * @bug 8026844 * @summary Test non constant subtractExact + * @library /testlibrary * @compile SubExactINonConstantTest.java Verify.java * @run main SubExactINonConstantTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/SubExactIRepeatTest.java b/hotspot/test/compiler/intrinsics/mathexact/SubExactIRepeatTest.java index 3c57f6f3f76..a7494c77583 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/SubExactIRepeatTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/SubExactIRepeatTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,12 +25,14 @@ * @test * @bug 8026844 * @summary Test repeating subtractExact + * @library /testlibrary * @compile SubExactIRepeatTest.java Verify.java * @run main SubExactIRepeatTest * */ -import java.lang.ArithmeticException; +import com.oracle.java.testlibrary.Utils; +import java.util.Random; public class SubExactIRepeatTest { public static void main(String[] args) { @@ -46,7 +48,7 @@ public class SubExactIRepeatTest { } public static void runTest(Verify.BinaryMethod method) { - java.util.Random rnd = new java.util.Random(); + Random rnd = Utils.getRandomInstance(); for (int i = 0; i < 50000; ++i) { int x = Integer.MIN_VALUE + 10; int y = Integer.MAX_VALUE - 10 + rnd.nextInt(5); diff --git a/hotspot/test/compiler/intrinsics/mathexact/SubExactLConstantTest.java b/hotspot/test/compiler/intrinsics/mathexact/SubExactLConstantTest.java index ec554d7662b..67427615f29 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/SubExactLConstantTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/SubExactLConstantTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -26,6 +26,7 @@ * @bug 8026844 * @bug 8027353 * @summary Test constant subtractExact + * @library /testlibrary * @compile SubExactLConstantTest.java Verify.java * @run main SubExactLConstantTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/SubExactLNonConstantTest.java b/hotspot/test/compiler/intrinsics/mathexact/SubExactLNonConstantTest.java index 86ecf20f366..ec68f88e061 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/SubExactLNonConstantTest.java +++ b/hotspot/test/compiler/intrinsics/mathexact/SubExactLNonConstantTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -26,6 +26,7 @@ * @bug 8026844 * @bug 8027353 * @summary Test non constant subtractExact + * @library /testlibrary * @compile SubExactLNonConstantTest.java Verify.java * @run main SubExactLNonConstantTest * diff --git a/hotspot/test/compiler/intrinsics/mathexact/Verify.java b/hotspot/test/compiler/intrinsics/mathexact/Verify.java index 3936bf1b4f4..1f5871aff95 100644 --- a/hotspot/test/compiler/intrinsics/mathexact/Verify.java +++ b/hotspot/test/compiler/intrinsics/mathexact/Verify.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -21,6 +21,13 @@ * questions. */ +import com.oracle.java.testlibrary.Utils; +import java.util.Random; + +/** + * The class depends on Utils class from testlibrary package. + * It uses factory method that obtains random generator. + */ public class Verify { public static String throwWord(boolean threw) { return (threw ? "threw" : "didn't throw"); @@ -134,7 +141,7 @@ public class Verify { public static class LoadTest { - public static java.util.Random rnd = new java.util.Random(); + public static Random rnd = Utils.getRandomInstance(); public static int[] values = new int[256]; public static void init() { @@ -159,7 +166,7 @@ public class Verify { } public static class NonConstantTest { - public static java.util.Random rnd = new java.util.Random(); + public static Random rnd = Utils.getRandomInstance(); public static int[] values = new int[] { Integer.MAX_VALUE, Integer.MIN_VALUE }; public static void verify(BinaryMethod method) { @@ -180,7 +187,7 @@ public class Verify { public static class NonConstantLongTest { public static long[] values = { Long.MIN_VALUE, Long.MAX_VALUE, 0, Long.MAX_VALUE - 1831 }; - public static java.util.Random rnd = new java.util.Random(); + public static Random rnd = Utils.getRandomInstance(); public static void verify(BinaryLongMethod method) { for (int i = 0; i < 50000; ++i) { @@ -199,7 +206,7 @@ public class Verify { } public static class LoopDependentTest { - public static java.util.Random rnd = new java.util.Random(); + public static Random rnd = Utils.getRandomInstance(); public static void verify(BinaryMethod method) { int rnd1 = rnd.nextInt(), rnd2 = rnd.nextInt(); diff --git a/hotspot/test/compiler/jsr292/ConcurrentClassLoadingTest.java b/hotspot/test/compiler/jsr292/ConcurrentClassLoadingTest.java index 06e900f077d..3aa6b061204 100644 --- a/hotspot/test/compiler/jsr292/ConcurrentClassLoadingTest.java +++ b/hotspot/test/compiler/jsr292/ConcurrentClassLoadingTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -25,18 +25,21 @@ * @test * @bug 8022595 * @summary JSR292: deadlock during class loading of MethodHandles, MethodHandleImpl & MethodHandleNatives - * + * @library /testlibrary * @run main/othervm ConcurrentClassLoadingTest */ -import java.util.*; +import com.oracle.java.testlibrary.Utils; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; public class ConcurrentClassLoadingTest { int numThreads = 0; - long seed = 0; CyclicBarrier l; - Random rand; + private static final Random rand = Utils.getRandomInstance(); public static void main(String[] args) throws Throwable { ConcurrentClassLoadingTest test = new ConcurrentClassLoadingTest(); @@ -49,9 +52,6 @@ public class ConcurrentClassLoadingTest { while (i < args.length) { String flag = args[i]; switch(flag) { - case "-seed": - seed = Long.parseLong(args[++i]); - break; case "-numThreads": numThreads = Integer.parseInt(args[++i]); break; @@ -67,15 +67,9 @@ public class ConcurrentClassLoadingTest { numThreads = Runtime.getRuntime().availableProcessors(); } - if (seed == 0) { - seed = (new Random()).nextLong(); - } - rand = new Random(seed); - l = new CyclicBarrier(numThreads + 1); System.out.printf("Threads: %d\n", numThreads); - System.out.printf("Seed: %d\n", seed); } final List loaders = new ArrayList<>(); @@ -90,7 +84,9 @@ public class ConcurrentClassLoadingTest { System.out.printf("Thread #%d:\n", t); for (int i = 0; i < count; i++) { - if (c.size() == 0) break; + if (c.isEmpty()) { + break; + } int k = rand.nextInt(c.size()); String elem = c.remove(k); diff --git a/hotspot/test/compiler/types/correctness/OffTest.java b/hotspot/test/compiler/types/correctness/OffTest.java index 04be21cd2ee..374db539b80 100644 --- a/hotspot/test/compiler/types/correctness/OffTest.java +++ b/hotspot/test/compiler/types/correctness/OffTest.java @@ -36,9 +36,9 @@ import com.oracle.java.testlibrary.OutputAnalyzer; import com.oracle.java.testlibrary.ProcessTools; -import scenarios.ProfilingType; - +import com.oracle.java.testlibrary.Utils; import java.util.Random; +import scenarios.ProfilingType; public class OffTest { private static final String[] OPTIONS = { @@ -63,14 +63,7 @@ public class OffTest { private static final int PROFILING_TYPE_INDEX = OPTIONS.length - 1; private static final int TYPE_PROFILE_INDEX = OPTIONS.length - 4; private static final int USE_TYPE_SPECULATION_INDEX = OPTIONS.length - 3; - private static final Random RNG; - - static { - String str = System.getProperty("seed"); - long seed = str != null ? Long.parseLong(str) : new Random().nextLong(); - RNG = new Random(seed); - System.out.printf("-Dseed=%d%n", seed); - } + private static final Random RNG = Utils.getRandomInstance(); public static void main(String[] args) throws Exception { int count = DEFAULT_COUNT; diff --git a/hotspot/test/compiler/unsafe/UnsafeRaw.java b/hotspot/test/compiler/unsafe/UnsafeRaw.java index 5d172a5f4a9..f2a19ff06af 100644 --- a/hotspot/test/compiler/unsafe/UnsafeRaw.java +++ b/hotspot/test/compiler/unsafe/UnsafeRaw.java @@ -80,7 +80,7 @@ public class UnsafeRaw { final int element_size = 4; final int magic = 0x12345678; - Random rnd = new Random(); + Random rnd = Utils.getRandomInstance(); long array = unsafe.allocateMemory(array_size * element_size); // 128 ints long addr = array + array_size * element_size / 2; // something in the middle to work with diff --git a/hotspot/test/serviceability/threads/TestFalseDeadLock.java b/hotspot/test/serviceability/threads/TestFalseDeadLock.java index 7ee0fe1116f..30d6e9735c5 100644 --- a/hotspot/test/serviceability/threads/TestFalseDeadLock.java +++ b/hotspot/test/serviceability/threads/TestFalseDeadLock.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, 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 @@ -21,6 +21,7 @@ * questions. */ +import com.oracle.java.testlibrary.Utils; import java.lang.management.ManagementFactory; import java.lang.management.ThreadMXBean; import java.util.Random; @@ -29,6 +30,7 @@ import java.util.Random; * @test * @bug 8016304 * @summary Make sure no deadlock is reported for this program which has no deadlocks. + * @library /testlibrary * @run main/othervm TestFalseDeadLock */ @@ -65,7 +67,7 @@ public class TestFalseDeadLock { public static class Test implements Runnable { public void run() { - Random r = new Random(); + Random r = Utils.getRandomInstance(); while (running) { try { synchronized (this) { diff --git a/hotspot/test/testlibrary/com/oracle/java/testlibrary/Utils.java b/hotspot/test/testlibrary/com/oracle/java/testlibrary/Utils.java index ba4bea5e937..8ad6155eeb9 100644 --- a/hotspot/test/testlibrary/com/oracle/java/testlibrary/Utils.java +++ b/hotspot/test/testlibrary/com/oracle/java/testlibrary/Utils.java @@ -24,21 +24,21 @@ package com.oracle.java.testlibrary; import static com.oracle.java.testlibrary.Asserts.assertTrue; - import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; +import java.lang.reflect.Field; import java.net.InetAddress; import java.net.ServerSocket; import java.net.UnknownHostException; import java.util.ArrayList; -import java.util.List; import java.util.Arrays; import java.util.Collections; -import java.util.regex.Pattern; +import java.util.List; +import java.util.Random; import java.util.regex.Matcher; -import java.lang.reflect.Field; +import java.util.regex.Pattern; import sun.misc.Unsafe; /** @@ -63,6 +63,21 @@ public final class Utils { private static Unsafe unsafe = null; + /** + * Defines property name for seed value. + */ + public static final String SEED_PROPERTY_NAME = "com.oracle.java.testlibrary.random.seed"; + + /* (non-javadoc) + * Random generator with (or without) predefined seed. Depends on + * "com.oracle.java.testlibrary.random.seed" property value. + */ + private static volatile Random RANDOM_GENERATOR; + + /** + * Contains the seed value used for {@link java.util.Random} creation. + */ + public static final long SEED = Long.getLong(SEED_PROPERTY_NAME, new Random().nextLong()); /** * Returns the value of 'test.timeout.factor' system property * converted to {@code double}. @@ -332,4 +347,24 @@ public final class Utils { } return new String(hexView); } + + /** + * Returns {@link java.util.Random} generator initialized with particular seed. + * The seed could be provided via system property {@link Utils#SEED_PROPERTY_NAME} + * In case no seed is provided, the method uses a random number. + * The used seed printed to stdout. + * @return {@link java.util.Random} generator with particular seed. + */ + public static Random getRandomInstance() { + if (RANDOM_GENERATOR == null) { + synchronized (Utils.class) { + if (RANDOM_GENERATOR == null) { + RANDOM_GENERATOR = new Random(SEED); + System.out.printf("For random generator using seed: %d%n", SEED); + System.out.printf("To re-run test with same seed value please add \"-D%s=%d\" to command line.%n", SEED_PROPERTY_NAME, SEED); + } + } + } + return RANDOM_GENERATOR; + } } diff --git a/hotspot/test/testlibrary_tests/RandomGeneratorTest.java b/hotspot/test/testlibrary_tests/RandomGeneratorTest.java new file mode 100644 index 00000000000..ff00999e9b9 --- /dev/null +++ b/hotspot/test/testlibrary_tests/RandomGeneratorTest.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @summary Verify correctnes of the random generator from Utility.java + * @library /testlibrary + * @run driver RandomGeneratorTest SAME_SEED + * @run driver RandomGeneratorTest NO_SEED + * @run driver RandomGeneratorTest DIFFERENT_SEED + */ + +import com.oracle.java.testlibrary.ProcessTools; +import com.oracle.java.testlibrary.Utils; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * The test verifies correctness of work {@link com.oracle.java.testlibrary.Utils#getRandomInstance()}. + * Test works in three modes: same seed provided, no seed provided and + * different seed provided. In the first case the test expects that all random numbers + * will be repeated in all next iterations. For other two modes test expects that + * randomly generated numbers differ from original. + */ +public class RandomGeneratorTest { + private static final String SEED_VM_OPTION = "-D" + Utils.SEED_PROPERTY_NAME + "="; + + public static void main( String[] args) throws Throwable { + if (args.length == 0) { + throw new Error("TESTBUG: No test mode provided."); + } + SeedOption seedOpt = SeedOption.valueOf(args[0]); + List jvmArgs = new ArrayList(); + String optStr = seedOpt.getSeedOption(); + if (optStr != null) { + jvmArgs.add(optStr); + } + jvmArgs.add(RandomRunner.class.getName()); + String[] cmdLineArgs = jvmArgs.toArray(new String[jvmArgs.size()]); + String etalon = ProcessTools.executeTestJvm(cmdLineArgs).getOutput().trim(); + seedOpt.verify(etalon, cmdLineArgs); + } + + /** + * The utility enum helps to generate an appropriate string that should be passed + * to the command line depends on the testing mode. It is also responsible for the result + * validation. + */ + private enum SeedOption { + SAME_SEED { + @Override + public String getSeedOption() { + return SEED_VM_OPTION + Utils.SEED; + } + + @Override + protected boolean isOutputExpected(String orig, String output) { + return output.equals(orig); + } + }, + DIFFERENT_SEED { + @Override + public String getSeedOption() { + return SEED_VM_OPTION + Utils.getRandomInstance().nextLong(); + } + + @Override + public void verify(String orig, String[] cmdLine) { + cmdLine[0] = getSeedOption(); + super.verify(orig, cmdLine); + } + }, + NO_SEED { + @Override + public String getSeedOption() { + return null; + } + }; + + /** + * Generates a string to be added as a command line argument. + * It contains "-D" prefix, system property name, '=' sign + * and seed value. + * @return command line argument + */ + public abstract String getSeedOption(); + + protected boolean isOutputExpected(String orig, String output) { + return !output.equals(orig); + } + + /** + * Verifies that the original output meets expectations + * depending on the test mode. It compares the output of second execution + * to original one. + * @param orig original output + * @param cmdLine command line arguments + * @throws Throwable - Throws an exception in case test failure. + */ + public void verify(String orig, String[] cmdLine) { + String lastLineOrig = getLastLine(orig); + String lastLine; + try { + lastLine = getLastLine(ProcessTools.executeTestJvm(cmdLine).getOutput().trim()); + } catch (Throwable t) { + throw new Error("TESTBUG: Unexpedted exception during jvm execution.", t); + } + if (!isOutputExpected(lastLineOrig, lastLine)) { + throw new AssertionError("Unexpected random number sequence for mode: " + this.name()); + } + } + + private static String getLastLine(String output) { + return output.substring(output.lastIndexOf(Utils.NEW_LINE)).trim(); + } + } + + /** + * The helper class generates several random numbers + * and prints them out. + */ + public static class RandomRunner { + private static final int COUNT = 10; + public static void main(String[] args) { + StringBuilder sb = new StringBuilder(); + Random rng = Utils.getRandomInstance(); + for (int i = 0; i < COUNT; i++) { + sb.append(rng.nextLong()).append(' '); + } + System.out.println(sb.toString()); + } + } +} From 8cd1a874e89d73f30841a9d6d152e9392f3d71d0 Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Mon, 3 Nov 2014 12:02:40 -0800 Subject: [PATCH 025/118] 8059780: SPECjvm2008-MPEG performance regressions on x64 platforms Back-out 8052081 changes made in lcm.cpp. Reviewed-by: iveresov, roland --- hotspot/src/share/vm/opto/lcm.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/hotspot/src/share/vm/opto/lcm.cpp b/hotspot/src/share/vm/opto/lcm.cpp index 187d2699768..a601752d4cd 100644 --- a/hotspot/src/share/vm/opto/lcm.cpp +++ b/hotspot/src/share/vm/opto/lcm.cpp @@ -464,9 +464,7 @@ Node* PhaseCFG::select(Block* block, Node_List &worklist, GrowableArray &re iop == Op_CreateEx || // Create-exception must start block iop == Op_CheckCastPP ) { - // select the node n - // remove n from worklist and retain the order of remaining nodes - worklist.remove((uint)i); + worklist.map(i,worklist.pop()); return n; } @@ -552,9 +550,7 @@ Node* PhaseCFG::select(Block* block, Node_List &worklist, GrowableArray &re assert(idx >= 0, "index should be set"); Node *n = worklist[(uint)idx]; // Get the winner - // select the node n - // remove n from worklist and retain the order of remaining nodes - worklist.remove((uint)idx); + worklist.map((uint)idx, worklist.pop()); // Compress worklist return n; } From 79738069b0d5bc58d62aa6cfff066936424b064e Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Mon, 20 Oct 2014 22:53:37 +0200 Subject: [PATCH 026/118] 8060252: JDK-7173584 compiler changes regress SPECjvm2008 on SPARC Arraycopy code misses opportunities to optimize copies to just allocated array. Reviewed-by: kvn --- hotspot/src/share/vm/opto/library_call.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hotspot/src/share/vm/opto/library_call.cpp b/hotspot/src/share/vm/opto/library_call.cpp index 91d82d30f5f..10c857ca0dd 100644 --- a/hotspot/src/share/vm/opto/library_call.cpp +++ b/hotspot/src/share/vm/opto/library_call.cpp @@ -4697,6 +4697,10 @@ bool LibraryCallKit::inline_arraycopy() { Node* dest_offset = argument(3); // type: int Node* length = argument(4); // type: int + // Check for allocation before we add nodes that would confuse + // tightly_coupled_allocation() + AllocateArrayNode* alloc = tightly_coupled_allocation(dest, NULL); + // The following tests must be performed // (1) src and dest are arrays. // (2) src and dest arrays must have elements of the same BasicType @@ -4870,7 +4874,6 @@ bool LibraryCallKit::inline_arraycopy() { return true; } - AllocateArrayNode* alloc = tightly_coupled_allocation(dest, NULL); ArrayCopyNode* ac = ArrayCopyNode::make(this, true, src, src_offset, dest, dest_offset, length, alloc != NULL, // Create LoadRange and LoadKlass nodes for use during macro expansion here // so the compiler has a chance to eliminate them: during macro expansion, From 209ffcd9a5eb7c2c21174967de14b1d791ad361d Mon Sep 17 00:00:00 2001 From: Frederic Parain Date: Wed, 22 Oct 2014 02:31:25 -0700 Subject: [PATCH 027/118] 8061618: Removed unused networking functions from os class Reviewed-by: lfoltan, hseigel, dholmes --- hotspot/src/os/aix/vm/os_aix.cpp | 9 --- hotspot/src/os/aix/vm/os_aix.inline.hpp | 78 ------------------- hotspot/src/os/bsd/vm/os_bsd.cpp | 15 ---- hotspot/src/os/bsd/vm/os_bsd.inline.hpp | 77 ------------------ hotspot/src/os/linux/vm/os_linux.cpp | 9 --- hotspot/src/os/linux/vm/os_linux.inline.hpp | 78 ------------------- hotspot/src/os/solaris/vm/os_solaris.cpp | 71 ----------------- .../src/os/solaris/vm/os_solaris.inline.hpp | 28 ------- hotspot/src/os/windows/vm/os_windows.cpp | 64 --------------- hotspot/src/share/vm/runtime/os.hpp | 18 ----- 10 files changed, 447 deletions(-) diff --git a/hotspot/src/os/aix/vm/os_aix.cpp b/hotspot/src/os/aix/vm/os_aix.cpp index bec34838920..fc873a11eed 100644 --- a/hotspot/src/os/aix/vm/os_aix.cpp +++ b/hotspot/src/os/aix/vm/os_aix.cpp @@ -4137,15 +4137,6 @@ int os::available(int fd, jlong *bytes) { return 1; } -int os::socket_available(int fd, jint *pbytes) { - // Linux doc says EINTR not returned, unlike Solaris - int ret = ::ioctl(fd, FIONREAD, pbytes); - - //%% note ioctl can return 0 when successful, JVM_SocketAvailable - // is expected to return 0 on failure and 1 on success to the jdk. - return (ret < 0) ? 0 : 1; -} - // Map a block of memory. char* os::pd_map_memory(int fd, const char* file_name, size_t file_offset, char *addr, size_t bytes, bool read_only, diff --git a/hotspot/src/os/aix/vm/os_aix.inline.hpp b/hotspot/src/os/aix/vm/os_aix.inline.hpp index bb3232bfbc7..5602342b4ff 100644 --- a/hotspot/src/os/aix/vm/os_aix.inline.hpp +++ b/hotspot/src/os/aix/vm/os_aix.inline.hpp @@ -178,92 +178,14 @@ inline int os::raw_send(int fd, char* buf, size_t nBytes, uint flags) { return os::send(fd, buf, nBytes, flags); } -inline int os::timeout(int fd, long timeout) { - julong prevtime,newtime; - struct timeval t; - - gettimeofday(&t, NULL); - prevtime = ((julong)t.tv_sec * 1000) + t.tv_usec / 1000; - - for(;;) { - struct pollfd pfd; - - pfd.fd = fd; - pfd.events = POLLIN | POLLERR; - - int res = ::poll(&pfd, 1, timeout); - - if (res == OS_ERR && errno == EINTR) { - - // On Linux any value < 0 means "forever" - - if(timeout >= 0) { - gettimeofday(&t, NULL); - newtime = ((julong)t.tv_sec * 1000) + t.tv_usec / 1000; - timeout -= newtime - prevtime; - if(timeout <= 0) - return OS_OK; - prevtime = newtime; - } - } else - return res; - } -} - -inline int os::listen(int fd, int count) { - return ::listen(fd, count); -} - inline int os::connect(int fd, struct sockaddr* him, socklen_t len) { RESTARTABLE_RETURN_INT(::connect(fd, him, len)); } -inline int os::accept(int fd, struct sockaddr* him, socklen_t* len) { - // Linux doc says this can't return EINTR, unlike accept() on Solaris. - // But see attachListener_linux.cpp, LinuxAttachListener::dequeue(). - return (int)::accept(fd, him, len); -} - -inline int os::recvfrom(int fd, char* buf, size_t nBytes, uint flags, - sockaddr* from, socklen_t* fromlen) { - RESTARTABLE_RETURN_INT((int)::recvfrom(fd, buf, nBytes, flags, from, fromlen)); -} - -inline int os::sendto(int fd, char* buf, size_t len, uint flags, - struct sockaddr* to, socklen_t tolen) { - RESTARTABLE_RETURN_INT((int)::sendto(fd, buf, len, flags, to, tolen)); -} - -inline int os::socket_shutdown(int fd, int howto) { - return ::shutdown(fd, howto); -} - -inline int os::bind(int fd, struct sockaddr* him, socklen_t len) { - return ::bind(fd, him, len); -} - -inline int os::get_sock_name(int fd, struct sockaddr* him, socklen_t* len) { - return ::getsockname(fd, him, len); -} - -inline int os::get_host_name(char* name, int namelen) { - return ::gethostname(name, namelen); -} - inline struct hostent* os::get_host_by_name(char* name) { return ::gethostbyname(name); } -inline int os::get_sock_opt(int fd, int level, int optname, - char* optval, socklen_t* optlen) { - return ::getsockopt(fd, level, optname, optval, optlen); -} - -inline int os::set_sock_opt(int fd, int level, int optname, - const char* optval, socklen_t optlen) { - return ::setsockopt(fd, level, optname, optval, optlen); -} - inline bool os::supports_monotonic_clock() { // mread_real_time() is monotonic on AIX (see os::javaTimeNanos() comments) return true; diff --git a/hotspot/src/os/bsd/vm/os_bsd.cpp b/hotspot/src/os/bsd/vm/os_bsd.cpp index 95e026651ac..0d8ba0a2367 100644 --- a/hotspot/src/os/bsd/vm/os_bsd.cpp +++ b/hotspot/src/os/bsd/vm/os_bsd.cpp @@ -3958,21 +3958,6 @@ int os::available(int fd, jlong *bytes) { return 1; } -int os::socket_available(int fd, jint *pbytes) { - if (fd < 0) { - return OS_OK; - } - - int ret; - - RESTARTABLE(::ioctl(fd, FIONREAD, pbytes), ret); - - //%% note ioctl can return 0 when successful, JVM_SocketAvailable - // is expected to return 0 on failure and 1 on success to the jdk. - - return (ret == OS_ERR) ? 0 : 1; -} - // Map a block of memory. char* os::pd_map_memory(int fd, const char* file_name, size_t file_offset, char *addr, size_t bytes, bool read_only, diff --git a/hotspot/src/os/bsd/vm/os_bsd.inline.hpp b/hotspot/src/os/bsd/vm/os_bsd.inline.hpp index 1eafb9c76e9..3afb5c21a83 100644 --- a/hotspot/src/os/bsd/vm/os_bsd.inline.hpp +++ b/hotspot/src/os/bsd/vm/os_bsd.inline.hpp @@ -181,91 +181,14 @@ inline int os::raw_send(int fd, char* buf, size_t nBytes, uint flags) { return os::send(fd, buf, nBytes, flags); } -inline int os::timeout(int fd, long timeout) { - julong prevtime,newtime; - struct timeval t; - - gettimeofday(&t, NULL); - prevtime = ((julong)t.tv_sec * 1000) + t.tv_usec / 1000; - - for(;;) { - struct pollfd pfd; - - pfd.fd = fd; - pfd.events = POLLIN | POLLERR; - - int res = ::poll(&pfd, 1, timeout); - - if (res == OS_ERR && errno == EINTR) { - - // On Bsd any value < 0 means "forever" - - if(timeout >= 0) { - gettimeofday(&t, NULL); - newtime = ((julong)t.tv_sec * 1000) + t.tv_usec / 1000; - timeout -= newtime - prevtime; - if(timeout <= 0) - return OS_OK; - prevtime = newtime; - } - } else - return res; - } -} - -inline int os::listen(int fd, int count) { - return ::listen(fd, count); -} - inline int os::connect(int fd, struct sockaddr* him, socklen_t len) { RESTARTABLE_RETURN_INT(::connect(fd, him, len)); } -inline int os::accept(int fd, struct sockaddr* him, socklen_t* len) { - // At least OpenBSD and FreeBSD can return EINTR from accept. - RESTARTABLE_RETURN_INT(::accept(fd, him, len)); -} - -inline int os::recvfrom(int fd, char* buf, size_t nBytes, uint flags, - sockaddr* from, socklen_t* fromlen) { - RESTARTABLE_RETURN_INT((int)::recvfrom(fd, buf, nBytes, flags, from, fromlen)); -} - -inline int os::sendto(int fd, char* buf, size_t len, uint flags, - struct sockaddr *to, socklen_t tolen) { - RESTARTABLE_RETURN_INT((int)::sendto(fd, buf, len, flags, to, tolen)); -} - -inline int os::socket_shutdown(int fd, int howto) { - return ::shutdown(fd, howto); -} - -inline int os::bind(int fd, struct sockaddr* him, socklen_t len) { - return ::bind(fd, him, len); -} - -inline int os::get_sock_name(int fd, struct sockaddr* him, socklen_t* len) { - return ::getsockname(fd, him, len); -} - -inline int os::get_host_name(char* name, int namelen) { - return ::gethostname(name, namelen); -} - inline struct hostent* os::get_host_by_name(char* name) { return ::gethostbyname(name); } -inline int os::get_sock_opt(int fd, int level, int optname, - char *optval, socklen_t* optlen) { - return ::getsockopt(fd, level, optname, optval, optlen); -} - -inline int os::set_sock_opt(int fd, int level, int optname, - const char* optval, socklen_t optlen) { - return ::setsockopt(fd, level, optname, optval, optlen); -} - inline bool os::supports_monotonic_clock() { #ifdef __APPLE__ return true; diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp index 3d82d976f37..3e5dc4066d5 100644 --- a/hotspot/src/os/linux/vm/os_linux.cpp +++ b/hotspot/src/os/linux/vm/os_linux.cpp @@ -5211,15 +5211,6 @@ int os::available(int fd, jlong *bytes) { return 1; } -int os::socket_available(int fd, jint *pbytes) { - // Linux doc says EINTR not returned, unlike Solaris - int ret = ::ioctl(fd, FIONREAD, pbytes); - - //%% note ioctl can return 0 when successful, JVM_SocketAvailable - // is expected to return 0 on failure and 1 on success to the jdk. - return (ret < 0) ? 0 : 1; -} - // Map a block of memory. char* os::pd_map_memory(int fd, const char* file_name, size_t file_offset, char *addr, size_t bytes, bool read_only, diff --git a/hotspot/src/os/linux/vm/os_linux.inline.hpp b/hotspot/src/os/linux/vm/os_linux.inline.hpp index d83fb5b7930..ba4b777d520 100644 --- a/hotspot/src/os/linux/vm/os_linux.inline.hpp +++ b/hotspot/src/os/linux/vm/os_linux.inline.hpp @@ -173,92 +173,14 @@ inline int os::raw_send(int fd, char* buf, size_t nBytes, uint flags) { return os::send(fd, buf, nBytes, flags); } -inline int os::timeout(int fd, long timeout) { - julong prevtime,newtime; - struct timeval t; - - gettimeofday(&t, NULL); - prevtime = ((julong)t.tv_sec * 1000) + t.tv_usec / 1000; - - for(;;) { - struct pollfd pfd; - - pfd.fd = fd; - pfd.events = POLLIN | POLLERR; - - int res = ::poll(&pfd, 1, timeout); - - if (res == OS_ERR && errno == EINTR) { - - // On Linux any value < 0 means "forever" - - if(timeout >= 0) { - gettimeofday(&t, NULL); - newtime = ((julong)t.tv_sec * 1000) + t.tv_usec / 1000; - timeout -= newtime - prevtime; - if(timeout <= 0) - return OS_OK; - prevtime = newtime; - } - } else - return res; - } -} - -inline int os::listen(int fd, int count) { - return ::listen(fd, count); -} - inline int os::connect(int fd, struct sockaddr* him, socklen_t len) { RESTARTABLE_RETURN_INT(::connect(fd, him, len)); } -inline int os::accept(int fd, struct sockaddr* him, socklen_t* len) { - // Linux doc says this can't return EINTR, unlike accept() on Solaris. - // But see attachListener_linux.cpp, LinuxAttachListener::dequeue(). - return (int)::accept(fd, him, len); -} - -inline int os::recvfrom(int fd, char* buf, size_t nBytes, uint flags, - sockaddr* from, socklen_t* fromlen) { - RESTARTABLE_RETURN_INT((int)::recvfrom(fd, buf, nBytes, flags, from, fromlen)); -} - -inline int os::sendto(int fd, char* buf, size_t len, uint flags, - struct sockaddr* to, socklen_t tolen) { - RESTARTABLE_RETURN_INT((int)::sendto(fd, buf, len, flags, to, tolen)); -} - -inline int os::socket_shutdown(int fd, int howto) { - return ::shutdown(fd, howto); -} - -inline int os::bind(int fd, struct sockaddr* him, socklen_t len) { - return ::bind(fd, him, len); -} - -inline int os::get_sock_name(int fd, struct sockaddr* him, socklen_t* len) { - return ::getsockname(fd, him, len); -} - -inline int os::get_host_name(char* name, int namelen) { - return ::gethostname(name, namelen); -} - inline struct hostent* os::get_host_by_name(char* name) { return ::gethostbyname(name); } -inline int os::get_sock_opt(int fd, int level, int optname, - char* optval, socklen_t* optlen) { - return ::getsockopt(fd, level, optname, optval, optlen); -} - -inline int os::set_sock_opt(int fd, int level, int optname, - const char* optval, socklen_t optlen) { - return ::setsockopt(fd, level, optname, optval, optlen); -} - inline bool os::supports_monotonic_clock() { return Linux::_clock_gettime != NULL; } diff --git a/hotspot/src/os/solaris/vm/os_solaris.cpp b/hotspot/src/os/solaris/vm/os_solaris.cpp index 109bf44bdc0..dddb4e7e567 100644 --- a/hotspot/src/os/solaris/vm/os_solaris.cpp +++ b/hotspot/src/os/solaris/vm/os_solaris.cpp @@ -5912,37 +5912,6 @@ int os::raw_send(int fd, char* buf, size_t nBytes, uint flags) { // a poll() is done with timeout == -1, in which case we repeat with this // "wait forever" value. -int os::timeout(int fd, long timeout) { - int res; - struct timeval t; - julong prevtime, newtime; - static const char* aNull = 0; - struct pollfd pfd; - pfd.fd = fd; - pfd.events = POLLIN; - - assert(((JavaThread*)Thread::current())->thread_state() == _thread_in_native, - "Assumed _thread_in_native"); - - gettimeofday(&t, &aNull); - prevtime = ((julong)t.tv_sec * 1000) + t.tv_usec / 1000; - - for (;;) { - res = ::poll(&pfd, 1, timeout); - if (res == OS_ERR && errno == EINTR) { - if (timeout != -1) { - gettimeofday(&t, &aNull); - newtime = ((julong)t.tv_sec * 1000) + t.tv_usec /1000; - timeout -= newtime - prevtime; - if (timeout <= 0) { - return OS_OK; - } - prevtime = newtime; - } - } else return res; - } -} - int os::connect(int fd, struct sockaddr *him, socklen_t len) { int _result; _result = ::connect(fd, him, len); @@ -5982,46 +5951,6 @@ int os::connect(int fd, struct sockaddr *him, socklen_t len) { return _result; } -int os::accept(int fd, struct sockaddr* him, socklen_t* len) { - if (fd < 0) { - return OS_ERR; - } - assert(((JavaThread*)Thread::current())->thread_state() == _thread_in_native, - "Assumed _thread_in_native"); - RESTARTABLE_RETURN_INT((int)::accept(fd, him, len)); -} - -int os::recvfrom(int fd, char* buf, size_t nBytes, uint flags, - sockaddr* from, socklen_t* fromlen) { - assert(((JavaThread*)Thread::current())->thread_state() == _thread_in_native, - "Assumed _thread_in_native"); - RESTARTABLE_RETURN_INT((int)::recvfrom(fd, buf, nBytes, flags, from, fromlen)); -} - -int os::sendto(int fd, char* buf, size_t len, uint flags, - struct sockaddr* to, socklen_t tolen) { - assert(((JavaThread*)Thread::current())->thread_state() == _thread_in_native, - "Assumed _thread_in_native"); - RESTARTABLE_RETURN_INT((int)::sendto(fd, buf, len, flags, to, tolen)); -} - -int os::socket_available(int fd, jint *pbytes) { - if (fd < 0) { - return OS_OK; - } - int ret; - RESTARTABLE(::ioctl(fd, FIONREAD, pbytes), ret); - // note: ioctl can return 0 when successful, JVM_SocketAvailable - // is expected to return 0 on failure and 1 on success to the jdk. - return (ret == OS_ERR) ? 0 : 1; -} - -int os::bind(int fd, struct sockaddr* him, socklen_t len) { - assert(((JavaThread*)Thread::current())->thread_state() == _thread_in_native, - "Assumed _thread_in_native"); - return ::bind(fd, him, len); -} - // Get the default path to the core file // Returns the length of the string int os::get_core_path(char* buffer, size_t bufferSize) { diff --git a/hotspot/src/os/solaris/vm/os_solaris.inline.hpp b/hotspot/src/os/solaris/vm/os_solaris.inline.hpp index 7609abac014..3456414f048 100644 --- a/hotspot/src/os/solaris/vm/os_solaris.inline.hpp +++ b/hotspot/src/os/solaris/vm/os_solaris.inline.hpp @@ -120,38 +120,10 @@ inline int os::socket(int domain, int type, int protocol) { return ::socket(domain, type, protocol); } -inline int os::listen(int fd, int count) { - if (fd < 0) return OS_ERR; - - return ::listen(fd, count); -} - -inline int os::socket_shutdown(int fd, int howto){ - return ::shutdown(fd, howto); -} - -inline int os::get_sock_name(int fd, struct sockaddr* him, socklen_t* len){ - return ::getsockname(fd, him, len); -} - -inline int os::get_host_name(char* name, int namelen){ - return ::gethostname(name, namelen); -} - inline struct hostent* os::get_host_by_name(char* name) { return ::gethostbyname(name); } -inline int os::get_sock_opt(int fd, int level, int optname, - char* optval, socklen_t* optlen) { - return ::getsockopt(fd, level, optname, optval, optlen); -} - -inline int os::set_sock_opt(int fd, int level, int optname, - const char *optval, socklen_t optlen) { - return ::setsockopt(fd, level, optname, optval, optlen); -} - inline bool os::supports_monotonic_clock() { // javaTimeNanos() is monotonic on Solaris, see getTimeNanos() comments return true; diff --git a/hotspot/src/os/windows/vm/os_windows.cpp b/hotspot/src/os/windows/vm/os_windows.cpp index 2bde459105b..cc3f8f75832 100644 --- a/hotspot/src/os/windows/vm/os_windows.cpp +++ b/hotspot/src/os/windows/vm/os_windows.cpp @@ -5091,39 +5091,14 @@ int os::socket_close(int fd) { return ::closesocket(fd); } -int os::socket_available(int fd, jint *pbytes) { - int ret = ::ioctlsocket(fd, FIONREAD, (u_long*)pbytes); - return (ret < 0) ? 0 : 1; -} - int os::socket(int domain, int type, int protocol) { return ::socket(domain, type, protocol); } -int os::listen(int fd, int count) { - return ::listen(fd, count); -} - int os::connect(int fd, struct sockaddr* him, socklen_t len) { return ::connect(fd, him, len); } -int os::accept(int fd, struct sockaddr* him, socklen_t* len) { - return ::accept(fd, him, len); -} - -int os::sendto(int fd, char* buf, size_t len, uint flags, - struct sockaddr* to, socklen_t tolen) { - - return ::sendto(fd, buf, (int)len, flags, to, tolen); -} - -int os::recvfrom(int fd, char *buf, size_t nBytes, uint flags, - sockaddr* from, socklen_t* fromlen) { - - return ::recvfrom(fd, buf, (int)nBytes, flags, from, fromlen); -} - int os::recv(int fd, char* buf, size_t nBytes, uint flags) { return ::recv(fd, buf, (int)nBytes, flags); } @@ -5136,45 +5111,6 @@ int os::raw_send(int fd, char* buf, size_t nBytes, uint flags) { return ::send(fd, buf, (int)nBytes, flags); } -int os::timeout(int fd, long timeout) { - fd_set tbl; - struct timeval t; - - t.tv_sec = timeout / 1000; - t.tv_usec = (timeout % 1000) * 1000; - - tbl.fd_count = 1; - tbl.fd_array[0] = fd; - - return ::select(1, &tbl, 0, 0, &t); -} - -int os::get_host_name(char* name, int namelen) { - return ::gethostname(name, namelen); -} - -int os::socket_shutdown(int fd, int howto) { - return ::shutdown(fd, howto); -} - -int os::bind(int fd, struct sockaddr* him, socklen_t len) { - return ::bind(fd, him, len); -} - -int os::get_sock_name(int fd, struct sockaddr* him, socklen_t* len) { - return ::getsockname(fd, him, len); -} - -int os::get_sock_opt(int fd, int level, int optname, - char* optval, socklen_t* optlen) { - return ::getsockopt(fd, level, optname, optval, optlen); -} - -int os::set_sock_opt(int fd, int level, int optname, - const char* optval, socklen_t optlen) { - return ::setsockopt(fd, level, optname, optval, optlen); -} - // WINDOWS CONTEXT Flags for THREAD_SAMPLING #if defined(IA32) #define sampling_context_flags (CONTEXT_FULL | CONTEXT_FLOATING_POINT | CONTEXT_EXTENDED_REGISTERS) diff --git a/hotspot/src/share/vm/runtime/os.hpp b/hotspot/src/share/vm/runtime/os.hpp index d2e78b43a4c..009e2f18c97 100644 --- a/hotspot/src/share/vm/runtime/os.hpp +++ b/hotspot/src/share/vm/runtime/os.hpp @@ -680,28 +680,10 @@ class os: AllStatic { // SocketInterface (ex HPI SocketInterface ) static int socket(int domain, int type, int protocol); static int socket_close(int fd); - static int socket_shutdown(int fd, int howto); static int recv(int fd, char* buf, size_t nBytes, uint flags); static int send(int fd, char* buf, size_t nBytes, uint flags); static int raw_send(int fd, char* buf, size_t nBytes, uint flags); - static int timeout(int fd, long timeout); - static int listen(int fd, int count); static int connect(int fd, struct sockaddr* him, socklen_t len); - static int bind(int fd, struct sockaddr* him, socklen_t len); - static int accept(int fd, struct sockaddr* him, socklen_t* len); - static int recvfrom(int fd, char* buf, size_t nbytes, uint flags, - struct sockaddr* from, socklen_t* fromlen); - static int get_sock_name(int fd, struct sockaddr* him, socklen_t* len); - static int sendto(int fd, char* buf, size_t len, uint flags, - struct sockaddr* to, socklen_t tolen); - static int socket_available(int fd, jint* pbytes); - - static int get_sock_opt(int fd, int level, int optname, - char* optval, socklen_t* optlen); - static int set_sock_opt(int fd, int level, int optname, - const char* optval, socklen_t optlen); - static int get_host_name(char* name, int namelen); - static struct hostent* get_host_by_name(char* name); // Support for signals (see JVM_RaiseSignal, JVM_RegisterSignal) From ea48bceb5e72162e695c15914dce027ae9a43a1f Mon Sep 17 00:00:00 2001 From: Andreas Eriksson Date: Wed, 22 Oct 2014 13:59:56 +0200 Subject: [PATCH 028/118] 8057043: Type annotations not retained during class redefine / retransform Reviewed-by: coleenp, sspitsyn, jfranck --- .../vm/prims/jvmtiClassFileReconstituter.cpp | 22 + .../share/vm/prims/jvmtiRedefineClasses.cpp | 622 +++++++++++++++++- .../share/vm/prims/jvmtiRedefineClasses.hpp | 17 + .../RedefineTests/RedefineAnnotations.java | 410 ++++++++++++ 4 files changed, 1054 insertions(+), 17 deletions(-) create mode 100644 hotspot/test/runtime/RedefineTests/RedefineAnnotations.java diff --git a/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.cpp b/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.cpp index c6633bf34a9..53b5850712e 100644 --- a/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.cpp +++ b/hotspot/src/share/vm/prims/jvmtiClassFileReconstituter.cpp @@ -41,6 +41,7 @@ void JvmtiClassFileReconstituter::write_field_infos() { HandleMark hm(thread()); Array* fields_anno = ikh()->fields_annotations(); + Array* fields_type_anno = ikh()->fields_type_annotations(); // Compute the real number of Java fields int java_fields = ikh()->java_fields_count(); @@ -55,6 +56,7 @@ void JvmtiClassFileReconstituter::write_field_infos() { // int offset = ikh()->field_offset( index ); int generic_signature_index = fs.generic_signature_index(); AnnotationArray* anno = fields_anno == NULL ? NULL : fields_anno->at(fs.index()); + AnnotationArray* type_anno = fields_type_anno == NULL ? NULL : fields_type_anno->at(fs.index()); // JVMSpec| field_info { // JVMSpec| u2 access_flags; @@ -80,6 +82,9 @@ void JvmtiClassFileReconstituter::write_field_infos() { if (anno != NULL) { ++attr_count; // has RuntimeVisibleAnnotations attribute } + if (type_anno != NULL) { + ++attr_count; // has RuntimeVisibleTypeAnnotations attribute + } write_u2(attr_count); @@ -97,6 +102,9 @@ void JvmtiClassFileReconstituter::write_field_infos() { if (anno != NULL) { write_annotations_attribute("RuntimeVisibleAnnotations", anno); } + if (type_anno != NULL) { + write_annotations_attribute("RuntimeVisibleTypeAnnotations", type_anno); + } } } @@ -537,6 +545,7 @@ void JvmtiClassFileReconstituter::write_method_info(methodHandle method) { AnnotationArray* anno = method->annotations(); AnnotationArray* param_anno = method->parameter_annotations(); AnnotationArray* default_anno = method->annotation_default(); + AnnotationArray* type_anno = method->type_annotations(); // skip generated default interface methods if (method->is_overpass()) { @@ -572,6 +581,9 @@ void JvmtiClassFileReconstituter::write_method_info(methodHandle method) { if (param_anno != NULL) { ++attr_count; // has RuntimeVisibleParameterAnnotations attribute } + if (type_anno != NULL) { + ++attr_count; // has RuntimeVisibleTypeAnnotations attribute + } write_u2(attr_count); if (const_method->code_size() > 0) { @@ -596,6 +608,9 @@ void JvmtiClassFileReconstituter::write_method_info(methodHandle method) { if (param_anno != NULL) { write_annotations_attribute("RuntimeVisibleParameterAnnotations", param_anno); } + if (type_anno != NULL) { + write_annotations_attribute("RuntimeVisibleTypeAnnotations", type_anno); + } } // Write the class attributes portion of ClassFile structure @@ -605,6 +620,7 @@ void JvmtiClassFileReconstituter::write_class_attributes() { u2 inner_classes_length = inner_classes_attribute_length(); Symbol* generic_signature = ikh()->generic_signature(); AnnotationArray* anno = ikh()->class_annotations(); + AnnotationArray* type_anno = ikh()->class_type_annotations(); int attr_count = 0; if (generic_signature != NULL) { @@ -622,6 +638,9 @@ void JvmtiClassFileReconstituter::write_class_attributes() { if (anno != NULL) { ++attr_count; // has RuntimeVisibleAnnotations attribute } + if (type_anno != NULL) { + ++attr_count; // has RuntimeVisibleTypeAnnotations attribute + } if (cpool()->operands() != NULL) { ++attr_count; } @@ -643,6 +662,9 @@ void JvmtiClassFileReconstituter::write_class_attributes() { if (anno != NULL) { write_annotations_attribute("RuntimeVisibleAnnotations", anno); } + if (type_anno != NULL) { + write_annotations_attribute("RuntimeVisibleTypeAnnotations", type_anno); + } if (cpool()->operands() != NULL) { write_bootstrapmethod_attribute(); } diff --git a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp index 796f5580326..9335857ee56 100644 --- a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp +++ b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp @@ -1569,6 +1569,29 @@ bool VM_RedefineClasses::rewrite_cp_refs(instanceKlassHandle scratch_class, return false; } + // rewrite constant pool references in the class_type_annotations: + if (!rewrite_cp_refs_in_class_type_annotations(scratch_class, THREAD)) { + // propagate failure back to caller + return false; + } + + // rewrite constant pool references in the fields_type_annotations: + if (!rewrite_cp_refs_in_fields_type_annotations(scratch_class, THREAD)) { + // propagate failure back to caller + return false; + } + + // rewrite constant pool references in the methods_type_annotations: + if (!rewrite_cp_refs_in_methods_type_annotations(scratch_class, THREAD)) { + // propagate failure back to caller + return false; + } + + // There can be type annotations in the Code part of a method_info attribute. + // These annotations are not accessible, even by reflection. + // Currently they are not even parsed by the ClassFileParser. + // If runtime access is added they will also need to be rewritten. + // rewrite source file name index: u2 source_file_name_idx = scratch_class->source_file_name_index(); if (source_file_name_idx != 0) { @@ -2239,6 +2262,588 @@ bool VM_RedefineClasses::rewrite_cp_refs_in_methods_default_annotations( } // end rewrite_cp_refs_in_methods_default_annotations() +// Rewrite constant pool references in a class_type_annotations field. +bool VM_RedefineClasses::rewrite_cp_refs_in_class_type_annotations( + instanceKlassHandle scratch_class, TRAPS) { + + AnnotationArray* class_type_annotations = scratch_class->class_type_annotations(); + if (class_type_annotations == NULL || class_type_annotations->length() == 0) { + // no class_type_annotations so nothing to do + return true; + } + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("class_type_annotations length=%d", class_type_annotations->length())); + + int byte_i = 0; // byte index into class_type_annotations + return rewrite_cp_refs_in_type_annotations_typeArray(class_type_annotations, + byte_i, "ClassFile", THREAD); +} // end rewrite_cp_refs_in_class_type_annotations() + + +// Rewrite constant pool references in a fields_type_annotations field. +bool VM_RedefineClasses::rewrite_cp_refs_in_fields_type_annotations( + instanceKlassHandle scratch_class, TRAPS) { + + Array* fields_type_annotations = scratch_class->fields_type_annotations(); + if (fields_type_annotations == NULL || fields_type_annotations->length() == 0) { + // no fields_type_annotations so nothing to do + return true; + } + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("fields_type_annotations length=%d", fields_type_annotations->length())); + + for (int i = 0; i < fields_type_annotations->length(); i++) { + AnnotationArray* field_type_annotations = fields_type_annotations->at(i); + if (field_type_annotations == NULL || field_type_annotations->length() == 0) { + // this field does not have any annotations so skip it + continue; + } + + int byte_i = 0; // byte index into field_type_annotations + if (!rewrite_cp_refs_in_type_annotations_typeArray(field_type_annotations, + byte_i, "field_info", THREAD)) { + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("bad field_type_annotations at %d", i)); + // propagate failure back to caller + return false; + } + } + + return true; +} // end rewrite_cp_refs_in_fields_type_annotations() + + +// Rewrite constant pool references in a methods_type_annotations field. +bool VM_RedefineClasses::rewrite_cp_refs_in_methods_type_annotations( + instanceKlassHandle scratch_class, TRAPS) { + + for (int i = 0; i < scratch_class->methods()->length(); i++) { + Method* m = scratch_class->methods()->at(i); + AnnotationArray* method_type_annotations = m->constMethod()->type_annotations(); + + if (method_type_annotations == NULL || method_type_annotations->length() == 0) { + // this method does not have any annotations so skip it + continue; + } + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("methods type_annotations length=%d", method_type_annotations->length())); + + int byte_i = 0; // byte index into method_type_annotations + if (!rewrite_cp_refs_in_type_annotations_typeArray(method_type_annotations, + byte_i, "method_info", THREAD)) { + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("bad method_type_annotations at %d", i)); + // propagate failure back to caller + return false; + } + } + + return true; +} // end rewrite_cp_refs_in_methods_type_annotations() + + +// Rewrite constant pool references in a type_annotations +// field. This "structure" is adapted from the +// RuntimeVisibleTypeAnnotations_attribute described in +// section 4.7.20 of the Java SE 8 Edition of the VM spec: +// +// type_annotations_typeArray { +// u2 num_annotations; +// type_annotation annotations[num_annotations]; +// } +// +bool VM_RedefineClasses::rewrite_cp_refs_in_type_annotations_typeArray( + AnnotationArray* type_annotations_typeArray, int &byte_i_ref, + const char * location_mesg, TRAPS) { + + if ((byte_i_ref + 2) > type_annotations_typeArray->length()) { + // not enough room for num_annotations field + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for num_annotations field")); + return false; + } + + u2 num_annotations = Bytes::get_Java_u2((address) + type_annotations_typeArray->adr_at(byte_i_ref)); + byte_i_ref += 2; + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("num_type_annotations=%d", num_annotations)); + + int calc_num_annotations = 0; + for (; calc_num_annotations < num_annotations; calc_num_annotations++) { + if (!rewrite_cp_refs_in_type_annotation_struct(type_annotations_typeArray, + byte_i_ref, location_mesg, THREAD)) { + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("bad type_annotation_struct at %d", calc_num_annotations)); + // propagate failure back to caller + return false; + } + } + assert(num_annotations == calc_num_annotations, "sanity check"); + + if (byte_i_ref != type_annotations_typeArray->length()) { + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("read wrong amount of bytes at end of processing " + "type_annotations_typeArray (%d of %d bytes were read)", + byte_i_ref, type_annotations_typeArray->length())); + return false; + } + + return true; +} // end rewrite_cp_refs_in_type_annotations_typeArray() + + +// Rewrite constant pool references in a type_annotation +// field. This "structure" is adapted from the +// RuntimeVisibleTypeAnnotations_attribute described in +// section 4.7.20 of the Java SE 8 Edition of the VM spec: +// +// type_annotation { +// u1 target_type; +// union { +// type_parameter_target; +// supertype_target; +// type_parameter_bound_target; +// empty_target; +// method_formal_parameter_target; +// throws_target; +// localvar_target; +// catch_target; +// offset_target; +// type_argument_target; +// } target_info; +// type_path target_path; +// annotation anno; +// } +// +bool VM_RedefineClasses::rewrite_cp_refs_in_type_annotation_struct( + AnnotationArray* type_annotations_typeArray, int &byte_i_ref, + const char * location_mesg, TRAPS) { + + if (!skip_type_annotation_target(type_annotations_typeArray, + byte_i_ref, location_mesg, THREAD)) { + return false; + } + + if (!skip_type_annotation_type_path(type_annotations_typeArray, + byte_i_ref, THREAD)) { + return false; + } + + if (!rewrite_cp_refs_in_annotation_struct(type_annotations_typeArray, + byte_i_ref, THREAD)) { + return false; + } + + return true; +} // end rewrite_cp_refs_in_type_annotation_struct() + + +// Read, verify and skip over the target_type and target_info part +// so that rewriting can continue in the later parts of the struct. +// +// u1 target_type; +// union { +// type_parameter_target; +// supertype_target; +// type_parameter_bound_target; +// empty_target; +// method_formal_parameter_target; +// throws_target; +// localvar_target; +// catch_target; +// offset_target; +// type_argument_target; +// } target_info; +// +bool VM_RedefineClasses::skip_type_annotation_target( + AnnotationArray* type_annotations_typeArray, int &byte_i_ref, + const char * location_mesg, TRAPS) { + + if ((byte_i_ref + 1) > type_annotations_typeArray->length()) { + // not enough room for a target_type let alone the rest of a type_annotation + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for a target_type")); + return false; + } + + u1 target_type = type_annotations_typeArray->at(byte_i_ref); + byte_i_ref += 1; + RC_TRACE_WITH_THREAD(0x02000000, THREAD, ("target_type=0x%.2x", target_type)); + RC_TRACE_WITH_THREAD(0x02000000, THREAD, ("location=%s", location_mesg)); + + // Skip over target_info + switch (target_type) { + case 0x00: + // kind: type parameter declaration of generic class or interface + // location: ClassFile + case 0x01: + // kind: type parameter declaration of generic method or constructor + // location: method_info + + { + // struct: + // type_parameter_target { + // u1 type_parameter_index; + // } + // + if ((byte_i_ref + 1) > type_annotations_typeArray->length()) { + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for a type_parameter_target")); + return false; + } + + u1 type_parameter_index = type_annotations_typeArray->at(byte_i_ref); + byte_i_ref += 1; + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("type_parameter_target: type_parameter_index=%d", + type_parameter_index)); + } break; + + case 0x10: + // kind: type in extends clause of class or interface declaration + // (including the direct superclass of an anonymous class declaration), + // or in implements clause of interface declaration + // location: ClassFile + + { + // struct: + // supertype_target { + // u2 supertype_index; + // } + // + if ((byte_i_ref + 2) > type_annotations_typeArray->length()) { + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for a supertype_target")); + return false; + } + + u2 supertype_index = Bytes::get_Java_u2((address) + type_annotations_typeArray->adr_at(byte_i_ref)); + byte_i_ref += 2; + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("supertype_target: supertype_index=%d", supertype_index)); + } break; + + case 0x11: + // kind: type in bound of type parameter declaration of generic class or interface + // location: ClassFile + case 0x12: + // kind: type in bound of type parameter declaration of generic method or constructor + // location: method_info + + { + // struct: + // type_parameter_bound_target { + // u1 type_parameter_index; + // u1 bound_index; + // } + // + if ((byte_i_ref + 2) > type_annotations_typeArray->length()) { + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for a type_parameter_bound_target")); + return false; + } + + u1 type_parameter_index = type_annotations_typeArray->at(byte_i_ref); + byte_i_ref += 1; + u1 bound_index = type_annotations_typeArray->at(byte_i_ref); + byte_i_ref += 1; + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("type_parameter_bound_target: type_parameter_index=%d, bound_index=%d", + type_parameter_index, bound_index)); + } break; + + case 0x13: + // kind: type in field declaration + // location: field_info + case 0x14: + // kind: return type of method, or type of newly constructed object + // location: method_info + case 0x15: + // kind: receiver type of method or constructor + // location: method_info + + { + // struct: + // empty_target { + // } + // + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("empty_target")); + } break; + + case 0x16: + // kind: type in formal parameter declaration of method, constructor, or lambda expression + // location: method_info + + { + // struct: + // formal_parameter_target { + // u1 formal_parameter_index; + // } + // + if ((byte_i_ref + 1) > type_annotations_typeArray->length()) { + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for a formal_parameter_target")); + return false; + } + + u1 formal_parameter_index = type_annotations_typeArray->at(byte_i_ref); + byte_i_ref += 1; + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("formal_parameter_target: formal_parameter_index=%d", + formal_parameter_index)); + } break; + + case 0x17: + // kind: type in throws clause of method or constructor + // location: method_info + + { + // struct: + // throws_target { + // u2 throws_type_index + // } + // + if ((byte_i_ref + 2) > type_annotations_typeArray->length()) { + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for a throws_target")); + return false; + } + + u2 throws_type_index = Bytes::get_Java_u2((address) + type_annotations_typeArray->adr_at(byte_i_ref)); + byte_i_ref += 2; + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("throws_target: throws_type_index=%d", throws_type_index)); + } break; + + case 0x40: + // kind: type in local variable declaration + // location: Code + case 0x41: + // kind: type in resource variable declaration + // location: Code + + { + // struct: + // localvar_target { + // u2 table_length; + // struct { + // u2 start_pc; + // u2 length; + // u2 index; + // } table[table_length]; + // } + // + if ((byte_i_ref + 2) > type_annotations_typeArray->length()) { + // not enough room for a table_length let alone the rest of a localvar_target + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for a localvar_target table_length")); + return false; + } + + u2 table_length = Bytes::get_Java_u2((address) + type_annotations_typeArray->adr_at(byte_i_ref)); + byte_i_ref += 2; + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("localvar_target: table_length=%d", table_length)); + + int table_struct_size = 2 + 2 + 2; // 3 u2 variables per table entry + int table_size = table_length * table_struct_size; + + if ((byte_i_ref + table_size) > type_annotations_typeArray->length()) { + // not enough room for a table + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for a table array of length %d", table_length)); + return false; + } + + // Skip over table + byte_i_ref += table_size; + } break; + + case 0x42: + // kind: type in exception parameter declaration + // location: Code + + { + // struct: + // catch_target { + // u2 exception_table_index; + // } + // + if ((byte_i_ref + 2) > type_annotations_typeArray->length()) { + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for a catch_target")); + return false; + } + + u2 exception_table_index = Bytes::get_Java_u2((address) + type_annotations_typeArray->adr_at(byte_i_ref)); + byte_i_ref += 2; + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("catch_target: exception_table_index=%d", exception_table_index)); + } break; + + case 0x43: + // kind: type in instanceof expression + // location: Code + case 0x44: + // kind: type in new expression + // location: Code + case 0x45: + // kind: type in method reference expression using ::new + // location: Code + case 0x46: + // kind: type in method reference expression using ::Identifier + // location: Code + + { + // struct: + // offset_target { + // u2 offset; + // } + // + if ((byte_i_ref + 2) > type_annotations_typeArray->length()) { + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for a offset_target")); + return false; + } + + u2 offset = Bytes::get_Java_u2((address) + type_annotations_typeArray->adr_at(byte_i_ref)); + byte_i_ref += 2; + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("offset_target: offset=%d", offset)); + } break; + + case 0x47: + // kind: type in cast expression + // location: Code + case 0x48: + // kind: type argument for generic constructor in new expression or + // explicit constructor invocation statement + // location: Code + case 0x49: + // kind: type argument for generic method in method invocation expression + // location: Code + case 0x4A: + // kind: type argument for generic constructor in method reference expression using ::new + // location: Code + case 0x4B: + // kind: type argument for generic method in method reference expression using ::Identifier + // location: Code + + { + // struct: + // type_argument_target { + // u2 offset; + // u1 type_argument_index; + // } + // + if ((byte_i_ref + 3) > type_annotations_typeArray->length()) { + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for a type_argument_target")); + return false; + } + + u2 offset = Bytes::get_Java_u2((address) + type_annotations_typeArray->adr_at(byte_i_ref)); + byte_i_ref += 2; + u1 type_argument_index = type_annotations_typeArray->at(byte_i_ref); + byte_i_ref += 1; + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("type_argument_target: offset=%d, type_argument_index=%d", + offset, type_argument_index)); + } break; + + default: + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("unknown target_type")); +#ifdef ASSERT + ShouldNotReachHere(); +#endif + return false; + } + + return true; +} // end skip_type_annotation_target() + + +// Read, verify and skip over the type_path part so that rewriting +// can continue in the later parts of the struct. +// +// type_path { +// u1 path_length; +// { +// u1 type_path_kind; +// u1 type_argument_index; +// } path[path_length]; +// } +// +bool VM_RedefineClasses::skip_type_annotation_type_path( + AnnotationArray* type_annotations_typeArray, int &byte_i_ref, TRAPS) { + + if ((byte_i_ref + 1) > type_annotations_typeArray->length()) { + // not enough room for a path_length let alone the rest of the type_path + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for a type_path")); + return false; + } + + u1 path_length = type_annotations_typeArray->at(byte_i_ref); + byte_i_ref += 1; + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("type_path: path_length=%d", path_length)); + + int calc_path_length = 0; + for (; calc_path_length < path_length; calc_path_length++) { + if ((byte_i_ref + 1 + 1) > type_annotations_typeArray->length()) { + // not enough room for a path + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("length() is too small for path entry %d of %d", + calc_path_length, path_length)); + return false; + } + + u1 type_path_kind = type_annotations_typeArray->at(byte_i_ref); + byte_i_ref += 1; + u1 type_argument_index = type_annotations_typeArray->at(byte_i_ref); + byte_i_ref += 1; + + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("type_path: path[%d]: type_path_kind=%d, type_argument_index=%d", + calc_path_length, type_path_kind, type_argument_index)); + + if (type_path_kind > 3 || (type_path_kind != 3 && type_argument_index != 0)) { + // not enough room for a path + RC_TRACE_WITH_THREAD(0x02000000, THREAD, + ("inconsistent type_path values")); + return false; + } + } + assert(path_length == calc_path_length, "sanity check"); + + return true; +} // end skip_type_annotation_type_path() + + // Rewrite constant pool references in the method's stackmap table. // These "structures" are adapted from the StackMapTable_attribute that // is described in section 4.8.4 of the 6.0 version of the VM spec @@ -3223,23 +3828,6 @@ void VM_RedefineClasses::compute_added_deleted_matching_methods() { void VM_RedefineClasses::swap_annotations(instanceKlassHandle the_class, instanceKlassHandle scratch_class) { - // Since there is currently no rewriting of type annotations indexes - // into the CP, we null out type annotations on scratch_class before - // we swap annotations with the_class rather than facing the - // possibility of shipping annotations with broken indexes to - // Java-land. - ClassLoaderData* loader_data = scratch_class->class_loader_data(); - AnnotationArray* new_class_type_annotations = scratch_class->class_type_annotations(); - if (new_class_type_annotations != NULL) { - MetadataFactory::free_array(loader_data, new_class_type_annotations); - scratch_class->annotations()->set_class_type_annotations(NULL); - } - Array* new_field_type_annotations = scratch_class->fields_type_annotations(); - if (new_field_type_annotations != NULL) { - Annotations::free_contents(loader_data, new_field_type_annotations); - scratch_class->annotations()->set_fields_type_annotations(NULL); - } - // Swap annotation fields values Annotations* old_annotations = the_class->annotations(); the_class->set_annotations(scratch_class->annotations()); diff --git a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp index 2383aa86e17..8394f5f50a3 100644 --- a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp +++ b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp @@ -452,6 +452,17 @@ class VM_RedefineClasses: public VM_Operation { instanceKlassHandle scratch_class, TRAPS); bool rewrite_cp_refs_in_element_value( AnnotationArray* class_annotations, int &byte_i_ref, TRAPS); + bool rewrite_cp_refs_in_type_annotations_typeArray( + AnnotationArray* type_annotations_typeArray, int &byte_i_ref, + const char * location_mesg, TRAPS); + bool rewrite_cp_refs_in_type_annotation_struct( + AnnotationArray* type_annotations_typeArray, int &byte_i_ref, + const char * location_mesg, TRAPS); + bool skip_type_annotation_target( + AnnotationArray* type_annotations_typeArray, int &byte_i_ref, + const char * location_mesg, TRAPS); + bool skip_type_annotation_type_path( + AnnotationArray* type_annotations_typeArray, int &byte_i_ref, TRAPS); bool rewrite_cp_refs_in_fields_annotations( instanceKlassHandle scratch_class, TRAPS); void rewrite_cp_refs_in_method(methodHandle method, @@ -463,6 +474,12 @@ class VM_RedefineClasses: public VM_Operation { instanceKlassHandle scratch_class, TRAPS); bool rewrite_cp_refs_in_methods_parameter_annotations( instanceKlassHandle scratch_class, TRAPS); + bool rewrite_cp_refs_in_class_type_annotations( + instanceKlassHandle scratch_class, TRAPS); + bool rewrite_cp_refs_in_fields_type_annotations( + instanceKlassHandle scratch_class, TRAPS); + bool rewrite_cp_refs_in_methods_type_annotations( + instanceKlassHandle scratch_class, TRAPS); void rewrite_cp_refs_in_stack_map_table(methodHandle method, TRAPS); void rewrite_cp_refs_in_verification_type_info( address& stackmap_addr_ref, address stackmap_end, u2 frame_i, diff --git a/hotspot/test/runtime/RedefineTests/RedefineAnnotations.java b/hotspot/test/runtime/RedefineTests/RedefineAnnotations.java new file mode 100644 index 00000000000..eb74b68426e --- /dev/null +++ b/hotspot/test/runtime/RedefineTests/RedefineAnnotations.java @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @library /testlibrary + * @summary Test that type annotations are retained after a retransform + * @run main RedefineAnnotations buildagent + * @run main/othervm -javaagent:redefineagent.jar RedefineAnnotations + */ + +import static com.oracle.java.testlibrary.Asserts.assertTrue; +import java.io.FileNotFoundException; +import java.io.PrintWriter; +import java.lang.NoSuchFieldException; +import java.lang.NoSuchMethodException; +import java.lang.RuntimeException; +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.instrument.ClassFileTransformer; +import java.lang.instrument.IllegalClassFormatException; +import java.lang.instrument.Instrumentation; +import java.lang.instrument.UnmodifiableClassException; +import java.lang.reflect.AnnotatedArrayType; +import java.lang.reflect.AnnotatedParameterizedType; +import java.lang.reflect.AnnotatedType; +import java.lang.reflect.AnnotatedWildcardType; +import java.lang.reflect.Executable; +import java.lang.reflect.TypeVariable; +import java.security.ProtectionDomain; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import jdk.internal.org.objectweb.asm.ClassReader; +import jdk.internal.org.objectweb.asm.ClassVisitor; +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.FieldVisitor; +import static jdk.internal.org.objectweb.asm.Opcodes.ASM5; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE_USE) +@interface TestAnn { + String site(); +} + +public class RedefineAnnotations { + static Instrumentation inst; + public static void premain(String agentArgs, Instrumentation inst) { + RedefineAnnotations.inst = inst; + } + + static class Transformer implements ClassFileTransformer { + + public byte[] asm(ClassLoader loader, String className, + Class classBeingRedefined, + ProtectionDomain protectionDomain, byte[] classfileBuffer) + throws IllegalClassFormatException { + + ClassWriter cw = new ClassWriter(0); + ClassVisitor cv = new ReAddDummyFieldsClassVisitor(ASM5, cw) { }; + ClassReader cr = new ClassReader(classfileBuffer); + cr.accept(cv, 0); + return cw.toByteArray(); + } + + public class ReAddDummyFieldsClassVisitor extends ClassVisitor { + + LinkedList fields = new LinkedList<>(); + + public ReAddDummyFieldsClassVisitor(int api, ClassVisitor cv) { + super(api, cv); + } + + @Override public FieldVisitor visitField(int access, String name, + String desc, String signature, Object value) { + if (name.startsWith("dummy")) { + // Remove dummy field + fields.addLast(new F(access, name, desc, signature, value)); + return null; + } + return cv.visitField(access, name, desc, signature, value); + } + + @Override public void visitEnd() { + F f; + while ((f = fields.pollFirst()) != null) { + // Re-add dummy fields + cv.visitField(f.access, f.name, f.desc, f.signature, f.value); + } + } + + private class F { + private int access; + private String name; + private String desc; + private String signature; + private Object value; + F(int access, String name, String desc, String signature, Object value) { + this.access = access; + this.name = name; + this.desc = desc; + this.signature = signature; + this.value = value; + } + } + } + + @Override public byte[] transform(ClassLoader loader, String className, + Class classBeingRedefined, + ProtectionDomain protectionDomain, byte[] classfileBuffer) + throws IllegalClassFormatException { + + if (className.contains("TypeAnnotatedTestClass")) { + try { + // Here we remove and re-add the dummy fields. This shuffles the constant pool + return asm(loader, className, classBeingRedefined, protectionDomain, classfileBuffer); + } catch (Throwable e) { + // The retransform native code that called this method does not propagate + // exceptions. Instead of getting an uninformative generic error, catch + // problems here and print it, then exit. + e.printStackTrace(); + System.exit(1); + } + } + return null; + } + } + + private static void buildAgent() { + try { + ClassFileInstaller.main("RedefineAnnotations"); + } catch (Exception e) { + throw new RuntimeException("Could not write agent classfile", e); + } + + try { + PrintWriter pw = new PrintWriter("MANIFEST.MF"); + pw.println("Premain-Class: RedefineAnnotations"); + pw.println("Agent-Class: RedefineAnnotations"); + pw.println("Can-Retransform-Classes: true"); + pw.close(); + } catch (FileNotFoundException e) { + throw new RuntimeException("Could not write manifest file for the agent", e); + } + + sun.tools.jar.Main jarTool = new sun.tools.jar.Main(System.out, System.err, "jar"); + if (!jarTool.run(new String[] { "-cmf", "MANIFEST.MF", "redefineagent.jar", "RedefineAnnotations.class" })) { + throw new RuntimeException("Could not write the agent jar file"); + } + } + + public static void main(String argv[]) throws NoSuchFieldException, NoSuchMethodException { + if (argv.length == 1 && argv[0].equals("buildagent")) { + buildAgent(); + return; + } + + if (inst == null) { + throw new RuntimeException("Instrumentation object was null"); + } + + RedefineAnnotations test = new RedefineAnnotations(); + test.testTransformAndVerify(); + } + + // Class type annotations + private Annotation classTypeParameterTA; + private Annotation extendsTA; + private Annotation implementsTA; + + // Field type annotations + private Annotation fieldTA; + private Annotation innerTA; + private Annotation[] arrayTA = new Annotation[4]; + private Annotation[] mapTA = new Annotation[5]; + + // Method type annotations + private Annotation returnTA, methodTypeParameterTA, formalParameterTA, throwsTA; + + private void testTransformAndVerify() + throws NoSuchFieldException, NoSuchMethodException { + + Class c = TypeAnnotatedTestClass.class; + Class myClass = c; + + /* + * Verify that the expected annotations are where they should be before transform. + */ + verifyClassTypeAnnotations(c); + verifyFieldTypeAnnotations(c); + verifyMethodTypeAnnotations(c); + + try { + inst.addTransformer(new Transformer(), true); + inst.retransformClasses(myClass); + } catch (UnmodifiableClassException e) { + throw new RuntimeException(e); + } + + /* + * Verify that the expected annotations are where they should be after transform. + * Also verify that before and after are equal. + */ + verifyClassTypeAnnotations(c); + verifyFieldTypeAnnotations(c); + verifyMethodTypeAnnotations(c); + } + + private void verifyClassTypeAnnotations(Class c) { + Annotation anno; + + anno = c.getTypeParameters()[0].getAnnotations()[0]; + verifyTestAnn(classTypeParameterTA, anno, "classTypeParameter"); + classTypeParameterTA = anno; + + anno = c.getAnnotatedSuperclass().getAnnotations()[0]; + verifyTestAnn(extendsTA, anno, "extends"); + extendsTA = anno; + + anno = c.getAnnotatedInterfaces()[0].getAnnotations()[0]; + verifyTestAnn(implementsTA, anno, "implements"); + implementsTA = anno; + } + + private void verifyFieldTypeAnnotations(Class c) + throws NoSuchFieldException, NoSuchMethodException { + + verifyBasicFieldTypeAnnotations(c); + verifyInnerFieldTypeAnnotations(c); + verifyArrayFieldTypeAnnotations(c); + verifyMapFieldTypeAnnotations(c); + } + + private void verifyBasicFieldTypeAnnotations(Class c) + throws NoSuchFieldException, NoSuchMethodException { + + Annotation anno = c.getDeclaredField("typeAnnotatedBoolean").getAnnotatedType().getAnnotations()[0]; + verifyTestAnn(fieldTA, anno, "field"); + fieldTA = anno; + } + + private void verifyInnerFieldTypeAnnotations(Class c) + throws NoSuchFieldException, NoSuchMethodException { + + AnnotatedType at = c.getDeclaredField("typeAnnotatedInner").getAnnotatedType(); + Annotation anno = at.getAnnotations()[0]; + verifyTestAnn(innerTA, anno, "inner"); + innerTA = anno; + } + + private void verifyArrayFieldTypeAnnotations(Class c) + throws NoSuchFieldException, NoSuchMethodException { + + Annotation anno; + AnnotatedType at; + + at = c.getDeclaredField("typeAnnotatedArray").getAnnotatedType(); + anno = at.getAnnotations()[0]; + verifyTestAnn(arrayTA[0], anno, "array1"); + arrayTA[0] = anno; + + for (int i = 1; i <= 3; i++) { + at = ((AnnotatedArrayType) at).getAnnotatedGenericComponentType(); + anno = at.getAnnotations()[0]; + verifyTestAnn(arrayTA[i], anno, "array" + (i + 1)); + arrayTA[i] = anno; + } + } + + private void verifyMapFieldTypeAnnotations(Class c) + throws NoSuchFieldException, NoSuchMethodException { + + Annotation anno; + AnnotatedType atBase; + AnnotatedType atParameter; + atBase = c.getDeclaredField("typeAnnotatedMap").getAnnotatedType(); + + anno = atBase.getAnnotations()[0]; + verifyTestAnn(mapTA[0], anno, "map1"); + mapTA[0] = anno; + + atParameter = + ((AnnotatedParameterizedType) atBase). + getAnnotatedActualTypeArguments()[0]; + anno = ((AnnotatedWildcardType) atParameter).getAnnotations()[0]; + verifyTestAnn(mapTA[1], anno, "map2"); + mapTA[1] = anno; + + anno = + ((AnnotatedWildcardType) atParameter). + getAnnotatedUpperBounds()[0].getAnnotations()[0]; + verifyTestAnn(mapTA[2], anno, "map3"); + mapTA[2] = anno; + + atParameter = + ((AnnotatedParameterizedType) atBase). + getAnnotatedActualTypeArguments()[1]; + anno = ((AnnotatedParameterizedType) atParameter).getAnnotations()[0]; + verifyTestAnn(mapTA[3], anno, "map4"); + mapTA[3] = anno; + + anno = + ((AnnotatedParameterizedType) atParameter). + getAnnotatedActualTypeArguments()[0].getAnnotations()[0]; + verifyTestAnn(mapTA[4], anno, "map5"); + mapTA[4] = anno; + } + + private void verifyMethodTypeAnnotations(Class c) + throws NoSuchFieldException, NoSuchMethodException { + Annotation anno; + Executable typeAnnotatedMethod = + c.getDeclaredMethod("typeAnnotatedMethod", TypeAnnotatedTestClass.class); + + anno = typeAnnotatedMethod.getAnnotatedReturnType().getAnnotations()[0]; + verifyTestAnn(returnTA, anno, "return"); + returnTA = anno; + + anno = typeAnnotatedMethod.getTypeParameters()[0].getAnnotations()[0]; + verifyTestAnn(methodTypeParameterTA, anno, "methodTypeParameter"); + methodTypeParameterTA = anno; + + anno = typeAnnotatedMethod.getAnnotatedParameterTypes()[0].getAnnotations()[0]; + verifyTestAnn(formalParameterTA, anno, "formalParameter"); + formalParameterTA = anno; + + anno = typeAnnotatedMethod.getAnnotatedExceptionTypes()[0].getAnnotations()[0]; + verifyTestAnn(throwsTA, anno, "throws"); + throwsTA = anno; + } + + private static void verifyTestAnn(Annotation verifyAgainst, Annotation anno, String expectedSite) { + verifyTestAnnSite(anno, expectedSite); + + // When called before transform verifyAgainst will be null, when called + // after transform it will be the annotation from before the transform + if (verifyAgainst != null) { + assertTrue(anno.equals(verifyAgainst), + "Annotations do not match before and after." + + " Before: \"" + verifyAgainst + "\", After: \"" + anno + "\""); + } + } + + private static void verifyTestAnnSite(Annotation testAnn, String expectedSite) { + String expectedAnn = "@TestAnn(site=" + expectedSite + ")"; + assertTrue(testAnn.toString().equals(expectedAnn), + "Expected \"" + expectedAnn + "\", got \"" + testAnn + "\""); + } + + public static class TypeAnnotatedTestClass <@TestAnn(site="classTypeParameter") S,T> + extends @TestAnn(site="extends") Thread + implements @TestAnn(site="implements") Runnable { + + public @TestAnn(site="field") boolean typeAnnotatedBoolean; + + public + RedefineAnnotations. + @TestAnn(site="inner") TypeAnnotatedTestClass + typeAnnotatedInner; + + public + @TestAnn(site="array4") boolean + @TestAnn(site="array1") [] + @TestAnn(site="array2") [] + @TestAnn(site="array3") [] + typeAnnotatedArray; + + public @TestAnn(site="map1") Map + <@TestAnn(site="map2") ? extends @TestAnn(site="map3") String, + @TestAnn(site="map4") List<@TestAnn(site="map5") Object>> typeAnnotatedMap; + + public int dummy1; + public int dummy2; + public int dummy3; + + @TestAnn(site="return") <@TestAnn(site="methodTypeParameter") U,V> Class + typeAnnotatedMethod(@TestAnn(site="formalParameter") TypeAnnotatedTestClass arg) + throws @TestAnn(site="throws") ClassNotFoundException { + + @TestAnn(site="local_variable_type") int foo = 0; + throw new ClassNotFoundException(); + } + + public void run() {} + } +} From 0e1283a8117af0f3ffe822f23876aeb0a949e0e0 Mon Sep 17 00:00:00 2001 From: Karen Kinnear Date: Wed, 22 Oct 2014 15:24:37 -0700 Subject: [PATCH 029/118] 8043275: Fix interface initialization for default methods Initialize interfaces that declare concrete instance methods. Reviewed-by: kamg, coleenp, psandoz --- .../share/vm/classfile/classFileParser.cpp | 20 +- .../vm/classfile/classFileParser.cpp.orig | 5274 +++++++++++++++++ .../share/vm/classfile/classFileParser.hpp | 2 +- hotspot/src/share/vm/oops/instanceKlass.cpp | 65 +- hotspot/src/share/vm/oops/instanceKlass.hpp | 27 +- .../share/vm/utilities/dtrace_disabled.hpp | 2 +- .../InvokespecialInterface.java | 4 +- .../lambda-features/TestInterfaceInit.java | 87 + .../lambda-features/TestInterfaceOrder.java | 88 + 9 files changed, 5525 insertions(+), 44 deletions(-) create mode 100644 hotspot/src/share/vm/classfile/classFileParser.cpp.orig create mode 100644 hotspot/test/runtime/lambda-features/TestInterfaceInit.java create mode 100644 hotspot/test/runtime/lambda-features/TestInterfaceOrder.java diff --git a/hotspot/src/share/vm/classfile/classFileParser.cpp b/hotspot/src/share/vm/classfile/classFileParser.cpp index 0f2f8d590c4..fe9f2443a80 100644 --- a/hotspot/src/share/vm/classfile/classFileParser.cpp +++ b/hotspot/src/share/vm/classfile/classFileParser.cpp @@ -2557,7 +2557,7 @@ methodHandle ClassFileParser::parse_method(bool is_interface, Array* ClassFileParser::parse_methods(bool is_interface, AccessFlags* promoted_flags, bool* has_final_method, - bool* has_default_methods, + bool* declares_default_methods, TRAPS) { ClassFileStream* cfs = stream(); cfs->guarantee_more(2, CHECK_NULL); // length @@ -2576,11 +2576,11 @@ Array* ClassFileParser::parse_methods(bool is_interface, if (method->is_final()) { *has_final_method = true; } - if (is_interface && !(*has_default_methods) - && !method->is_abstract() && !method->is_static() - && !method->is_private()) { - // default method - *has_default_methods = true; + // declares_default_methods: declares concrete instance methods, any access flags + // used for interface initialization, and default method inheritance analysis + if (is_interface && !(*declares_default_methods) + && !method->is_abstract() && !method->is_static()) { + *declares_default_methods = true; } _methods->at_put(index, method()); } @@ -3739,6 +3739,7 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, JvmtiCachedClassFileData *cached_class_file = NULL; Handle class_loader(THREAD, loader_data->class_loader()); bool has_default_methods = false; + bool declares_default_methods = false; ResourceMark rm(THREAD); ClassFileStream* cfs = stream(); @@ -3976,9 +3977,13 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, Array* methods = parse_methods(access_flags.is_interface(), &promoted_flags, &has_final_method, - &has_default_methods, + &declares_default_methods, CHECK_(nullHandle)); + if (declares_default_methods) { + has_default_methods = true; + } + // Additional attributes ClassAnnotationCollector parsed_annotations; parse_classfile_attributes(&parsed_annotations, CHECK_(nullHandle)); @@ -4120,6 +4125,7 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, this_klass->set_minor_version(minor_version); this_klass->set_major_version(major_version); this_klass->set_has_default_methods(has_default_methods); + this_klass->set_declares_default_methods(declares_default_methods); if (!host_klass.is_null()) { assert (this_klass->is_anonymous(), "should be the same"); diff --git a/hotspot/src/share/vm/classfile/classFileParser.cpp.orig b/hotspot/src/share/vm/classfile/classFileParser.cpp.orig new file mode 100644 index 00000000000..0f2f8d590c4 --- /dev/null +++ b/hotspot/src/share/vm/classfile/classFileParser.cpp.orig @@ -0,0 +1,5274 @@ +/* + * Copyright (c) 1997, 2014, 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 "precompiled.hpp" +#include "classfile/classFileParser.hpp" +#include "classfile/classLoader.hpp" +#include "classfile/classLoaderData.hpp" +#include "classfile/classLoaderData.inline.hpp" +#include "classfile/defaultMethods.hpp" +#include "classfile/javaClasses.hpp" +#include "classfile/symbolTable.hpp" +#include "classfile/systemDictionary.hpp" +#if INCLUDE_CDS +#include "classfile/systemDictionaryShared.hpp" +#endif +#include "classfile/verificationType.hpp" +#include "classfile/verifier.hpp" +#include "classfile/vmSymbols.hpp" +#include "memory/allocation.hpp" +#include "memory/gcLocker.hpp" +#include "memory/metadataFactory.hpp" +#include "memory/oopFactory.hpp" +#include "memory/referenceType.hpp" +#include "memory/universe.inline.hpp" +#include "oops/constantPool.hpp" +#include "oops/fieldStreams.hpp" +#include "oops/instanceKlass.hpp" +#include "oops/instanceMirrorKlass.hpp" +#include "oops/klass.inline.hpp" +#include "oops/klassVtable.hpp" +#include "oops/method.hpp" +#include "oops/symbol.hpp" +#include "prims/jvm.h" +#include "prims/jvmtiExport.hpp" +#include "prims/jvmtiThreadState.hpp" +#include "runtime/javaCalls.hpp" +#include "runtime/perfData.hpp" +#include "runtime/reflection.hpp" +#include "runtime/signature.hpp" +#include "runtime/timer.hpp" +#include "services/classLoadingService.hpp" +#include "services/threadService.hpp" +#include "utilities/array.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/ostream.hpp" + +// We generally try to create the oops directly when parsing, rather than +// allocating temporary data structures and copying the bytes twice. A +// temporary area is only needed when parsing utf8 entries in the constant +// pool and when parsing line number tables. + +// We add assert in debug mode when class format is not checked. + +#define JAVA_CLASSFILE_MAGIC 0xCAFEBABE +#define JAVA_MIN_SUPPORTED_VERSION 45 +#define JAVA_MAX_SUPPORTED_VERSION 52 +#define JAVA_MAX_SUPPORTED_MINOR_VERSION 0 + +// Used for two backward compatibility reasons: +// - to check for new additions to the class file format in JDK1.5 +// - to check for bug fixes in the format checker in JDK1.5 +#define JAVA_1_5_VERSION 49 + +// Used for backward compatibility reasons: +// - to check for javac bug fixes that happened after 1.5 +// - also used as the max version when running in jdk6 +#define JAVA_6_VERSION 50 + +// Used for backward compatibility reasons: +// - to check NameAndType_info signatures more aggressively +#define JAVA_7_VERSION 51 + +// Extension method support. +#define JAVA_8_VERSION 52 + +void ClassFileParser::parse_constant_pool_entries(int length, TRAPS) { + // Use a local copy of ClassFileStream. It helps the C++ compiler to optimize + // this function (_current can be allocated in a register, with scalar + // replacement of aggregates). The _current pointer is copied back to + // stream() when this function returns. DON'T call another method within + // this method that uses stream(). + ClassFileStream* cfs0 = stream(); + ClassFileStream cfs1 = *cfs0; + ClassFileStream* cfs = &cfs1; +#ifdef ASSERT + assert(cfs->allocated_on_stack(),"should be local"); + u1* old_current = cfs0->current(); +#endif + Handle class_loader(THREAD, _loader_data->class_loader()); + + // Used for batching symbol allocations. + const char* names[SymbolTable::symbol_alloc_batch_size]; + int lengths[SymbolTable::symbol_alloc_batch_size]; + int indices[SymbolTable::symbol_alloc_batch_size]; + unsigned int hashValues[SymbolTable::symbol_alloc_batch_size]; + int names_count = 0; + + // parsing Index 0 is unused + for (int index = 1; index < length; index++) { + // 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. + u1 tag = cfs->get_u1_fast(); + switch (tag) { + case JVM_CONSTANT_Class : + { + cfs->guarantee_more(3, CHECK); // name_index, tag/access_flags + u2 name_index = cfs->get_u2_fast(); + _cp->klass_index_at_put(index, name_index); + } + break; + case JVM_CONSTANT_Fieldref : + { + cfs->guarantee_more(5, CHECK); // class_index, name_and_type_index, tag/access_flags + u2 class_index = cfs->get_u2_fast(); + u2 name_and_type_index = cfs->get_u2_fast(); + _cp->field_at_put(index, class_index, name_and_type_index); + } + break; + case JVM_CONSTANT_Methodref : + { + cfs->guarantee_more(5, CHECK); // class_index, name_and_type_index, tag/access_flags + u2 class_index = cfs->get_u2_fast(); + u2 name_and_type_index = cfs->get_u2_fast(); + _cp->method_at_put(index, class_index, name_and_type_index); + } + break; + case JVM_CONSTANT_InterfaceMethodref : + { + cfs->guarantee_more(5, CHECK); // class_index, name_and_type_index, tag/access_flags + u2 class_index = cfs->get_u2_fast(); + u2 name_and_type_index = cfs->get_u2_fast(); + _cp->interface_method_at_put(index, class_index, name_and_type_index); + } + break; + case JVM_CONSTANT_String : + { + cfs->guarantee_more(3, CHECK); // string_index, tag/access_flags + u2 string_index = cfs->get_u2_fast(); + _cp->string_index_at_put(index, string_index); + } + break; + case JVM_CONSTANT_MethodHandle : + case JVM_CONSTANT_MethodType : + if (_major_version < Verifier::INVOKEDYNAMIC_MAJOR_VERSION) { + classfile_parse_error( + "Class file version does not support constant tag %u in class file %s", + tag, CHECK); + } + if (tag == JVM_CONSTANT_MethodHandle) { + cfs->guarantee_more(4, CHECK); // ref_kind, method_index, tag/access_flags + u1 ref_kind = cfs->get_u1_fast(); + u2 method_index = cfs->get_u2_fast(); + _cp->method_handle_index_at_put(index, ref_kind, method_index); + } else if (tag == JVM_CONSTANT_MethodType) { + cfs->guarantee_more(3, CHECK); // signature_index, tag/access_flags + u2 signature_index = cfs->get_u2_fast(); + _cp->method_type_index_at_put(index, signature_index); + } else { + ShouldNotReachHere(); + } + break; + case JVM_CONSTANT_InvokeDynamic : + { + if (_major_version < Verifier::INVOKEDYNAMIC_MAJOR_VERSION) { + classfile_parse_error( + "Class file version does not support constant tag %u in class file %s", + tag, CHECK); + } + cfs->guarantee_more(5, CHECK); // bsm_index, nt, tag/access_flags + u2 bootstrap_specifier_index = cfs->get_u2_fast(); + u2 name_and_type_index = cfs->get_u2_fast(); + if (_max_bootstrap_specifier_index < (int) bootstrap_specifier_index) + _max_bootstrap_specifier_index = (int) bootstrap_specifier_index; // collect for later + _cp->invoke_dynamic_at_put(index, bootstrap_specifier_index, name_and_type_index); + } + break; + case JVM_CONSTANT_Integer : + { + cfs->guarantee_more(5, CHECK); // bytes, tag/access_flags + u4 bytes = cfs->get_u4_fast(); + _cp->int_at_put(index, (jint) bytes); + } + break; + case JVM_CONSTANT_Float : + { + cfs->guarantee_more(5, CHECK); // bytes, tag/access_flags + u4 bytes = cfs->get_u4_fast(); + _cp->float_at_put(index, *(jfloat*)&bytes); + } + break; + case JVM_CONSTANT_Long : + // A mangled type might cause you to overrun allocated memory + guarantee_property(index+1 < length, + "Invalid constant pool entry %u in class file %s", + index, CHECK); + { + cfs->guarantee_more(9, CHECK); // bytes, tag/access_flags + u8 bytes = cfs->get_u8_fast(); + _cp->long_at_put(index, bytes); + } + index++; // Skip entry following eigth-byte constant, see JVM book p. 98 + break; + case JVM_CONSTANT_Double : + // A mangled type might cause you to overrun allocated memory + guarantee_property(index+1 < length, + "Invalid constant pool entry %u in class file %s", + index, CHECK); + { + cfs->guarantee_more(9, CHECK); // bytes, tag/access_flags + u8 bytes = cfs->get_u8_fast(); + _cp->double_at_put(index, *(jdouble*)&bytes); + } + index++; // Skip entry following eigth-byte constant, see JVM book p. 98 + break; + case JVM_CONSTANT_NameAndType : + { + cfs->guarantee_more(5, CHECK); // name_index, signature_index, tag/access_flags + u2 name_index = cfs->get_u2_fast(); + u2 signature_index = cfs->get_u2_fast(); + _cp->name_and_type_at_put(index, name_index, signature_index); + } + break; + case JVM_CONSTANT_Utf8 : + { + cfs->guarantee_more(2, CHECK); // utf8_length + u2 utf8_length = cfs->get_u2_fast(); + u1* utf8_buffer = cfs->get_u1_buffer(); + assert(utf8_buffer != NULL, "null utf8 buffer"); + // Got utf8 string, guarantee utf8_length+1 bytes, set stream position forward. + cfs->guarantee_more(utf8_length+1, CHECK); // utf8 string, tag/access_flags + cfs->skip_u1_fast(utf8_length); + + // Before storing the symbol, make sure it's legal + if (_need_verify) { + verify_legal_utf8((unsigned char*)utf8_buffer, utf8_length, CHECK); + } + + if (has_cp_patch_at(index)) { + Handle patch = clear_cp_patch_at(index); + guarantee_property(java_lang_String::is_instance(patch()), + "Illegal utf8 patch at %d in class file %s", + index, CHECK); + char* str = java_lang_String::as_utf8_string(patch()); + // (could use java_lang_String::as_symbol instead, but might as well batch them) + utf8_buffer = (u1*) str; + utf8_length = (int) strlen(str); + } + + unsigned int hash; + Symbol* result = SymbolTable::lookup_only((char*)utf8_buffer, utf8_length, hash); + if (result == NULL) { + names[names_count] = (char*)utf8_buffer; + lengths[names_count] = utf8_length; + indices[names_count] = index; + hashValues[names_count++] = hash; + if (names_count == SymbolTable::symbol_alloc_batch_size) { + SymbolTable::new_symbols(_loader_data, _cp, names_count, names, lengths, indices, hashValues, CHECK); + names_count = 0; + } + } else { + _cp->symbol_at_put(index, result); + } + } + break; + default: + classfile_parse_error( + "Unknown constant tag %u in class file %s", tag, CHECK); + break; + } + } + + // Allocate the remaining symbols + if (names_count > 0) { + SymbolTable::new_symbols(_loader_data, _cp, names_count, names, lengths, indices, hashValues, CHECK); + } + + // Copy _current pointer of local copy back to stream(). +#ifdef ASSERT + assert(cfs0->current() == old_current, "non-exclusive use of stream()"); +#endif + cfs0->set_current(cfs1.current()); +} + +bool inline valid_cp_range(int index, int length) { return (index > 0 && index < length); } + +inline Symbol* check_symbol_at(constantPoolHandle cp, int index) { + if (valid_cp_range(index, cp->length()) && cp->tag_at(index).is_utf8()) + return cp->symbol_at(index); + else + return NULL; +} + +constantPoolHandle ClassFileParser::parse_constant_pool(TRAPS) { + ClassFileStream* cfs = stream(); + constantPoolHandle nullHandle; + + cfs->guarantee_more(3, CHECK_(nullHandle)); // length, first cp tag + u2 length = cfs->get_u2_fast(); + guarantee_property( + length >= 1, "Illegal constant pool size %u in class file %s", + length, CHECK_(nullHandle)); + ConstantPool* constant_pool = ConstantPool::allocate(_loader_data, length, + CHECK_(nullHandle)); + _cp = constant_pool; // save in case of errors + constantPoolHandle cp (THREAD, constant_pool); + + // parsing constant pool entries + parse_constant_pool_entries(length, CHECK_(nullHandle)); + + int index = 1; // declared outside of loops for portability + + // first verification pass - validate cross references and fixup class and string constants + for (index = 1; index < length; index++) { // Index 0 is unused + jbyte tag = cp->tag_at(index).value(); + switch (tag) { + case JVM_CONSTANT_Class : + ShouldNotReachHere(); // Only JVM_CONSTANT_ClassIndex should be present + break; + case JVM_CONSTANT_Fieldref : + // fall through + case JVM_CONSTANT_Methodref : + // fall through + case JVM_CONSTANT_InterfaceMethodref : { + if (!_need_verify) break; + int klass_ref_index = cp->klass_ref_index_at(index); + int name_and_type_ref_index = cp->name_and_type_ref_index_at(index); + check_property(valid_klass_reference_at(klass_ref_index), + "Invalid constant pool index %u in class file %s", + klass_ref_index, + CHECK_(nullHandle)); + check_property(valid_cp_range(name_and_type_ref_index, length) && + cp->tag_at(name_and_type_ref_index).is_name_and_type(), + "Invalid constant pool index %u in class file %s", + name_and_type_ref_index, + CHECK_(nullHandle)); + break; + } + case JVM_CONSTANT_String : + ShouldNotReachHere(); // Only JVM_CONSTANT_StringIndex should be present + break; + case JVM_CONSTANT_Integer : + break; + case JVM_CONSTANT_Float : + break; + case JVM_CONSTANT_Long : + case JVM_CONSTANT_Double : + index++; + check_property( + (index < length && cp->tag_at(index).is_invalid()), + "Improper constant pool long/double index %u in class file %s", + index, CHECK_(nullHandle)); + break; + case JVM_CONSTANT_NameAndType : { + if (!_need_verify) break; + int name_ref_index = cp->name_ref_index_at(index); + int signature_ref_index = cp->signature_ref_index_at(index); + check_property(valid_symbol_at(name_ref_index), + "Invalid constant pool index %u in class file %s", + name_ref_index, CHECK_(nullHandle)); + check_property(valid_symbol_at(signature_ref_index), + "Invalid constant pool index %u in class file %s", + signature_ref_index, CHECK_(nullHandle)); + break; + } + case JVM_CONSTANT_Utf8 : + break; + case JVM_CONSTANT_UnresolvedClass : // fall-through + case JVM_CONSTANT_UnresolvedClassInError: + ShouldNotReachHere(); // Only JVM_CONSTANT_ClassIndex should be present + break; + case JVM_CONSTANT_ClassIndex : + { + 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_(nullHandle)); + cp->unresolved_klass_at_put(index, cp->symbol_at(class_index)); + } + break; + case JVM_CONSTANT_StringIndex : + { + int string_index = cp->string_index_at(index); + check_property(valid_symbol_at(string_index), + "Invalid constant pool index %u in class file %s", + string_index, CHECK_(nullHandle)); + Symbol* sym = cp->symbol_at(string_index); + cp->unresolved_string_at_put(index, sym); + } + break; + case JVM_CONSTANT_MethodHandle : + { + int ref_index = cp->method_handle_index_at(index); + check_property( + valid_cp_range(ref_index, length), + "Invalid constant pool index %u in class file %s", + ref_index, CHECK_(nullHandle)); + constantTag tag = cp->tag_at(ref_index); + int ref_kind = cp->method_handle_ref_kind_at(index); + switch (ref_kind) { + case JVM_REF_getField: + case JVM_REF_getStatic: + case JVM_REF_putField: + case JVM_REF_putStatic: + check_property( + tag.is_field(), + "Invalid constant pool index %u in class file %s (not a field)", + ref_index, CHECK_(nullHandle)); + break; + case JVM_REF_invokeVirtual: + case JVM_REF_newInvokeSpecial: + check_property( + tag.is_method(), + "Invalid constant pool index %u in class file %s (not a method)", + ref_index, CHECK_(nullHandle)); + break; + case JVM_REF_invokeStatic: + case JVM_REF_invokeSpecial: + check_property(tag.is_method() || + ((_major_version >= JAVA_8_VERSION) && tag.is_interface_method()), + "Invalid constant pool index %u in class file %s (not a method)", + ref_index, CHECK_(nullHandle)); + break; + case JVM_REF_invokeInterface: + check_property( + tag.is_interface_method(), + "Invalid constant pool index %u in class file %s (not an interface method)", + ref_index, CHECK_(nullHandle)); + break; + default: + classfile_parse_error( + "Bad method handle kind at constant pool index %u in class file %s", + index, CHECK_(nullHandle)); + } + // Keep the ref_index unchanged. It will be indirected at link-time. + } + break; + case JVM_CONSTANT_MethodType : + { + int ref_index = cp->method_type_index_at(index); + check_property(valid_symbol_at(ref_index), + "Invalid constant pool index %u in class file %s", + ref_index, CHECK_(nullHandle)); + } + break; + case JVM_CONSTANT_InvokeDynamic : + { + int name_and_type_ref_index = cp->invoke_dynamic_name_and_type_ref_index_at(index); + check_property(valid_cp_range(name_and_type_ref_index, length) && + cp->tag_at(name_and_type_ref_index).is_name_and_type(), + "Invalid constant pool index %u in class file %s", + name_and_type_ref_index, + CHECK_(nullHandle)); + // bootstrap specifier index must be checked later, when BootstrapMethods attr is available + break; + } + default: + fatal(err_msg("bad constant pool tag value %u", + cp->tag_at(index).value())); + ShouldNotReachHere(); + break; + } // end of switch + } // end of for + + if (_cp_patches != NULL) { + // need to treat this_class specially... + int this_class_index; + { + cfs->guarantee_more(8, CHECK_(nullHandle)); // flags, this_class, super_class, infs_len + u1* mark = cfs->current(); + u2 flags = cfs->get_u2_fast(); + this_class_index = cfs->get_u2_fast(); + cfs->set_current(mark); // revert to mark + } + + for (index = 1; index < length; index++) { // Index 0 is unused + if (has_cp_patch_at(index)) { + guarantee_property(index != this_class_index, + "Illegal constant pool patch to self at %d in class file %s", + index, CHECK_(nullHandle)); + patch_constant_pool(cp, index, cp_patch_at(index), CHECK_(nullHandle)); + } + } + } + + if (!_need_verify) { + return cp; + } + + // second verification pass - checks the strings are of the right format. + // but not yet to the other entries + for (index = 1; index < length; index++) { + jbyte tag = cp->tag_at(index).value(); + switch (tag) { + case JVM_CONSTANT_UnresolvedClass: { + Symbol* class_name = cp->klass_name_at(index); + // check the name, even if _cp_patches will overwrite it + verify_legal_class_name(class_name, CHECK_(nullHandle)); + break; + } + case JVM_CONSTANT_NameAndType: { + if (_need_verify && _major_version >= JAVA_7_VERSION) { + int sig_index = cp->signature_ref_index_at(index); + int name_index = cp->name_ref_index_at(index); + Symbol* name = cp->symbol_at(name_index); + Symbol* sig = cp->symbol_at(sig_index); + if (sig->byte_at(0) == JVM_SIGNATURE_FUNC) { + verify_legal_method_signature(name, sig, CHECK_(nullHandle)); + } else { + verify_legal_field_signature(name, sig, CHECK_(nullHandle)); + } + } + break; + } + case JVM_CONSTANT_InvokeDynamic: + case JVM_CONSTANT_Fieldref: + case JVM_CONSTANT_Methodref: + case JVM_CONSTANT_InterfaceMethodref: { + int name_and_type_ref_index = cp->name_and_type_ref_index_at(index); + // already verified to be utf8 + int name_ref_index = cp->name_ref_index_at(name_and_type_ref_index); + // already verified to be utf8 + int signature_ref_index = cp->signature_ref_index_at(name_and_type_ref_index); + Symbol* name = cp->symbol_at(name_ref_index); + Symbol* signature = cp->symbol_at(signature_ref_index); + if (tag == JVM_CONSTANT_Fieldref) { + verify_legal_field_name(name, CHECK_(nullHandle)); + if (_need_verify && _major_version >= JAVA_7_VERSION) { + // Signature is verified above, when iterating NameAndType_info. + // Need only to be sure it's the right type. + if (signature->byte_at(0) == JVM_SIGNATURE_FUNC) { + throwIllegalSignature( + "Field", name, signature, CHECK_(nullHandle)); + } + } else { + verify_legal_field_signature(name, signature, CHECK_(nullHandle)); + } + } else { + verify_legal_method_name(name, CHECK_(nullHandle)); + if (_need_verify && _major_version >= JAVA_7_VERSION) { + // Signature is verified above, when iterating NameAndType_info. + // Need only to be sure it's the right type. + if (signature->byte_at(0) != JVM_SIGNATURE_FUNC) { + throwIllegalSignature( + "Method", name, signature, CHECK_(nullHandle)); + } + } else { + verify_legal_method_signature(name, signature, CHECK_(nullHandle)); + } + if (tag == JVM_CONSTANT_Methodref) { + // 4509014: If a class method name begins with '<', it must be "". + assert(name != NULL, "method name in constant pool is null"); + unsigned int name_len = name->utf8_length(); + assert(name_len > 0, "bad method name"); // already verified as legal name + if (name->byte_at(0) == '<') { + if (name != vmSymbols::object_initializer_name()) { + classfile_parse_error( + "Bad method name at constant pool index %u in class file %s", + name_ref_index, CHECK_(nullHandle)); + } + } + } + } + break; + } + case JVM_CONSTANT_MethodHandle: { + int ref_index = cp->method_handle_index_at(index); + int ref_kind = cp->method_handle_ref_kind_at(index); + switch (ref_kind) { + case JVM_REF_invokeVirtual: + case JVM_REF_invokeStatic: + case JVM_REF_invokeSpecial: + case JVM_REF_newInvokeSpecial: + { + int name_and_type_ref_index = cp->name_and_type_ref_index_at(ref_index); + int name_ref_index = cp->name_ref_index_at(name_and_type_ref_index); + Symbol* name = cp->symbol_at(name_ref_index); + if (ref_kind == JVM_REF_newInvokeSpecial) { + if (name != vmSymbols::object_initializer_name()) { + classfile_parse_error( + "Bad constructor name at constant pool index %u in class file %s", + name_ref_index, CHECK_(nullHandle)); + } + } else { + if (name == vmSymbols::object_initializer_name()) { + classfile_parse_error( + "Bad method name at constant pool index %u in class file %s", + name_ref_index, CHECK_(nullHandle)); + } + } + } + break; + // Other ref_kinds are already fully checked in previous pass. + } + break; + } + case JVM_CONSTANT_MethodType: { + Symbol* no_name = vmSymbols::type_name(); // place holder + Symbol* signature = cp->method_type_signature_at(index); + verify_legal_method_signature(no_name, signature, CHECK_(nullHandle)); + break; + } + case JVM_CONSTANT_Utf8: { + assert(cp->symbol_at(index)->refcount() != 0, "count corrupted"); + } + } // end of switch + } // end of for + + return cp; +} + + +void ClassFileParser::patch_constant_pool(constantPoolHandle cp, int index, Handle patch, TRAPS) { + BasicType patch_type = T_VOID; + + switch (cp->tag_at(index).value()) { + + case JVM_CONSTANT_UnresolvedClass : + // Patching a class means pre-resolving it. + // The name in the constant pool is ignored. + if (java_lang_Class::is_instance(patch())) { + guarantee_property(!java_lang_Class::is_primitive(patch()), + "Illegal class patch at %d in class file %s", + index, CHECK); + cp->klass_at_put(index, java_lang_Class::as_Klass(patch())); + } else { + guarantee_property(java_lang_String::is_instance(patch()), + "Illegal class patch at %d in class file %s", + index, CHECK); + Symbol* name = java_lang_String::as_symbol(patch(), CHECK); + cp->unresolved_klass_at_put(index, name); + } + break; + + case JVM_CONSTANT_String : + // skip this patch and don't clear it. Needs the oop array for resolved + // references to be created first. + return; + + case JVM_CONSTANT_Integer : patch_type = T_INT; goto patch_prim; + case JVM_CONSTANT_Float : patch_type = T_FLOAT; goto patch_prim; + case JVM_CONSTANT_Long : patch_type = T_LONG; goto patch_prim; + case JVM_CONSTANT_Double : patch_type = T_DOUBLE; goto patch_prim; + patch_prim: + { + jvalue value; + BasicType value_type = java_lang_boxing_object::get_value(patch(), &value); + guarantee_property(value_type == patch_type, + "Illegal primitive patch at %d in class file %s", + index, CHECK); + switch (value_type) { + case T_INT: cp->int_at_put(index, value.i); break; + case T_FLOAT: cp->float_at_put(index, value.f); break; + case T_LONG: cp->long_at_put(index, value.j); break; + case T_DOUBLE: cp->double_at_put(index, value.d); break; + default: assert(false, ""); + } + } + break; + + default: + // %%% TODO: put method handles into CONSTANT_InterfaceMethodref, etc. + guarantee_property(!has_cp_patch_at(index), + "Illegal unexpected patch at %d in class file %s", + index, CHECK); + return; + } + + // On fall-through, mark the patch as used. + clear_cp_patch_at(index); +} + + + +class NameSigHash: public ResourceObj { + public: + Symbol* _name; // name + Symbol* _sig; // signature + NameSigHash* _next; // Next entry in hash table +}; + + +#define HASH_ROW_SIZE 256 + +unsigned int hash(Symbol* name, Symbol* sig) { + unsigned int raw_hash = 0; + raw_hash += ((unsigned int)(uintptr_t)name) >> (LogHeapWordSize + 2); + raw_hash += ((unsigned int)(uintptr_t)sig) >> LogHeapWordSize; + + return (raw_hash + (unsigned int)(uintptr_t)name) % HASH_ROW_SIZE; +} + + +void initialize_hashtable(NameSigHash** table) { + memset((void*)table, 0, sizeof(NameSigHash*) * HASH_ROW_SIZE); +} + +// Return false if the name/sig combination is found in table. +// Return true if no duplicate is found. And name/sig is added as a new entry in table. +// The old format checker uses heap sort to find duplicates. +// NOTE: caller should guarantee that GC doesn't happen during the life cycle +// of table since we don't expect Symbol*'s to move. +bool put_after_lookup(Symbol* name, Symbol* sig, NameSigHash** table) { + assert(name != NULL, "name in constant pool is NULL"); + + // First lookup for duplicates + int index = hash(name, sig); + NameSigHash* entry = table[index]; + while (entry != NULL) { + if (entry->_name == name && entry->_sig == sig) { + return false; + } + entry = entry->_next; + } + + // No duplicate is found, allocate a new entry and fill it. + entry = new NameSigHash(); + entry->_name = name; + entry->_sig = sig; + + // Insert into hash table + entry->_next = table[index]; + table[index] = entry; + + return true; +} + + +Array* ClassFileParser::parse_interfaces(int length, + Handle protection_domain, + Symbol* class_name, + bool* has_default_methods, + TRAPS) { + if (length == 0) { + _local_interfaces = Universe::the_empty_klass_array(); + } else { + ClassFileStream* cfs = stream(); + assert(length > 0, "only called for length>0"); + _local_interfaces = MetadataFactory::new_array(_loader_data, length, NULL, CHECK_NULL); + + int index; + for (index = 0; index < length; index++) { + u2 interface_index = cfs->get_u2(CHECK_NULL); + KlassHandle interf; + check_property( + valid_klass_reference_at(interface_index), + "Interface name has bad constant pool index %u in class file %s", + interface_index, CHECK_NULL); + if (_cp->tag_at(interface_index).is_klass()) { + interf = KlassHandle(THREAD, _cp->resolved_klass_at(interface_index)); + } else { + Symbol* unresolved_klass = _cp->klass_name_at(interface_index); + + // Don't need to check legal name because it's checked when parsing constant pool. + // But need to make sure it's not an array type. + guarantee_property(unresolved_klass->byte_at(0) != JVM_SIGNATURE_ARRAY, + "Bad interface name in class file %s", CHECK_NULL); + Handle class_loader(THREAD, _loader_data->class_loader()); + + // Call resolve_super so classcircularity is checked + Klass* k = SystemDictionary::resolve_super_or_fail(class_name, + unresolved_klass, class_loader, protection_domain, + false, CHECK_NULL); + interf = KlassHandle(THREAD, k); + } + + if (!interf()->is_interface()) { + THROW_MSG_(vmSymbols::java_lang_IncompatibleClassChangeError(), "Implementing class", NULL); + } + if (InstanceKlass::cast(interf())->has_default_methods()) { + *has_default_methods = true; + } + _local_interfaces->at_put(index, interf()); + } + + if (!_need_verify || length <= 1) { + return _local_interfaces; + } + + // Check if there's any duplicates in interfaces + ResourceMark rm(THREAD); + NameSigHash** interface_names = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, NameSigHash*, HASH_ROW_SIZE); + initialize_hashtable(interface_names); + bool dup = false; + { + debug_only(No_Safepoint_Verifier nsv;) + for (index = 0; index < length; index++) { + Klass* k = _local_interfaces->at(index); + Symbol* name = InstanceKlass::cast(k)->name(); + // If no duplicates, add (name, NULL) in hashtable interface_names. + if (!put_after_lookup(name, NULL, interface_names)) { + dup = true; + break; + } + } + } + if (dup) { + classfile_parse_error("Duplicate interface name in class file %s", CHECK_NULL); + } + } + return _local_interfaces; +} + + +void ClassFileParser::verify_constantvalue(int constantvalue_index, int signature_index, TRAPS) { + // Make sure the constant pool entry is of a type appropriate to this field + guarantee_property( + (constantvalue_index > 0 && + constantvalue_index < _cp->length()), + "Bad initial value index %u in ConstantValue attribute in class file %s", + constantvalue_index, CHECK); + constantTag value_type = _cp->tag_at(constantvalue_index); + switch ( _cp->basic_type_for_signature_at(signature_index) ) { + case T_LONG: + guarantee_property(value_type.is_long(), "Inconsistent constant value type in class file %s", CHECK); + break; + case T_FLOAT: + guarantee_property(value_type.is_float(), "Inconsistent constant value type in class file %s", CHECK); + break; + case T_DOUBLE: + guarantee_property(value_type.is_double(), "Inconsistent constant value type in class file %s", CHECK); + break; + case T_BYTE: case T_CHAR: case T_SHORT: case T_BOOLEAN: case T_INT: + guarantee_property(value_type.is_int(), "Inconsistent constant value type in class file %s", CHECK); + break; + case T_OBJECT: + guarantee_property((_cp->symbol_at(signature_index)->equals("Ljava/lang/String;") + && value_type.is_string()), + "Bad string initial value in class file %s", CHECK); + break; + default: + classfile_parse_error( + "Unable to set initial value %u in class file %s", + constantvalue_index, CHECK); + } +} + + +// Parse attributes for a field. +void ClassFileParser::parse_field_attributes(u2 attributes_count, + bool is_static, u2 signature_index, + u2* constantvalue_index_addr, + bool* is_synthetic_addr, + u2* generic_signature_index_addr, + ClassFileParser::FieldAnnotationCollector* parsed_annotations, + TRAPS) { + ClassFileStream* cfs = stream(); + assert(attributes_count > 0, "length should be greater than 0"); + u2 constantvalue_index = 0; + u2 generic_signature_index = 0; + bool is_synthetic = false; + u1* runtime_visible_annotations = NULL; + int runtime_visible_annotations_length = 0; + u1* runtime_invisible_annotations = NULL; + int runtime_invisible_annotations_length = 0; + u1* runtime_visible_type_annotations = NULL; + int runtime_visible_type_annotations_length = 0; + u1* runtime_invisible_type_annotations = NULL; + int runtime_invisible_type_annotations_length = 0; + bool runtime_invisible_annotations_exists = false; + bool runtime_invisible_type_annotations_exists = false; + while (attributes_count--) { + cfs->guarantee_more(6, CHECK); // attribute_name_index, attribute_length + u2 attribute_name_index = cfs->get_u2_fast(); + u4 attribute_length = cfs->get_u4_fast(); + check_property(valid_symbol_at(attribute_name_index), + "Invalid field attribute index %u in class file %s", + attribute_name_index, + CHECK); + Symbol* attribute_name = _cp->symbol_at(attribute_name_index); + if (is_static && attribute_name == vmSymbols::tag_constant_value()) { + // ignore if non-static + if (constantvalue_index != 0) { + classfile_parse_error("Duplicate ConstantValue attribute in class file %s", CHECK); + } + check_property( + attribute_length == 2, + "Invalid ConstantValue field attribute length %u in class file %s", + attribute_length, CHECK); + constantvalue_index = cfs->get_u2(CHECK); + if (_need_verify) { + verify_constantvalue(constantvalue_index, signature_index, CHECK); + } + } else if (attribute_name == vmSymbols::tag_synthetic()) { + if (attribute_length != 0) { + classfile_parse_error( + "Invalid Synthetic field attribute length %u in class file %s", + attribute_length, CHECK); + } + is_synthetic = true; + } else if (attribute_name == vmSymbols::tag_deprecated()) { // 4276120 + if (attribute_length != 0) { + classfile_parse_error( + "Invalid Deprecated field attribute length %u in class file %s", + attribute_length, CHECK); + } + } else if (_major_version >= JAVA_1_5_VERSION) { + if (attribute_name == vmSymbols::tag_signature()) { + if (attribute_length != 2) { + classfile_parse_error( + "Wrong size %u for field's Signature attribute in class file %s", + attribute_length, CHECK); + } + generic_signature_index = parse_generic_signature_attribute(CHECK); + } else if (attribute_name == vmSymbols::tag_runtime_visible_annotations()) { + if (runtime_visible_annotations != NULL) { + classfile_parse_error( + "Multiple RuntimeVisibleAnnotations attributes for field in class file %s", CHECK); + } + runtime_visible_annotations_length = attribute_length; + runtime_visible_annotations = cfs->get_u1_buffer(); + assert(runtime_visible_annotations != NULL, "null visible annotations"); + parse_annotations(runtime_visible_annotations, + runtime_visible_annotations_length, + parsed_annotations, + CHECK); + cfs->skip_u1(runtime_visible_annotations_length, CHECK); + } else if (attribute_name == vmSymbols::tag_runtime_invisible_annotations()) { + if (runtime_invisible_annotations_exists) { + classfile_parse_error( + "Multiple RuntimeInvisibleAnnotations attributes for field in class file %s", CHECK); + } + runtime_invisible_annotations_exists = true; + if (PreserveAllAnnotations) { + runtime_invisible_annotations_length = attribute_length; + runtime_invisible_annotations = cfs->get_u1_buffer(); + assert(runtime_invisible_annotations != NULL, "null invisible annotations"); + } + cfs->skip_u1(attribute_length, CHECK); + } else if (attribute_name == vmSymbols::tag_runtime_visible_type_annotations()) { + if (runtime_visible_type_annotations != NULL) { + classfile_parse_error( + "Multiple RuntimeVisibleTypeAnnotations attributes for field in class file %s", CHECK); + } + runtime_visible_type_annotations_length = attribute_length; + runtime_visible_type_annotations = cfs->get_u1_buffer(); + assert(runtime_visible_type_annotations != NULL, "null visible type annotations"); + cfs->skip_u1(runtime_visible_type_annotations_length, CHECK); + } else if (attribute_name == vmSymbols::tag_runtime_invisible_type_annotations()) { + if (runtime_invisible_type_annotations_exists) { + classfile_parse_error( + "Multiple RuntimeInvisibleTypeAnnotations attributes for field in class file %s", CHECK); + } else { + runtime_invisible_type_annotations_exists = true; + } + if (PreserveAllAnnotations) { + runtime_invisible_type_annotations_length = attribute_length; + runtime_invisible_type_annotations = cfs->get_u1_buffer(); + assert(runtime_invisible_type_annotations != NULL, "null invisible type annotations"); + } + cfs->skip_u1(attribute_length, CHECK); + } else { + cfs->skip_u1(attribute_length, CHECK); // Skip unknown attributes + } + } else { + cfs->skip_u1(attribute_length, CHECK); // Skip unknown attributes + } + } + + *constantvalue_index_addr = constantvalue_index; + *is_synthetic_addr = is_synthetic; + *generic_signature_index_addr = generic_signature_index; + AnnotationArray* a = assemble_annotations(runtime_visible_annotations, + runtime_visible_annotations_length, + runtime_invisible_annotations, + runtime_invisible_annotations_length, + CHECK); + parsed_annotations->set_field_annotations(a); + a = assemble_annotations(runtime_visible_type_annotations, + runtime_visible_type_annotations_length, + runtime_invisible_type_annotations, + runtime_invisible_type_annotations_length, + CHECK); + parsed_annotations->set_field_type_annotations(a); + return; +} + + +// Field allocation types. Used for computing field offsets. + +enum FieldAllocationType { + STATIC_OOP, // Oops + STATIC_BYTE, // Boolean, Byte, char + STATIC_SHORT, // shorts + STATIC_WORD, // ints + STATIC_DOUBLE, // aligned long or double + NONSTATIC_OOP, + NONSTATIC_BYTE, + NONSTATIC_SHORT, + NONSTATIC_WORD, + NONSTATIC_DOUBLE, + MAX_FIELD_ALLOCATION_TYPE, + BAD_ALLOCATION_TYPE = -1 +}; + +static FieldAllocationType _basic_type_to_atype[2 * (T_CONFLICT + 1)] = { + BAD_ALLOCATION_TYPE, // 0 + BAD_ALLOCATION_TYPE, // 1 + BAD_ALLOCATION_TYPE, // 2 + BAD_ALLOCATION_TYPE, // 3 + NONSTATIC_BYTE , // T_BOOLEAN = 4, + NONSTATIC_SHORT, // T_CHAR = 5, + NONSTATIC_WORD, // T_FLOAT = 6, + NONSTATIC_DOUBLE, // T_DOUBLE = 7, + NONSTATIC_BYTE, // T_BYTE = 8, + 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, + BAD_ALLOCATION_TYPE, // 0 + BAD_ALLOCATION_TYPE, // 1 + BAD_ALLOCATION_TYPE, // 2 + BAD_ALLOCATION_TYPE, // 3 + STATIC_BYTE , // T_BOOLEAN = 4, + STATIC_SHORT, // T_CHAR = 5, + STATIC_WORD, // T_FLOAT = 6, + STATIC_DOUBLE, // T_DOUBLE = 7, + STATIC_BYTE, // T_BYTE = 8, + 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 FieldAllocationType basic_type_to_atype(bool is_static, BasicType 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"); + return result; +} + +class FieldAllocationCount: public ResourceObj { + public: + u2 count[MAX_FIELD_ALLOCATION_TYPE]; + + FieldAllocationCount() { + for (int i = 0; i < MAX_FIELD_ALLOCATION_TYPE; i++) { + count[i] = 0; + } + } + + FieldAllocationType update(bool is_static, BasicType type) { + FieldAllocationType atype = basic_type_to_atype(is_static, type); + // Make sure there is no overflow with injected fields. + assert(count[atype] < 0xFFFF, "More than 65535 fields"); + count[atype]++; + return atype; + } +}; + +Array* ClassFileParser::parse_fields(Symbol* class_name, + bool is_interface, + FieldAllocationCount *fac, + u2* java_fields_count_ptr, TRAPS) { + ClassFileStream* cfs = stream(); + cfs->guarantee_more(2, CHECK_NULL); // length + u2 length = cfs->get_u2_fast(); + *java_fields_count_ptr = length; + + int num_injected = 0; + InjectedField* injected = JavaClasses::get_injected(class_name, &num_injected); + int total_fields = length + num_injected; + + // 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 + // JVM_ACC_FIELD_HAS_GENERIC_SIGNATURE for that field. The generic + // signature slots are at the end of the field array and after all + // other fields data. + // + // f1: [access, name index, sig index, initial value index, low_offset, high_offset] + // f2: [access, name index, sig index, initial value index, low_offset, high_offset] + // ... + // fn: [access, name index, sig index, initial value index, low_offset, high_offset] + // [generic signature index] + // [generic signature index] + // ... + // + // Allocate a temporary resource array for field data. For each field, + // a slot is reserved in the temporary array for the generic signature + // index. After parsing all fields, the data are copied to a permanent + // array and any unused slots will be discarded. + ResourceMark rm(THREAD); + u2* fa = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, u2, 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; + for (int n = 0; n < length; n++) { + cfs->guarantee_more(8, CHECK_NULL); // access_flags, name_index, descriptor_index, attributes_count + + AccessFlags access_flags; + jint flags = cfs->get_u2_fast() & JVM_RECOGNIZED_FIELD_MODIFIERS; + verify_legal_field_modifiers(flags, is_interface, CHECK_NULL); + access_flags.set_flags(flags); + + u2 name_index = cfs->get_u2_fast(); + int cp_size = _cp->length(); + check_property(valid_symbol_at(name_index), + "Invalid constant pool index %u for field name in class file %s", + name_index, + CHECK_NULL); + Symbol* name = _cp->symbol_at(name_index); + verify_legal_field_name(name, CHECK_NULL); + + u2 signature_index = cfs->get_u2_fast(); + check_property(valid_symbol_at(signature_index), + "Invalid constant pool index %u for field signature in class file %s", + signature_index, CHECK_NULL); + Symbol* sig = _cp->symbol_at(signature_index); + verify_legal_field_signature(name, sig, CHECK_NULL); + + u2 constantvalue_index = 0; + bool is_synthetic = false; + u2 generic_signature_index = 0; + bool is_static = access_flags.is_static(); + FieldAnnotationCollector parsed_annotations(_loader_data); + + u2 attributes_count = cfs->get_u2_fast(); + if (attributes_count > 0) { + parse_field_attributes(attributes_count, is_static, signature_index, + &constantvalue_index, &is_synthetic, + &generic_signature_index, &parsed_annotations, + CHECK_NULL); + if (parsed_annotations.field_annotations() != NULL) { + if (_fields_annotations == NULL) { + _fields_annotations = MetadataFactory::new_array( + _loader_data, length, NULL, + CHECK_NULL); + } + _fields_annotations->at_put(n, parsed_annotations.field_annotations()); + parsed_annotations.set_field_annotations(NULL); + } + if (parsed_annotations.field_type_annotations() != NULL) { + if (_fields_type_annotations == NULL) { + _fields_type_annotations = MetadataFactory::new_array( + _loader_data, length, NULL, + CHECK_NULL); + } + _fields_type_annotations->at_put(n, parsed_annotations.field_type_annotations()); + parsed_annotations.set_field_type_annotations(NULL); + } + + if (is_synthetic) { + access_flags.set_is_synthetic(); + } + if (generic_signature_index != 0) { + access_flags.set_field_has_generic_signature(); + fa[generic_signature_slot] = generic_signature_index; + generic_signature_slot ++; + num_generic_signature ++; + } + } + + FieldInfo* field = FieldInfo::from_field_array(fa, n); + field->initialize(access_flags.as_short(), + name_index, + signature_index, + constantvalue_index); + BasicType type = _cp->basic_type_for_signature_at(signature_index); + + // Remember how many oops we encountered and compute allocation type + FieldAllocationType atype = fac->update(is_static, type); + field->set_allocation_type(atype); + + // After field is initialized with type, we can augment it with aux info + if (parsed_annotations.has_any_annotations()) + parsed_annotations.apply_to(field); + } + + int index = length; + if (num_injected != 0) { + for (int n = 0; n < num_injected; n++) { + // Check for duplicates + if (injected[n].may_be_java) { + Symbol* name = injected[n].name(); + Symbol* signature = injected[n].signature(); + bool duplicate = false; + for (int i = 0; i < length; i++) { + FieldInfo* f = FieldInfo::from_field_array(fa, i); + if (name == _cp->symbol_at(f->name_index()) && + signature == _cp->symbol_at(f->signature_index())) { + // Symbol is desclared in Java so skip this one + duplicate = true; + break; + } + } + if (duplicate) { + // These will be removed from the field array at the end + continue; + } + } + + // Injected field + FieldInfo* field = FieldInfo::from_field_array(fa, index); + field->initialize(JVM_ACC_FIELD_INTERNAL, + injected[n].name_index, + injected[n].signature_index, + 0); + + BasicType type = FieldType::basic_type(injected[n].signature()); + + // Remember how many oops we encountered and compute allocation type + FieldAllocationType atype = fac->update(false, type); + field->set_allocation_type(atype); + index++; + } + } + + // Now copy the fields' data from the temporary resource array. + // Sometimes injected fields already exist in the Java source so + // the fields array could be too long. In that case the + // fields array is trimed. Also unused slots that were reserved + // for generic signature indexes are discarded. + Array* fields = MetadataFactory::new_array( + _loader_data, index * FieldInfo::field_slots + num_generic_signature, + CHECK_NULL); + _fields = fields; // save in case of error + { + int i = 0; + for (; i < index * FieldInfo::field_slots; i++) { + fields->at_put(i, fa[i]); + } + for (int j = total_fields * FieldInfo::field_slots; + j < generic_signature_slot; j++) { + fields->at_put(i++, fa[j]); + } + assert(i == fields->length(), ""); + } + + if (_need_verify && length > 1) { + // Check duplicated fields + ResourceMark rm(THREAD); + NameSigHash** names_and_sigs = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, NameSigHash*, HASH_ROW_SIZE); + initialize_hashtable(names_and_sigs); + bool dup = false; + { + debug_only(No_Safepoint_Verifier nsv;) + for (AllFieldStream fs(fields, _cp); !fs.done(); fs.next()) { + Symbol* name = fs.name(); + Symbol* sig = fs.signature(); + // If no duplicates, add name/signature in hashtable names_and_sigs. + if (!put_after_lookup(name, sig, names_and_sigs)) { + dup = true; + break; + } + } + } + if (dup) { + classfile_parse_error("Duplicate field name&signature in class file %s", + CHECK_NULL); + } + } + + return fields; +} + + +static void copy_u2_with_conversion(u2* dest, u2* src, int length) { + while (length-- > 0) { + *dest++ = Bytes::get_Java_u2((u1*) (src++)); + } +} + + +u2* ClassFileParser::parse_exception_table(u4 code_length, + u4 exception_table_length, + TRAPS) { + ClassFileStream* cfs = stream(); + + u2* exception_table_start = cfs->get_u2_buffer(); + assert(exception_table_start != NULL, "null exception table"); + cfs->guarantee_more(8 * exception_table_length, CHECK_NULL); // start_pc, end_pc, handler_pc, catch_type_index + // Will check legal target after parsing code array in verifier. + if (_need_verify) { + for (unsigned int i = 0; i < exception_table_length; i++) { + u2 start_pc = cfs->get_u2_fast(); + u2 end_pc = cfs->get_u2_fast(); + u2 handler_pc = cfs->get_u2_fast(); + u2 catch_type_index = cfs->get_u2_fast(); + guarantee_property((start_pc < end_pc) && (end_pc <= code_length), + "Illegal exception table range in class file %s", + CHECK_NULL); + guarantee_property(handler_pc < code_length, + "Illegal exception table handler in class file %s", + CHECK_NULL); + if (catch_type_index != 0) { + guarantee_property(valid_klass_reference_at(catch_type_index), + "Catch type in exception table has bad constant type in class file %s", CHECK_NULL); + } + } + } else { + cfs->skip_u2_fast(exception_table_length * 4); + } + return exception_table_start; +} + +void ClassFileParser::parse_linenumber_table( + u4 code_attribute_length, u4 code_length, + CompressedLineNumberWriteStream** write_stream, TRAPS) { + ClassFileStream* cfs = stream(); + unsigned int num_entries = cfs->get_u2(CHECK); + + // Each entry is a u2 start_pc, and a u2 line_number + unsigned int length_in_bytes = num_entries * (sizeof(u2) + sizeof(u2)); + + // Verify line number attribute and table length + check_property( + code_attribute_length == sizeof(u2) + length_in_bytes, + "LineNumberTable attribute has wrong length in class file %s", CHECK); + + cfs->guarantee_more(length_in_bytes, CHECK); + + if ((*write_stream) == NULL) { + if (length_in_bytes > fixed_buffer_size) { + (*write_stream) = new CompressedLineNumberWriteStream(length_in_bytes); + } else { + (*write_stream) = new CompressedLineNumberWriteStream( + linenumbertable_buffer, fixed_buffer_size); + } + } + + while (num_entries-- > 0) { + u2 bci = cfs->get_u2_fast(); // start_pc + u2 line = cfs->get_u2_fast(); // line_number + guarantee_property(bci < code_length, + "Invalid pc in LineNumberTable in class file %s", CHECK); + (*write_stream)->write_pair(bci, line); + } +} + + +// Class file LocalVariableTable elements. +class Classfile_LVT_Element VALUE_OBJ_CLASS_SPEC { + public: + u2 start_bci; + u2 length; + u2 name_cp_index; + u2 descriptor_cp_index; + u2 slot; +}; + + +class LVT_Hash: public CHeapObj { + public: + LocalVariableTableElement *_elem; // element + LVT_Hash* _next; // Next entry in hash table +}; + +unsigned int hash(LocalVariableTableElement *elem) { + unsigned int raw_hash = elem->start_bci; + + raw_hash = elem->length + raw_hash * 37; + raw_hash = elem->name_cp_index + raw_hash * 37; + raw_hash = elem->slot + raw_hash * 37; + + return raw_hash % HASH_ROW_SIZE; +} + +void initialize_hashtable(LVT_Hash** table) { + for (int i = 0; i < HASH_ROW_SIZE; i++) { + table[i] = NULL; + } +} + +void clear_hashtable(LVT_Hash** table) { + for (int i = 0; i < HASH_ROW_SIZE; i++) { + LVT_Hash* current = table[i]; + LVT_Hash* next; + while (current != NULL) { + next = current->_next; + current->_next = NULL; + delete(current); + current = next; + } + table[i] = NULL; + } +} + +LVT_Hash* LVT_lookup(LocalVariableTableElement *elem, int index, LVT_Hash** table) { + LVT_Hash* entry = table[index]; + + /* + * 3-tuple start_bci/length/slot has to be unique key, + * so the following comparison seems to be redundant: + * && elem->name_cp_index == entry->_elem->name_cp_index + */ + while (entry != NULL) { + if (elem->start_bci == entry->_elem->start_bci + && elem->length == entry->_elem->length + && elem->name_cp_index == entry->_elem->name_cp_index + && elem->slot == entry->_elem->slot + ) { + return entry; + } + entry = entry->_next; + } + return NULL; +} + +// Return false if the local variable is found in table. +// Return true if no duplicate is found. +// And local variable is added as a new entry in table. +bool LVT_put_after_lookup(LocalVariableTableElement *elem, LVT_Hash** table) { + // First lookup for duplicates + int index = hash(elem); + LVT_Hash* entry = LVT_lookup(elem, index, table); + + if (entry != NULL) { + return false; + } + // No duplicate is found, allocate a new entry and fill it. + if ((entry = new LVT_Hash()) == NULL) { + return false; + } + entry->_elem = elem; + + // Insert into hash table + entry->_next = table[index]; + table[index] = entry; + + return true; +} + +void copy_lvt_element(Classfile_LVT_Element *src, LocalVariableTableElement *lvt) { + lvt->start_bci = Bytes::get_Java_u2((u1*) &src->start_bci); + lvt->length = Bytes::get_Java_u2((u1*) &src->length); + lvt->name_cp_index = Bytes::get_Java_u2((u1*) &src->name_cp_index); + lvt->descriptor_cp_index = Bytes::get_Java_u2((u1*) &src->descriptor_cp_index); + lvt->signature_cp_index = 0; + lvt->slot = Bytes::get_Java_u2((u1*) &src->slot); +} + +// Function is used to parse both attributes: +// LocalVariableTable (LVT) and LocalVariableTypeTable (LVTT) +u2* ClassFileParser::parse_localvariable_table(u4 code_length, + u2 max_locals, + u4 code_attribute_length, + u2* localvariable_table_length, + bool isLVTT, + TRAPS) { + ClassFileStream* cfs = stream(); + const char * tbl_name = (isLVTT) ? "LocalVariableTypeTable" : "LocalVariableTable"; + *localvariable_table_length = cfs->get_u2(CHECK_NULL); + unsigned int size = (*localvariable_table_length) * sizeof(Classfile_LVT_Element) / sizeof(u2); + // Verify local variable table attribute has right length + if (_need_verify) { + guarantee_property(code_attribute_length == (sizeof(*localvariable_table_length) + size * sizeof(u2)), + "%s has wrong length in class file %s", tbl_name, CHECK_NULL); + } + u2* localvariable_table_start = cfs->get_u2_buffer(); + assert(localvariable_table_start != NULL, "null local variable table"); + if (!_need_verify) { + cfs->skip_u2_fast(size); + } else { + cfs->guarantee_more(size * 2, CHECK_NULL); + for(int i = 0; i < (*localvariable_table_length); i++) { + u2 start_pc = cfs->get_u2_fast(); + u2 length = cfs->get_u2_fast(); + u2 name_index = cfs->get_u2_fast(); + u2 descriptor_index = cfs->get_u2_fast(); + u2 index = cfs->get_u2_fast(); + // Assign to a u4 to avoid overflow + u4 end_pc = (u4)start_pc + (u4)length; + + if (start_pc >= code_length) { + classfile_parse_error( + "Invalid start_pc %u in %s in class file %s", + start_pc, tbl_name, CHECK_NULL); + } + if (end_pc > code_length) { + classfile_parse_error( + "Invalid length %u in %s in class file %s", + length, tbl_name, CHECK_NULL); + } + int cp_size = _cp->length(); + guarantee_property(valid_symbol_at(name_index), + "Name index %u in %s has bad constant type in class file %s", + name_index, tbl_name, CHECK_NULL); + guarantee_property(valid_symbol_at(descriptor_index), + "Signature index %u in %s has bad constant type in class file %s", + descriptor_index, tbl_name, CHECK_NULL); + + Symbol* name = _cp->symbol_at(name_index); + Symbol* sig = _cp->symbol_at(descriptor_index); + verify_legal_field_name(name, CHECK_NULL); + u2 extra_slot = 0; + if (!isLVTT) { + verify_legal_field_signature(name, sig, CHECK_NULL); + + // 4894874: check special cases for double and long local variables + if (sig == vmSymbols::type_signature(T_DOUBLE) || + sig == vmSymbols::type_signature(T_LONG)) { + extra_slot = 1; + } + } + guarantee_property((index + extra_slot) < max_locals, + "Invalid index %u in %s in class file %s", + index, tbl_name, CHECK_NULL); + } + } + return localvariable_table_start; +} + + +void ClassFileParser::parse_type_array(u2 array_length, u4 code_length, u4* u1_index, u4* u2_index, + u1* u1_array, u2* u2_array, TRAPS) { + ClassFileStream* cfs = stream(); + u2 index = 0; // index in the array with long/double occupying two slots + u4 i1 = *u1_index; + u4 i2 = *u2_index + 1; + for(int i = 0; i < array_length; i++) { + u1 tag = u1_array[i1++] = cfs->get_u1(CHECK); + index++; + if (tag == ITEM_Long || tag == ITEM_Double) { + index++; + } else if (tag == ITEM_Object) { + u2 class_index = u2_array[i2++] = cfs->get_u2(CHECK); + guarantee_property(valid_klass_reference_at(class_index), + "Bad class index %u in StackMap in class file %s", + class_index, CHECK); + } else if (tag == ITEM_Uninitialized) { + u2 offset = u2_array[i2++] = cfs->get_u2(CHECK); + guarantee_property( + offset < code_length, + "Bad uninitialized type offset %u in StackMap in class file %s", + offset, CHECK); + } else { + guarantee_property( + tag <= (u1)ITEM_Uninitialized, + "Unknown variable type %u in StackMap in class file %s", + tag, CHECK); + } + } + u2_array[*u2_index] = index; + *u1_index = i1; + *u2_index = i2; +} + +u1* ClassFileParser::parse_stackmap_table( + u4 code_attribute_length, TRAPS) { + if (code_attribute_length == 0) + return NULL; + + ClassFileStream* cfs = stream(); + u1* stackmap_table_start = cfs->get_u1_buffer(); + assert(stackmap_table_start != NULL, "null stackmap table"); + + // check code_attribute_length first + stream()->skip_u1(code_attribute_length, CHECK_NULL); + + if (!_need_verify && !DumpSharedSpaces) { + return NULL; + } + return stackmap_table_start; +} + +u2* ClassFileParser::parse_checked_exceptions(u2* checked_exceptions_length, + u4 method_attribute_length, + TRAPS) { + ClassFileStream* cfs = stream(); + cfs->guarantee_more(2, CHECK_NULL); // checked_exceptions_length + *checked_exceptions_length = cfs->get_u2_fast(); + unsigned int size = (*checked_exceptions_length) * sizeof(CheckedExceptionElement) / sizeof(u2); + u2* checked_exceptions_start = cfs->get_u2_buffer(); + assert(checked_exceptions_start != NULL, "null checked exceptions"); + if (!_need_verify) { + cfs->skip_u2_fast(size); + } else { + // Verify each value in the checked exception table + u2 checked_exception; + u2 len = *checked_exceptions_length; + cfs->guarantee_more(2 * len, CHECK_NULL); + for (int i = 0; i < len; i++) { + checked_exception = cfs->get_u2_fast(); + check_property( + valid_klass_reference_at(checked_exception), + "Exception name has bad type at constant pool %u in class file %s", + checked_exception, CHECK_NULL); + } + } + // check exceptions attribute length + if (_need_verify) { + guarantee_property(method_attribute_length == (sizeof(*checked_exceptions_length) + + sizeof(u2) * size), + "Exceptions attribute has wrong length in class file %s", CHECK_NULL); + } + return checked_exceptions_start; +} + +void ClassFileParser::throwIllegalSignature( + const char* type, Symbol* name, Symbol* sig, TRAPS) { + 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()); +} + +// Skip an annotation. Return >=limit if there is any problem. +int ClassFileParser::skip_annotation(u1* buffer, int limit, int index) { + // annotation := atype:u2 do(nmem:u2) {member:u2 value} + // value := switch (tag:u1) { ... } + index += 2; // skip atype + if ((index += 2) >= limit) return limit; // read nmem + int nmem = Bytes::get_Java_u2(buffer+index-2); + while (--nmem >= 0 && index < limit) { + index += 2; // skip member + index = skip_annotation_value(buffer, limit, index); + } + return index; +} + +// Skip an annotation value. Return >=limit if there is any problem. +int ClassFileParser::skip_annotation_value(u1* buffer, int limit, int index) { + // value := switch (tag:u1) { + // case B, C, I, S, Z, D, F, J, c: con:u2; + // case e: e_class:u2 e_name:u2; + // case s: s_con:u2; + // case [: do(nval:u2) {value}; + // case @: annotation; + // case s: s_con:u2; + // } + if ((index += 1) >= limit) return limit; // read tag + u1 tag = buffer[index-1]; + switch (tag) { + case 'B': case 'C': case 'I': case 'S': case 'Z': + case 'D': case 'F': case 'J': case 'c': case 's': + index += 2; // skip con or s_con + break; + case 'e': + index += 4; // skip e_class, e_name + break; + case '[': + { + if ((index += 2) >= limit) return limit; // read nval + int nval = Bytes::get_Java_u2(buffer+index-2); + while (--nval >= 0 && index < limit) { + index = skip_annotation_value(buffer, limit, index); + } + } + break; + case '@': + index = skip_annotation(buffer, limit, index); + break; + default: + assert(false, "annotation tag"); + return limit; // bad tag byte + } + return index; +} + +// Sift through annotations, looking for those significant to the VM: +void ClassFileParser::parse_annotations(u1* buffer, int limit, + ClassFileParser::AnnotationCollector* coll, + TRAPS) { + // annotations := do(nann:u2) {annotation} + int index = 0; + if ((index += 2) >= limit) return; // read nann + int nann = Bytes::get_Java_u2(buffer+index-2); + enum { // initial annotation layout + atype_off = 0, // utf8 such as 'Ljava/lang/annotation/Retention;' + count_off = 2, // u2 such as 1 (one value) + member_off = 4, // utf8 such as 'value' + tag_off = 6, // u1 such as 'c' (type) or 'e' (enum) + e_tag_val = 'e', + e_type_off = 7, // utf8 such as 'Ljava/lang/annotation/RetentionPolicy;' + e_con_off = 9, // utf8 payload, such as 'SOURCE', 'CLASS', 'RUNTIME' + e_size = 11, // end of 'e' annotation + c_tag_val = 'c', // payload is type + c_con_off = 7, // utf8 payload, such as 'I' + c_size = 9, // end of 'c' annotation + s_tag_val = 's', // payload is String + s_con_off = 7, // utf8 payload, such as 'Ljava/lang/String;' + s_size = 9, + min_size = 6 // smallest possible size (zero members) + }; + while ((--nann) >= 0 && (index-2 + min_size <= limit)) { + int index0 = index; + index = skip_annotation(buffer, limit, index); + u1* abase = buffer + index0; + int atype = Bytes::get_Java_u2(abase + atype_off); + int count = Bytes::get_Java_u2(abase + count_off); + Symbol* aname = check_symbol_at(_cp, atype); + if (aname == NULL) break; // invalid annotation name + Symbol* member = NULL; + if (count >= 1) { + int member_index = Bytes::get_Java_u2(abase + member_off); + member = check_symbol_at(_cp, member_index); + if (member == NULL) break; // invalid member name + } + + // Here is where parsing particular annotations will take place. + AnnotationCollector::ID id = coll->annotation_index(_loader_data, aname); + if (id == AnnotationCollector::_unknown) continue; + coll->set_annotation(id); + + if (id == AnnotationCollector::_sun_misc_Contended) { + // @Contended can optionally specify the contention group. + // + // Contended group defines the equivalence class over the fields: + // the fields within the same contended group are not treated distinct. + // The only exception is default group, which does not incur the + // equivalence. Naturally, contention group for classes is meaningless. + // + // While the contention group is specified as String, annotation + // values are already interned, and we might as well use the constant + // pool index as the group tag. + // + u2 group_index = 0; // default contended group + if (count == 1 + && s_size == (index - index0) // match size + && s_tag_val == *(abase + tag_off) + && member == vmSymbols::value_name()) { + group_index = Bytes::get_Java_u2(abase + s_con_off); + if (_cp->symbol_at(group_index)->utf8_length() == 0) { + group_index = 0; // default contended group + } + } + coll->set_contended_group(group_index); + } + } +} + +ClassFileParser::AnnotationCollector::ID +ClassFileParser::AnnotationCollector::annotation_index(ClassLoaderData* loader_data, + Symbol* name) { + vmSymbols::SID sid = vmSymbols::find_sid(name); + // Privileged code can use all annotations. Other code silently drops some. + const bool privileged = loader_data->is_the_null_class_loader_data() || + loader_data->is_ext_class_loader_data() || + loader_data->is_anonymous(); + switch (sid) { + case vmSymbols::VM_SYMBOL_ENUM_NAME(sun_reflect_CallerSensitive_signature): + if (_location != _in_method) break; // only allow for methods + if (!privileged) break; // only allow in privileged code + return _method_CallerSensitive; + case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_ForceInline_signature): + if (_location != _in_method) break; // only allow for methods + if (!privileged) break; // only allow in privileged code + return _method_ForceInline; + case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_DontInline_signature): + if (_location != _in_method) break; // only allow for methods + if (!privileged) break; // only allow in privileged code + return _method_DontInline; + case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_LambdaForm_Compiled_signature): + if (_location != _in_method) break; // only allow for methods + if (!privileged) break; // only allow in privileged code + return _method_LambdaForm_Compiled; + case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_LambdaForm_Hidden_signature): + if (_location != _in_method) break; // only allow for methods + if (!privileged) break; // only allow in privileged code + return _method_LambdaForm_Hidden; + case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_Stable_signature): + if (_location != _in_field) break; // only allow for fields + if (!privileged) break; // only allow in privileged code + return _field_Stable; + case vmSymbols::VM_SYMBOL_ENUM_NAME(sun_misc_Contended_signature): + if (_location != _in_field && _location != _in_class) break; // only allow for fields and classes + if (!EnableContended || (RestrictContended && !privileged)) break; // honor privileges + return _sun_misc_Contended; + default: break; + } + return AnnotationCollector::_unknown; +} + +void ClassFileParser::FieldAnnotationCollector::apply_to(FieldInfo* f) { + if (is_contended()) + f->set_contended_group(contended_group()); + if (is_stable()) + f->set_stable(true); +} + +ClassFileParser::FieldAnnotationCollector::~FieldAnnotationCollector() { + // If there's an error deallocate metadata for field annotations + MetadataFactory::free_array(_loader_data, _field_annotations); + MetadataFactory::free_array(_loader_data, _field_type_annotations); +} + +void ClassFileParser::MethodAnnotationCollector::apply_to(methodHandle m) { + if (has_annotation(_method_CallerSensitive)) + m->set_caller_sensitive(true); + if (has_annotation(_method_ForceInline)) + m->set_force_inline(true); + if (has_annotation(_method_DontInline)) + m->set_dont_inline(true); + if (has_annotation(_method_LambdaForm_Compiled) && m->intrinsic_id() == vmIntrinsics::_none) + m->set_intrinsic_id(vmIntrinsics::_compiledLambdaForm); + if (has_annotation(_method_LambdaForm_Hidden)) + m->set_hidden(true); +} + +void ClassFileParser::ClassAnnotationCollector::apply_to(instanceKlassHandle k) { + k->set_is_contended(is_contended()); +} + + +#define MAX_ARGS_SIZE 255 +#define MAX_CODE_SIZE 65535 +#define INITIAL_MAX_LVT_NUMBER 256 + +/* Copy class file LVT's/LVTT's into the HotSpot internal LVT. + * + * Rules for LVT's and LVTT's are: + * - There can be any number of LVT's and LVTT's. + * - If there are n LVT's, it is the same as if there was just + * one LVT containing all the entries from the n LVT's. + * - There may be no more than one LVT entry per local variable. + * Two LVT entries are 'equal' if these fields are the same: + * start_pc, length, name, slot + * - There may be no more than one LVTT entry per each LVT entry. + * Each LVTT entry has to match some LVT entry. + * - HotSpot internal LVT keeps natural ordering of class file LVT entries. + */ +void ClassFileParser::copy_localvariable_table(ConstMethod* cm, + int lvt_cnt, + u2* localvariable_table_length, + u2** localvariable_table_start, + int lvtt_cnt, + u2* localvariable_type_table_length, + u2** localvariable_type_table_start, + TRAPS) { + + LVT_Hash** lvt_Hash = NEW_RESOURCE_ARRAY(LVT_Hash*, HASH_ROW_SIZE); + initialize_hashtable(lvt_Hash); + + // To fill LocalVariableTable in + Classfile_LVT_Element* cf_lvt; + LocalVariableTableElement* lvt = cm->localvariable_table_start(); + + for (int tbl_no = 0; tbl_no < lvt_cnt; tbl_no++) { + cf_lvt = (Classfile_LVT_Element *) localvariable_table_start[tbl_no]; + for (int idx = 0; idx < localvariable_table_length[tbl_no]; idx++, lvt++) { + copy_lvt_element(&cf_lvt[idx], lvt); + // If no duplicates, add LVT elem in hashtable lvt_Hash. + if (LVT_put_after_lookup(lvt, lvt_Hash) == false + && _need_verify + && _major_version >= JAVA_1_5_VERSION) { + clear_hashtable(lvt_Hash); + classfile_parse_error("Duplicated LocalVariableTable attribute " + "entry for '%s' in class file %s", + _cp->symbol_at(lvt->name_cp_index)->as_utf8(), + CHECK); + } + } + } + + // To merge LocalVariableTable and LocalVariableTypeTable + Classfile_LVT_Element* cf_lvtt; + LocalVariableTableElement lvtt_elem; + + for (int tbl_no = 0; tbl_no < lvtt_cnt; tbl_no++) { + cf_lvtt = (Classfile_LVT_Element *) localvariable_type_table_start[tbl_no]; + for (int idx = 0; idx < localvariable_type_table_length[tbl_no]; idx++) { + copy_lvt_element(&cf_lvtt[idx], &lvtt_elem); + int index = hash(&lvtt_elem); + LVT_Hash* entry = LVT_lookup(&lvtt_elem, index, lvt_Hash); + if (entry == NULL) { + if (_need_verify) { + clear_hashtable(lvt_Hash); + classfile_parse_error("LVTT entry for '%s' in class file %s " + "does not match any LVT entry", + _cp->symbol_at(lvtt_elem.name_cp_index)->as_utf8(), + CHECK); + } + } else if (entry->_elem->signature_cp_index != 0 && _need_verify) { + clear_hashtable(lvt_Hash); + classfile_parse_error("Duplicated LocalVariableTypeTable attribute " + "entry for '%s' in class file %s", + _cp->symbol_at(lvtt_elem.name_cp_index)->as_utf8(), + CHECK); + } else { + // to add generic signatures into LocalVariableTable + entry->_elem->signature_cp_index = lvtt_elem.descriptor_cp_index; + } + } + } + clear_hashtable(lvt_Hash); +} + + +void ClassFileParser::copy_method_annotations(ConstMethod* cm, + u1* runtime_visible_annotations, + int runtime_visible_annotations_length, + u1* runtime_invisible_annotations, + int runtime_invisible_annotations_length, + u1* runtime_visible_parameter_annotations, + int runtime_visible_parameter_annotations_length, + u1* runtime_invisible_parameter_annotations, + int runtime_invisible_parameter_annotations_length, + u1* runtime_visible_type_annotations, + int runtime_visible_type_annotations_length, + u1* runtime_invisible_type_annotations, + int runtime_invisible_type_annotations_length, + u1* annotation_default, + int annotation_default_length, + TRAPS) { + + AnnotationArray* a; + + if (runtime_visible_annotations_length + + runtime_invisible_annotations_length > 0) { + a = assemble_annotations(runtime_visible_annotations, + runtime_visible_annotations_length, + runtime_invisible_annotations, + runtime_invisible_annotations_length, + CHECK); + cm->set_method_annotations(a); + } + + if (runtime_visible_parameter_annotations_length + + runtime_invisible_parameter_annotations_length > 0) { + a = assemble_annotations(runtime_visible_parameter_annotations, + runtime_visible_parameter_annotations_length, + runtime_invisible_parameter_annotations, + runtime_invisible_parameter_annotations_length, + CHECK); + cm->set_parameter_annotations(a); + } + + if (annotation_default_length > 0) { + a = assemble_annotations(annotation_default, + annotation_default_length, + NULL, + 0, + CHECK); + cm->set_default_annotations(a); + } + + if (runtime_visible_type_annotations_length + + runtime_invisible_type_annotations_length > 0) { + a = assemble_annotations(runtime_visible_type_annotations, + runtime_visible_type_annotations_length, + runtime_invisible_type_annotations, + runtime_invisible_type_annotations_length, + CHECK); + cm->set_type_annotations(a); + } +} + + +// Note: the parse_method below is big and clunky because all parsing of the code and exceptions +// attribute is inlined. This is cumbersome to avoid since we inline most of the parts in the +// Method* to save footprint, so we only know the size of the resulting Method* when the +// entire method attribute is parsed. +// +// The promoted_flags parameter is used to pass relevant access_flags +// from the method back up to the containing klass. These flag values +// are added to klass's access_flags. + +methodHandle ClassFileParser::parse_method(bool is_interface, + AccessFlags *promoted_flags, + TRAPS) { + ClassFileStream* cfs = stream(); + methodHandle nullHandle; + ResourceMark rm(THREAD); + // Parse fixed parts + cfs->guarantee_more(8, CHECK_(nullHandle)); // access_flags, name_index, descriptor_index, attributes_count + + int flags = cfs->get_u2_fast(); + u2 name_index = cfs->get_u2_fast(); + int cp_size = _cp->length(); + check_property( + valid_symbol_at(name_index), + "Illegal constant pool index %u for method name in class file %s", + name_index, CHECK_(nullHandle)); + Symbol* name = _cp->symbol_at(name_index); + verify_legal_method_name(name, CHECK_(nullHandle)); + + u2 signature_index = cfs->get_u2_fast(); + guarantee_property( + valid_symbol_at(signature_index), + "Illegal constant pool index %u for method signature in class file %s", + signature_index, CHECK_(nullHandle)); + Symbol* signature = _cp->symbol_at(signature_index); + + AccessFlags access_flags; + if (name == vmSymbols::class_initializer_name()) { + // We ignore the other access flags for a valid class initializer. + // (JVM Spec 2nd ed., chapter 4.6) + if (_major_version < 51) { // backward compatibility + flags = JVM_ACC_STATIC; + } else if ((flags & JVM_ACC_STATIC) == JVM_ACC_STATIC) { + flags &= JVM_ACC_STATIC | JVM_ACC_STRICT; + } + } else { + verify_legal_method_modifiers(flags, is_interface, name, CHECK_(nullHandle)); + } + + int args_size = -1; // only used when _need_verify is true + if (_need_verify) { + args_size = ((flags & JVM_ACC_STATIC) ? 0 : 1) + + verify_legal_method_signature(name, signature, CHECK_(nullHandle)); + if (args_size > MAX_ARGS_SIZE) { + classfile_parse_error("Too many arguments in method signature in class file %s", CHECK_(nullHandle)); + } + } + + access_flags.set_flags(flags & JVM_RECOGNIZED_METHOD_MODIFIERS); + + // Default values for code and exceptions attribute elements + u2 max_stack = 0; + u2 max_locals = 0; + u4 code_length = 0; + u1* code_start = 0; + u2 exception_table_length = 0; + u2* exception_table_start = NULL; + Array* exception_handlers = Universe::the_empty_int_array(); + u2 checked_exceptions_length = 0; + u2* checked_exceptions_start = NULL; + CompressedLineNumberWriteStream* linenumber_table = NULL; + int linenumber_table_length = 0; + int total_lvt_length = 0; + u2 lvt_cnt = 0; + u2 lvtt_cnt = 0; + bool lvt_allocated = false; + u2 max_lvt_cnt = INITIAL_MAX_LVT_NUMBER; + u2 max_lvtt_cnt = INITIAL_MAX_LVT_NUMBER; + u2* localvariable_table_length; + u2** localvariable_table_start; + u2* localvariable_type_table_length; + u2** localvariable_type_table_start; + u2 method_parameters_length = 0; + u1* method_parameters_data = NULL; + bool method_parameters_seen = false; + bool parsed_code_attribute = false; + bool parsed_checked_exceptions_attribute = false; + bool parsed_stackmap_attribute = false; + // stackmap attribute - JDK1.5 + u1* stackmap_data = NULL; + int stackmap_data_length = 0; + u2 generic_signature_index = 0; + MethodAnnotationCollector parsed_annotations; + u1* runtime_visible_annotations = NULL; + int runtime_visible_annotations_length = 0; + u1* runtime_invisible_annotations = NULL; + int runtime_invisible_annotations_length = 0; + u1* runtime_visible_parameter_annotations = NULL; + int runtime_visible_parameter_annotations_length = 0; + u1* runtime_invisible_parameter_annotations = NULL; + int runtime_invisible_parameter_annotations_length = 0; + u1* runtime_visible_type_annotations = NULL; + int runtime_visible_type_annotations_length = 0; + u1* runtime_invisible_type_annotations = NULL; + int runtime_invisible_type_annotations_length = 0; + bool runtime_invisible_annotations_exists = false; + bool runtime_invisible_type_annotations_exists = false; + bool runtime_invisible_parameter_annotations_exists = false; + u1* annotation_default = NULL; + int annotation_default_length = 0; + + // Parse code and exceptions attribute + u2 method_attributes_count = cfs->get_u2_fast(); + while (method_attributes_count--) { + cfs->guarantee_more(6, CHECK_(nullHandle)); // method_attribute_name_index, method_attribute_length + u2 method_attribute_name_index = cfs->get_u2_fast(); + u4 method_attribute_length = cfs->get_u4_fast(); + check_property( + valid_symbol_at(method_attribute_name_index), + "Invalid method attribute name index %u in class file %s", + method_attribute_name_index, CHECK_(nullHandle)); + + Symbol* method_attribute_name = _cp->symbol_at(method_attribute_name_index); + if (method_attribute_name == vmSymbols::tag_code()) { + // Parse Code attribute + if (_need_verify) { + guarantee_property( + !access_flags.is_native() && !access_flags.is_abstract(), + "Code attribute in native or abstract methods in class file %s", + CHECK_(nullHandle)); + } + if (parsed_code_attribute) { + classfile_parse_error("Multiple Code attributes in class file %s", CHECK_(nullHandle)); + } + parsed_code_attribute = true; + + // Stack size, locals size, and code size + if (_major_version == 45 && _minor_version <= 2) { + cfs->guarantee_more(4, CHECK_(nullHandle)); + max_stack = cfs->get_u1_fast(); + max_locals = cfs->get_u1_fast(); + code_length = cfs->get_u2_fast(); + } else { + cfs->guarantee_more(8, CHECK_(nullHandle)); + max_stack = cfs->get_u2_fast(); + max_locals = cfs->get_u2_fast(); + code_length = cfs->get_u4_fast(); + } + if (_need_verify) { + guarantee_property(args_size <= max_locals, + "Arguments can't fit into locals in class file %s", CHECK_(nullHandle)); + guarantee_property(code_length > 0 && code_length <= MAX_CODE_SIZE, + "Invalid method Code length %u in class file %s", + code_length, CHECK_(nullHandle)); + } + // Code pointer + code_start = cfs->get_u1_buffer(); + assert(code_start != NULL, "null code start"); + cfs->guarantee_more(code_length, CHECK_(nullHandle)); + cfs->skip_u1_fast(code_length); + + // Exception handler table + cfs->guarantee_more(2, CHECK_(nullHandle)); // exception_table_length + exception_table_length = cfs->get_u2_fast(); + if (exception_table_length > 0) { + exception_table_start = + parse_exception_table(code_length, exception_table_length, CHECK_(nullHandle)); + } + + // Parse additional attributes in code attribute + cfs->guarantee_more(2, CHECK_(nullHandle)); // code_attributes_count + u2 code_attributes_count = cfs->get_u2_fast(); + + unsigned int calculated_attribute_length = 0; + + if (_major_version > 45 || (_major_version == 45 && _minor_version > 2)) { + calculated_attribute_length = + sizeof(max_stack) + sizeof(max_locals) + sizeof(code_length); + } else { + // max_stack, locals and length are smaller in pre-version 45.2 classes + calculated_attribute_length = sizeof(u1) + sizeof(u1) + sizeof(u2); + } + calculated_attribute_length += + code_length + + sizeof(exception_table_length) + + sizeof(code_attributes_count) + + exception_table_length * + ( sizeof(u2) + // start_pc + sizeof(u2) + // end_pc + sizeof(u2) + // handler_pc + sizeof(u2) ); // catch_type_index + + while (code_attributes_count--) { + cfs->guarantee_more(6, CHECK_(nullHandle)); // code_attribute_name_index, code_attribute_length + u2 code_attribute_name_index = cfs->get_u2_fast(); + u4 code_attribute_length = cfs->get_u4_fast(); + calculated_attribute_length += code_attribute_length + + sizeof(code_attribute_name_index) + + sizeof(code_attribute_length); + check_property(valid_symbol_at(code_attribute_name_index), + "Invalid code attribute name index %u in class file %s", + code_attribute_name_index, + CHECK_(nullHandle)); + if (LoadLineNumberTables && + _cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_line_number_table()) { + // Parse and compress line number table + parse_linenumber_table(code_attribute_length, code_length, + &linenumber_table, CHECK_(nullHandle)); + + } else if (LoadLocalVariableTables && + _cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_local_variable_table()) { + // Parse local variable table + if (!lvt_allocated) { + localvariable_table_length = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, u2, INITIAL_MAX_LVT_NUMBER); + localvariable_table_start = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, u2*, INITIAL_MAX_LVT_NUMBER); + localvariable_type_table_length = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, u2, INITIAL_MAX_LVT_NUMBER); + localvariable_type_table_start = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, u2*, INITIAL_MAX_LVT_NUMBER); + lvt_allocated = true; + } + if (lvt_cnt == max_lvt_cnt) { + max_lvt_cnt <<= 1; + localvariable_table_length = REALLOC_RESOURCE_ARRAY(u2, localvariable_table_length, lvt_cnt, max_lvt_cnt); + localvariable_table_start = REALLOC_RESOURCE_ARRAY(u2*, localvariable_table_start, lvt_cnt, max_lvt_cnt); + } + localvariable_table_start[lvt_cnt] = + parse_localvariable_table(code_length, + max_locals, + code_attribute_length, + &localvariable_table_length[lvt_cnt], + false, // is not LVTT + CHECK_(nullHandle)); + total_lvt_length += localvariable_table_length[lvt_cnt]; + lvt_cnt++; + } else if (LoadLocalVariableTypeTables && + _major_version >= JAVA_1_5_VERSION && + _cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_local_variable_type_table()) { + if (!lvt_allocated) { + localvariable_table_length = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, u2, INITIAL_MAX_LVT_NUMBER); + localvariable_table_start = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, u2*, INITIAL_MAX_LVT_NUMBER); + localvariable_type_table_length = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, u2, INITIAL_MAX_LVT_NUMBER); + localvariable_type_table_start = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, u2*, INITIAL_MAX_LVT_NUMBER); + lvt_allocated = true; + } + // Parse local variable type table + if (lvtt_cnt == max_lvtt_cnt) { + max_lvtt_cnt <<= 1; + localvariable_type_table_length = REALLOC_RESOURCE_ARRAY(u2, localvariable_type_table_length, lvtt_cnt, max_lvtt_cnt); + localvariable_type_table_start = REALLOC_RESOURCE_ARRAY(u2*, localvariable_type_table_start, lvtt_cnt, max_lvtt_cnt); + } + localvariable_type_table_start[lvtt_cnt] = + parse_localvariable_table(code_length, + max_locals, + code_attribute_length, + &localvariable_type_table_length[lvtt_cnt], + true, // is LVTT + CHECK_(nullHandle)); + lvtt_cnt++; + } else if (_major_version >= Verifier::STACKMAP_ATTRIBUTE_MAJOR_VERSION && + _cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_stack_map_table()) { + // Stack map is only needed by the new verifier in JDK1.5. + if (parsed_stackmap_attribute) { + classfile_parse_error("Multiple StackMapTable attributes in class file %s", CHECK_(nullHandle)); + } + stackmap_data = parse_stackmap_table(code_attribute_length, CHECK_(nullHandle)); + stackmap_data_length = code_attribute_length; + parsed_stackmap_attribute = true; + } else { + // Skip unknown attributes + cfs->skip_u1(code_attribute_length, CHECK_(nullHandle)); + } + } + // check method attribute length + if (_need_verify) { + guarantee_property(method_attribute_length == calculated_attribute_length, + "Code segment has wrong length in class file %s", CHECK_(nullHandle)); + } + } else if (method_attribute_name == vmSymbols::tag_exceptions()) { + // Parse Exceptions attribute + if (parsed_checked_exceptions_attribute) { + classfile_parse_error("Multiple Exceptions attributes in class file %s", CHECK_(nullHandle)); + } + parsed_checked_exceptions_attribute = true; + checked_exceptions_start = + parse_checked_exceptions(&checked_exceptions_length, + method_attribute_length, + CHECK_(nullHandle)); + } else if (method_attribute_name == vmSymbols::tag_method_parameters()) { + // reject multiple method parameters + if (method_parameters_seen) { + classfile_parse_error("Multiple MethodParameters attributes in class file %s", CHECK_(nullHandle)); + } + method_parameters_seen = true; + method_parameters_length = cfs->get_u1_fast(); + if (method_attribute_length != (method_parameters_length * 4u) + 1u) { + classfile_parse_error( + "Invalid MethodParameters method attribute length %u in class file", + method_attribute_length, CHECK_(nullHandle)); + } + method_parameters_data = cfs->get_u1_buffer(); + cfs->skip_u2_fast(method_parameters_length); + cfs->skip_u2_fast(method_parameters_length); + // ignore this attribute if it cannot be reflected + if (!SystemDictionary::Parameter_klass_loaded()) + method_parameters_length = 0; + } else if (method_attribute_name == vmSymbols::tag_synthetic()) { + if (method_attribute_length != 0) { + classfile_parse_error( + "Invalid Synthetic method attribute length %u in class file %s", + method_attribute_length, CHECK_(nullHandle)); + } + // Should we check that there hasn't already been a synthetic attribute? + access_flags.set_is_synthetic(); + } else if (method_attribute_name == vmSymbols::tag_deprecated()) { // 4276120 + if (method_attribute_length != 0) { + classfile_parse_error( + "Invalid Deprecated method attribute length %u in class file %s", + method_attribute_length, CHECK_(nullHandle)); + } + } else if (_major_version >= JAVA_1_5_VERSION) { + if (method_attribute_name == vmSymbols::tag_signature()) { + if (method_attribute_length != 2) { + classfile_parse_error( + "Invalid Signature attribute length %u in class file %s", + method_attribute_length, CHECK_(nullHandle)); + } + generic_signature_index = parse_generic_signature_attribute(CHECK_(nullHandle)); + } else if (method_attribute_name == vmSymbols::tag_runtime_visible_annotations()) { + if (runtime_visible_annotations != NULL) { + classfile_parse_error( + "Multiple RuntimeVisibleAnnotations attributes for method in class file %s", CHECK_(nullHandle)); + } + runtime_visible_annotations_length = method_attribute_length; + runtime_visible_annotations = cfs->get_u1_buffer(); + assert(runtime_visible_annotations != NULL, "null visible annotations"); + parse_annotations(runtime_visible_annotations, + runtime_visible_annotations_length, &parsed_annotations, + CHECK_(nullHandle)); + cfs->skip_u1(runtime_visible_annotations_length, CHECK_(nullHandle)); + } else if (method_attribute_name == vmSymbols::tag_runtime_invisible_annotations()) { + if (runtime_invisible_annotations_exists) { + classfile_parse_error( + "Multiple RuntimeInvisibleAnnotations attributes for method in class file %s", CHECK_(nullHandle)); + } + runtime_invisible_annotations_exists = true; + if (PreserveAllAnnotations) { + runtime_invisible_annotations_length = method_attribute_length; + runtime_invisible_annotations = cfs->get_u1_buffer(); + assert(runtime_invisible_annotations != NULL, "null invisible annotations"); + } + cfs->skip_u1(method_attribute_length, CHECK_(nullHandle)); + } else if (method_attribute_name == vmSymbols::tag_runtime_visible_parameter_annotations()) { + if (runtime_visible_parameter_annotations != NULL) { + classfile_parse_error( + "Multiple RuntimeVisibleParameterAnnotations attributes for method in class file %s", CHECK_(nullHandle)); + } + runtime_visible_parameter_annotations_length = method_attribute_length; + runtime_visible_parameter_annotations = cfs->get_u1_buffer(); + assert(runtime_visible_parameter_annotations != NULL, "null visible parameter annotations"); + cfs->skip_u1(runtime_visible_parameter_annotations_length, CHECK_(nullHandle)); + } else if (method_attribute_name == vmSymbols::tag_runtime_invisible_parameter_annotations()) { + if (runtime_invisible_parameter_annotations_exists) { + classfile_parse_error( + "Multiple RuntimeInvisibleParameterAnnotations attributes for method in class file %s", CHECK_(nullHandle)); + } + runtime_invisible_parameter_annotations_exists = true; + if (PreserveAllAnnotations) { + runtime_invisible_parameter_annotations_length = method_attribute_length; + runtime_invisible_parameter_annotations = cfs->get_u1_buffer(); + assert(runtime_invisible_parameter_annotations != NULL, "null invisible parameter annotations"); + } + cfs->skip_u1(method_attribute_length, CHECK_(nullHandle)); + } else if (method_attribute_name == vmSymbols::tag_annotation_default()) { + if (annotation_default != NULL) { + classfile_parse_error( + "Multiple AnnotationDefault attributes for method in class file %s", + CHECK_(nullHandle)); + } + annotation_default_length = method_attribute_length; + annotation_default = cfs->get_u1_buffer(); + assert(annotation_default != NULL, "null annotation default"); + cfs->skip_u1(annotation_default_length, CHECK_(nullHandle)); + } else if (method_attribute_name == vmSymbols::tag_runtime_visible_type_annotations()) { + if (runtime_visible_type_annotations != NULL) { + classfile_parse_error( + "Multiple RuntimeVisibleTypeAnnotations attributes for method in class file %s", + CHECK_(nullHandle)); + } + runtime_visible_type_annotations_length = method_attribute_length; + runtime_visible_type_annotations = cfs->get_u1_buffer(); + assert(runtime_visible_type_annotations != NULL, "null visible type annotations"); + // No need for the VM to parse Type annotations + cfs->skip_u1(runtime_visible_type_annotations_length, CHECK_(nullHandle)); + } else if (method_attribute_name == vmSymbols::tag_runtime_invisible_type_annotations()) { + if (runtime_invisible_type_annotations_exists) { + classfile_parse_error( + "Multiple RuntimeInvisibleTypeAnnotations attributes for method in class file %s", + CHECK_(nullHandle)); + } else { + runtime_invisible_type_annotations_exists = true; + } + if (PreserveAllAnnotations) { + runtime_invisible_type_annotations_length = method_attribute_length; + runtime_invisible_type_annotations = cfs->get_u1_buffer(); + assert(runtime_invisible_type_annotations != NULL, "null invisible type annotations"); + } + cfs->skip_u1(method_attribute_length, CHECK_(nullHandle)); + } else { + // Skip unknown attributes + cfs->skip_u1(method_attribute_length, CHECK_(nullHandle)); + } + } else { + // Skip unknown attributes + cfs->skip_u1(method_attribute_length, CHECK_(nullHandle)); + } + } + + if (linenumber_table != NULL) { + linenumber_table->write_terminator(); + linenumber_table_length = linenumber_table->position(); + } + + // Make sure there's at least one Code attribute in non-native/non-abstract method + if (_need_verify) { + guarantee_property(access_flags.is_native() || access_flags.is_abstract() || parsed_code_attribute, + "Absent Code attribute in method that is not native or abstract in class file %s", CHECK_(nullHandle)); + } + + // All sizing information for a Method* is finally available, now create it + InlineTableSizes sizes( + total_lvt_length, + linenumber_table_length, + exception_table_length, + checked_exceptions_length, + method_parameters_length, + generic_signature_index, + runtime_visible_annotations_length + + runtime_invisible_annotations_length, + runtime_visible_parameter_annotations_length + + runtime_invisible_parameter_annotations_length, + runtime_visible_type_annotations_length + + runtime_invisible_type_annotations_length, + annotation_default_length, + 0); + + Method* m = Method::allocate( + _loader_data, code_length, access_flags, &sizes, + ConstMethod::NORMAL, CHECK_(nullHandle)); + + ClassLoadingService::add_class_method_size(m->size()*HeapWordSize); + + // Fill in information from fixed part (access_flags already set) + m->set_constants(_cp); + m->set_name_index(name_index); + m->set_signature_index(signature_index); +#ifdef CC_INTERP + // hmm is there a gc issue here?? + ResultTypeFinder rtf(_cp->symbol_at(signature_index)); + m->set_result_index(rtf.type()); +#endif + + if (args_size >= 0) { + m->set_size_of_parameters(args_size); + } else { + m->compute_size_of_parameters(THREAD); + } +#ifdef ASSERT + if (args_size >= 0) { + m->compute_size_of_parameters(THREAD); + assert(args_size == m->size_of_parameters(), ""); + } +#endif + + // Fill in code attribute information + m->set_max_stack(max_stack); + m->set_max_locals(max_locals); + if (stackmap_data != NULL) { + m->constMethod()->copy_stackmap_data(_loader_data, stackmap_data, + stackmap_data_length, CHECK_NULL); + } + + // Copy byte codes + m->set_code(code_start); + + // Copy line number table + if (linenumber_table != NULL) { + memcpy(m->compressed_linenumber_table(), + linenumber_table->buffer(), linenumber_table_length); + } + + // Copy exception table + if (exception_table_length > 0) { + int size = + exception_table_length * sizeof(ExceptionTableElement) / sizeof(u2); + copy_u2_with_conversion((u2*) m->exception_table_start(), + exception_table_start, size); + } + + // Copy method parameters + if (method_parameters_length > 0) { + MethodParametersElement* elem = m->constMethod()->method_parameters_start(); + for (int i = 0; i < method_parameters_length; i++) { + elem[i].name_cp_index = Bytes::get_Java_u2(method_parameters_data); + method_parameters_data += 2; + elem[i].flags = Bytes::get_Java_u2(method_parameters_data); + method_parameters_data += 2; + } + } + + // Copy checked exceptions + if (checked_exceptions_length > 0) { + int size = checked_exceptions_length * sizeof(CheckedExceptionElement) / sizeof(u2); + copy_u2_with_conversion((u2*) m->checked_exceptions_start(), checked_exceptions_start, size); + } + + // Copy class file LVT's/LVTT's into the HotSpot internal LVT. + if (total_lvt_length > 0) { + promoted_flags->set_has_localvariable_table(); + copy_localvariable_table(m->constMethod(), lvt_cnt, + localvariable_table_length, + localvariable_table_start, + lvtt_cnt, + localvariable_type_table_length, + localvariable_type_table_start, CHECK_NULL); + } + + if (parsed_annotations.has_any_annotations()) + parsed_annotations.apply_to(m); + + // Copy annotations + copy_method_annotations(m->constMethod(), + runtime_visible_annotations, + runtime_visible_annotations_length, + runtime_invisible_annotations, + runtime_invisible_annotations_length, + runtime_visible_parameter_annotations, + runtime_visible_parameter_annotations_length, + runtime_invisible_parameter_annotations, + runtime_invisible_parameter_annotations_length, + runtime_visible_type_annotations, + runtime_visible_type_annotations_length, + runtime_invisible_type_annotations, + runtime_invisible_type_annotations_length, + annotation_default, + annotation_default_length, + CHECK_NULL); + + if (name == vmSymbols::finalize_method_name() && + signature == vmSymbols::void_method_signature()) { + if (m->is_empty_method()) { + _has_empty_finalizer = true; + } else { + _has_finalizer = true; + } + } + if (name == vmSymbols::object_initializer_name() && + signature == vmSymbols::void_method_signature() && + m->is_vanilla_constructor()) { + _has_vanilla_constructor = true; + } + + NOT_PRODUCT(m->verify()); + return m; +} + + +// The promoted_flags parameter is used to pass relevant access_flags +// from the methods back up to the containing klass. These flag values +// are added to klass's access_flags. + +Array* ClassFileParser::parse_methods(bool is_interface, + AccessFlags* promoted_flags, + bool* has_final_method, + bool* has_default_methods, + TRAPS) { + ClassFileStream* cfs = stream(); + cfs->guarantee_more(2, CHECK_NULL); // length + u2 length = cfs->get_u2_fast(); + if (length == 0) { + _methods = Universe::the_empty_method_array(); + } else { + _methods = MetadataFactory::new_array(_loader_data, length, NULL, CHECK_NULL); + + HandleMark hm(THREAD); + for (int index = 0; index < length; index++) { + methodHandle method = parse_method(is_interface, + promoted_flags, + CHECK_NULL); + + if (method->is_final()) { + *has_final_method = true; + } + if (is_interface && !(*has_default_methods) + && !method->is_abstract() && !method->is_static() + && !method->is_private()) { + // default method + *has_default_methods = true; + } + _methods->at_put(index, method()); + } + + if (_need_verify && length > 1) { + // Check duplicated methods + ResourceMark rm(THREAD); + NameSigHash** names_and_sigs = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, NameSigHash*, HASH_ROW_SIZE); + initialize_hashtable(names_and_sigs); + bool dup = false; + { + debug_only(No_Safepoint_Verifier nsv;) + for (int i = 0; i < length; i++) { + Method* m = _methods->at(i); + // If no duplicates, add name/signature in hashtable names_and_sigs. + if (!put_after_lookup(m->name(), m->signature(), names_and_sigs)) { + dup = true; + break; + } + } + } + if (dup) { + classfile_parse_error("Duplicate method name&signature in class file %s", + CHECK_NULL); + } + } + } + return _methods; +} + + +intArray* ClassFileParser::sort_methods(Array* methods) { + int length = methods->length(); + // If JVMTI original method ordering or sharing is enabled we have to + // remember the original class file ordering. + // We temporarily use the vtable_index field in the Method* to store the + // class file index, so we can read in after calling qsort. + // Put the method ordering in the shared archive. + if (JvmtiExport::can_maintain_original_method_order() || DumpSharedSpaces) { + for (int index = 0; index < length; index++) { + Method* m = methods->at(index); + assert(!m->valid_vtable_index(), "vtable index should not be set"); + m->set_vtable_index(index); + } + } + // Sort method array by ascending method name (for faster lookups & vtable construction) + // Note that the ordering is not alphabetical, see Symbol::fast_compare + Method::sort_methods(methods); + + intArray* method_ordering = NULL; + // If JVMTI original method ordering or sharing is enabled construct int + // array remembering the original ordering + if (JvmtiExport::can_maintain_original_method_order() || DumpSharedSpaces) { + method_ordering = new intArray(length); + for (int index = 0; index < length; index++) { + Method* m = methods->at(index); + int old_index = m->vtable_index(); + assert(old_index >= 0 && old_index < length, "invalid method index"); + method_ordering->at_put(index, old_index); + m->set_vtable_index(Method::invalid_vtable_index); + } + } + return method_ordering; +} + +// Parse generic_signature attribute for methods and fields +u2 ClassFileParser::parse_generic_signature_attribute(TRAPS) { + ClassFileStream* cfs = stream(); + cfs->guarantee_more(2, CHECK_0); // generic_signature_index + u2 generic_signature_index = cfs->get_u2_fast(); + check_property( + valid_symbol_at(generic_signature_index), + "Invalid Signature attribute at constant pool index %u in class file %s", + generic_signature_index, CHECK_0); + return generic_signature_index; +} + +void ClassFileParser::parse_classfile_sourcefile_attribute(TRAPS) { + ClassFileStream* cfs = stream(); + cfs->guarantee_more(2, CHECK); // sourcefile_index + u2 sourcefile_index = cfs->get_u2_fast(); + check_property( + valid_symbol_at(sourcefile_index), + "Invalid SourceFile attribute at constant pool index %u in class file %s", + sourcefile_index, CHECK); + set_class_sourcefile_index(sourcefile_index); +} + + + +void ClassFileParser::parse_classfile_source_debug_extension_attribute(int length, TRAPS) { + ClassFileStream* cfs = stream(); + u1* sde_buffer = cfs->get_u1_buffer(); + assert(sde_buffer != NULL, "null sde buffer"); + + // Don't bother storing it if there is no way to retrieve it + if (JvmtiExport::can_get_source_debug_extension()) { + assert((length+1) > length, "Overflow checking"); + u1* sde = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, u1, length+1); + for (int i = 0; i < length; i++) { + sde[i] = sde_buffer[i]; + } + sde[length] = '\0'; + set_class_sde_buffer((char*)sde, length); + } + // Got utf8 string, set stream position forward + cfs->skip_u1(length, CHECK); +} + + +// Inner classes can be static, private or protected (classic VM does this) +#define RECOGNIZED_INNER_CLASS_MODIFIERS (JVM_RECOGNIZED_CLASS_MODIFIERS | JVM_ACC_PRIVATE | JVM_ACC_PROTECTED | JVM_ACC_STATIC) + +// Return number of classes in the inner classes attribute table +u2 ClassFileParser::parse_classfile_inner_classes_attribute(u1* inner_classes_attribute_start, + bool parsed_enclosingmethod_attribute, + u2 enclosing_method_class_index, + u2 enclosing_method_method_index, + TRAPS) { + ClassFileStream* cfs = stream(); + u1* current_mark = cfs->current(); + u2 length = 0; + if (inner_classes_attribute_start != NULL) { + cfs->set_current(inner_classes_attribute_start); + cfs->guarantee_more(2, CHECK_0); // length + length = cfs->get_u2_fast(); + } + + // 4-tuples of shorts of inner classes data and 2 shorts of enclosing + // method data: + // [inner_class_info_index, + // outer_class_info_index, + // inner_name_index, + // inner_class_access_flags, + // ... + // enclosing_method_class_index, + // enclosing_method_method_index] + int size = length * 4 + (parsed_enclosingmethod_attribute ? 2 : 0); + Array* inner_classes = MetadataFactory::new_array(_loader_data, size, CHECK_0); + _inner_classes = inner_classes; + + int index = 0; + int cp_size = _cp->length(); + cfs->guarantee_more(8 * length, CHECK_0); // 4-tuples of u2 + for (int n = 0; n < length; n++) { + // Inner class index + u2 inner_class_info_index = cfs->get_u2_fast(); + check_property( + inner_class_info_index == 0 || + valid_klass_reference_at(inner_class_info_index), + "inner_class_info_index %u has bad constant type in class file %s", + inner_class_info_index, CHECK_0); + // Outer class index + u2 outer_class_info_index = cfs->get_u2_fast(); + check_property( + outer_class_info_index == 0 || + valid_klass_reference_at(outer_class_info_index), + "outer_class_info_index %u has bad constant type in class file %s", + outer_class_info_index, CHECK_0); + // Inner class name + u2 inner_name_index = cfs->get_u2_fast(); + check_property( + inner_name_index == 0 || valid_symbol_at(inner_name_index), + "inner_name_index %u has bad constant type in class file %s", + 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 + AccessFlags inner_access_flags; + jint flags = cfs->get_u2_fast() & RECOGNIZED_INNER_CLASS_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); + inner_access_flags.set_flags(flags); + + inner_classes->at_put(index++, inner_class_info_index); + inner_classes->at_put(index++, outer_class_info_index); + inner_classes->at_put(index++, inner_name_index); + inner_classes->at_put(index++, inner_access_flags.as_short()); + } + + // 4347400: make sure there's no duplicate entry in the classes array + if (_need_verify && _major_version >= JAVA_1_5_VERSION) { + for(int i = 0; i < length * 4; i += 4) { + for(int j = i + 4; j < length * 4; j += 4) { + guarantee_property((inner_classes->at(i) != inner_classes->at(j) || + inner_classes->at(i+1) != inner_classes->at(j+1) || + inner_classes->at(i+2) != inner_classes->at(j+2) || + inner_classes->at(i+3) != inner_classes->at(j+3)), + "Duplicate entry in InnerClasses in class file %s", + CHECK_0); + } + } + } + + // Set EnclosingMethod class and method indexes. + if (parsed_enclosingmethod_attribute) { + inner_classes->at_put(index++, enclosing_method_class_index); + inner_classes->at_put(index++, enclosing_method_method_index); + } + assert(index == size, "wrong size"); + + // Restore buffer's current position. + cfs->set_current(current_mark); + + return length; +} + +void ClassFileParser::parse_classfile_synthetic_attribute(TRAPS) { + set_class_synthetic_flag(true); +} + +void ClassFileParser::parse_classfile_signature_attribute(TRAPS) { + ClassFileStream* cfs = stream(); + u2 signature_index = cfs->get_u2(CHECK); + check_property( + valid_symbol_at(signature_index), + "Invalid constant pool index %u in Signature attribute in class file %s", + signature_index, CHECK); + set_class_generic_signature_index(signature_index); +} + +void ClassFileParser::parse_classfile_bootstrap_methods_attribute(u4 attribute_byte_length, TRAPS) { + ClassFileStream* cfs = stream(); + u1* current_start = cfs->current(); + + guarantee_property(attribute_byte_length >= sizeof(u2), + "Invalid BootstrapMethods attribute length %u in class file %s", + attribute_byte_length, + CHECK); + + cfs->guarantee_more(attribute_byte_length, CHECK); + + int attribute_array_length = cfs->get_u2_fast(); + + guarantee_property(_max_bootstrap_specifier_index < attribute_array_length, + "Short length on BootstrapMethods in class file %s", + CHECK); + + + // The attribute contains a counted array of counted tuples of shorts, + // represending bootstrap specifiers: + // length*{bootstrap_method_index, argument_count*{argument_index}} + int operand_count = (attribute_byte_length - sizeof(u2)) / sizeof(u2); + // operand_count = number of shorts in attr, except for leading length + + // The attribute is copied into a short[] array. + // The array begins with a series of short[2] pairs, one for each tuple. + int index_size = (attribute_array_length * 2); + + Array* operands = MetadataFactory::new_array(_loader_data, index_size + operand_count, CHECK); + + // Eagerly assign operands so they will be deallocated with the constant + // pool if there is an error. + _cp->set_operands(operands); + + int operand_fill_index = index_size; + int cp_size = _cp->length(); + + for (int n = 0; n < attribute_array_length; n++) { + // Store a 32-bit offset into the header of the operand array. + ConstantPool::operand_offset_at_put(operands, n, operand_fill_index); + + // Read a bootstrap specifier. + cfs->guarantee_more(sizeof(u2) * 2, CHECK); // bsm, argc + u2 bootstrap_method_index = cfs->get_u2_fast(); + u2 argument_count = cfs->get_u2_fast(); + check_property( + valid_cp_range(bootstrap_method_index, cp_size) && + _cp->tag_at(bootstrap_method_index).is_method_handle(), + "bootstrap_method_index %u has bad constant type in class file %s", + bootstrap_method_index, + CHECK); + + guarantee_property((operand_fill_index + 1 + argument_count) < operands->length(), + "Invalid BootstrapMethods num_bootstrap_methods or num_bootstrap_arguments value in class file %s", + CHECK); + + operands->at_put(operand_fill_index++, bootstrap_method_index); + operands->at_put(operand_fill_index++, argument_count); + + cfs->guarantee_more(sizeof(u2) * argument_count, CHECK); // argv[argc] + for (int j = 0; j < argument_count; j++) { + u2 argument_index = cfs->get_u2_fast(); + check_property( + valid_cp_range(argument_index, cp_size) && + _cp->tag_at(argument_index).is_loadable_constant(), + "argument_index %u has bad constant type in class file %s", + argument_index, + CHECK); + operands->at_put(operand_fill_index++, argument_index); + } + } + + u1* current_end = cfs->current(); + guarantee_property(current_end == current_start + attribute_byte_length, + "Bad length on BootstrapMethods in class file %s", + CHECK); +} + +void ClassFileParser::parse_classfile_attributes(ClassFileParser::ClassAnnotationCollector* parsed_annotations, + TRAPS) { + ClassFileStream* cfs = stream(); + // Set inner classes attribute to default sentinel + _inner_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_enclosingmethod_attribute = false; + bool parsed_bootstrap_methods_attribute = false; + u1* runtime_visible_annotations = NULL; + int runtime_visible_annotations_length = 0; + u1* runtime_invisible_annotations = NULL; + int runtime_invisible_annotations_length = 0; + u1* runtime_visible_type_annotations = NULL; + int runtime_visible_type_annotations_length = 0; + u1* runtime_invisible_type_annotations = NULL; + int runtime_invisible_type_annotations_length = 0; + bool runtime_invisible_type_annotations_exists = false; + bool runtime_invisible_annotations_exists = false; + bool parsed_source_debug_ext_annotations_exist = false; + u1* inner_classes_attribute_start = NULL; + u4 inner_classes_attribute_length = 0; + u2 enclosing_method_class_index = 0; + u2 enclosing_method_method_index = 0; + // Iterate over attributes + while (attributes_count--) { + cfs->guarantee_more(6, CHECK); // attribute_name_index, attribute_length + u2 attribute_name_index = cfs->get_u2_fast(); + u4 attribute_length = cfs->get_u4_fast(); + check_property( + valid_symbol_at(attribute_name_index), + "Attribute name has bad constant pool index %u in class file %s", + attribute_name_index, CHECK); + Symbol* tag = _cp->symbol_at(attribute_name_index); + if (tag == vmSymbols::tag_source_file()) { + // Check for SourceFile tag + if (_need_verify) { + guarantee_property(attribute_length == 2, "Wrong SourceFile attribute length in class file %s", CHECK); + } + if (parsed_sourcefile_attribute) { + classfile_parse_error("Multiple SourceFile attributes in class file %s", CHECK); + } else { + parsed_sourcefile_attribute = true; + } + parse_classfile_sourcefile_attribute(CHECK); + } else if (tag == vmSymbols::tag_source_debug_extension()) { + // Check for SourceDebugExtension tag + if (parsed_source_debug_ext_annotations_exist) { + classfile_parse_error( + "Multiple SourceDebugExtension attributes in class file %s", CHECK); + } + parsed_source_debug_ext_annotations_exist = true; + parse_classfile_source_debug_extension_attribute((int)attribute_length, CHECK); + } else if (tag == vmSymbols::tag_inner_classes()) { + // Check for InnerClasses tag + if (parsed_innerclasses_attribute) { + classfile_parse_error("Multiple InnerClasses attributes in class file %s", CHECK); + } else { + parsed_innerclasses_attribute = true; + } + inner_classes_attribute_start = cfs->get_u1_buffer(); + inner_classes_attribute_length = attribute_length; + cfs->skip_u1(inner_classes_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 + if (attribute_length != 0) { + classfile_parse_error( + "Invalid Synthetic classfile attribute length %u in class file %s", + attribute_length, CHECK); + } + parse_classfile_synthetic_attribute(CHECK); + } else if (tag == vmSymbols::tag_deprecated()) { + // Check for Deprecatd tag - 4276120 + if (attribute_length != 0) { + classfile_parse_error( + "Invalid Deprecated classfile attribute length %u in class file %s", + attribute_length, CHECK); + } + } else if (_major_version >= JAVA_1_5_VERSION) { + if (tag == vmSymbols::tag_signature()) { + if (attribute_length != 2) { + classfile_parse_error( + "Wrong Signature attribute length %u in class file %s", + attribute_length, CHECK); + } + parse_classfile_signature_attribute(CHECK); + } else if (tag == vmSymbols::tag_runtime_visible_annotations()) { + if (runtime_visible_annotations != NULL) { + classfile_parse_error( + "Multiple RuntimeVisibleAnnotations attributes in class file %s", CHECK); + } + runtime_visible_annotations_length = attribute_length; + runtime_visible_annotations = cfs->get_u1_buffer(); + assert(runtime_visible_annotations != NULL, "null visible annotations"); + parse_annotations(runtime_visible_annotations, + runtime_visible_annotations_length, + parsed_annotations, + CHECK); + cfs->skip_u1(runtime_visible_annotations_length, CHECK); + } else if (tag == vmSymbols::tag_runtime_invisible_annotations()) { + if (runtime_invisible_annotations_exists) { + classfile_parse_error( + "Multiple RuntimeInvisibleAnnotations attributes in class file %s", CHECK); + } + runtime_invisible_annotations_exists = true; + if (PreserveAllAnnotations) { + runtime_invisible_annotations_length = attribute_length; + runtime_invisible_annotations = cfs->get_u1_buffer(); + assert(runtime_invisible_annotations != NULL, "null invisible annotations"); + } + cfs->skip_u1(attribute_length, CHECK); + } else if (tag == vmSymbols::tag_enclosing_method()) { + if (parsed_enclosingmethod_attribute) { + classfile_parse_error("Multiple EnclosingMethod attributes in class file %s", CHECK); + } else { + parsed_enclosingmethod_attribute = true; + } + guarantee_property(attribute_length == 4, + "Wrong EnclosingMethod attribute length %u in class file %s", + attribute_length, CHECK); + cfs->guarantee_more(4, CHECK); // class_index, method_index + enclosing_method_class_index = cfs->get_u2_fast(); + enclosing_method_method_index = cfs->get_u2_fast(); + if (enclosing_method_class_index == 0) { + classfile_parse_error("Invalid class index in EnclosingMethod attribute in class file %s", CHECK); + } + // Validate the constant pool indices and types + check_property(valid_klass_reference_at(enclosing_method_class_index), + "Invalid or out-of-bounds class index in EnclosingMethod attribute in class file %s", CHECK); + if (enclosing_method_method_index != 0 && + (!_cp->is_within_bounds(enclosing_method_method_index) || + !_cp->tag_at(enclosing_method_method_index).is_name_and_type())) { + classfile_parse_error("Invalid or out-of-bounds method index in EnclosingMethod attribute in class file %s", CHECK); + } + } else if (tag == vmSymbols::tag_bootstrap_methods() && + _major_version >= Verifier::INVOKEDYNAMIC_MAJOR_VERSION) { + if (parsed_bootstrap_methods_attribute) + classfile_parse_error("Multiple BootstrapMethods attributes in class file %s", CHECK); + parsed_bootstrap_methods_attribute = true; + parse_classfile_bootstrap_methods_attribute(attribute_length, CHECK); + } else if (tag == vmSymbols::tag_runtime_visible_type_annotations()) { + if (runtime_visible_type_annotations != NULL) { + classfile_parse_error( + "Multiple RuntimeVisibleTypeAnnotations attributes in class file %s", CHECK); + } + runtime_visible_type_annotations_length = attribute_length; + runtime_visible_type_annotations = cfs->get_u1_buffer(); + assert(runtime_visible_type_annotations != NULL, "null visible type annotations"); + // No need for the VM to parse Type annotations + cfs->skip_u1(runtime_visible_type_annotations_length, CHECK); + } else if (tag == vmSymbols::tag_runtime_invisible_type_annotations()) { + if (runtime_invisible_type_annotations_exists) { + classfile_parse_error( + "Multiple RuntimeInvisibleTypeAnnotations attributes in class file %s", CHECK); + } else { + runtime_invisible_type_annotations_exists = true; + } + if (PreserveAllAnnotations) { + runtime_invisible_type_annotations_length = attribute_length; + runtime_invisible_type_annotations = cfs->get_u1_buffer(); + assert(runtime_invisible_type_annotations != NULL, "null invisible type annotations"); + } + cfs->skip_u1(attribute_length, CHECK); + } else { + // Unknown attribute + cfs->skip_u1(attribute_length, CHECK); + } + } else { + // Unknown attribute + cfs->skip_u1(attribute_length, CHECK); + } + } + _annotations = assemble_annotations(runtime_visible_annotations, + runtime_visible_annotations_length, + runtime_invisible_annotations, + runtime_invisible_annotations_length, + CHECK); + _type_annotations = assemble_annotations(runtime_visible_type_annotations, + runtime_visible_type_annotations_length, + runtime_invisible_type_annotations, + runtime_invisible_type_annotations_length, + CHECK); + + if (parsed_innerclasses_attribute || parsed_enclosingmethod_attribute) { + u2 num_of_classes = parse_classfile_inner_classes_attribute( + inner_classes_attribute_start, + parsed_innerclasses_attribute, + enclosing_method_class_index, + enclosing_method_method_index, + CHECK); + if (parsed_innerclasses_attribute &&_need_verify && _major_version >= JAVA_1_5_VERSION) { + guarantee_property( + inner_classes_attribute_length == sizeof(num_of_classes) + 4 * sizeof(u2) * num_of_classes, + "Wrong InnerClasses 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); + } +} + +void ClassFileParser::apply_parsed_class_attributes(instanceKlassHandle k) { + if (_synthetic_flag) + k->set_is_synthetic(); + if (_sourcefile_index != 0) { + k->set_source_file_name_index(_sourcefile_index); + } + if (_generic_signature_index != 0) { + k->set_generic_signature_index(_generic_signature_index); + } + if (_sde_buffer != NULL) { + k->set_source_debug_extension(_sde_buffer, _sde_length); + } +} + +// Transfer ownership of metadata allocated to the InstanceKlass. +void ClassFileParser::apply_parsed_class_metadata( + instanceKlassHandle this_klass, + int java_fields_count, TRAPS) { + // Assign annotations if needed + if (_annotations != NULL || _type_annotations != NULL || + _fields_annotations != NULL || _fields_type_annotations != NULL) { + Annotations* annotations = Annotations::allocate(_loader_data, CHECK); + annotations->set_class_annotations(_annotations); + annotations->set_class_type_annotations(_type_annotations); + annotations->set_fields_annotations(_fields_annotations); + annotations->set_fields_type_annotations(_fields_type_annotations); + this_klass->set_annotations(annotations); + } + + _cp->set_pool_holder(this_klass()); + this_klass->set_constants(_cp); + this_klass->set_fields(_fields, java_fields_count); + this_klass->set_methods(_methods); + this_klass->set_inner_classes(_inner_classes); + this_klass->set_local_interfaces(_local_interfaces); + this_klass->set_transitive_interfaces(_transitive_interfaces); + + // Clear out these fields so they don't get deallocated by the destructor + clear_class_metadata(); +} + +AnnotationArray* ClassFileParser::assemble_annotations(u1* runtime_visible_annotations, + int runtime_visible_annotations_length, + u1* runtime_invisible_annotations, + int runtime_invisible_annotations_length, TRAPS) { + AnnotationArray* annotations = NULL; + if (runtime_visible_annotations != NULL || + runtime_invisible_annotations != NULL) { + annotations = MetadataFactory::new_array(_loader_data, + runtime_visible_annotations_length + + runtime_invisible_annotations_length, + CHECK_(annotations)); + if (runtime_visible_annotations != NULL) { + for (int i = 0; i < runtime_visible_annotations_length; i++) { + annotations->at_put(i, runtime_visible_annotations[i]); + } + } + if (runtime_invisible_annotations != NULL) { + for (int i = 0; i < runtime_invisible_annotations_length; i++) { + int append = runtime_visible_annotations_length+i; + annotations->at_put(append, runtime_invisible_annotations[i]); + } + } + } + return annotations; +} + +instanceKlassHandle ClassFileParser::parse_super_class(int super_class_index, + TRAPS) { + instanceKlassHandle super_klass; + if (super_class_index == 0) { + check_property(_class_name == vmSymbols::java_lang_Object(), + "Invalid superclass index %u in class file %s", + super_class_index, + CHECK_NULL); + } else { + check_property(valid_klass_reference_at(super_class_index), + "Invalid superclass index %u in class file %s", + super_class_index, + CHECK_NULL); + // The class name should be legal because it is checked when parsing constant pool. + // However, make sure it is not an array type. + bool is_array = false; + if (_cp->tag_at(super_class_index).is_klass()) { + super_klass = instanceKlassHandle(THREAD, _cp->resolved_klass_at(super_class_index)); + if (_need_verify) + is_array = super_klass->oop_is_array(); + } else if (_need_verify) { + is_array = (_cp->klass_name_at(super_class_index)->byte_at(0) == JVM_SIGNATURE_ARRAY); + } + if (_need_verify) { + guarantee_property(!is_array, + "Bad superclass name in class file %s", CHECK_NULL); + } + } + return super_klass; +} + + +// Values needed for oopmap and InstanceKlass creation +class FieldLayoutInfo : public StackObj { + public: + int* nonstatic_oop_offsets; + unsigned int* nonstatic_oop_counts; + unsigned int nonstatic_oop_map_count; + unsigned int total_oop_map_count; + int instance_size; + int nonstatic_field_size; + int static_field_size; + bool has_nonstatic_fields; +}; + +// Layout fields and fill in FieldLayoutInfo. Could use more refactoring! +void ClassFileParser::layout_fields(Handle class_loader, + FieldAllocationCount* fac, + ClassAnnotationCollector* parsed_annotations, + FieldLayoutInfo* info, + TRAPS) { + + // Field size and offset computation + int nonstatic_field_size = _super_klass() == NULL ? 0 : _super_klass()->nonstatic_field_size(); + int next_static_oop_offset; + int next_static_double_offset; + int next_static_word_offset; + int next_static_short_offset; + int next_static_byte_offset; + int next_nonstatic_oop_offset; + int next_nonstatic_double_offset; + int next_nonstatic_word_offset; + int next_nonstatic_short_offset; + int next_nonstatic_byte_offset; + int first_nonstatic_oop_offset; + int next_nonstatic_field_offset; + int next_nonstatic_padded_offset; + + // Count the contended fields by type. + // + // We ignore static fields, because @Contended is not supported for them. + // The layout code below will also ignore the static fields. + int nonstatic_contended_count = 0; + FieldAllocationCount fac_contended; + for (AllFieldStream fs(_fields, _cp); !fs.done(); fs.next()) { + FieldAllocationType atype = (FieldAllocationType) fs.allocation_type(); + if (fs.is_contended()) { + fac_contended.count[atype]++; + if (!fs.access_flags().is_static()) { + nonstatic_contended_count++; + } + } + } + + + // Calculate the starting byte offsets + next_static_oop_offset = InstanceMirrorKlass::offset_of_static_fields(); + next_static_double_offset = next_static_oop_offset + + ((fac->count[STATIC_OOP]) * heapOopSize); + if ( fac->count[STATIC_DOUBLE] && + (Universe::field_type_should_be_aligned(T_DOUBLE) || + Universe::field_type_should_be_aligned(T_LONG)) ) { + next_static_double_offset = align_size_up(next_static_double_offset, BytesPerLong); + } + + next_static_word_offset = next_static_double_offset + + ((fac->count[STATIC_DOUBLE]) * BytesPerLong); + next_static_short_offset = next_static_word_offset + + ((fac->count[STATIC_WORD]) * BytesPerInt); + next_static_byte_offset = next_static_short_offset + + ((fac->count[STATIC_SHORT]) * BytesPerShort); + + int nonstatic_fields_start = instanceOopDesc::base_offset_in_bytes() + + nonstatic_field_size * heapOopSize; + + next_nonstatic_field_offset = nonstatic_fields_start; + + bool is_contended_class = parsed_annotations->is_contended(); + + // Class is contended, pad before all the fields + if (is_contended_class) { + next_nonstatic_field_offset += ContendedPaddingWidth; + } + + // Compute the non-contended fields count. + // The packing code below relies on these counts to determine if some field + // can be squeezed into the alignment gap. Contended fields are obviously + // exempt from that. + unsigned int nonstatic_double_count = fac->count[NONSTATIC_DOUBLE] - fac_contended.count[NONSTATIC_DOUBLE]; + unsigned int nonstatic_word_count = fac->count[NONSTATIC_WORD] - fac_contended.count[NONSTATIC_WORD]; + unsigned int nonstatic_short_count = fac->count[NONSTATIC_SHORT] - fac_contended.count[NONSTATIC_SHORT]; + unsigned int nonstatic_byte_count = fac->count[NONSTATIC_BYTE] - fac_contended.count[NONSTATIC_BYTE]; + unsigned int nonstatic_oop_count = fac->count[NONSTATIC_OOP] - fac_contended.count[NONSTATIC_OOP]; + + // Total non-static fields count, including every contended field + unsigned int nonstatic_fields_count = fac->count[NONSTATIC_DOUBLE] + fac->count[NONSTATIC_WORD] + + fac->count[NONSTATIC_SHORT] + fac->count[NONSTATIC_BYTE] + + fac->count[NONSTATIC_OOP]; + + bool super_has_nonstatic_fields = + (_super_klass() != NULL && _super_klass->has_nonstatic_fields()); + bool has_nonstatic_fields = super_has_nonstatic_fields || (nonstatic_fields_count != 0); + + + // Prepare list of oops for oop map generation. + // + // "offset" and "count" lists are describing the set of contiguous oop + // regions. offset[i] is the start of the i-th region, which then has + // count[i] oops following. Before we know how many regions are required, + // we pessimistically allocate the maps to fit all the oops into the + // distinct regions. + // + // TODO: We add +1 to always allocate non-zero resource arrays; we need + // to figure out if we still need to do this. + int* nonstatic_oop_offsets; + unsigned int* nonstatic_oop_counts; + unsigned int nonstatic_oop_map_count = 0; + unsigned int max_nonstatic_oop_maps = fac->count[NONSTATIC_OOP] + 1; + + nonstatic_oop_offsets = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, int, max_nonstatic_oop_maps); + nonstatic_oop_counts = NEW_RESOURCE_ARRAY_IN_THREAD( + THREAD, unsigned int, max_nonstatic_oop_maps); + + first_nonstatic_oop_offset = 0; // will be set for first oop field + + bool compact_fields = CompactFields; + int allocation_style = FieldsAllocationStyle; + if( allocation_style < 0 || allocation_style > 2 ) { // Out of range? + assert(false, "0 <= FieldsAllocationStyle <= 2"); + allocation_style = 1; // Optimistic + } + + // The next classes have predefined hard-coded fields offsets + // (see in JavaClasses::compute_hard_coded_offsets()). + // Use default fields allocation order for them. + if( (allocation_style != 0 || compact_fields ) && class_loader.is_null() && + (_class_name == vmSymbols::java_lang_AssertionStatusDirectives() || + _class_name == vmSymbols::java_lang_Class() || + _class_name == vmSymbols::java_lang_ClassLoader() || + _class_name == vmSymbols::java_lang_ref_Reference() || + _class_name == vmSymbols::java_lang_ref_SoftReference() || + _class_name == vmSymbols::java_lang_StackTraceElement() || + _class_name == vmSymbols::java_lang_String() || + _class_name == vmSymbols::java_lang_Throwable() || + _class_name == vmSymbols::java_lang_Boolean() || + _class_name == vmSymbols::java_lang_Character() || + _class_name == vmSymbols::java_lang_Float() || + _class_name == vmSymbols::java_lang_Double() || + _class_name == vmSymbols::java_lang_Byte() || + _class_name == vmSymbols::java_lang_Short() || + _class_name == vmSymbols::java_lang_Integer() || + _class_name == vmSymbols::java_lang_Long())) { + allocation_style = 0; // Allocate oops first + compact_fields = false; // Don't compact fields + } + + // Rearrange fields for a given allocation style + if( allocation_style == 0 ) { + // Fields order: oops, longs/doubles, ints, shorts/chars, bytes, padded fields + next_nonstatic_oop_offset = next_nonstatic_field_offset; + next_nonstatic_double_offset = next_nonstatic_oop_offset + + (nonstatic_oop_count * heapOopSize); + } else if( allocation_style == 1 ) { + // Fields order: longs/doubles, ints, shorts/chars, bytes, oops, padded fields + next_nonstatic_double_offset = next_nonstatic_field_offset; + } else if( allocation_style == 2 ) { + // Fields allocation: oops fields in super and sub classes are together. + if( nonstatic_field_size > 0 && _super_klass() != NULL && + _super_klass->nonstatic_oop_map_size() > 0 ) { + unsigned int map_count = _super_klass->nonstatic_oop_map_count(); + OopMapBlock* first_map = _super_klass->start_of_nonstatic_oop_maps(); + OopMapBlock* last_map = first_map + map_count - 1; + int next_offset = last_map->offset() + (last_map->count() * heapOopSize); + if (next_offset == next_nonstatic_field_offset) { + allocation_style = 0; // allocate oops first + next_nonstatic_oop_offset = next_nonstatic_field_offset; + next_nonstatic_double_offset = next_nonstatic_oop_offset + + (nonstatic_oop_count * heapOopSize); + } + } + if( allocation_style == 2 ) { + allocation_style = 1; // allocate oops last + next_nonstatic_double_offset = next_nonstatic_field_offset; + } + } else { + ShouldNotReachHere(); + } + + int nonstatic_oop_space_count = 0; + int nonstatic_word_space_count = 0; + int nonstatic_short_space_count = 0; + int nonstatic_byte_space_count = 0; + int nonstatic_oop_space_offset; + int nonstatic_word_space_offset; + int nonstatic_short_space_offset; + int nonstatic_byte_space_offset; + + // Try to squeeze some of the fields into the gaps due to + // long/double alignment. + if( nonstatic_double_count > 0 ) { + int offset = next_nonstatic_double_offset; + next_nonstatic_double_offset = align_size_up(offset, BytesPerLong); + if( compact_fields && offset != next_nonstatic_double_offset ) { + // Allocate available fields into the gap before double field. + int length = next_nonstatic_double_offset - offset; + assert(length == BytesPerInt, ""); + nonstatic_word_space_offset = offset; + if( nonstatic_word_count > 0 ) { + nonstatic_word_count -= 1; + nonstatic_word_space_count = 1; // Only one will fit + length -= BytesPerInt; + offset += BytesPerInt; + } + nonstatic_short_space_offset = offset; + while( length >= BytesPerShort && nonstatic_short_count > 0 ) { + nonstatic_short_count -= 1; + nonstatic_short_space_count += 1; + length -= BytesPerShort; + offset += BytesPerShort; + } + nonstatic_byte_space_offset = offset; + while( length > 0 && nonstatic_byte_count > 0 ) { + nonstatic_byte_count -= 1; + nonstatic_byte_space_count += 1; + length -= 1; + } + // Allocate oop field in the gap if there are no other fields for that. + nonstatic_oop_space_offset = offset; + if( length >= heapOopSize && nonstatic_oop_count > 0 && + allocation_style != 0 ) { // when oop fields not first + nonstatic_oop_count -= 1; + nonstatic_oop_space_count = 1; // Only one will fit + length -= heapOopSize; + offset += heapOopSize; + } + } + } + + next_nonstatic_word_offset = next_nonstatic_double_offset + + (nonstatic_double_count * BytesPerLong); + next_nonstatic_short_offset = next_nonstatic_word_offset + + (nonstatic_word_count * BytesPerInt); + next_nonstatic_byte_offset = next_nonstatic_short_offset + + (nonstatic_short_count * BytesPerShort); + next_nonstatic_padded_offset = next_nonstatic_byte_offset + + nonstatic_byte_count; + + // let oops jump before padding with this allocation style + if( allocation_style == 1 ) { + next_nonstatic_oop_offset = next_nonstatic_padded_offset; + if( nonstatic_oop_count > 0 ) { + next_nonstatic_oop_offset = align_size_up(next_nonstatic_oop_offset, heapOopSize); + } + next_nonstatic_padded_offset = next_nonstatic_oop_offset + (nonstatic_oop_count * heapOopSize); + } + + // Iterate over fields again and compute correct offsets. + // The field allocation type was temporarily stored in the offset slot. + // oop fields are located before non-oop fields (static and non-static). + for (AllFieldStream fs(_fields, _cp); !fs.done(); fs.next()) { + + // skip already laid out fields + if (fs.is_offset_set()) continue; + + // contended instance fields are handled below + if (fs.is_contended() && !fs.access_flags().is_static()) continue; + + int real_offset; + FieldAllocationType atype = (FieldAllocationType) fs.allocation_type(); + + // pack the rest of the fields + switch (atype) { + case STATIC_OOP: + real_offset = next_static_oop_offset; + next_static_oop_offset += heapOopSize; + break; + case STATIC_BYTE: + real_offset = next_static_byte_offset; + next_static_byte_offset += 1; + break; + case STATIC_SHORT: + real_offset = next_static_short_offset; + next_static_short_offset += BytesPerShort; + break; + case STATIC_WORD: + real_offset = next_static_word_offset; + next_static_word_offset += BytesPerInt; + break; + case STATIC_DOUBLE: + real_offset = next_static_double_offset; + next_static_double_offset += BytesPerLong; + break; + case NONSTATIC_OOP: + if( nonstatic_oop_space_count > 0 ) { + real_offset = nonstatic_oop_space_offset; + nonstatic_oop_space_offset += heapOopSize; + nonstatic_oop_space_count -= 1; + } else { + real_offset = next_nonstatic_oop_offset; + next_nonstatic_oop_offset += heapOopSize; + } + // Update oop maps + if( nonstatic_oop_map_count > 0 && + nonstatic_oop_offsets[nonstatic_oop_map_count - 1] == + real_offset - + int(nonstatic_oop_counts[nonstatic_oop_map_count - 1]) * + heapOopSize ) { + // Extend current oop map + assert(nonstatic_oop_map_count - 1 < max_nonstatic_oop_maps, "range check"); + nonstatic_oop_counts[nonstatic_oop_map_count - 1] += 1; + } else { + // Create new oop map + assert(nonstatic_oop_map_count < max_nonstatic_oop_maps, "range check"); + nonstatic_oop_offsets[nonstatic_oop_map_count] = real_offset; + nonstatic_oop_counts [nonstatic_oop_map_count] = 1; + nonstatic_oop_map_count += 1; + if( first_nonstatic_oop_offset == 0 ) { // Undefined + first_nonstatic_oop_offset = real_offset; + } + } + break; + case NONSTATIC_BYTE: + if( nonstatic_byte_space_count > 0 ) { + real_offset = nonstatic_byte_space_offset; + nonstatic_byte_space_offset += 1; + nonstatic_byte_space_count -= 1; + } else { + real_offset = next_nonstatic_byte_offset; + next_nonstatic_byte_offset += 1; + } + break; + case NONSTATIC_SHORT: + if( nonstatic_short_space_count > 0 ) { + real_offset = nonstatic_short_space_offset; + nonstatic_short_space_offset += BytesPerShort; + nonstatic_short_space_count -= 1; + } else { + real_offset = next_nonstatic_short_offset; + next_nonstatic_short_offset += BytesPerShort; + } + break; + case NONSTATIC_WORD: + if( nonstatic_word_space_count > 0 ) { + real_offset = nonstatic_word_space_offset; + nonstatic_word_space_offset += BytesPerInt; + nonstatic_word_space_count -= 1; + } else { + real_offset = next_nonstatic_word_offset; + next_nonstatic_word_offset += BytesPerInt; + } + break; + case NONSTATIC_DOUBLE: + real_offset = next_nonstatic_double_offset; + next_nonstatic_double_offset += BytesPerLong; + break; + default: + ShouldNotReachHere(); + } + fs.set_offset(real_offset); + } + + + // Handle the contended cases. + // + // Each contended field should not intersect the cache line with another contended field. + // In the absence of alignment information, we end up with pessimistically separating + // the fields with full-width padding. + // + // Additionally, this should not break alignment for the fields, so we round the alignment up + // for each field. + if (nonstatic_contended_count > 0) { + + // if there is at least one contended field, we need to have pre-padding for them + next_nonstatic_padded_offset += ContendedPaddingWidth; + + // collect all contended groups + BitMap bm(_cp->size()); + for (AllFieldStream fs(_fields, _cp); !fs.done(); fs.next()) { + // skip already laid out fields + if (fs.is_offset_set()) continue; + + if (fs.is_contended()) { + bm.set_bit(fs.contended_group()); + } + } + + int current_group = -1; + while ((current_group = (int)bm.get_next_one_offset(current_group + 1)) != (int)bm.size()) { + + for (AllFieldStream fs(_fields, _cp); !fs.done(); fs.next()) { + + // skip already laid out fields + if (fs.is_offset_set()) continue; + + // skip non-contended fields and fields from different group + if (!fs.is_contended() || (fs.contended_group() != current_group)) continue; + + // handle statics below + if (fs.access_flags().is_static()) continue; + + int real_offset; + FieldAllocationType atype = (FieldAllocationType) fs.allocation_type(); + + switch (atype) { + case NONSTATIC_BYTE: + next_nonstatic_padded_offset = align_size_up(next_nonstatic_padded_offset, 1); + real_offset = next_nonstatic_padded_offset; + next_nonstatic_padded_offset += 1; + break; + + case NONSTATIC_SHORT: + next_nonstatic_padded_offset = align_size_up(next_nonstatic_padded_offset, BytesPerShort); + real_offset = next_nonstatic_padded_offset; + next_nonstatic_padded_offset += BytesPerShort; + break; + + case NONSTATIC_WORD: + next_nonstatic_padded_offset = align_size_up(next_nonstatic_padded_offset, BytesPerInt); + real_offset = next_nonstatic_padded_offset; + next_nonstatic_padded_offset += BytesPerInt; + break; + + case NONSTATIC_DOUBLE: + next_nonstatic_padded_offset = align_size_up(next_nonstatic_padded_offset, BytesPerLong); + real_offset = next_nonstatic_padded_offset; + next_nonstatic_padded_offset += BytesPerLong; + break; + + case NONSTATIC_OOP: + next_nonstatic_padded_offset = align_size_up(next_nonstatic_padded_offset, heapOopSize); + real_offset = next_nonstatic_padded_offset; + next_nonstatic_padded_offset += heapOopSize; + + // Create new oop map + assert(nonstatic_oop_map_count < max_nonstatic_oop_maps, "range check"); + nonstatic_oop_offsets[nonstatic_oop_map_count] = real_offset; + nonstatic_oop_counts [nonstatic_oop_map_count] = 1; + nonstatic_oop_map_count += 1; + if( first_nonstatic_oop_offset == 0 ) { // Undefined + first_nonstatic_oop_offset = real_offset; + } + break; + + default: + ShouldNotReachHere(); + } + + if (fs.contended_group() == 0) { + // Contended group defines the equivalence class over the fields: + // the fields within the same contended group are not inter-padded. + // The only exception is default group, which does not incur the + // equivalence, and so requires intra-padding. + next_nonstatic_padded_offset += ContendedPaddingWidth; + } + + fs.set_offset(real_offset); + } // for + + // Start laying out the next group. + // Note that this will effectively pad the last group in the back; + // this is expected to alleviate memory contention effects for + // subclass fields and/or adjacent object. + // If this was the default group, the padding is already in place. + if (current_group != 0) { + next_nonstatic_padded_offset += ContendedPaddingWidth; + } + } + + // handle static fields + } + + // Entire class is contended, pad in the back. + // This helps to alleviate memory contention effects for subclass fields + // and/or adjacent object. + if (is_contended_class) { + next_nonstatic_padded_offset += ContendedPaddingWidth; + } + + int notaligned_nonstatic_fields_end = next_nonstatic_padded_offset; + + int nonstatic_fields_end = align_size_up(notaligned_nonstatic_fields_end, heapOopSize); + int instance_end = align_size_up(notaligned_nonstatic_fields_end, wordSize); + int static_fields_end = align_size_up(next_static_byte_offset, wordSize); + + int static_field_size = (static_fields_end - + InstanceMirrorKlass::offset_of_static_fields()) / wordSize; + nonstatic_field_size = nonstatic_field_size + + (nonstatic_fields_end - nonstatic_fields_start) / heapOopSize; + + int instance_size = align_object_size(instance_end / wordSize); + + assert(instance_size == align_object_size(align_size_up( + (instanceOopDesc::base_offset_in_bytes() + nonstatic_field_size*heapOopSize), + wordSize) / wordSize), "consistent layout helper value"); + + // Invariant: nonstatic_field end/start should only change if there are + // nonstatic fields in the class, or if the class is contended. We compare + // against the non-aligned value, so that end alignment will not fail the + // assert without actually having the fields. + assert((notaligned_nonstatic_fields_end == nonstatic_fields_start) || + is_contended_class || + (nonstatic_fields_count > 0), "double-check nonstatic start/end"); + + // Number of non-static oop map blocks allocated at end of klass. + const unsigned int total_oop_map_count = + compute_oop_map_count(_super_klass, nonstatic_oop_map_count, + first_nonstatic_oop_offset); + +#ifndef PRODUCT + if (PrintFieldLayout) { + print_field_layout(_class_name, + _fields, + _cp, + instance_size, + nonstatic_fields_start, + nonstatic_fields_end, + static_fields_end); + } + +#endif + // Pass back information needed for InstanceKlass creation + info->nonstatic_oop_offsets = nonstatic_oop_offsets; + info->nonstatic_oop_counts = nonstatic_oop_counts; + info->nonstatic_oop_map_count = nonstatic_oop_map_count; + info->total_oop_map_count = total_oop_map_count; + info->instance_size = instance_size; + info->static_field_size = static_field_size; + info->nonstatic_field_size = nonstatic_field_size; + info->has_nonstatic_fields = has_nonstatic_fields; +} + + +instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, + ClassLoaderData* loader_data, + Handle protection_domain, + KlassHandle host_klass, + GrowableArray* cp_patches, + TempNewSymbol& parsed_name, + bool verify, + TRAPS) { + + // When a retransformable agent is attached, JVMTI caches the + // class bytes that existed before the first retransformation. + // If RedefineClasses() was used before the retransformable + // agent attached, then the cached class bytes may not be the + // original class bytes. + JvmtiCachedClassFileData *cached_class_file = NULL; + Handle class_loader(THREAD, loader_data->class_loader()); + bool has_default_methods = false; + ResourceMark rm(THREAD); + + ClassFileStream* cfs = stream(); + // Timing + assert(THREAD->is_Java_thread(), "must be a JavaThread"); + JavaThread* jt = (JavaThread*) THREAD; + + PerfClassTraceTime ctimer(ClassLoader::perf_class_parse_time(), + ClassLoader::perf_class_parse_selftime(), + NULL, + jt->get_thread_stat()->perf_recursion_counts_addr(), + jt->get_thread_stat()->perf_timers_addr(), + PerfClassTraceTime::PARSE_CLASS); + + init_parsed_class_attributes(loader_data); + + if (JvmtiExport::should_post_class_file_load_hook()) { + // Get the cached class file bytes (if any) from the class that + // is being redefined or retransformed. We use jvmti_thread_state() + // instead of JvmtiThreadState::state_for(jt) so we don't allocate + // a JvmtiThreadState any earlier than necessary. This will help + // avoid the bug described by 7126851. + JvmtiThreadState *state = jt->jvmti_thread_state(); + if (state != NULL) { + KlassHandle *h_class_being_redefined = + state->get_class_being_redefined(); + if (h_class_being_redefined != NULL) { + instanceKlassHandle ikh_class_being_redefined = + instanceKlassHandle(THREAD, (*h_class_being_redefined)()); + cached_class_file = ikh_class_being_redefined->get_cached_class_file(); + } + } + + unsigned char* ptr = cfs->buffer(); + unsigned char* end_ptr = cfs->buffer() + cfs->length(); + + JvmtiExport::post_class_file_load_hook(name, class_loader(), protection_domain, + &ptr, &end_ptr, &cached_class_file); + + if (ptr != cfs->buffer()) { + // JVMTI agent has modified class file data. + // Set new class file stream using JVMTI agent modified + // class file data. + cfs = new ClassFileStream(ptr, end_ptr - ptr, cfs->source()); + set_stream(cfs); + } + } + + _host_klass = host_klass; + _cp_patches = cp_patches; + + instanceKlassHandle nullHandle; + + // Figure out whether we can skip format checking (matching classic VM behavior) + if (DumpSharedSpaces) { + // verify == true means it's a 'remote' class (i.e., non-boot class) + // Verification decision is based on BytecodeVerificationRemote flag + // for those classes. + _need_verify = (verify) ? BytecodeVerificationRemote : + BytecodeVerificationLocal; + } else { + _need_verify = Verifier::should_verify_for(class_loader(), verify); + } + + // Set the verify flag in stream + cfs->set_verify(_need_verify); + + // Save the class file name for easier error message printing. + _class_name = (name != NULL) ? name : vmSymbols::unknown_class_name(); + + cfs->guarantee_more(8, CHECK_(nullHandle)); // magic, major, minor + // Magic value + u4 magic = cfs->get_u4_fast(); + guarantee_property(magic == JAVA_CLASSFILE_MAGIC, + "Incompatible magic value %u in class file %s", + magic, CHECK_(nullHandle)); + + // Version numbers + u2 minor_version = cfs->get_u2_fast(); + u2 major_version = cfs->get_u2_fast(); + + if (DumpSharedSpaces && major_version < JAVA_1_5_VERSION) { + ResourceMark rm; + warning("Pre JDK 1.5 class not supported by CDS: %u.%u %s", + major_version, minor_version, name->as_C_string()); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbols::java_lang_UnsupportedClassVersionError(), + "Unsupported major.minor version for dump time %u.%u", + major_version, + minor_version); + } + + // Check version numbers - we check this even with verifier off + if (!is_supported_version(major_version, minor_version)) { + if (name == NULL) { + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbols::java_lang_UnsupportedClassVersionError(), + "Unsupported class file version %u.%u, " + "this version of the Java Runtime only recognizes class file versions up to %u.%u", + major_version, + minor_version, + JAVA_MAX_SUPPORTED_VERSION, + JAVA_MAX_SUPPORTED_MINOR_VERSION); + } else { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbols::java_lang_UnsupportedClassVersionError(), + "%s has been compiled by a more recent version of the Java Runtime (class file version %u.%u), " + "this version of the Java Runtime only recognizes class file versions up to %u.%u", + name->as_C_string(), + major_version, + minor_version, + JAVA_MAX_SUPPORTED_VERSION, + JAVA_MAX_SUPPORTED_MINOR_VERSION); + } + return nullHandle; + } + + _major_version = major_version; + _minor_version = minor_version; + + + // Check if verification needs to be relaxed for this class file + // Do not restrict it to jdk1.0 or jdk1.1 to maintain backward compatibility (4982376) + _relax_verify = Verifier::relax_verify_for(class_loader()); + + // Constant pool + constantPoolHandle cp = parse_constant_pool(CHECK_(nullHandle)); + + int cp_size = cp->length(); + + cfs->guarantee_more(8, CHECK_(nullHandle)); // flags, this_class, super_class, infs_len + + // Access flags + AccessFlags access_flags; + jint flags = cfs->get_u2_fast() & JVM_RECOGNIZED_CLASS_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_(nullHandle)); + access_flags.set_flags(flags); + + // This class and superclass + u2 this_class_index = cfs->get_u2_fast(); + check_property( + valid_cp_range(this_class_index, cp_size) && + cp->tag_at(this_class_index).is_unresolved_klass(), + "Invalid this class index %u in constant pool in class file %s", + this_class_index, CHECK_(nullHandle)); + + Symbol* class_name = cp->klass_name_at(this_class_index); + assert(class_name != NULL, "class_name can't be null"); + + // It's important to set parsed_name *before* resolving the super class. + // (it's used for cleanup by the caller if parsing fails) + parsed_name = class_name; + // parsed_name is returned and can be used if there's an error, so add to + // its reference count. Caller will decrement the refcount. + parsed_name->increment_refcount(); + + // Update _class_name which could be null previously to be class_name + _class_name = class_name; + + // Don't need to check whether this class name is legal or not. + // It has been checked when constant pool is parsed. + // However, make sure it is not an array type. + if (_need_verify) { + guarantee_property(class_name->byte_at(0) != JVM_SIGNATURE_ARRAY, + "Bad class name in class file %s", + CHECK_(nullHandle)); + } + + Klass* preserve_this_klass; // for storing result across HandleMark + + // release all handles when parsing is done + { HandleMark hm(THREAD); + + // Checks if name in class file matches requested name + if (name != NULL && class_name != name) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbols::java_lang_NoClassDefFoundError(), + "%s (wrong name: %s)", + name->as_C_string(), + class_name->as_C_string() + ); + return nullHandle; + } + + if (TraceClassLoadingPreorder) { + tty->print("[Loading %s", (name != NULL) ? name->as_klass_external_name() : "NoName"); + if (cfs->source() != NULL) tty->print(" from %s", cfs->source()); + tty->print_cr("]"); + } +#if INCLUDE_CDS + if (DumpLoadedClassList != NULL && cfs->source() != NULL && classlist_file->is_open()) { + // Only dump the classes that can be stored into CDS archive + if (SystemDictionaryShared::is_sharing_possible(loader_data)) { + if (name != NULL) { + ResourceMark rm(THREAD); + classlist_file->print_cr("%s", name->as_C_string()); + classlist_file->flush(); + } + } + } +#endif + + u2 super_class_index = cfs->get_u2_fast(); + instanceKlassHandle super_klass = parse_super_class(super_class_index, + CHECK_NULL); + + // Interfaces + u2 itfs_len = cfs->get_u2_fast(); + Array* local_interfaces = + parse_interfaces(itfs_len, protection_domain, _class_name, + &has_default_methods, CHECK_(nullHandle)); + + u2 java_fields_count = 0; + // Fields (offsets are filled in later) + FieldAllocationCount fac; + Array* fields = parse_fields(class_name, + access_flags.is_interface(), + &fac, &java_fields_count, + CHECK_(nullHandle)); + // Methods + bool has_final_method = false; + AccessFlags promoted_flags; + promoted_flags.set_flags(0); + Array* methods = parse_methods(access_flags.is_interface(), + &promoted_flags, + &has_final_method, + &has_default_methods, + CHECK_(nullHandle)); + + // Additional attributes + ClassAnnotationCollector parsed_annotations; + parse_classfile_attributes(&parsed_annotations, CHECK_(nullHandle)); + + // Make sure this is the end of class file stream + guarantee_property(cfs->at_eos(), "Extra bytes at the end of class file %s", CHECK_(nullHandle)); + + // We check super class after class file is parsed and format is checked + if (super_class_index > 0 && super_klass.is_null()) { + Symbol* sk = cp->klass_name_at(super_class_index); + if (access_flags.is_interface()) { + // Before attempting to resolve the superclass, check for class format + // errors not checked yet. + guarantee_property(sk == vmSymbols::java_lang_Object(), + "Interfaces must have java.lang.Object as superclass in class file %s", + CHECK_(nullHandle)); + } + Klass* k = SystemDictionary::resolve_super_or_fail(class_name, sk, + class_loader, + protection_domain, + true, + CHECK_(nullHandle)); + + KlassHandle kh (THREAD, k); + super_klass = instanceKlassHandle(THREAD, kh()); + } + if (super_klass.not_null()) { + + if (super_klass->has_default_methods()) { + has_default_methods = true; + } + + if (super_klass->is_interface()) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbols::java_lang_IncompatibleClassChangeError(), + "class %s has interface %s as super class", + class_name->as_klass_external_name(), + super_klass->external_name() + ); + return nullHandle; + } + // Make sure super class is not final + if (super_klass->is_final()) { + THROW_MSG_(vmSymbols::java_lang_VerifyError(), "Cannot inherit from final class", nullHandle); + } + } + + // save super klass for error handling. + _super_klass = super_klass; + + // Compute the transitive list of all unique interfaces implemented by this class + _transitive_interfaces = + compute_transitive_interfaces(super_klass, local_interfaces, CHECK_(nullHandle)); + + // sort methods + intArray* method_ordering = sort_methods(methods); + + // promote flags from parse_methods() to the klass' flags + access_flags.add_promoted_flags(promoted_flags.as_int()); + + // Size of Java vtable (in words) + int vtable_size = 0; + int itable_size = 0; + int num_miranda_methods = 0; + + GrowableArray all_mirandas(20); + + klassVtable::compute_vtable_size_and_num_mirandas( + &vtable_size, &num_miranda_methods, &all_mirandas, super_klass(), methods, + access_flags, class_loader, class_name, local_interfaces, + CHECK_(nullHandle)); + + // Size of Java itable (in words) + itable_size = access_flags.is_interface() ? 0 : klassItable::compute_itable_size(_transitive_interfaces); + + FieldLayoutInfo info; + layout_fields(class_loader, &fac, &parsed_annotations, &info, CHECK_NULL); + + int total_oop_map_size2 = + InstanceKlass::nonstatic_oop_map_size(info.total_oop_map_count); + + // Compute reference type + ReferenceType rt; + if (super_klass() == NULL) { + rt = REF_NONE; + } else { + rt = super_klass->reference_type(); + } + + // We can now create the basic Klass* for this klass + _klass = InstanceKlass::allocate_instance_klass(loader_data, + vtable_size, + itable_size, + info.static_field_size, + total_oop_map_size2, + rt, + access_flags, + name, + super_klass(), + !host_klass.is_null(), + CHECK_(nullHandle)); + instanceKlassHandle this_klass (THREAD, _klass); + + assert(this_klass->static_field_size() == info.static_field_size, "sanity"); + assert(this_klass->nonstatic_oop_map_count() == info.total_oop_map_count, + "sanity"); + + // Fill in information already parsed + this_klass->set_should_verify_class(verify); + jint lh = Klass::instance_layout_helper(info.instance_size, false); + this_klass->set_layout_helper(lh); + assert(this_klass->oop_is_instance(), "layout is correct"); + assert(this_klass->size_helper() == info.instance_size, "correct size_helper"); + // Not yet: supers are done below to support the new subtype-checking fields + //this_klass->set_super(super_klass()); + this_klass->set_class_loader_data(loader_data); + this_klass->set_nonstatic_field_size(info.nonstatic_field_size); + this_klass->set_has_nonstatic_fields(info.has_nonstatic_fields); + this_klass->set_static_oop_field_count(fac.count[STATIC_OOP]); + + apply_parsed_class_metadata(this_klass, java_fields_count, CHECK_NULL); + + if (has_final_method) { + this_klass->set_has_final_method(); + } + this_klass->copy_method_ordering(method_ordering, CHECK_NULL); + // The InstanceKlass::_methods_jmethod_ids cache + // is managed on the assumption that the initial cache + // size is equal to the number of methods in the class. If + // that changes, then InstanceKlass::idnum_can_increment() + // has to be changed accordingly. + this_klass->set_initial_method_idnum(methods->length()); + this_klass->set_name(cp->klass_name_at(this_class_index)); + if (is_anonymous()) // I am well known to myself + cp->klass_at_put(this_class_index, this_klass()); // eagerly resolve + + this_klass->set_minor_version(minor_version); + this_klass->set_major_version(major_version); + this_klass->set_has_default_methods(has_default_methods); + + if (!host_klass.is_null()) { + assert (this_klass->is_anonymous(), "should be the same"); + this_klass->set_host_klass(host_klass()); + } + + // Set up Method*::intrinsic_id as soon as we know the names of methods. + // (We used to do this lazily, but now we query it in Rewriter, + // which is eagerly done for every method, so we might as well do it now, + // when everything is fresh in memory.) + if (Method::klass_id_for_intrinsics(this_klass()) != vmSymbols::NO_SID) { + for (int j = 0; j < methods->length(); j++) { + methods->at(j)->init_intrinsic_id(); + } + } + + if (cached_class_file != NULL) { + // JVMTI: we have an InstanceKlass now, tell it about the cached bytes + this_klass->set_cached_class_file(cached_class_file); + } + + // Fill in field values obtained by parse_classfile_attributes + if (parsed_annotations.has_any_annotations()) + parsed_annotations.apply_to(this_klass); + apply_parsed_class_attributes(this_klass); + + // Miranda methods + if ((num_miranda_methods > 0) || + // if this class introduced new miranda methods or + (super_klass.not_null() && (super_klass->has_miranda_methods())) + // super class exists and this class inherited miranda methods + ) { + this_klass->set_has_miranda_methods(); // then set a flag + } + + // Fill in information needed to compute superclasses. + this_klass->initialize_supers(super_klass(), CHECK_(nullHandle)); + + // Initialize itable offset tables + klassItable::setup_itable_offset_table(this_klass); + + // Compute transitive closure of interfaces this class implements + // Do final class setup + fill_oop_maps(this_klass, info.nonstatic_oop_map_count, info.nonstatic_oop_offsets, info.nonstatic_oop_counts); + + // Fill in has_finalizer, has_vanilla_constructor, and layout_helper + set_precomputed_flags(this_klass); + + // reinitialize modifiers, using the InnerClasses attribute + int computed_modifiers = this_klass->compute_modifier_flags(CHECK_(nullHandle)); + this_klass->set_modifier_flags(computed_modifiers); + + // check if this class can access its super class + check_super_class_access(this_klass, CHECK_(nullHandle)); + + // check if this class can access its superinterfaces + check_super_interface_access(this_klass, CHECK_(nullHandle)); + + // check if this class overrides any final method + check_final_method_override(this_klass, CHECK_(nullHandle)); + + // check that if this class is an interface then it doesn't have static methods + if (this_klass->is_interface()) { + /* An interface in a JAVA 8 classfile can be static */ + if (_major_version < JAVA_8_VERSION) { + check_illegal_static_method(this_klass, CHECK_(nullHandle)); + } + } + + // Allocate mirror and initialize static fields + java_lang_Class::create_mirror(this_klass, class_loader, protection_domain, + CHECK_(nullHandle)); + + // Generate any default methods - default methods are interface methods + // that have a default implementation. This is new with Lambda project. + if (has_default_methods ) { + DefaultMethods::generate_default_methods( + this_klass(), &all_mirandas, CHECK_(nullHandle)); + } + + // Update the loader_data graph. + record_defined_class_dependencies(this_klass, CHECK_NULL); + + ClassLoadingService::notify_class_loaded(InstanceKlass::cast(this_klass()), + false /* not shared class */); + + if (TraceClassLoading) { + ResourceMark rm; + // print in a single call to reduce interleaving of output + if (cfs->source() != NULL) { + tty->print("[Loaded %s from %s]\n", this_klass->external_name(), + cfs->source()); + } else if (class_loader.is_null()) { + Klass* caller = + THREAD->is_Java_thread() + ? ((JavaThread*)THREAD)->security_get_caller_class(1) + : NULL; + // caller can be NULL, for example, during a JVMTI VM_Init hook + if (caller != NULL) { + tty->print("[Loaded %s by instance of %s]\n", + this_klass->external_name(), + InstanceKlass::cast(caller)->external_name()); + } else { + tty->print("[Loaded %s]\n", this_klass->external_name()); + } + } else { + tty->print("[Loaded %s from %s]\n", this_klass->external_name(), + InstanceKlass::cast(class_loader->klass())->external_name()); + } + } + + if (TraceClassResolution) { + ResourceMark rm; + // print out the superclass. + const char * from = this_klass()->external_name(); + if (this_klass->java_super() != NULL) { + tty->print("RESOLVE %s %s (super)\n", from, InstanceKlass::cast(this_klass->java_super())->external_name()); + } + // print out each of the interface classes referred to by this class. + Array* local_interfaces = this_klass->local_interfaces(); + if (local_interfaces != NULL) { + int length = local_interfaces->length(); + for (int i = 0; i < length; i++) { + Klass* k = local_interfaces->at(i); + InstanceKlass* to_class = InstanceKlass::cast(k); + const char * to = to_class->external_name(); + tty->print("RESOLVE %s %s (interface)\n", from, to); + } + } + } + + // preserve result across HandleMark + preserve_this_klass = this_klass(); + } + + // Create new handle outside HandleMark (might be needed for + // Extended Class Redefinition) + instanceKlassHandle this_klass (THREAD, preserve_this_klass); + debug_only(this_klass->verify();) + + // Clear class if no error has occurred so destructor doesn't deallocate it + _klass = NULL; + return this_klass; +} + +// Destructor to clean up if there's an error +ClassFileParser::~ClassFileParser() { + MetadataFactory::free_metadata(_loader_data, _cp); + MetadataFactory::free_array(_loader_data, _fields); + + // Free methods + InstanceKlass::deallocate_methods(_loader_data, _methods); + + // beware of the Universe::empty_blah_array!! + if (_inner_classes != Universe::the_empty_short_array()) { + MetadataFactory::free_array(_loader_data, _inner_classes); + } + + // Free interfaces + InstanceKlass::deallocate_interfaces(_loader_data, _super_klass(), + _local_interfaces, _transitive_interfaces); + + MetadataFactory::free_array(_loader_data, _annotations); + MetadataFactory::free_array(_loader_data, _type_annotations); + Annotations::free_contents(_loader_data, _fields_annotations); + Annotations::free_contents(_loader_data, _fields_type_annotations); + + clear_class_metadata(); + + // deallocate the klass if already created. Don't directly deallocate, but add + // to the deallocate list so that the klass is removed from the CLD::_klasses list + // at a safepoint. + if (_klass != NULL) { + _loader_data->add_to_deallocate_list(_klass); + } + _klass = NULL; +} + +void ClassFileParser::print_field_layout(Symbol* name, + Array* fields, + constantPoolHandle cp, + int instance_size, + int instance_fields_start, + int instance_fields_end, + int static_fields_end) { + tty->print("%s: field layout\n", name->as_klass_external_name()); + tty->print(" @%3d %s\n", instance_fields_start, "--- instance fields start ---"); + for (AllFieldStream fs(fields, cp); !fs.done(); fs.next()) { + if (!fs.access_flags().is_static()) { + tty->print(" @%3d \"%s\" %s\n", + fs.offset(), + fs.name()->as_klass_external_name(), + fs.signature()->as_klass_external_name()); + } + } + tty->print(" @%3d %s\n", instance_fields_end, "--- instance fields end ---"); + tty->print(" @%3d %s\n", instance_size * wordSize, "--- instance ends ---"); + tty->print(" @%3d %s\n", InstanceMirrorKlass::offset_of_static_fields(), "--- static fields start ---"); + for (AllFieldStream fs(fields, cp); !fs.done(); fs.next()) { + if (fs.access_flags().is_static()) { + tty->print(" @%3d \"%s\" %s\n", + fs.offset(), + fs.name()->as_klass_external_name(), + fs.signature()->as_klass_external_name()); + } + } + tty->print(" @%3d %s\n", static_fields_end, "--- static fields end ---"); + tty->print("\n"); +} + +unsigned int +ClassFileParser::compute_oop_map_count(instanceKlassHandle super, + unsigned int nonstatic_oop_map_count, + int first_nonstatic_oop_offset) { + unsigned int map_count = + super.is_null() ? 0 : super->nonstatic_oop_map_count(); + if (nonstatic_oop_map_count > 0) { + // We have oops to add to map + if (map_count == 0) { + map_count = nonstatic_oop_map_count; + } else { + // Check whether we should add a new map block or whether the last one can + // be extended + OopMapBlock* const first_map = super->start_of_nonstatic_oop_maps(); + OopMapBlock* const last_map = first_map + map_count - 1; + + int next_offset = last_map->offset() + last_map->count() * heapOopSize; + if (next_offset == first_nonstatic_oop_offset) { + // There is no gap bettwen superklass's last oop field and first + // local oop field, merge maps. + nonstatic_oop_map_count -= 1; + } else { + // Superklass didn't end with a oop field, add extra maps + assert(next_offset < first_nonstatic_oop_offset, "just checking"); + } + map_count += nonstatic_oop_map_count; + } + } + return map_count; +} + + +void ClassFileParser::fill_oop_maps(instanceKlassHandle k, + unsigned int nonstatic_oop_map_count, + int* nonstatic_oop_offsets, + unsigned int* nonstatic_oop_counts) { + OopMapBlock* this_oop_map = k->start_of_nonstatic_oop_maps(); + const InstanceKlass* const super = k->superklass(); + const unsigned int super_count = super ? super->nonstatic_oop_map_count() : 0; + if (super_count > 0) { + // Copy maps from superklass + OopMapBlock* super_oop_map = super->start_of_nonstatic_oop_maps(); + for (unsigned int i = 0; i < super_count; ++i) { + *this_oop_map++ = *super_oop_map++; + } + } + + if (nonstatic_oop_map_count > 0) { + if (super_count + nonstatic_oop_map_count > k->nonstatic_oop_map_count()) { + // The counts differ because there is no gap between superklass's last oop + // field and the first local oop field. Extend the last oop map copied + // from the superklass instead of creating new one. + nonstatic_oop_map_count--; + nonstatic_oop_offsets++; + this_oop_map--; + this_oop_map->set_count(this_oop_map->count() + *nonstatic_oop_counts++); + this_oop_map++; + } + + // Add new map blocks, fill them + while (nonstatic_oop_map_count-- > 0) { + this_oop_map->set_offset(*nonstatic_oop_offsets++); + this_oop_map->set_count(*nonstatic_oop_counts++); + this_oop_map++; + } + assert(k->start_of_nonstatic_oop_maps() + k->nonstatic_oop_map_count() == + this_oop_map, "sanity"); + } +} + + +void ClassFileParser::set_precomputed_flags(instanceKlassHandle k) { + Klass* super = k->super(); + + // Check if this klass has an empty finalize method (i.e. one with return bytecode only), + // in which case we don't have to register objects as finalizable + if (!_has_empty_finalizer) { + if (_has_finalizer || + (super != NULL && super->has_finalizer())) { + k->set_has_finalizer(); + } + } + +#ifdef ASSERT + bool f = false; + Method* m = k->lookup_method(vmSymbols::finalize_method_name(), + vmSymbols::void_method_signature()); + if (m != NULL && !m->is_empty_method()) { + f = true; + } + + // Spec doesn't prevent agent from redefinition of empty finalizer. + // Despite the fact that it's generally bad idea and redefined finalizer + // will not work as expected we shouldn't abort vm in this case + if (!k->has_redefined_this_or_super()) { + assert(f == k->has_finalizer(), "inconsistent has_finalizer"); + } +#endif + + // Check if this klass supports the java.lang.Cloneable interface + if (SystemDictionary::Cloneable_klass_loaded()) { + if (k->is_subtype_of(SystemDictionary::Cloneable_klass())) { + k->set_is_cloneable(); + } + } + + // Check if this klass has a vanilla default constructor + if (super == NULL) { + // java.lang.Object has empty default constructor + k->set_has_vanilla_constructor(); + } else { + if (super->has_vanilla_constructor() && + _has_vanilla_constructor) { + k->set_has_vanilla_constructor(); + } +#ifdef ASSERT + bool v = false; + if (super->has_vanilla_constructor()) { + Method* constructor = k->find_method(vmSymbols::object_initializer_name( +), vmSymbols::void_method_signature()); + if (constructor != NULL && constructor->is_vanilla_constructor()) { + v = true; + } + } + assert(v == k->has_vanilla_constructor(), "inconsistent has_vanilla_constructor"); +#endif + } + + // If it cannot be fast-path allocated, set a bit in the layout helper. + // See documentation of InstanceKlass::can_be_fastpath_allocated(). + assert(k->size_helper() > 0, "layout_helper is initialized"); + if ((!RegisterFinalizersAtInit && k->has_finalizer()) + || k->is_abstract() || k->is_interface() + || (k->name() == vmSymbols::java_lang_Class() && k->class_loader() == NULL) + || k->size_helper() >= FastAllocateSizeLimit) { + // Forbid fast-path allocation. + jint lh = Klass::instance_layout_helper(k->size_helper(), true); + k->set_layout_helper(lh); + } +} + +// Attach super classes and interface classes to class loader data +void ClassFileParser::record_defined_class_dependencies(instanceKlassHandle defined_klass, TRAPS) { + ClassLoaderData * defining_loader_data = defined_klass->class_loader_data(); + if (defining_loader_data->is_the_null_class_loader_data()) { + // Dependencies to null class loader data are implicit. + return; + } else { + // add super class dependency + Klass* super = defined_klass->super(); + if (super != NULL) { + defining_loader_data->record_dependency(super, CHECK); + } + + // add super interface dependencies + Array* local_interfaces = defined_klass->local_interfaces(); + if (local_interfaces != NULL) { + int length = local_interfaces->length(); + for (int i = 0; i < length; i++) { + defining_loader_data->record_dependency(local_interfaces->at(i), CHECK); + } + } + } +} + +// utility methods for appending an array with check for duplicates + +void append_interfaces(GrowableArray* result, Array* ifs) { + // iterate over new interfaces + for (int i = 0; i < ifs->length(); i++) { + Klass* e = ifs->at(i); + assert(e->is_klass() && InstanceKlass::cast(e)->is_interface(), "just checking"); + // add new interface + result->append_if_missing(e); + } +} + +Array* ClassFileParser::compute_transitive_interfaces( + instanceKlassHandle super, + Array* local_ifs, TRAPS) { + // Compute maximum size for transitive interfaces + int max_transitive_size = 0; + int super_size = 0; + // Add superclass transitive interfaces size + if (super.not_null()) { + super_size = super->transitive_interfaces()->length(); + max_transitive_size += super_size; + } + // Add local interfaces' super interfaces + int local_size = local_ifs->length(); + for (int i = 0; i < local_size; i++) { + Klass* l = local_ifs->at(i); + max_transitive_size += InstanceKlass::cast(l)->transitive_interfaces()->length(); + } + // Finally add local interfaces + max_transitive_size += local_size; + // Construct array + if (max_transitive_size == 0) { + // no interfaces, use canonicalized array + return Universe::the_empty_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; + } else { + ResourceMark rm; + GrowableArray* result = new GrowableArray(max_transitive_size); + + // Copy down from superclass + if (super.not_null()) { + append_interfaces(result, super->transitive_interfaces()); + } + + // Copy down from local interfaces' superinterfaces + for (int i = 0; i < local_ifs->length(); i++) { + Klass* l = local_ifs->at(i); + append_interfaces(result, InstanceKlass::cast(l)->transitive_interfaces()); + } + // Finally add local interfaces + append_interfaces(result, local_ifs); + + // length will be less than the max_transitive_size if duplicates were removed + int length = result->length(); + assert(length <= max_transitive_size, "just checking"); + Array* new_result = MetadataFactory::new_array(_loader_data, length, CHECK_NULL); + for (int i = 0; i < length; i++) { + Klass* e = result->at(i); + assert(e != NULL, "just checking"); + new_result->at_put(i, e); + } + return new_result; + } +} + +void ClassFileParser::check_super_class_access(instanceKlassHandle this_klass, TRAPS) { + Klass* super = this_klass->super(); + if ((super != NULL) && + (!Reflection::verify_class_access(this_klass(), super, false))) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbols::java_lang_IllegalAccessError(), + "class %s cannot access its superclass %s", + this_klass->external_name(), + InstanceKlass::cast(super)->external_name() + ); + return; + } +} + + +void ClassFileParser::check_super_interface_access(instanceKlassHandle this_klass, TRAPS) { + Array* local_interfaces = this_klass->local_interfaces(); + int lng = local_interfaces->length(); + for (int i = lng - 1; i >= 0; i--) { + Klass* k = local_interfaces->at(i); + assert (k != NULL && k->is_interface(), "invalid interface"); + if (!Reflection::verify_class_access(this_klass(), k, false)) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbols::java_lang_IllegalAccessError(), + "class %s cannot access its superinterface %s", + this_klass->external_name(), + InstanceKlass::cast(k)->external_name() + ); + return; + } + } +} + + +void ClassFileParser::check_final_method_override(instanceKlassHandle this_klass, TRAPS) { + Array* methods = this_klass->methods(); + int num_methods = methods->length(); + + // go thru each method and check if it overrides a final method + for (int index = 0; index < num_methods; index++) { + Method* m = methods->at(index); + + // skip private, static, and methods + if ((!m->is_private() && !m->is_static()) && + (m->name() != vmSymbols::object_initializer_name())) { + + Symbol* name = m->name(); + Symbol* signature = m->signature(); + Klass* k = this_klass->super(); + Method* super_m = NULL; + while (k != NULL) { + // skip supers that don't have final methods. + if (k->has_final_method()) { + // lookup a matching method in the super class hierarchy + super_m = InstanceKlass::cast(k)->lookup_method(name, signature); + if (super_m == NULL) { + break; // didn't find any match; get out + } + + if (super_m->is_final() && !super_m->is_static() && + // matching method in super is final, and not static + (Reflection::verify_field_access(this_klass(), + super_m->method_holder(), + super_m->method_holder(), + super_m->access_flags(), false)) + // this class can access super final method and therefore override + ) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbols::java_lang_VerifyError(), + "class %s overrides final method %s.%s%s", + this_klass->external_name(), + super_m->method_holder()->external_name(), + name->as_C_string(), + signature->as_C_string() + ); + return; + } + + // continue to look from super_m's holder's super. + k = super_m->method_holder()->super(); + continue; + } + + k = k->super(); + } + } + } +} + + +// assumes that this_klass is an interface +void ClassFileParser::check_illegal_static_method(instanceKlassHandle this_klass, TRAPS) { + assert(this_klass->is_interface(), "not an interface"); + Array* methods = this_klass->methods(); + int num_methods = methods->length(); + + for (int index = 0; index < num_methods; index++) { + Method* m = methods->at(index); + // if m is static and not the init method, throw a verify error + if ((m->is_static()) && (m->name() != vmSymbols::class_initializer_name())) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbols::java_lang_VerifyError(), + "Illegal static method %s in interface %s", + m->name()->as_C_string(), + this_klass->external_name() + ); + return; + } + } +} + +// utility methods for format checking + +void ClassFileParser::verify_legal_class_modifiers(jint flags, TRAPS) { + if (!_need_verify) { return; } + + const bool is_interface = (flags & JVM_ACC_INTERFACE) != 0; + const bool is_abstract = (flags & JVM_ACC_ABSTRACT) != 0; + const bool is_final = (flags & JVM_ACC_FINAL) != 0; + const bool is_super = (flags & JVM_ACC_SUPER) != 0; + const bool is_enum = (flags & JVM_ACC_ENUM) != 0; + const bool is_annotation = (flags & JVM_ACC_ANNOTATION) != 0; + const bool major_gte_15 = _major_version >= JAVA_1_5_VERSION; + + if ((is_abstract && is_final) || + (is_interface && !is_abstract) || + (is_interface && major_gte_15 && (is_super || is_enum)) || + (!is_interface && major_gte_15 && is_annotation)) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbols::java_lang_ClassFormatError(), + "Illegal class modifiers in class %s: 0x%X", + _class_name->as_C_string(), flags + ); + return; + } +} + +bool ClassFileParser::has_illegal_visibility(jint flags) { + const bool is_public = (flags & JVM_ACC_PUBLIC) != 0; + const bool is_protected = (flags & JVM_ACC_PROTECTED) != 0; + const bool is_private = (flags & JVM_ACC_PRIVATE) != 0; + + return ((is_public && is_protected) || + (is_public && is_private) || + (is_protected && is_private)); +} + +bool ClassFileParser::is_supported_version(u2 major, u2 minor) { + u2 max_version = JAVA_MAX_SUPPORTED_VERSION; + return (major >= JAVA_MIN_SUPPORTED_VERSION) && + (major <= max_version) && + ((major != max_version) || + (minor <= JAVA_MAX_SUPPORTED_MINOR_VERSION)); +} + +void ClassFileParser::verify_legal_field_modifiers( + jint flags, bool is_interface, TRAPS) { + if (!_need_verify) { return; } + + const bool is_public = (flags & JVM_ACC_PUBLIC) != 0; + const bool is_protected = (flags & JVM_ACC_PROTECTED) != 0; + const bool is_private = (flags & JVM_ACC_PRIVATE) != 0; + const bool is_static = (flags & JVM_ACC_STATIC) != 0; + const bool is_final = (flags & JVM_ACC_FINAL) != 0; + const bool is_volatile = (flags & JVM_ACC_VOLATILE) != 0; + const bool is_transient = (flags & JVM_ACC_TRANSIENT) != 0; + const bool is_enum = (flags & JVM_ACC_ENUM) != 0; + const bool major_gte_15 = _major_version >= JAVA_1_5_VERSION; + + bool is_illegal = false; + + if (is_interface) { + if (!is_public || !is_static || !is_final || is_private || + is_protected || is_volatile || is_transient || + (major_gte_15 && is_enum)) { + is_illegal = true; + } + } else { // not interface + if (has_illegal_visibility(flags) || (is_final && is_volatile)) { + is_illegal = true; + } + } + + if (is_illegal) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbols::java_lang_ClassFormatError(), + "Illegal field modifiers in class %s: 0x%X", + _class_name->as_C_string(), flags); + return; + } +} + +void ClassFileParser::verify_legal_method_modifiers( + jint flags, bool is_interface, Symbol* name, TRAPS) { + if (!_need_verify) { return; } + + const bool is_public = (flags & JVM_ACC_PUBLIC) != 0; + const bool is_private = (flags & JVM_ACC_PRIVATE) != 0; + const bool is_static = (flags & JVM_ACC_STATIC) != 0; + const bool is_final = (flags & JVM_ACC_FINAL) != 0; + const bool is_native = (flags & JVM_ACC_NATIVE) != 0; + const bool is_abstract = (flags & JVM_ACC_ABSTRACT) != 0; + const bool is_bridge = (flags & JVM_ACC_BRIDGE) != 0; + const bool is_strict = (flags & JVM_ACC_STRICT) != 0; + const bool is_synchronized = (flags & JVM_ACC_SYNCHRONIZED) != 0; + const bool is_protected = (flags & JVM_ACC_PROTECTED) != 0; + const bool major_gte_15 = _major_version >= JAVA_1_5_VERSION; + const bool major_gte_8 = _major_version >= JAVA_8_VERSION; + const bool is_initializer = (name == vmSymbols::object_initializer_name()); + + bool is_illegal = false; + + 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 + // have exactly one of the ACC_PUBLIC or ACC_PRIVATE flags set. + if ((is_public == is_private) || /* Only one of private and public should be true - XNOR */ + (is_native || is_protected || is_final || is_synchronized) || + // If a specific method of a class or interface has its + // ACC_ABSTRACT flag set, it must not have any of its + // ACC_FINAL, ACC_NATIVE, ACC_PRIVATE, ACC_STATIC, + // ACC_STRICT, or ACC_SYNCHRONIZED flags set. No need to + // check for ACC_FINAL, ACC_NATIVE or ACC_SYNCHRONIZED as + // those flags are illegal irrespective of ACC_ABSTRACT being set or not. + (is_abstract && (is_private || is_static || is_strict))) { + is_illegal = true; + } + } else if (major_gte_15) { + // Class file version in the interval [JAVA_1_5_VERSION, JAVA_8_VERSION) + if (!is_public || is_static || is_final || is_synchronized || + is_native || !is_abstract || is_strict) { + is_illegal = true; + } + } else { + // Class file version is pre-JAVA_1_5_VERSION + if (!is_public || is_static || is_final || is_native || !is_abstract) { + is_illegal = true; + } + } + } else { // not interface + if (is_initializer) { + if (is_static || is_final || is_synchronized || is_native || + is_abstract || (major_gte_15 && is_bridge)) { + is_illegal = true; + } + } else { // not initializer + if (is_abstract) { + if ((is_final || is_native || is_private || is_static || + (major_gte_15 && (is_synchronized || is_strict)))) { + is_illegal = true; + } + } + if (has_illegal_visibility(flags)) { + 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); + return; + } +} + +void ClassFileParser::verify_legal_utf8(const unsigned char* buffer, int length, TRAPS) { + assert(_need_verify, "only called when _need_verify is true"); + int i = 0; + int count = length >> 2; + for (int k=0; k= 128 (highest bit 1) for v == 0 or v >= 128. + unsigned char res = b0 | b0 - 1 | + b1 | b1 - 1 | + b2 | b2 - 1 | + b3 | b3 - 1; + if (res >= 128) break; + i += 4; + } + for(; i < length; i++) { + unsigned short c; + // no embedded zeros + guarantee_property((buffer[i] != 0), "Illegal UTF8 string in constant pool in class file %s", CHECK); + if(buffer[i] < 128) { + continue; + } + if ((i + 5) < length) { // see if it's legal supplementary character + if (UTF8::is_supplementary_character(&buffer[i])) { + c = UTF8::get_supplementary_character(&buffer[i]); + i += 5; + continue; + } + } + switch (buffer[i] >> 4) { + default: break; + case 0x8: case 0x9: case 0xA: case 0xB: case 0xF: + classfile_parse_error("Illegal UTF8 string in constant pool in class file %s", CHECK); + case 0xC: case 0xD: // 110xxxxx 10xxxxxx + c = (buffer[i] & 0x1F) << 6; + i++; + if ((i < length) && ((buffer[i] & 0xC0) == 0x80)) { + c += buffer[i] & 0x3F; + if (_major_version <= 47 || c == 0 || c >= 0x80) { + // for classes with major > 47, c must a null or a character in its shortest form + break; + } + } + classfile_parse_error("Illegal UTF8 string in constant pool in class file %s", CHECK); + case 0xE: // 1110xxxx 10xxxxxx 10xxxxxx + c = (buffer[i] & 0xF) << 12; + i += 2; + if ((i < length) && ((buffer[i-1] & 0xC0) == 0x80) && ((buffer[i] & 0xC0) == 0x80)) { + c += ((buffer[i-1] & 0x3F) << 6) + (buffer[i] & 0x3F); + if (_major_version <= 47 || c >= 0x800) { + // for classes with major > 47, c must be in its shortest form + break; + } + } + classfile_parse_error("Illegal UTF8 string in constant pool in class file %s", CHECK); + } // end of switch + } // end of for +} + +// Checks if name is a legal class name. +void ClassFileParser::verify_legal_class_name(Symbol* name, TRAPS) { + if (!_need_verify || _relax_verify) { return; } + + char buf[fixed_buffer_size]; + char* bytes = name->as_utf8_flexible_buffer(THREAD, buf, fixed_buffer_size); + unsigned int length = name->utf8_length(); + bool legal = false; + + if (length > 0) { + char* p; + if (bytes[0] == JVM_SIGNATURE_ARRAY) { + p = skip_over_field_signature(bytes, false, length, CHECK); + legal = (p != NULL) && ((p - bytes) == (int)length); + } else if (_major_version < JAVA_1_5_VERSION) { + if (bytes[0] != '<') { + p = skip_over_field_name(bytes, true, length); + legal = (p != NULL) && ((p - bytes) == (int)length); + } + } 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. + legal = verify_unqualified_name(bytes, length, LegalClass); + } + } + if (!legal) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbols::java_lang_ClassFormatError(), + "Illegal class name \"%s\" in class file %s", bytes, + _class_name->as_C_string() + ); + return; + } +} + +// Checks if name is a legal field name. +void ClassFileParser::verify_legal_field_name(Symbol* name, TRAPS) { + if (!_need_verify || _relax_verify) { return; } + + char buf[fixed_buffer_size]; + char* bytes = name->as_utf8_flexible_buffer(THREAD, buf, fixed_buffer_size); + unsigned int length = name->utf8_length(); + bool legal = false; + + if (length > 0) { + if (_major_version < JAVA_1_5_VERSION) { + if (bytes[0] != '<') { + char* p = skip_over_field_name(bytes, false, length); + legal = (p != NULL) && ((p - bytes) == (int)length); + } + } else { + // 4881221: relax the constraints based on JSR202 spec + legal = verify_unqualified_name(bytes, length, LegalField); + } + } + + if (!legal) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbols::java_lang_ClassFormatError(), + "Illegal field name \"%s\" in class %s", bytes, + _class_name->as_C_string() + ); + return; + } +} + +// Checks if name is a legal method name. +void ClassFileParser::verify_legal_method_name(Symbol* name, TRAPS) { + if (!_need_verify || _relax_verify) { return; } + + assert(name != NULL, "method name is null"); + char buf[fixed_buffer_size]; + char* bytes = name->as_utf8_flexible_buffer(THREAD, buf, fixed_buffer_size); + unsigned int length = name->utf8_length(); + bool legal = false; + + if (length > 0) { + if (bytes[0] == '<') { + if (name == vmSymbols::object_initializer_name() || name == vmSymbols::class_initializer_name()) { + legal = true; + } + } else if (_major_version < JAVA_1_5_VERSION) { + char* p; + p = skip_over_field_name(bytes, false, length); + legal = (p != NULL) && ((p - bytes) == (int)length); + } else { + // 4881221: relax the constraints based on JSR202 spec + legal = verify_unqualified_name(bytes, length, LegalMethod); + } + } + + if (!legal) { + ResourceMark rm(THREAD); + Exceptions::fthrow( + THREAD_AND_LOCATION, + vmSymbols::java_lang_ClassFormatError(), + "Illegal method name \"%s\" in class %s", bytes, + _class_name->as_C_string() + ); + return; + } +} + + +// Checks if signature is a legal field signature. +void ClassFileParser::verify_legal_field_signature(Symbol* name, Symbol* signature, TRAPS) { + if (!_need_verify) { return; } + + char buf[fixed_buffer_size]; + char* bytes = signature->as_utf8_flexible_buffer(THREAD, buf, fixed_buffer_size); + unsigned int length = signature->utf8_length(); + char* p = skip_over_field_signature(bytes, false, length, CHECK); + + if (p == NULL || (p - bytes) != (int)length) { + throwIllegalSignature("Field", name, signature, CHECK); + } +} + +// Checks if signature is a legal method signature. +// Returns number of parameters +int ClassFileParser::verify_legal_method_signature(Symbol* name, Symbol* signature, TRAPS) { + if (!_need_verify) { + // make sure caller's args_size will be less than 0 even for non-static + // method so it will be recomputed in compute_size_of_parameters(). + return -2; + } + + unsigned int args_size = 0; + char buf[fixed_buffer_size]; + char* p = signature->as_utf8_flexible_buffer(THREAD, buf, fixed_buffer_size); + unsigned int length = signature->utf8_length(); + char* nextp; + + // The first character must be a '(' + if ((length > 0) && (*p++ == JVM_SIGNATURE_FUNC)) { + length--; + // Skip over legal field signatures + nextp = skip_over_field_signature(p, false, length, CHECK_0); + while ((length > 0) && (nextp != NULL)) { + args_size++; + if (p[0] == 'J' || p[0] == 'D') { + args_size++; + } + length -= nextp - p; + p = nextp; + nextp = skip_over_field_signature(p, false, length, CHECK_0); + } + // The first non-signature thing better be a ')' + if ((length > 0) && (*p++ == JVM_SIGNATURE_ENDFUNC)) { + length--; + if (name->utf8_length() > 0 && name->byte_at(0) == '<') { + // All internal methods must return void + if ((length == 1) && (p[0] == JVM_SIGNATURE_VOID)) { + return args_size; + } + } else { + // Now we better just have a return value + nextp = skip_over_field_signature(p, true, length, CHECK_0); + if (nextp && ((int)length == (nextp - p))) { + return args_size; + } + } + } + } + // Report error + throwIllegalSignature("Method", name, signature, CHECK_0); + return 0; +} + + +// Unqualified names may not contain the characters '.', ';', '[', or '/'. +// Method names also may not contain the characters '<' or '>', unless +// or . Note that method names may not be or in this +// method. Because these names have been checked as special cases before +// calling this method in verify_legal_method_name. +bool ClassFileParser::verify_unqualified_name( + char* name, unsigned int length, int type) { + jchar ch; + + for (char* p = name; p != name + length; ) { + ch = *p; + if (ch < 128) { + p++; + if (ch == '.' || ch == ';' || ch == '[' ) { + return false; // do not permit '.', ';', or '[' + } + if (type != LegalClass && ch == '/') { + return false; // do not permit '/' unless it's class name + } + if (type == LegalMethod && (ch == '<' || ch == '>')) { + return false; // do not permit '<' or '>' in method names + } + } else { + char* tmp_p = UTF8::next(p, &ch); + p = tmp_p; + } + } + return true; +} + + +// Take pointer to a string. Skip over the longest part of the string that could +// be taken as a fieldname. Allow '/' if slash_ok is true. +// Return a pointer to just past the fieldname. +// Return NULL if no fieldname at all was found, or in the case of slash_ok +// being true, we saw consecutive slashes (meaning we were looking for a +// qualified path but found something that was badly-formed). +char* ClassFileParser::skip_over_field_name(char* name, bool slash_ok, unsigned int length) { + char* p; + jchar ch; + jboolean last_is_slash = false; + jboolean not_first_ch = false; + + for (p = name; p != name + length; not_first_ch = true) { + char* old_p = p; + ch = *p; + if (ch < 128) { + p++; + // quick check for ascii + if ((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch == '_' || ch == '$') || + (not_first_ch && ch >= '0' && ch <= '9')) { + last_is_slash = false; + continue; + } + if (slash_ok && ch == '/') { + if (last_is_slash) { + return NULL; // Don't permit consecutive slashes + } + last_is_slash = true; + continue; + } + } else { + jint unicode_ch; + char* tmp_p = UTF8::next_character(p, &unicode_ch); + p = tmp_p; + last_is_slash = false; + // Check if ch is Java identifier start or is Java identifier part + // 4672820: call java.lang.Character methods directly without generating separate tables. + EXCEPTION_MARK; + instanceKlassHandle klass (THREAD, SystemDictionary::Character_klass()); + + // return value + JavaValue result(T_BOOLEAN); + // Set up the arguments to isJavaIdentifierStart and isJavaIdentifierPart + JavaCallArguments args; + args.push_int(unicode_ch); + + // public static boolean isJavaIdentifierStart(char ch); + JavaCalls::call_static(&result, + klass, + vmSymbols::isJavaIdentifierStart_name(), + vmSymbols::int_bool_signature(), + &args, + THREAD); + + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return 0; + } + if (result.get_jboolean()) { + continue; + } + + if (not_first_ch) { + // public static boolean isJavaIdentifierPart(char ch); + JavaCalls::call_static(&result, + klass, + vmSymbols::isJavaIdentifierPart_name(), + vmSymbols::int_bool_signature(), + &args, + THREAD); + + if (HAS_PENDING_EXCEPTION) { + CLEAR_PENDING_EXCEPTION; + return 0; + } + + if (result.get_jboolean()) { + continue; + } + } + } + return (not_first_ch) ? old_p : NULL; + } + return (not_first_ch) ? p : NULL; +} + + +// Take pointer to a string. Skip over the longest part of the string that could +// be taken as a field signature. Allow "void" if void_ok. +// Return a pointer to just past the signature. +// Return NULL if no legal signature is found. +char* ClassFileParser::skip_over_field_signature(char* signature, + bool void_ok, + unsigned int length, + TRAPS) { + unsigned int array_dim = 0; + while (length > 0) { + switch (signature[0]) { + case JVM_SIGNATURE_VOID: if (!void_ok) { return NULL; } + case JVM_SIGNATURE_BOOLEAN: + case JVM_SIGNATURE_BYTE: + case JVM_SIGNATURE_CHAR: + case JVM_SIGNATURE_SHORT: + case JVM_SIGNATURE_INT: + case JVM_SIGNATURE_FLOAT: + case JVM_SIGNATURE_LONG: + case JVM_SIGNATURE_DOUBLE: + return signature + 1; + case JVM_SIGNATURE_CLASS: { + if (_major_version < JAVA_1_5_VERSION) { + // Skip over the class name if one is there + char* p = skip_over_field_name(signature + 1, true, --length); + + // The next character better be a semicolon + if (p && (p - signature) > 1 && p[0] == ';') { + return p + 1; + } + } else { + // 4900761: For class version > 48, any unicode is allowed in class name. + length--; + signature++; + while (length > 0 && signature[0] != ';') { + if (signature[0] == '.') { + classfile_parse_error("Class name contains illegal character '.' in descriptor in class file %s", CHECK_0); + } + length--; + signature++; + } + if (signature[0] == ';') { return signature + 1; } + } + + return NULL; + } + case JVM_SIGNATURE_ARRAY: + array_dim++; + if (array_dim > 255) { + // 4277370: array descriptor is valid only if it represents 255 or fewer dimensions. + classfile_parse_error("Array type descriptor has more than 255 dimensions in class file %s", CHECK_0); + } + // The rest of what's there better be a legal signature + signature++; + length--; + void_ok = false; + break; + + default: + return NULL; + } + } + return NULL; +} diff --git a/hotspot/src/share/vm/classfile/classFileParser.hpp b/hotspot/src/share/vm/classfile/classFileParser.hpp index 6ce366a21f8..b2efa8ceba3 100644 --- a/hotspot/src/share/vm/classfile/classFileParser.hpp +++ b/hotspot/src/share/vm/classfile/classFileParser.hpp @@ -247,7 +247,7 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC { Array* parse_methods(bool is_interface, AccessFlags* promoted_flags, bool* has_final_method, - bool* has_default_method, + bool* declares_default_methods, TRAPS); intArray* sort_methods(Array* methods); diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp index 8ea08cd54c7..1ef55c6875d 100644 --- a/hotspot/src/share/vm/oops/instanceKlass.cpp +++ b/hotspot/src/share/vm/oops/instanceKlass.cpp @@ -736,6 +736,41 @@ void InstanceKlass::link_methods(TRAPS) { } } +// Eagerly initialize superinterfaces that declare default methods (concrete instance: any access) +void InstanceKlass::initialize_super_interfaces(instanceKlassHandle this_k, TRAPS) { + if (this_k->has_default_methods()) { + for (int i = 0; i < this_k->local_interfaces()->length(); ++i) { + Klass* iface = this_k->local_interfaces()->at(i); + InstanceKlass* ik = InstanceKlass::cast(iface); + if (ik->should_be_initialized()) { + if (ik->has_default_methods()) { + ik->initialize_super_interfaces(ik, THREAD); + } + // Only initialize() interfaces that "declare" concrete methods. + // has_default_methods drives searching superinterfaces since it + // means has_default_methods in its superinterface hierarchy + if (!HAS_PENDING_EXCEPTION && ik->declares_default_methods()) { + ik->initialize(THREAD); + } + if (HAS_PENDING_EXCEPTION) { + Handle e(THREAD, PENDING_EXCEPTION); + CLEAR_PENDING_EXCEPTION; + { + EXCEPTION_MARK; + // Locks object, set state, and notify all waiting threads + this_k->set_initialization_state_and_notify( + initialization_error, THREAD); + + // ignore any exception thrown, superclass initialization error is + // thrown below + CLEAR_PENDING_EXCEPTION; + } + THROW_OOP(e()); + } + } + } + } +} void InstanceKlass::initialize_impl(instanceKlassHandle this_k, TRAPS) { // Make sure klass is linked (verified) before initialization @@ -815,33 +850,11 @@ void InstanceKlass::initialize_impl(instanceKlassHandle this_k, TRAPS) { } } + // Recursively initialize any superinterfaces that declare default methods + // Only need to recurse if has_default_methods which includes declaring and + // inheriting default methods if (this_k->has_default_methods()) { - // Step 7.5: initialize any interfaces which have default methods - for (int i = 0; i < this_k->local_interfaces()->length(); ++i) { - Klass* iface = this_k->local_interfaces()->at(i); - InstanceKlass* ik = InstanceKlass::cast(iface); - if (ik->has_default_methods() && ik->should_be_initialized()) { - ik->initialize(THREAD); - - if (HAS_PENDING_EXCEPTION) { - Handle e(THREAD, PENDING_EXCEPTION); - CLEAR_PENDING_EXCEPTION; - { - EXCEPTION_MARK; - // Locks object, set state, and notify all waiting threads - this_k->set_initialization_state_and_notify( - initialization_error, THREAD); - - // ignore any exception thrown, superclass initialization error is - // thrown below - CLEAR_PENDING_EXCEPTION; - } - DTRACE_CLASSINIT_PROBE_WAIT( - super__failed, InstanceKlass::cast(this_k()), -1, wait); - THROW_OOP(e()); - } - } - } + this_k->initialize_super_interfaces(this_k, CHECK); } // Step 8 diff --git a/hotspot/src/share/vm/oops/instanceKlass.hpp b/hotspot/src/share/vm/oops/instanceKlass.hpp index e50aa45eb69..64fb16bda64 100644 --- a/hotspot/src/share/vm/oops/instanceKlass.hpp +++ b/hotspot/src/share/vm/oops/instanceKlass.hpp @@ -199,13 +199,14 @@ class InstanceKlass: public Klass { bool _has_unloaded_dependent; enum { - _misc_rewritten = 1 << 0, // methods rewritten. - _misc_has_nonstatic_fields = 1 << 1, // for sizing with UseCompressedOops - _misc_should_verify_class = 1 << 2, // allow caching of preverification - _misc_is_anonymous = 1 << 3, // has embedded _host_klass field - _misc_is_contended = 1 << 4, // marked with contended annotation - _misc_has_default_methods = 1 << 5, // class/superclass/implemented interfaces has default methods - _misc_has_been_redefined = 1 << 6 // class has been redefined + _misc_rewritten = 1 << 0, // methods rewritten. + _misc_has_nonstatic_fields = 1 << 1, // for sizing with UseCompressedOops + _misc_should_verify_class = 1 << 2, // allow caching of preverification + _misc_is_anonymous = 1 << 3, // has embedded _host_klass field + _misc_is_contended = 1 << 4, // marked with contended annotation + _misc_has_default_methods = 1 << 5, // class/superclass/implemented interfaces has default methods + _misc_declares_default_methods = 1 << 6, // directly declares default methods (any access) + _misc_has_been_redefined = 1 << 7 // class has been redefined }; u2 _misc_flags; u2 _minor_version; // minor version number of class file @@ -651,6 +652,17 @@ class InstanceKlass: public Klass { } } + bool declares_default_methods() const { + return (_misc_flags & _misc_declares_default_methods) != 0; + } + void set_declares_default_methods(bool b) { + if (b) { + _misc_flags |= _misc_declares_default_methods; + } else { + _misc_flags &= ~_misc_declares_default_methods; + } + } + // for adding methods, ConstMethod::UNSET_IDNUM means no more ids available inline u2 next_method_idnum(); void set_initial_method_idnum(u2 value) { _idnum_allocated_count = value; } @@ -1022,6 +1034,7 @@ private: static bool link_class_impl (instanceKlassHandle this_k, bool throw_verifyerror, TRAPS); static bool verify_code (instanceKlassHandle this_k, bool throw_verifyerror, TRAPS); static void initialize_impl (instanceKlassHandle this_k, TRAPS); + static void initialize_super_interfaces (instanceKlassHandle this_k, TRAPS); static void eager_initialize_impl (instanceKlassHandle this_k); static void set_initialization_state_and_notify_impl (instanceKlassHandle this_k, ClassState state, TRAPS); static void call_class_initializer_impl (instanceKlassHandle this_k, TRAPS); diff --git a/hotspot/src/share/vm/utilities/dtrace_disabled.hpp b/hotspot/src/share/vm/utilities/dtrace_disabled.hpp index 2906fe22e93..40093592791 100644 --- a/hotspot/src/share/vm/utilities/dtrace_disabled.hpp +++ b/hotspot/src/share/vm/utilities/dtrace_disabled.hpp @@ -27,7 +27,7 @@ /* This file contains dummy provider probes needed when compiling a hotspot * that does not support dtrace probes. This could be because we're building - * on a system that doesn't suuport dtrace or because we're bulding a variant + * on a system that doesn't support dtrace or because we're bulding a variant * of hotspot (like core) where we do not support dtrace */ #if !defined(DTRACE_ENABLED) diff --git a/hotspot/test/runtime/lambda-features/InvokespecialInterface.java b/hotspot/test/runtime/lambda-features/InvokespecialInterface.java index c33dd56ef64..3b32dd7e552 100644 --- a/hotspot/test/runtime/lambda-features/InvokespecialInterface.java +++ b/hotspot/test/runtime/lambda-features/InvokespecialInterface.java @@ -33,11 +33,12 @@ import java.util.function.*; import java.util.*; +public class InvokespecialInterface { interface I { default void imethod() { System.out.println("I::imethod"); } } -class C implements I { +static class C implements I { public void foo() { I.super.imethod(); } // invokespecial InterfaceMethod public void bar() { I i = this; i.imethod(); } // invokeinterface same public void doSomeInvokedynamic() { @@ -48,7 +49,6 @@ class C implements I { } } -public class InvokespecialInterface { public static void main(java.lang.String[] unused) { // need to create C and call I::foo() C c = new C(); diff --git a/hotspot/test/runtime/lambda-features/TestInterfaceInit.java b/hotspot/test/runtime/lambda-features/TestInterfaceInit.java new file mode 100644 index 00000000000..0493a60bb1e --- /dev/null +++ b/hotspot/test/runtime/lambda-features/TestInterfaceInit.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2014, 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. + * + */ + +/* + * @test + * @bug 8034275 + * @summary [JDK 8u40] Test interface initialization: only for interfaces declaring default methods + * @run main TestInterfaceInit + */ +import java.util.List; +import java.util.Arrays; +import java.util.ArrayList; + +public class TestInterfaceInit { + + static List> cInitOrder = new ArrayList<>(); + + // Declares a default method and initializes + interface I { + boolean v = TestInterfaceInit.out(I.class); + default void x() {} + } + + // Declares a default method and initializes + interface J extends I { + boolean v = TestInterfaceInit.out(J.class); + default void x() {} + } + // No default method, does not initialize + interface JN extends J { + boolean v = TestInterfaceInit.out(JN.class); + } + + // Declares a default method and initializes + interface K extends I { + boolean v = TestInterfaceInit.out(K.class); + default void x() {} + } + + // No default method, does not initialize + interface KN extends K { + boolean v = TestInterfaceInit.out(KN.class); + } + + interface L extends JN, KN { + boolean v = TestInterfaceInit.out(L.class); + default void x() {} + } + + public static void main(String[] args) { + // Trigger initialization + boolean v = L.v; + + List> expectedCInitOrder = Arrays.asList(I.class,J.class,K.class,L.class); + if (!cInitOrder.equals(expectedCInitOrder)) { + throw new RuntimeException(String.format("Class initialization array %s not equal to expected array %s", cInitOrder, expectedCInitOrder)); + } + } + + static boolean out(Class c) { + System.out.println("#: initializing " + c.getName()); + cInitOrder.add(c); + return true; + } + +} diff --git a/hotspot/test/runtime/lambda-features/TestInterfaceOrder.java b/hotspot/test/runtime/lambda-features/TestInterfaceOrder.java new file mode 100644 index 00000000000..245742ece14 --- /dev/null +++ b/hotspot/test/runtime/lambda-features/TestInterfaceOrder.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2014, 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. + * + */ + +/* + * @test + * @bug 8034275 + * @summary [JDK 8u40] Test interface initialization order + * @run main TestInterfaceOrder + */ + +import java.util.List; +import java.util.Arrays; +import java.util.ArrayList; + +public class TestInterfaceOrder { + static List> cInitOrder = new ArrayList<>(); + + public static void main(java.lang.String[] args) { + //Trigger initialization + C c = new C(); + + List> expectedCInitOrder = Arrays.asList(I.class, J.class, A.class, K.class, B.class, L.class, C.class); + if (!cInitOrder.equals(expectedCInitOrder)) { + throw new RuntimeException(String.format("Class initialization order %s not equal to expected order %s", cInitOrder, expectedCInitOrder)); + } + } + + interface I { + boolean v = TestInterfaceOrder.out(I.class); + default void i() {} + } + + interface J extends I { + boolean v = TestInterfaceOrder.out(J.class); + default void j() {} + } + + static class A implements J { + static boolean v = TestInterfaceOrder.out(A.class); + } + + interface K extends I { + boolean v = TestInterfaceOrder.out(K.class); + default void k() {} + } + + static class B extends A implements K { + static boolean v = TestInterfaceOrder.out(B.class); + } + + interface L { + boolean v = TestInterfaceOrder.out(L.class); + default void l() {} + } + + static class C extends B implements L { + static boolean v = TestInterfaceOrder.out(C.class); + } + + + static boolean out(Class c) { + System.out.println("#: initializing " + c.getName()); + cInitOrder.add(c); + return true; + } + +} From 3a3e45ab40993611bdc0795e92bc3d72eeac6878 Mon Sep 17 00:00:00 2001 From: Erik Helin Date: Thu, 23 Oct 2014 11:43:29 +0200 Subject: [PATCH 030/118] 8061630: G1 iterates over JNIHandles two times Reviewed-by: mgerdin, brutisso --- .../src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp index 7ac2782772b..5d1ba94cc27 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp @@ -5760,14 +5760,10 @@ void G1CollectedHeap::evacuate_collection_set(EvacuationInfo& evacuation_info) { // not copied during the pause. process_discovered_references(n_workers); - // Weak root processing. - { + if (G1StringDedup::is_enabled()) { G1STWIsAliveClosure is_alive(this); G1KeepAliveClosure keep_alive(this); - JNIHandles::weak_oops_do(&is_alive, &keep_alive); - if (G1StringDedup::is_enabled()) { - G1StringDedup::unlink_or_oops_do(&is_alive, &keep_alive); - } + G1StringDedup::unlink_or_oops_do(&is_alive, &keep_alive); } _allocator->release_gc_alloc_regions(n_workers, evacuation_info); From 1aa3da106723aa92da1ba3267494bbfb83ca23b6 Mon Sep 17 00:00:00 2001 From: Calvin Cheung Date: Thu, 23 Oct 2014 10:08:02 -0700 Subject: [PATCH 031/118] 8038268: VM Crashes in MetaspaceShared::generate_vtable_methods while creating CDS archive with limiting SharedMiscCodeSize Estimate the minimum required size for the misc code region and check if the specified misc code region size meets the minimum size requirement Reviewed-by: jiangli, dholmes --- hotspot/src/share/vm/memory/metaspace.cpp | 10 ++++++++++ hotspot/src/share/vm/memory/metaspaceShared.hpp | 15 ++++++++++----- hotspot/src/share/vm/utilities/debug.cpp | 8 +++++--- hotspot/src/share/vm/utilities/debug.hpp | 3 ++- .../SharedArchiveFile/LimitSharedSizes.java | 9 ++++++--- 5 files changed, 33 insertions(+), 12 deletions(-) diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp index c39c1353fe3..2f5d5843ee2 100644 --- a/hotspot/src/share/vm/memory/metaspace.cpp +++ b/hotspot/src/share/vm/memory/metaspace.cpp @@ -3157,6 +3157,16 @@ void Metaspace::global_initialize() { SharedMiscDataSize = align_size_up(SharedMiscDataSize, max_alignment); SharedMiscCodeSize = align_size_up(SharedMiscCodeSize, max_alignment); + // the min_misc_code_size estimate is based on MetaspaceShared::generate_vtable_methods() + uintx min_misc_code_size = align_size_up( + (MetaspaceShared::num_virtuals * MetaspaceShared::vtbl_list_size) * + (sizeof(void*) + MetaspaceShared::vtbl_method_size) + MetaspaceShared::vtbl_common_code_size, + max_alignment); + + if (SharedMiscCodeSize < min_misc_code_size) { + report_out_of_shared_space(SharedMiscCode); + } + // Initialize with the sum of the shared space sizes. The read-only // and read write metaspace chunks will be allocated out of this and the // remainder is the misc code and data chunks. diff --git a/hotspot/src/share/vm/memory/metaspaceShared.hpp b/hotspot/src/share/vm/memory/metaspaceShared.hpp index cc514c05050..db344359082 100644 --- a/hotspot/src/share/vm/memory/metaspaceShared.hpp +++ b/hotspot/src/share/vm/memory/metaspaceShared.hpp @@ -57,11 +57,16 @@ class MetaspaceShared : AllStatic { static bool _archive_loading_failed; public: enum { - vtbl_list_size = 17, // number of entries in the shared space vtable list. - num_virtuals = 200 // maximum number of virtual functions - // If virtual functions are added to Metadata, - // this number needs to be increased. Also, - // SharedMiscCodeSize will need to be increased. + vtbl_list_size = 17, // number of entries in the shared space vtable list. + num_virtuals = 200, // maximum number of virtual functions + // If virtual functions are added to Metadata, + // this number needs to be increased. Also, + // SharedMiscCodeSize will need to be increased. + // The following 2 sizes were based on + // MetaspaceShared::generate_vtable_methods() + vtbl_method_size = 16, // conservative size of the mov1 and jmp instructions + // for the x64 platform + vtbl_common_code_size = (1*K) // conservative size of the "common_code" for the x64 platform }; enum { diff --git a/hotspot/src/share/vm/utilities/debug.cpp b/hotspot/src/share/vm/utilities/debug.cpp index bb0195d58d1..adacdc4233f 100644 --- a/hotspot/src/share/vm/utilities/debug.cpp +++ b/hotspot/src/share/vm/utilities/debug.cpp @@ -256,16 +256,18 @@ void report_out_of_shared_space(SharedSpaceType shared_space) { static const char* name[] = { "shared read only space", "shared read write space", - "shared miscellaneous data space" + "shared miscellaneous data space", + "shared miscellaneous code space" }; static const char* flag[] = { "SharedReadOnlySize", "SharedReadWriteSize", - "SharedMiscDataSize" + "SharedMiscDataSize", + "SharedMiscCodeSize" }; warning("\nThe %s is not large enough\n" - "to preload requested classes. Use -XX:%s=\n" + "to preload requested classes. Use -XX:%s=\n" "to increase the initial size of %s.\n", name[shared_space], flag[shared_space], name[shared_space]); exit(2); diff --git a/hotspot/src/share/vm/utilities/debug.hpp b/hotspot/src/share/vm/utilities/debug.hpp index 51e516a5bb5..7995e6c085d 100644 --- a/hotspot/src/share/vm/utilities/debug.hpp +++ b/hotspot/src/share/vm/utilities/debug.hpp @@ -245,7 +245,8 @@ template <> struct StaticAssert {}; enum SharedSpaceType { SharedReadOnly, SharedReadWrite, - SharedMiscData + SharedMiscData, + SharedMiscCode }; void report_out_of_shared_space(SharedSpaceType space_type); diff --git a/hotspot/test/runtime/SharedArchiveFile/LimitSharedSizes.java b/hotspot/test/runtime/SharedArchiveFile/LimitSharedSizes.java index 6989a643f59..b2505be0d7d 100644 --- a/hotspot/test/runtime/SharedArchiveFile/LimitSharedSizes.java +++ b/hotspot/test/runtime/SharedArchiveFile/LimitSharedSizes.java @@ -51,9 +51,12 @@ public class LimitSharedSizes { // Known issue, JDK-8038422 (assert() on Windows) // new SharedSizeTestData("-XX:SharedMiscDataSize", "500k", "miscellaneous data"), - // This will cause a VM crash; commenting out for now; see bug JDK-8038268 - // @ignore JDK-8038268 - // new SharedSizeTestData("-XX:SharedMiscCodeSize", "20k", "miscellaneous code"), + // Too small of a misc code size should not cause a vm crash. + // It should result in the following error message: + // The shared miscellaneous code space is not large enough + // to preload requested classes. Use -XX:SharedMiscCodeSize= + // to increase the initial size of shared miscellaneous code space. + new SharedSizeTestData("-XX:SharedMiscCodeSize", "20k", "miscellaneous code"), // these values are larger than default ones, but should // be acceptable and not cause failure From 8f8d87ece5f2496305208a4b0c072de5cea0d810 Mon Sep 17 00:00:00 2001 From: Kim Barrett Date: Thu, 23 Oct 2014 16:19:32 -0400 Subject: [PATCH 032/118] 8061748: Remove check_ct_logs_at_safepoint() Remove unused function and related closure class Reviewed-by: jwilhelm, mgerdin --- .../gc_implementation/g1/g1CollectedHeap.cpp | 77 ------------------- .../gc_implementation/g1/g1CollectedHeap.hpp | 3 - 2 files changed, 80 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp index 5d1ba94cc27..ebd77eb0b1e 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp @@ -127,41 +127,6 @@ public: }; -class ClearLoggedCardTableEntryClosure: public CardTableEntryClosure { - size_t _num_processed; - CardTableModRefBS* _ctbs; - int _histo[256]; - - public: - ClearLoggedCardTableEntryClosure() : - _num_processed(0), _ctbs(G1CollectedHeap::heap()->g1_barrier_set()) - { - for (int i = 0; i < 256; i++) _histo[i] = 0; - } - - bool do_card_ptr(jbyte* card_ptr, uint worker_i) { - unsigned char* ujb = (unsigned char*)card_ptr; - int ind = (int)(*ujb); - _histo[ind]++; - - *card_ptr = (jbyte)CardTableModRefBS::clean_card_val(); - _num_processed++; - - return true; - } - - size_t num_processed() { return _num_processed; } - - void print_histo() { - gclog_or_tty->print_cr("Card table value histogram:"); - for (int i = 0; i < 256; i++) { - if (_histo[i] != 0) { - gclog_or_tty->print_cr(" %d: %d", i, _histo[i]); - } - } - } -}; - class RedirtyLoggedCardTableEntryClosure : public CardTableEntryClosure { private: size_t _num_processed; @@ -475,48 +440,6 @@ bool G1CollectedHeap::is_scavengable(const void* p) { return !hr->is_humongous(); } -void G1CollectedHeap::check_ct_logs_at_safepoint() { - DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set(); - CardTableModRefBS* ct_bs = g1_barrier_set(); - - // Count the dirty cards at the start. - CountNonCleanMemRegionClosure count1(this); - ct_bs->mod_card_iterate(&count1); - int orig_count = count1.n(); - - // First clear the logged cards. - ClearLoggedCardTableEntryClosure clear; - dcqs.apply_closure_to_all_completed_buffers(&clear); - dcqs.iterate_closure_all_threads(&clear, false); - clear.print_histo(); - - // Now ensure that there's no dirty cards. - CountNonCleanMemRegionClosure count2(this); - ct_bs->mod_card_iterate(&count2); - if (count2.n() != 0) { - gclog_or_tty->print_cr("Card table has %d entries; %d originally", - count2.n(), orig_count); - } - guarantee(count2.n() == 0, "Card table should be clean."); - - RedirtyLoggedCardTableEntryClosure redirty; - dcqs.apply_closure_to_all_completed_buffers(&redirty); - dcqs.iterate_closure_all_threads(&redirty, false); - gclog_or_tty->print_cr("Log entries = %d, dirty cards = %d.", - clear.num_processed(), orig_count); - guarantee(redirty.num_processed() == clear.num_processed(), - err_msg("Redirtied "SIZE_FORMAT" cards, bug cleared "SIZE_FORMAT, - redirty.num_processed(), clear.num_processed())); - - CountNonCleanMemRegionClosure count3(this); - ct_bs->mod_card_iterate(&count3); - if (count3.n() != orig_count) { - gclog_or_tty->print_cr("Should have restored them all: orig = %d, final = %d.", - orig_count, count3.n()); - guarantee(count3.n() >= orig_count, "Should have restored them all."); - } -} - // Private class members. G1CollectedHeap* G1CollectedHeap::_g1h; diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp index 1307d4a6efd..f1aaceaacb4 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp @@ -797,9 +797,6 @@ protected: // The closure used to refine a single card. RefineCardTableEntryClosure* _refine_cte_cl; - // A function to check the consistency of dirty card logs. - void check_ct_logs_at_safepoint(); - // A DirtyCardQueueSet that is used to hold cards that contain // references into the current collection set. This is used to // update the remembered sets of the regions in the collection From 5306f2430a160a68fb8a24bdf5c437a9f4910479 Mon Sep 17 00:00:00 2001 From: Chris Plummer Date: Thu, 23 Oct 2014 14:43:08 -0700 Subject: [PATCH 033/118] 6191224: (reflect) Misleading detail string in IllegalArgumentException thrown by Array.get The test case shows that an exception is thrown with the message "Argument is not an array", when in fact the argument is an array, but an array of a primitive type is actually what was expected. Fixed by differentiating between failing because an array was expected and failing because an array of a primitive type was expected. Reviewed-by: dholmes, ctornqvi, lfoltan --- hotspot/src/share/vm/prims/jvm.cpp | 4 +- .../runtime/reflect/ArrayGetIntException.java | 76 +++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 hotspot/test/runtime/reflect/ArrayGetIntException.java diff --git a/hotspot/src/share/vm/prims/jvm.cpp b/hotspot/src/share/vm/prims/jvm.cpp index 98be942d9c7..ad58d7bf966 100644 --- a/hotspot/src/share/vm/prims/jvm.cpp +++ b/hotspot/src/share/vm/prims/jvm.cpp @@ -3271,8 +3271,10 @@ static inline arrayOop check_array(JNIEnv *env, jobject arr, bool type_array_onl THROW_0(vmSymbols::java_lang_NullPointerException()); } oop a = JNIHandles::resolve_non_null(arr); - if (!a->is_array() || (type_array_only && !a->is_typeArray())) { + if (!a->is_array()) { THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Argument is not an array"); + } else if (type_array_only && !a->is_typeArray()) { + THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Argument is not an array of primitive type"); } return arrayOop(a); } diff --git a/hotspot/test/runtime/reflect/ArrayGetIntException.java b/hotspot/test/runtime/reflect/ArrayGetIntException.java new file mode 100644 index 00000000000..e5ee5cb93dc --- /dev/null +++ b/hotspot/test/runtime/reflect/ArrayGetIntException.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test + * @bug 6191224 + * @summary (reflect) Misleading detail string in IllegalArgumentException thrown by Array.get + * @run main ArrayGetIntException + */ +import java.io.*; +import java.lang.reflect.Array; + +public class ArrayGetIntException { + public static void main(String[] args) throws Exception { + Object[] objArray = {new Integer(Integer.MAX_VALUE)}; + + // this access is legal + try { + System.out.println(Array.get(objArray, 0)); + System.out.println("Test #1 PASSES"); + } catch(Exception e) { + failTest("Test #1 FAILS - legal access denied" + e.getMessage()); + } + + // this access is not legal, but needs to generate the proper exception message + try { + System.out.println(Array.getInt(objArray, 0)); + failTest("Test #2 FAILS - no exception"); + } catch(Exception e) { + System.out.println(e); + if (e.getMessage().equals("Argument is not an array of primitive type")) { + System.out.println("Test #2 PASSES"); + } else { + failTest("Test #2 FAILS - incorrect message: " + e.getMessage()); + } + } + + // this access is not legal, but needs to generate the proper exception message + try { + System.out.println(Array.getInt(new Object(), 0)); + failTest("Test #3 FAILS - no exception"); + } catch(Exception e) { + System.out.println(e); + if (e.getMessage().equals("Argument is not an array")) { + System.out.println("Test #3 PASSES"); + } else { + failTest("Test #3 FAILS - incorrect message: " + e.getMessage()); + } + } + } + + private static void failTest(String errStr) { + System.out.println(errStr); + throw new Error(errStr); + } +} From aca235b12101c803f3864c5e5c080b110783fb4c Mon Sep 17 00:00:00 2001 From: Eric McCorkle Date: Thu, 6 Nov 2014 17:39:57 -0500 Subject: [PATCH 034/118] 8062245: Test executes incorrect class Fix issue with test executing incorrect class, and trivial test failure linked to different subclasses of CharSequance. Reviewed-by: jjg --- .../processing/model/type/BoundsTest.java | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/langtools/test/tools/javac/processing/model/type/BoundsTest.java b/langtools/test/tools/javac/processing/model/type/BoundsTest.java index c67f4882a71..23ddcca9d06 100644 --- a/langtools/test/tools/javac/processing/model/type/BoundsTest.java +++ b/langtools/test/tools/javac/processing/model/type/BoundsTest.java @@ -25,7 +25,6 @@ * @test * @bug 6499673 * @library /tools/javac/lib - * @ignore 8062245 Test executes incorrect class * @build JavacTestingAbstractProcessor BoundsTest * @run main BoundsTest * @summary Assertion check for TypeVariable.getUpperBound() fails @@ -86,8 +85,8 @@ public class BoundsTest { }; private static final String[] NoBounds_supers = {}; - private HashSet expected_bounds; - private HashSet expected_supers; + private HashSet expected_bounds; + private HashSet expected_supers; private static final File classesdir = new File("intersectionproperties"); private static final JavaCompiler comp = @@ -99,8 +98,8 @@ public class BoundsTest { final String[] Test_bounds, final String[] Test_supers) throws IOException { System.err.println("Testing " + Test_name); - expected_bounds = new HashSet(Arrays.asList(Test_bounds)); - expected_supers = new HashSet(Arrays.asList(Test_supers)); + expected_bounds = new HashSet<>(Arrays.asList(Test_bounds)); + expected_supers = new HashSet<>(Arrays.asList(Test_supers)); final Iterable files = fm.getJavaFileObjectsFromFiles(Collections.singleton(writeFile(classesdir, Test_name, Test_contents))); final JavacTask ct = @@ -130,7 +129,7 @@ public class BoundsTest { } public static void main(String... args) throws IOException { - new IntersectionPropertiesTest().run(); + new BoundsTest().run(); } private static File writeFile(File dir, String path, String body) @@ -166,18 +165,17 @@ public class BoundsTest { final List bounds = typeParameterElement.getBounds(); final List supers = processingEnv.getTypeUtils().directSupertypes(upperBound); - final HashSet actual_bounds = new HashSet(); - final HashSet actual_supers = new HashSet(); + final HashSet actual_bounds = new HashSet<>(); + final HashSet actual_supers = new HashSet<>(); for(TypeMirror ty : bounds) { - actual_bounds.add(((TypeElement)((DeclaredType)ty).asElement()).getQualifiedName()); + actual_bounds.add(((TypeElement)((DeclaredType)ty).asElement()).getQualifiedName().toString()); } for(TypeMirror ty : supers) { - actual_supers.add(((TypeElement)((DeclaredType)ty).asElement()).getQualifiedName()); + actual_supers.add(((TypeElement)((DeclaredType)ty).asElement()).getQualifiedName().toString()); } - if (!expected_bounds.equals(actual_bounds)) { System.err.println("Mismatched expected and actual bounds."); System.err.println("Expected:"); @@ -190,7 +188,7 @@ public class BoundsTest { } if (!expected_supers.equals(actual_supers)) { - System.err.println("Mismatched expected and actual bounds."); + System.err.println("Mismatched expected and actual supers."); System.err.println("Expected:"); for(CharSequence tm : expected_supers) System.err.println(" " + tm); From ab1b9a9cf13b0f208bb413bb8b9a96cd44045a0c Mon Sep 17 00:00:00 2001 From: Eric McCorkle Date: Fri, 7 Nov 2014 07:54:35 -0500 Subject: [PATCH 035/118] 8029012: parameter_index for type annotation not updated after outer.this added Fix javac's handling of type annotations when synthetic parameters are added Reviewed-by: jjg, mcimadamore --- .../javac/code/TypeAnnotationPosition.java | 2 +- .../com/sun/tools/javac/comp/Lower.java | 20 +- .../classfile/ClassfileInspector.java | 950 ++++++++++++++++++ .../classfile/SyntheticParameters.java | 130 +++ .../referenceinfos/Constructors.java | 12 +- 5 files changed, 1104 insertions(+), 10 deletions(-) create mode 100644 langtools/test/tools/javac/annotations/typeAnnotations/classfile/ClassfileInspector.java create mode 100644 langtools/test/tools/javac/annotations/typeAnnotations/classfile/SyntheticParameters.java diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotationPosition.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotationPosition.java index fdf746f6d0d..d3aecdb2126 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotationPosition.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotationPosition.java @@ -145,7 +145,7 @@ public class TypeAnnotationPosition { public final int bound_index; // For type parameter and method parameter - public final int parameter_index; + public int parameter_index; // For class extends, implements, and throws clauses public final int type_index; diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java index ac78ad720bc..1a470cc9112 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java @@ -2642,9 +2642,10 @@ public class Lower extends TreeTranslator { syms.intType, tree.sym); ordParam.mods.flags |= SYNTHETIC; ordParam.sym.flags_field |= SYNTHETIC; - tree.params = tree.params.prepend(ordParam).prepend(nameParam); - MethodSymbol m = tree.sym; + tree.params = tree.params.prepend(ordParam).prepend(nameParam); + incrementParamTypeAnnoIndexes(m, 2); + m.extraParams = m.extraParams.prepend(ordParam.sym); m.extraParams = m.extraParams.prepend(nameParam.sym); Type olderasure = m.erasure(types); @@ -2667,6 +2668,17 @@ public class Lower extends TreeTranslator { } } //where + private void incrementParamTypeAnnoIndexes(MethodSymbol m, + int amount) { + for (final Attribute.TypeCompound anno : m.getRawTypeAttributes()) { + // Increment the parameter_index of any existing formal + // parameter annotations. + if (anno.position.type == TargetType.METHOD_FORMAL_PARAMETER) { + anno.position.parameter_index += amount; + } + } + } + private void visitMethodDefInternal(JCMethodDecl tree) { if (tree.name == names.init && (currentClass.isInner() || currentClass.isLocal())) { @@ -2697,8 +2709,10 @@ public class Lower extends TreeTranslator { // Add this$n (if needed) in front of and free variables behind // constructor parameter list. tree.params = tree.params.appendList(fvdefs); - if (currentClass.hasOuterInstance()) + if (currentClass.hasOuterInstance()) { tree.params = tree.params.prepend(otdef); + incrementParamTypeAnnoIndexes(m, 1); + } // If this is an initial constructor, i.e., it does not start with // this(...), insert initializers for this$n and proxies diff --git a/langtools/test/tools/javac/annotations/typeAnnotations/classfile/ClassfileInspector.java b/langtools/test/tools/javac/annotations/typeAnnotations/classfile/ClassfileInspector.java new file mode 100644 index 00000000000..864787e2f3a --- /dev/null +++ b/langtools/test/tools/javac/annotations/typeAnnotations/classfile/ClassfileInspector.java @@ -0,0 +1,950 @@ +/* + * Copyright (c) 2012, 2014, 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. + */ + +import java.lang.annotation.*; +import java.io.*; +import java.net.URL; +import java.util.List; + +import com.sun.tools.classfile.*; + +/** + * A class providing utilities for writing tests that inspect class + * files directly, looking for specific type annotations. + * + * Note: this framework does not currently handle repeating + * annotations. + */ +public class ClassfileInspector { + + /** + * A group of expected annotations to be found in a given class. + * If the class name is null, then the template will be applied to + * every class. + */ + public static class Expected { + /** + * The name of the class. If {@code null} this template will + * apply to every class; otherwise, it will only be applied to + * the named class. + */ + public final String classname; + + /** + * The expected class annotations. These will be checked + * against the class' attributes. + */ + public final ExpectedTypeAnnotation[] classAnnos; + + /** + * The expected method annotations. These will be checked + * against all methods in the class. + */ + public final ExpectedMethodTypeAnnotation[] methodAnnos; + + /** + * The expected field annotations. These will be checked + * against all fields in the class. + */ + public final ExpectedFieldTypeAnnotation[] fieldAnnos; + + /** + * Create an {@code Expected} from its components. + * + * @param classname The name of the class to match, or {@code + * null} for all classes. + * @param classAnnos The expected class annotations. + * @param methodAnnos The expected method annotations. + * @param fieldAnnos The expected field annotations. + */ + public Expected(String classname, + ExpectedTypeAnnotation[] classAnnos, + ExpectedMethodTypeAnnotation[] methodAnnos, + ExpectedFieldTypeAnnotation[] fieldAnnos) { + this.classname = classname; + this.classAnnos = classAnnos; + this.methodAnnos = methodAnnos; + this.fieldAnnos = fieldAnnos; + } + + public String toString() { + final StringBuilder sb = new StringBuilder(); + final String newline = System.lineSeparator(); + sb.append("Expected on class ").append(classname); + if (null != classAnnos) { + sb.append(newline).append("Class annotations:").append(newline); + for(ExpectedTypeAnnotation anno : classAnnos) { + sb.append(anno).append(newline); + } + } + if (null != methodAnnos) { + sb.append(newline).append("Method annotations:").append(newline); + for(ExpectedTypeAnnotation anno : methodAnnos) { + sb.append(anno).append(newline); + } + } + if (null != fieldAnnos) { + sb.append(newline).append("Field annotations:").append(newline); + for(ExpectedTypeAnnotation anno : fieldAnnos) { + sb.append(anno).append(newline); + } + } + return sb.toString(); + } + + /** + * See if this template applies to a class. + * + * @param classname The classname to check. + * @return Whether or not this template should apply. + */ + public boolean matchClassName(String classname) { + return this.classname == null || this.classname.equals(classname); + } + + /** + * After applying the template to all classes, check to see if + * any of the expected annotations weren't matched. + * + * @return The number of missed matches. + */ + public int check() { + int count = 0; + if (classAnnos != null) { + for(ExpectedTypeAnnotation expected : classAnnos) { + if (!expected.check()) { + count++; + } + } + } + if (methodAnnos != null) { + for(ExpectedMethodTypeAnnotation expected : methodAnnos) { + if (!expected.check()) { + count++; + } + } + } + if (fieldAnnos != null) { + for(ExpectedFieldTypeAnnotation expected : fieldAnnos) { + if (!expected.check()) { + count++; + } + } + } + return count; + } + } + + /** + * An expected type annotation. This is both a superclass for + * method and field type annotations, as well as a class for type + * annotations on a class. + */ + public static class ExpectedTypeAnnotation { + private int count = 0; + protected final String expectedName; + protected final int expectedCount; + protected final TypeAnnotation.TargetType targetType; + protected final int bound_index; + protected final int parameter_index; + protected final int type_index; + protected final int exception_index; + protected final TypeAnnotation.Position.TypePathEntry[] typePath; + protected final boolean visibility; + + /** + * Create an {@code ExpectedTypeAnnotation} from its + * components. It is usually a better idea to use a {@code + * Builder} to do this. + * + * @param expectedName The expected annotation name. + * @param visibility Whether this annotation should be runtime-visible. + * @param expectedCount The number of annotations that should + * be seen. If 0, this asserts that the + * described annotation is not present. + * @param targetType The expected target type. + * @param bound_index The expected bound index, or {@code Integer.MIN_VALUE}. + * @param parameter_index The expected parameter index, or + * {@code Integer.MIN_VALUE}. + * @param type_index The expected type index, or {@code Integer.MIN_VALUE}. + * @param exception_index The expected exception index, or + * {@code Integer.MIN_VALUE}. + * @param typePath The expected type path. + */ + public ExpectedTypeAnnotation(String expectedName, + boolean visibility, + int expectedCount, + TypeAnnotation.TargetType targetType, + int bound_index, + int parameter_index, + int type_index, + int exception_index, + TypeAnnotation.Position.TypePathEntry... typePath) { + this.expectedName = expectedName; + this.visibility = visibility; + this.expectedCount = expectedCount; + this.targetType = targetType; + this.bound_index = bound_index; + this.parameter_index = parameter_index; + this.type_index = type_index; + this.exception_index = exception_index; + this.typePath = typePath; + } + + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("Expected "); + sb.append(expectedCount); + sb.append(" annotation "); + sb.append(expectedName); + sb.append(visibility ? ", runtime visibile " : ", runtime invisibile "); + sb.append(targetType); + sb.append(", bound_index = "); + sb.append(bound_index); + sb.append(", parameter_index = "); + sb.append(parameter_index); + sb.append(", type_index = "); + sb.append(type_index); + sb.append(", exception_index = "); + sb.append(exception_index); + sb.append(", type_path = ["); + for(int i = 0; i < typePath.length; i++) { + if (i != 0) { + sb.append(", "); + } + sb.append(typePath[i]); + } + sb.append("]"); + return sb.toString(); + } + + /** + * See if this template matches the given visibility. + * + * @param Whether or not the annotation is visible at runtime. + * @return Whether or not this template matches the visibility. + */ + public boolean matchVisibility(boolean visibility) { + return this.visibility == visibility; + } + + /** + * Attempty to match this template against an annotation. If + * it does match, then the match count for the template will + * be incremented. Otherwise, nothing will be done. + * + * @param anno The annotation to attempt to match. + */ + public void matchAnnotation(TypeAnnotation anno) { + boolean matches = true; + + try { + matches = anno.constant_pool.getUTF8Info(anno.annotation.type_index).value.equals("L" + expectedName + ";"); + } catch(Exception e) { + matches = false; + } + + matches = matches && anno.position.type == targetType; + matches = matches && anno.position.bound_index == bound_index; + matches = matches && anno.position.parameter_index == parameter_index; + matches = matches && anno.position.type_index == type_index; + matches = matches && anno.position.exception_index == exception_index; + matches = matches && anno.position.location.size() == typePath.length; + + if (matches) { + int i = 0; + for(TypeAnnotation.Position.TypePathEntry entry : + anno.position.location) { + matches = matches && typePath[i++].equals(entry); + } + } + + if (matches) { + count++; + } + } + + /** + * After all matching, check to see if the expected number of + * matches equals the actual number. If not, then print a + * failure message and return {@code false}. + * + * @return Whether or not the expected number of matched + * equals the actual number. + */ + public boolean check() { + if (count != expectedCount) { + System.err.println(this + ", but saw " + count); + return false; + } else { + return true; + } + } + + /** + * A builder class for creating {@code + * ExpectedTypeAnnotation}s in a more convenient fashion. The + * constructor for {@code ExpectedTypeAnnotation} takes a + * large number of parameters (by necessity). This class + * allows users to construct a {@code ExpectedTypeAnnotation}s + * using only the ones they need. + */ + public static class Builder { + protected final String expectedName; + protected final int expectedCount; + protected final TypeAnnotation.TargetType targetType; + protected final boolean visibility; + protected int bound_index = Integer.MIN_VALUE; + protected int parameter_index = Integer.MIN_VALUE; + protected int type_index = Integer.MIN_VALUE; + protected int exception_index = Integer.MIN_VALUE; + protected TypeAnnotation.Position.TypePathEntry[] typePath = + new TypeAnnotation.Position.TypePathEntry[0]; + + /** + * Create a {@code Builder} from the mandatory parameters. + * + * @param expectedName The expected annotation name. + * @param targetType The expected target type. + * @param visibility Whether this annotation should be runtime-visible. + * @param expectedCount The number of annotations that should be seen. + */ + public Builder(String expectedName, + TypeAnnotation.TargetType targetType, + boolean visibility, + int expectedCount) { + this.expectedName = expectedName; + this.visibility = visibility; + this.expectedCount = expectedCount; + this.targetType = targetType; + } + + /** + * Create an {@code ExpectedTypeAnnotation} from all + * parameters that have been provided. The default values + * will be used for those that have not. + * + * @return The cretaed {@code ExpectedTypeAnnotation}. + */ + public ExpectedTypeAnnotation build() { + return new ExpectedTypeAnnotation(expectedName, visibility, + expectedCount, targetType, + bound_index, parameter_index, + type_index, exception_index, + typePath); + } + + /** + * Provide a bound index parameter. + * + * @param bound_index The bound_index value. + */ + public Builder setBoundIndex(int bound_index) { + this.bound_index = bound_index; + return this; + } + + /** + * Provide a parameter index parameter. + * + * @param bound_index The parameter_index value. + */ + public Builder setParameterIndex(int parameter_index) { + this.parameter_index = parameter_index; + return this; + } + + /** + * Provide a type index parameter. + * + * @param type_index The type_index value. + */ + public Builder setTypeIndex(int type_index) { + this.type_index = type_index; + return this; + } + + /** + * Provide an exception index parameter. + * + * @param exception_index The exception_index value. + */ + public Builder setExceptionIndex(int exception_index) { + this.exception_index = exception_index; + return this; + } + + /** + * Provide a type path parameter. + * + * @param typePath The type path value. + */ + public Builder setTypePath(TypeAnnotation.Position.TypePathEntry[] typePath) { + this.typePath = typePath; + return this; + } + } + } + + /** + * A type annotation found on a method. + */ + public static class ExpectedMethodTypeAnnotation extends ExpectedTypeAnnotation { + private final String methodname; + + /** + * Create an {@code ExpectedMethodTypeAnnotation} from its + * components. It is usually a better idea to use a {@code + * Builder} to do this. + * + * @param methodname The expected method name. + * @param expectedName The expected annotation name. + * @param visibility Whether this annotation should be runtime-visible. + * @param expectedCount The number of annotations that should be seen. + * @param targetType The expected target type. + * @param bound_index The expected bound index, or {@code Integer.MIN_VALUE}. + * @param parameter_index The expected parameter index, or + * {@code Integer.MIN_VALUE}. + * @param type_index The expected type index, or {@code Integer.MIN_VALUE}. + * @param exception_index The expected exception index, or + * {@code Integer.MIN_VALUE}. + * @param typePath The expected type path. + */ + public ExpectedMethodTypeAnnotation(String methodname, + String expectedName, + boolean visibility, + int expectedCount, + TypeAnnotation.TargetType targetType, + int bound_index, + int parameter_index, + int type_index, + int exception_index, + TypeAnnotation.Position.TypePathEntry... typePath) { + super(expectedName, visibility, expectedCount, targetType, bound_index, + parameter_index, type_index, exception_index, typePath); + this.methodname = methodname; + } + + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("Expected "); + sb.append(expectedCount); + sb.append(" annotation "); + sb.append(expectedName); + sb.append(visibility ? ", runtime visibile " : ", runtime invisibile "); + sb.append(targetType); + sb.append(", bound_index = "); + sb.append(bound_index); + sb.append(", parameter_index = "); + sb.append(parameter_index); + sb.append(", type_index = "); + sb.append(type_index); + sb.append(", exception_index = "); + sb.append(exception_index); + sb.append(", type_path = ["); + for(int i = 0; i < typePath.length; i++) { + if (i != 0) { + sb.append(", "); + } + sb.append(typePath[i]); + } + sb.append("]"); + sb.append(" on method "); + sb.append(methodname); + return sb.toString(); + } + + /** + * See if this template applies to a method. + * + * @param methodname The method name to check. + * @return Whether or not this template should apply. + */ + public boolean matchMethodName(String methodname) { + return this.methodname.equals(methodname); + } + + /** + * A builder class for creating {@code + * ExpectedMethodTypeAnnotation}s in a more convenient fashion. The + * constructor for {@code ExpectedMethodTypeAnnotation} takes a + * large number of parameters (by necessity). This class + * allows users to construct a {@code ExpectedMethodTypeAnnotation}s + * using only the ones they need. + */ + public static class Builder extends ExpectedTypeAnnotation.Builder { + protected final String methodname; + + /** + * Create a {@code Builder} from the mandatory parameters. + * + * @param methodname The expected method name. + * @param expectedName The expected annotation name. + * @param targetType The expected target type. + * @param visibility Whether this annotation should be runtime-visible. + * @param expectedCount The number of annotations that should be seen. + */ + public Builder(String methodname, + String expectedName, + TypeAnnotation.TargetType targetType, + boolean visibility, + int expectedCount) { + super(expectedName, targetType, visibility, expectedCount); + this.methodname = methodname; + } + + /** + * Create an {@code ExpectedMethodTypeAnnotation} from all + * parameters that have been provided. The default values + * will be used for those that have not. + * + * @return The cretaed {@code ExpectedMethodTypeAnnotation}. + */ + public ExpectedMethodTypeAnnotation build() { + return new ExpectedMethodTypeAnnotation(methodname, expectedName, + visibility, expectedCount, + targetType, bound_index, + parameter_index, type_index, + exception_index, typePath); + } + } + } + + /** + * A type annotation found on a field. + */ + public static class ExpectedFieldTypeAnnotation extends ExpectedTypeAnnotation { + private final String fieldname; + + /** + * Create an {@code ExpectedFieldTypeAnnotation} from its + * components. It is usually a better idea to use a {@code + * Builder} to do this. + * + * @param fieldname The expected field name. + * @param expectedName The expected annotation name. + * @param visibility Whether this annotation should be runtime-visible. + * @param expectedCount The number of annotations that should be seen. + * @param targetType The expected target type. + * @param bound_index The expected bound index, or {@code Integer.MIN_VALUE}. + * @param parameter_index The expected parameter index, or + * {@code Integer.MIN_VALUE}. + * @param type_index The expected type index, or {@code Integer.MIN_VALUE}. + * @param exception_index The expected exception index, or + * {@code Integer.MIN_VALUE}. + * @param typePath The expected type path. + */ + public ExpectedFieldTypeAnnotation(String fieldname, + String expectedName, + boolean visibility, + int expectedCount, + TypeAnnotation.TargetType targetType, + int bound_index, + int parameter_index, + int type_index, + int exception_index, + TypeAnnotation.Position.TypePathEntry... typePath) { + super(expectedName, visibility, expectedCount, targetType, bound_index, + parameter_index, type_index, exception_index, typePath); + this.fieldname = fieldname; + } + + public String toString() { + final StringBuilder sb = new StringBuilder(); + sb.append("Expected ").append(expectedCount) + .append(" annotation ").append(expectedName) + .append(visibility ? ", runtime visibile " : ", runtime invisibile ") + .append(targetType) + .append(", bound_index = ").append(bound_index) + .append(", parameter_index = ").append(parameter_index) + .append(", type_index = ").append(type_index) + .append(", exception_index = ").append(exception_index) + .append(", type_path = ["); + + for(int i = 0; i < typePath.length; i++) { + if (i != 0) { + sb.append(", "); + } + sb.append(typePath[i]); + } + sb.append("]") + .append(" on field ").append(fieldname); + return sb.toString(); + } + + /** + * See if this template applies to a field. + * + * @param fieldname The field name to check. + * @return Whether or not this template should apply. + */ + public boolean matchFieldName(String fieldname) { + return this.fieldname.equals(fieldname); + } + + /** + * A builder class for creating {@code + * ExpectedFieldTypeAnnotation}s in a more convenient fashion. The + * constructor for {@code ExpectedFieldTypeAnnotation} takes a + * large number of parameters (by necessity). This class + * allows users to construct a {@code ExpectedFieldTypeAnnotation}s + * using only the ones they need. + */ + public static class Builder extends ExpectedTypeAnnotation.Builder { + protected final String fieldname; + + /** + * Create a {@code Builder} from the mandatory parameters. + * + * @param fieldname The expected field name. + * @param expectedName The expected annotation name. + * @param targetType The expected target type. + * @param visibility Whether this annotation should be runtime-visible. + * @param expectedCount The number of annotations that should be seen. + */ + public Builder(String fieldname, + String expectedName, + TypeAnnotation.TargetType targetType, + boolean visibility, + int expectedCount) { + super(expectedName, targetType, visibility, expectedCount); + this.fieldname = fieldname; + } + + /** + * Create an {@code ExpectedFieldTypeAnnotation} from all + * parameters that have been provided. The default values + * will be used for those that have not. + * + * @return The cretaed {@code ExpectedFieldTypeAnnotation}. + */ + public ExpectedFieldTypeAnnotation build() { + return new ExpectedFieldTypeAnnotation(fieldname, expectedName, + visibility, expectedCount, + targetType, bound_index, + parameter_index, type_index, + exception_index, typePath); + } + } + } + + private void matchClassTypeAnnotation(ClassFile classfile, + ExpectedTypeAnnotation expected) + throws ConstantPoolException { + for(Attribute attr : classfile.attributes) { + attr.accept(typeAnnoMatcher, expected); + } + } + + private void matchMethodTypeAnnotation(ClassFile classfile, + ExpectedMethodTypeAnnotation expected) + throws ConstantPoolException { + for(Method meth : classfile.methods) { + if (expected.matchMethodName(meth.getName(classfile.constant_pool))) { + for(Attribute attr : meth.attributes) { + attr.accept(typeAnnoMatcher, expected); + } + } + } + } + + private void matchFieldTypeAnnotation(ClassFile classfile, + ExpectedFieldTypeAnnotation expected) + throws ConstantPoolException { + for(Field field : classfile.fields) { + if (expected.matchFieldName(field.getName(classfile.constant_pool))) { + for(Attribute attr : field.attributes) { + attr.accept(typeAnnoMatcher, expected); + } + } + } + } + + private void matchClassTypeAnnotations(ClassFile classfile, + ExpectedTypeAnnotation[] expected) + throws ConstantPoolException { + for(ExpectedTypeAnnotation one : expected) { + matchClassTypeAnnotation(classfile, one); + } + } + + private void matchMethodTypeAnnotations(ClassFile classfile, + ExpectedMethodTypeAnnotation[] expected) + throws ConstantPoolException { + for(ExpectedMethodTypeAnnotation one : expected) { + matchMethodTypeAnnotation(classfile, one); + } + } + + private void matchFieldTypeAnnotations(ClassFile classfile, + ExpectedFieldTypeAnnotation[] expected) + throws ConstantPoolException { + for(ExpectedFieldTypeAnnotation one : expected) { + matchFieldTypeAnnotation(classfile, one); + } + } + + /** + * Run a template on a single {@code ClassFile}. + * + * @param classfile The {@code ClassFile} on which to run tests. + * @param expected The expected annotation template. + */ + public void run(ClassFile classfile, + Expected... expected) + throws ConstantPoolException { + run(new ClassFile[] { classfile }, expected); + } + + /** + * Run a template on multiple {@code ClassFile}s. + * + * @param classfile The {@code ClassFile}s on which to run tests. + * @param expected The expected annotation template. + */ + public void run(ClassFile[] classfiles, + Expected... expected) + throws ConstantPoolException { + for(ClassFile classfile : classfiles) { + for(Expected one : expected) { + if (one.matchClassName(classfile.getName())) { + if (one.classAnnos != null) + matchClassTypeAnnotations(classfile, one.classAnnos); + if (one.methodAnnos != null) + matchMethodTypeAnnotations(classfile, one.methodAnnos); + if (one.fieldAnnos != null) + matchFieldTypeAnnotations(classfile, one.fieldAnnos); + } + } + } + int count = 0; + for (Expected one : expected) { + count += one.check(); + } + + if (count != 0) { + throw new RuntimeException(count + " errors occurred in test"); + } + } + + /** + * Get a {@code ClassFile} from its file name. + * + * @param name The class' file name. + * @return The {@code ClassFile} + */ + public static ClassFile getClassFile(String name) + throws IOException, ConstantPoolException { + final URL url = ClassfileInspector.class.getResource(name); + final InputStream in = url.openStream(); + try { + return ClassFile.read(in); + } finally { + in.close(); + } + } + + private static final Attribute.Visitor typeAnnoMatcher = + new Attribute.Visitor() { + + @Override + public Void visitBootstrapMethods(BootstrapMethods_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitDefault(DefaultAttribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitAnnotationDefault(AnnotationDefault_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitCharacterRangeTable(CharacterRangeTable_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitCode(Code_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitCompilationID(CompilationID_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitConstantValue(ConstantValue_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitDeprecated(Deprecated_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitEnclosingMethod(EnclosingMethod_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitExceptions(Exceptions_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitInnerClasses(InnerClasses_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitLineNumberTable(LineNumberTable_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitLocalVariableTable(LocalVariableTable_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitLocalVariableTypeTable(LocalVariableTypeTable_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitMethodParameters(MethodParameters_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitRuntimeVisibleAnnotations(RuntimeVisibleAnnotations_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitRuntimeInvisibleAnnotations(RuntimeInvisibleAnnotations_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitRuntimeInvisibleParameterAnnotations(RuntimeInvisibleParameterAnnotations_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitSignature(Signature_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitSourceDebugExtension(SourceDebugExtension_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitSourceFile(SourceFile_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitSourceID(SourceID_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitStackMap(StackMap_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitStackMapTable(StackMapTable_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitSynthetic(Synthetic_attribute attr, + ExpectedTypeAnnotation expected) { + return null; + } + + @Override + public Void visitRuntimeVisibleTypeAnnotations(RuntimeVisibleTypeAnnotations_attribute attr, + ExpectedTypeAnnotation expected) { + if (expected.matchVisibility(true)) { + for(TypeAnnotation anno : attr.annotations) { + expected.matchAnnotation(anno); + } + } + + return null; + } + + @Override + public Void visitRuntimeInvisibleTypeAnnotations(RuntimeInvisibleTypeAnnotations_attribute attr, + ExpectedTypeAnnotation expected) { + if (expected.matchVisibility(false)) { + for(TypeAnnotation anno : attr.annotations) { + expected.matchAnnotation(anno); + } + } + + return null; + } + }; +} diff --git a/langtools/test/tools/javac/annotations/typeAnnotations/classfile/SyntheticParameters.java b/langtools/test/tools/javac/annotations/typeAnnotations/classfile/SyntheticParameters.java new file mode 100644 index 00000000000..35477d21086 --- /dev/null +++ b/langtools/test/tools/javac/annotations/typeAnnotations/classfile/SyntheticParameters.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2014, 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. + */ + +/* + * @test SyntheticParameters + * @summary Test generation of annotations on inner class parameters. + * @build ClassfileInspector + * @run main SyntheticParameters + */ + +import java.io.*; +import java.lang.annotation.*; + +import com.sun.tools.classfile.*; + +public class SyntheticParameters extends ClassfileInspector { + + private static final String Inner_class = "SyntheticParameters$Inner.class"; + private static final String Foo_class = "SyntheticParameters$Foo.class"; + private static final Expected Inner_expected = + new Expected("SyntheticParameters$Inner", + null, + new ExpectedMethodTypeAnnotation[] { + (ExpectedMethodTypeAnnotation) + // Assert there is no annotation on the + // this$0 parameter. + new ExpectedMethodTypeAnnotation.Builder( + "", + "A", + TypeAnnotation.TargetType.METHOD_FORMAL_PARAMETER, + false, + 0).setParameterIndex(0).build(), + (ExpectedMethodTypeAnnotation) + // Assert there is an annotation on the + // first parameter. + new ExpectedMethodTypeAnnotation.Builder( + "", + "A", + TypeAnnotation.TargetType.METHOD_FORMAL_PARAMETER, + false, + 1).setParameterIndex(1).build(), + (ExpectedMethodTypeAnnotation) + new ExpectedMethodTypeAnnotation.Builder( + "foo", + "A", + TypeAnnotation.TargetType.METHOD_FORMAL_PARAMETER, + false, + 1).setParameterIndex(0).build(), + (ExpectedMethodTypeAnnotation) + new ExpectedMethodTypeAnnotation.Builder( + "foo", + "A", + TypeAnnotation.TargetType.METHOD_FORMAL_PARAMETER, + false, + 0).setParameterIndex(1).build() + }, + null); + private static final Expected Foo_expected = + new Expected("SyntheticParameters$Foo", + null, + new ExpectedMethodTypeAnnotation[] { + (ExpectedMethodTypeAnnotation) + // Assert there is no annotation on the + // $enum$name parameter. + new ExpectedMethodTypeAnnotation.Builder( + "", + "A", + TypeAnnotation.TargetType.METHOD_FORMAL_PARAMETER, + false, + 0).setParameterIndex(0).build(), + (ExpectedMethodTypeAnnotation) + // Assert there is no annotation on the + // $enum$ordinal parameter. + new ExpectedMethodTypeAnnotation.Builder( + "", + "A", + TypeAnnotation.TargetType.METHOD_FORMAL_PARAMETER, + false, + 0).setParameterIndex(1).build(), + (ExpectedMethodTypeAnnotation) + // Assert there is an annotation on the + // first parameter. + new ExpectedMethodTypeAnnotation.Builder( + "", + "A", + TypeAnnotation.TargetType.METHOD_FORMAL_PARAMETER, + false, + 1).setParameterIndex(2).build() + }, + null); + + public static void main(String... args) throws Exception { + new SyntheticParameters().run( + new ClassFile[] { getClassFile(Inner_class), getClassFile(Foo_class) }, + new Expected[] { Inner_expected, Foo_expected }); + } + + public class Inner { + public Inner(@A int a) {} + public void foo(@A int a, int b) {} + } + + public static enum Foo { + ONE(null); + Foo(@A Object a) {} + } +} + +@Target({ElementType.TYPE_USE}) +@interface A {} diff --git a/langtools/test/tools/javac/annotations/typeAnnotations/referenceinfos/Constructors.java b/langtools/test/tools/javac/annotations/typeAnnotations/referenceinfos/Constructors.java index 78e9acab2ab..2775fdd9790 100644 --- a/langtools/test/tools/javac/annotations/typeAnnotations/referenceinfos/Constructors.java +++ b/langtools/test/tools/javac/annotations/typeAnnotations/referenceinfos/Constructors.java @@ -43,7 +43,7 @@ public class Constructors { @TADescription(annotation = "TA", type = METHOD_RETURN, genericLocation = {1, 0}) @TADescription(annotation = "TB", type = METHOD_RETURN, genericLocation = {1, 0}) - @TADescription(annotation = "TC", type = METHOD_FORMAL_PARAMETER, paramIndex = 0) + @TADescription(annotation = "TC", type = METHOD_FORMAL_PARAMETER, paramIndex = 1) @TestClass("%TEST_CLASS_NAME%$Inner") public String innerClass() { return "class %TEST_CLASS_NAME% { class Inner {" + @@ -56,7 +56,7 @@ public class Constructors { @TADescription(annotation = "TB", type = METHOD_RETURN, genericLocation = {1, 0}) @TADescription(annotation = "TC", type = METHOD_RECEIVER) @TADescription(annotation = "TD", type = METHOD_RETURN, genericLocation = {1, 0}) - @TADescription(annotation = "TE", type = METHOD_FORMAL_PARAMETER, paramIndex = 0) + @TADescription(annotation = "TE", type = METHOD_FORMAL_PARAMETER, paramIndex = 1) @TestClass("%TEST_CLASS_NAME%$Inner") public String innerClass2() { return "class %TEST_CLASS_NAME% { class Inner {" + @@ -70,7 +70,7 @@ public class Constructors { @TADescription(annotation = "TC", type = METHOD_RETURN, genericLocation = {1, 0, 1, 0}) @TADescription(annotation = "TD", type = METHOD_RECEIVER, genericLocation = {1, 0}) @TADescription(annotation = "TE", type = METHOD_RETURN, genericLocation = {1, 0, 1, 0}) - @TADescription(annotation = "TF", type = METHOD_FORMAL_PARAMETER, paramIndex = 0) + @TADescription(annotation = "TF", type = METHOD_FORMAL_PARAMETER, paramIndex = 1) @TestClass("Outer$Middle$Inner") public String innerClass3() { return "class Outer { class Middle { class Inner {" + @@ -89,7 +89,7 @@ public class Constructors { @TADescription(annotation = "RTAs", type = METHOD_RETURN, genericLocation = {1, 0}) @TADescription(annotation = "RTBs", type = METHOD_RETURN, genericLocation = {1, 0}) - @TADescription(annotation = "RTCs", type = METHOD_FORMAL_PARAMETER, paramIndex = 0) + @TADescription(annotation = "RTCs", type = METHOD_FORMAL_PARAMETER, paramIndex = 1) @TestClass("%TEST_CLASS_NAME%$Inner") public String innerClassRepeatableAnnotation() { return "class %TEST_CLASS_NAME% { class Inner {" + @@ -102,7 +102,7 @@ public class Constructors { @TADescription(annotation = "RTBs", type = METHOD_RETURN, genericLocation = {1, 0}) @TADescription(annotation = "RTCs", type = METHOD_RECEIVER) @TADescription(annotation = "RTDs", type = METHOD_RETURN, genericLocation = {1, 0}) - @TADescription(annotation = "RTEs", type = METHOD_FORMAL_PARAMETER, paramIndex = 0) + @TADescription(annotation = "RTEs", type = METHOD_FORMAL_PARAMETER, paramIndex = 1) @TestClass("%TEST_CLASS_NAME%$Inner") public String innerClassRepeatableAnnotation2() { return "class %TEST_CLASS_NAME% { class Inner {" + @@ -116,7 +116,7 @@ public class Constructors { @TADescription(annotation = "RTCs", type = METHOD_RETURN, genericLocation = {1, 0, 1, 0}) @TADescription(annotation = "RTDs", type = METHOD_RECEIVER, genericLocation = {1, 0}) @TADescription(annotation = "RTEs", type = METHOD_RETURN, genericLocation = {1, 0, 1, 0}) - @TADescription(annotation = "RTFs", type = METHOD_FORMAL_PARAMETER, paramIndex = 0) + @TADescription(annotation = "RTFs", type = METHOD_FORMAL_PARAMETER, paramIndex = 1) @TestClass("Outer$Middle$Inner") public String innerClassRepatableAnnotation3() { return "class Outer { class Middle { class Inner {" + From 3f3f44af47cd52b4d03ed169cecbeac3e4bd33a1 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Fri, 7 Nov 2014 18:22:36 +0100 Subject: [PATCH 036/118] 8058489: More adjustments of langtools/make/build.xml to modularized layout Re-structuring the langtools/make/build.xml to reflect the modular layout, cleanup of the build script Reviewed-by: jjg, mcimadamore, ksrini --- langtools/make/build.properties | 191 +--- langtools/make/build.xml | 971 +++++------------- langtools/make/launcher.sh-template | 9 +- langtools/make/netbeans/langtools/build.xml | 127 ++- .../netbeans/langtools/nbproject/project.xml | 42 +- .../make/tools/anttasks/SelectToolTask.java | 54 +- 6 files changed, 420 insertions(+), 974 deletions(-) diff --git a/langtools/make/build.properties b/langtools/make/build.properties index 9d92c73944c..b224a9721d2 100644 --- a/langtools/make/build.properties +++ b/langtools/make/build.properties @@ -23,25 +23,46 @@ # questions. # -# This is the JDK used to build and run the bootstrap version of javac. -# The bootstrap javac is used to compile both boostrap versions of the -# other tools, and product versions of all the tools. -# Override this path as needed, either on the command line or in -# one of the standard user build.properties files (see build.xml) +#javac configuration for "normal build" (these will be passed to the bootstrap compiler): +javac.debug = true +javac.debuglevel = source,lines,vars +javac.extra.opts=-XDignore.symbol.file=true +javac.includes= +javac.lint.opts = -Xlint:all,-deprecation -Werror +javac.source = 8 +javac.target = 8 -# boot.java.home = /opt/jdk/1.7.0 -boot.java = ${boot.java.home}/bin/java -boot.javac = ${boot.java.home}/bin/javac +#javac configuration for bootstrap build (these will be passed to the compiler from the given boot JDK): +boot.javac.extra.opts=-XDignore.symbol.file=true +boot.javac.includes = \ + javax/annotation/processing/ \ + javax/lang/model/ \ + javax/tools/ \ + jdk/ \ + com/sun/source/ \ + com/sun/tools/javac/ \ + com/sun/tools/doclint/ +boot.javac.lint.opts= boot.javac.source = 8 boot.javac.target = 8 -# This is the JDK used to run the product version of the tools, -# for example, for testing. If you're building a complete JDK, specify that. -# Override this path as needed, either on the command line or in -# one of the standard user build.properties files (see build.xml) +#configuration of submodules (share by both the bootstrap and normal compilation): +langtools.modules=java.base:java.compiler:jdk.compiler:jdk.dev:jdk.javadoc +java.base.dependencies= +java.compiler.dependencies=java.base +jdk.compiler.dependencies=java.base:java.compiler +jdk.javadoc.dependencies=java.base:java.compiler:jdk.compiler +jdk.dev.dependencies=java.base:java.compiler:jdk.compiler -# target.java.home = /opt/jdk/1.8.0 -target.java = ${target.java.home}/bin/java +#test configuration: +jtreg.tests= +boot.javac.tests = tools/javac +crules.tests = ../make/test/crules + +#javadoc configuration +javadoc.jls.cite=The Java™ Language Specification +javadoc.jls.option=-tag "jls:a:See <cite>${javadoc.jls.cite}</cite>:" \ + -tag "implNote:a:Implementation Note:" # Version info -- override as needed jdk.version = 1.9.0 @@ -55,146 +76,4 @@ milestone = internal # timestamps # FIXME -- need to include openjdk as needed release = ${jdk.version}-${milestone} -bootstrap.release = ${release}_bootstrap full.version = ${release}-${build.number} -bootstrap.full.version = ${bootstrap.release}-${build.number} - -# options for the tasks used to compile the tools -javac.source = 8 -javac.target = 8 -javac.debug = true -javac.debuglevel = source,lines -javac.no.jdk.warnings = -XDignore.symbol.file=true -# set the following to -version to verify the versions of javac being used -javac.version.opt = -# in time, there should be no exceptions to -Xlint:all -javac.lint.opts = -Xlint:all,-deprecation -Werror - -# options for the task for javac -#javadoc.jls3.url=http://java.sun.com/docs/books/jls/ -#javadoc.jls3.cite=<a href="${javadoc.jls3.url}">The Java Language Specification, Third Edition</a> -#javadoc.jls3.option=-tag "jls3:a:See <cite>${javadoc.jls3.cite}</cite>:" - - -javadoc.jls.cite=The Java™ Language Specification - -javadoc.jls.option=-tag "jls:a:See <cite>${javadoc.jls.cite}</cite>:" - - - - - -# jtreg, used to run the JDK regression tests -# See http://openjdk.java.net/jtreg/ -# Override this path as needed, either on the command line or in -# one of the standard user build.properties files (see build.xml) - -# jtreg.home = /opt/jtreg/4.1 - -# findbugs -# See http://findbugs.sourceforge.net/ -# Override this path as needed, either on the command line or in -# one of the standard user build.properties files (see build.xml) - -# findbugs.home = /opt/findbugs/1.2.1 - -# vizant (graph visualization tool for Ant) -# See http://vizant.sourceforge.net/ -# Override this path as needed, either on the command line or in -# one of the standard user build.properties files (see build.xml) - -# vizant.jar = /opt/vizant/0.1.2/vizant-0.1.2.jar -# dot = dot - -#------------------------------------------------------------ - -# The following properties define the packages for each of the tools. -# Syntactically, they should be suitable as arguments for the "includes" -# parameter of Ant filesets. In particular, note the trailing '/'. - -javac.includes = \ - javax/annotation/processing/ \ - javax/lang/model/ \ - javax/tools/ \ - jdk/ \ - com/sun/source/ \ - com/sun/tools/javac/ \ - com/sun/tools/doclint/ - -javac.tests = \ - tools/javac - -# - -javadoc.includes = \ - com/sun/javadoc/ \ - com/sun/tools/javadoc/ \ - com/sun/tools/doclets/ - -javadoc.tests = \ - tools/javadoc/ \ - com/sun/javadoc/ - -# - -javah.includes = \ - com/sun/tools/javah/ - -javah.tests = \ - tools/javah/ - -# - -javap.includes = \ - com/sun/tools/classfile/ \ - com/sun/tools/javap/ \ - com/sun/tools/jdeps/ \ - sun/tools/javap/ - -javap.tests = \ - tools/javap/ - -# - -sjavac.includes = \ - com/sun/tools/sjavac/ - -sjavac.tests = \ - tools/sjavac - -crules.tests = ../make/test/crules - -# - -# The following files require the latest JDK to be available. -# The API can be provided by using a suitable boot.java.home -# or by setting import.jdk -require.latest.jdk.files = \ - com/sun/tools/javac/nio/*.java - -# The following files in the import jdk source directory are required -# in order to compile the files defined in ${require.latest.jdk.files} -# -# For NIO, the list of stub files is defined by the contents of the primary -# API packages, together with such types that may be required in order to -# compile the stubs. Some of these dependencies would go away if the stub -# generator were to be improved -- e.g. by removing unnecessary imports. -# -import.jdk.stub.files = \ - java/io/File.java \ - java/nio/file/**.java \ - java/nio/file/attribute/**.java \ - java/nio/file/spi/**.java \ - java/nio/channels/AsynchronousChannel.java \ - java/nio/channels/AsynchronousFileChannel.java \ - java/nio/channels/CompletionHandler.java \ - java/nio/channels/SeekableByteChannel.java - -# The following value is used by the main jtreg target. -# An empty value means all tests -# Override as desired to run a specific set of tests -jtreg.tests = - -# Check style configuration -# overridable name and version -checkstyle.name.version = checkstyle-5.4 diff --git a/langtools/make/build.xml b/langtools/make/build.xml index 2d4488d4f3f..d2e543a6d56 100644 --- a/langtools/make/build.xml +++ b/langtools/make/build.xml @@ -25,10 +25,10 @@ --> @@ -99,13 +68,6 @@ **** Global property definitions. --> - - - - - @@ -119,36 +81,24 @@ - - - - - - + + - + - - - - - - - - - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -325,9 +254,13 @@ warningsProperty="findbugs.all.warnings" jvm="${target.java.home}/bin/java" jvmargs="-Xmx512M"> - + + + + + - + @@ -339,49 +272,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -391,7 +282,7 @@ destdir="${build.dir}/diag-examples/classes" includes="ArgTypeCompilerFactory.java,Example.java,FileManager.java,HTMLWriter.java,RunExamples.java,DocCommentProcessor.java" sourcepath="" - classpath="${dist.lib.dir}/javac.jar;${dist.lib.dir}/javap.jar" + classpath="${langtools.classes}" includeAntRuntime="no" debug="${javac.debug}" debuglevel="${javac.debuglevel}"> @@ -400,7 +291,7 @@ @@ -413,56 +304,16 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + @@ -515,17 +365,12 @@ - + - - - ant.home = ${ant.home} @@ -536,257 +381,32 @@ checkstyle.home = ${checkstyle.home} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + crules.CodingRulesAnalyzerPlugin - - - - - - - - - - - + extra.jvmargs="-Xbootclasspath/a:${build.crules.dir}/classes" /> - - + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + @@ -866,84 +516,73 @@ - - - - - - - - + + + - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + - - - - - - + + + - - + + - - + + + + + + + + + + + + + + + + + - + - + @@ -957,13 +596,20 @@ + + + + + + + - - - - - + + + + - + - + @@ -991,42 +636,15 @@ - + - - - - - - - - - - - - - - - - + + @@ -1048,55 +666,9 @@ classpath="${build.toolclasses.dir}/"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -1106,7 +678,7 @@
Unofficial Javadoc generated from developer sources for preview purposes only]]>
- - - + - + - + - - + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -1184,6 +729,7 @@ + @@ -1191,6 +737,7 @@ + @@ -1204,7 +751,7 @@ samevm="@{samevm}" verbose="@{verbose}" failonerror="false" resultproperty="jtreg.@{name}.result" javacoptions="-g" - vmoptions="${coverage.options} -Xbootclasspath/p:${coverage.classpath}${path.separator}${build.classes.dir} @{jpda.jvmargs} @{extra.jvmargs}"> + vmoptions="${coverage.options} -Xbootclasspath/p:${coverage.classpath}${path.separator}@{langtools.classes} @{jpda.jvmargs} @{extra.jvmargs}"> @@ -1220,21 +767,12 @@
- - - - - - - - - - - + - + + + @@ -1265,7 +803,7 @@ jvmargs="-Xmx512M" > - + @@ -1276,11 +814,6 @@ - - - - - diff --git a/langtools/make/launcher.sh-template b/langtools/make/launcher.sh-template index 05d765022f2..f82d610564d 100644 --- a/langtools/make/launcher.sh-template +++ b/langtools/make/launcher.sh-template @@ -43,10 +43,9 @@ mylib="$mydir/../lib" # dependent jar files for additional dependencies. if [ "$LANGTOOLS_USE_BOOTCLASSPATH" != "no" ]; then - cp=`unzip -c "$mylib/#PROGRAM#.jar" META-INF/MANIFEST.MF | - grep "Class-Path:" | - sed -e 's|Class-Path: *||' -e 's|\([a-z]*\.jar\) *|'"$mylib"'/\1#PS#|g'` - bcp="$mylib/#PROGRAM#.jar#PS#$cp" + cp=`echo "$mylib"/*.jar | + sed -e 's|\([a-z.]*\.jar\) *|\1#PS#|g'` + bcp=$cp fi # tools currently assumes that assertions are enabled in the launcher @@ -72,4 +71,4 @@ done unset DUALCASE IFS=$nl -"#TARGET_JAVA#" "${bcp:+-Xbootclasspath/p:"$bcp"}" ${ea} ${javaOpts} -jar "${mylib}/#PROGRAM#.jar" ${toolOpts} +"#TARGET_JAVA#" "${bcp:+-Xbootclasspath/p:"$bcp"}" ${ea} ${javaOpts} com.sun.tools.#PROGRAM#.Main ${toolOpts} diff --git a/langtools/make/netbeans/langtools/build.xml b/langtools/make/netbeans/langtools/build.xml index 4e85a1e4790..bb57ee77a8a 100644 --- a/langtools/make/netbeans/langtools/build.xml +++ b/langtools/make/netbeans/langtools/build.xml @@ -46,48 +46,34 @@ --> - + - - - - - - - - - - - - - + + + - - - - + - + Must set property 'includes' - + + debuglevel="${javac.debuglevel}"> + + - + + + + + + + + - + Must set property 'run.classname' @@ -115,22 +109,22 @@ test all tools. --> - - - - + + + - + - + - + @@ -165,7 +159,7 @@ - + @@ -179,20 +173,29 @@ - + Must set property 'jtreg.tests' - + - + Must set property 'class' + + + @@ -205,31 +208,10 @@ test all tools. --> - - - - - - - - - - - - - - - - - - - + - - + @@ -253,6 +235,15 @@ /> + + + + + + + + + @@ -262,7 +253,7 @@ - + @@ -278,11 +269,11 @@ - + - + compile-single - src/java.base/share/classes + java.base includes ${root}/src/java.base/share/classes @@ -120,7 +120,7 @@ compile-single - src/java.compiler/share/classes + java.compiler includes ${root}/src/java.compiler/share/classes @@ -133,7 +133,7 @@ compile-single - src/jdk.compiler/share/classes + jdk.compiler includes ${root}/src/jdk.compiler/share/classes @@ -146,7 +146,7 @@ compile-single - src/jdk.dev/share/classes + jdk.dev includes ${root}/src/jdk.dev/share/classes @@ -159,7 +159,7 @@ compile-single - src/jdk.javadoc/share/classes + jdk.javadoc includes ${root}/src/jdk.javadoc/share/classes @@ -333,7 +333,7 @@ debug-fix - src/java.base/share/classes + java.base class ${root}/src/java.base/share/classes @@ -346,7 +346,7 @@ debug-fix - src/java.compiler/share/classes + java.compiler class ${root}/src/java.compiler/share/classes @@ -359,7 +359,7 @@ debug-fix - src/jdk.compiler/share/classes + jdk.compiler class ${root}/src/jdk.compiler/share/classes @@ -372,7 +372,7 @@ debug-fix - src/jdk.dev/share/classes + jdk.dev class ${root}/src/jdk.dev/share/classes @@ -385,7 +385,7 @@ debug-fix - src/jdk.dev/share/classes + jdk.javadoc class ${root}/src/jdk.javadoc/share/classes @@ -478,11 +478,31 @@ ${root}/src/java.base/share/classes + ${root}/build/java.base/classes + 1.8 + + ${root}/src/java.compiler/share/classes + ${root}/build/java.base/classes + ${root}/build/java.compiler/classes + 1.8 + + ${root}/src/jdk.compiler/share/classes + ${root}/build/java.base/classes:${root}/build/java.compiler/classes + ${root}/build/jdk.compiler/classes + 1.8 + + ${root}/src/jdk.dev/share/classes + ${root}/build/java.base/classes:${root}/build/java.compiler/classes:${root}/build/jdk.compiler/classes + ${root}/build/jdk.dev/classes + 1.8 + + ${root}/src/jdk.javadoc/share/classes - ${root}/build/classes + ${root}/build/java.base/classes:${root}/build/java.compiler/classes:${root}/build/jdk.compiler/classes + ${root}/build/jdk.javadoc/classes 1.8 diff --git a/langtools/make/tools/anttasks/SelectToolTask.java b/langtools/make/tools/anttasks/SelectToolTask.java index a26bc3e689f..db4451db5ab 100644 --- a/langtools/make/tools/anttasks/SelectToolTask.java +++ b/langtools/make/tools/anttasks/SelectToolTask.java @@ -74,7 +74,18 @@ public class SelectToolTask extends Task { enum ToolChoices { NONE(""), - JAVAC("javac"), + BOOSTRAP_JAVAC("bootstrap-javac", true) { + @Override + public ToolChoices baseTool() { + return JAVAC; + } + }, + JAVAC("javac") { + @Override + public ToolChoices asBootstrap() { + return BOOSTRAP_JAVAC; + } + }, JAVADOC("javadoc"), JAVAH("javah"), JAVAP("javap"); @@ -91,6 +102,14 @@ public class SelectToolTask extends Task { this.bootstrap = bootstrap; } + public ToolChoices asBootstrap() { + return this; + } + + public ToolChoices baseTool() { + return this; + } + @Override public String toString() { return toolName; @@ -176,9 +195,11 @@ public class SelectToolTask extends Task { JOptionPane p = createPane(guiProps); p.createDialog("Select Tool").setVisible(true); - toolName = ((ToolChoices)toolChoice.getSelectedItem()).toolName; + ToolChoices tool = (ToolChoices)toolChoice.getSelectedItem(); + + toolName = tool.baseTool().toolName; + toolBootstrap = tool.bootstrap; toolArgs = argsField.getText(); - toolBootstrap = bootstrapCheckbox.isSelected(); if (defaultCheck.isSelected()) { if (toolName.equals("")) { fileProps.remove("tool.name"); @@ -213,30 +234,31 @@ public class SelectToolTask extends Task { EnumSet toolChoices = toolProperty == null ? EnumSet.allOf(ToolChoices.class) : EnumSet.range(ToolChoices.JAVAC, ToolChoices.JAVAP); toolChoice = new JComboBox<>(toolChoices.toArray()); - if (toolName != null) - toolChoice.setSelectedItem(ToolChoices.valueOf(toolName.toUpperCase())); + ToolChoices tool = toolName != null ? ToolChoices.valueOf(toolName.toUpperCase()) : null; + if (toolName != null) { + if (toolBootstrap) + tool = tool.asBootstrap(); + toolChoice.setSelectedItem(tool); + } toolChoice.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { - String tn = ((ToolChoices)e.getItem()).toolName; - argsField.setText(getDefaultArgsForTool(props, tn)); + ToolChoices tool = (ToolChoices)e.getItem(); + argsField.setText(getDefaultArgsForTool(props, tool)); if (toolProperty != null) - okButton.setEnabled(!tn.equals("")); + okButton.setEnabled(tool != ToolChoices.NONE); } }); - GridBagConstraints checkConstraint = new GridBagConstraints(); fc.anchor = GridBagConstraints.EAST; GridBagConstraints toolConstraint = new GridBagConstraints(); fc.anchor = GridBagConstraints.WEST; toolPane.add(toolChoice, toolConstraint); - bootstrapCheckbox = new JCheckBox("bootstrap", toolBootstrap); - toolPane.add(bootstrapCheckbox, checkConstraint); body.add(toolPane, fc); - argsField = new JTextField(getDefaultArgsForTool(props, toolName), 40); + argsField = new JTextField(getDefaultArgsForTool(props, tool), 40); if (toolProperty == null || argsProperty != null) { JLabel argsLabel = new JLabel("Args:"); body.add(argsLabel, lc); @@ -322,8 +344,11 @@ public class SelectToolTask extends Task { } } - String getDefaultArgsForTool(Properties props, String tn) { - return (tn == null || tn.equals("")) ? "" : props.getProperty(tn + ".args", ""); + String getDefaultArgsForTool(Properties props, ToolChoices tool) { + if (tool == null) + return ""; + String toolName = tool.baseTool().toolName; + return toolName.equals("") ? "" : props.getProperty(toolName + ".args", ""); } // Ant task parameters @@ -335,7 +360,6 @@ public class SelectToolTask extends Task { // GUI components private JComboBox toolChoice; - private JCheckBox bootstrapCheckbox; private JTextField argsField; private JCheckBox defaultCheck; private JButton okButton; From de2b97f13351a01dbd38de957bcf00c2f69a7d64 Mon Sep 17 00:00:00 2001 From: Jonathan Gibbons Date: Fri, 7 Nov 2014 14:51:35 -0800 Subject: [PATCH 037/118] 8063145: ToolBox should support extracting classes from a JavaFileManager/Location Reviewed-by: ksrini --- .../javac/6508981/TestInferBinaryName.java | 84 ++++++--- langtools/test/tools/lib/ToolBox.java | 174 ++++++++++++++---- 2 files changed, 194 insertions(+), 64 deletions(-) diff --git a/langtools/test/tools/javac/6508981/TestInferBinaryName.java b/langtools/test/tools/javac/6508981/TestInferBinaryName.java index e94ba29b4dc..01da20b42ba 100644 --- a/langtools/test/tools/javac/6508981/TestInferBinaryName.java +++ b/langtools/test/tools/javac/6508981/TestInferBinaryName.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2014, 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 @@ -26,7 +26,8 @@ * @bug 6508981 * @summary cleanup file separator handling in JavacFileManager * (This test is specifically to test the new impl of inferBinaryName) - * @build p.A + * @library /tools/lib + * @build ToolBox p.A * @run main TestInferBinaryName */ @@ -61,51 +62,75 @@ public class TestInferBinaryName { //System.err.println(System.getProperties()); testDirectory(); testSymbolArchive(); - testZipArchive(); - testZipFileIndexArchive(); - testZipFileIndexArchive2(); + + File testJar = createJar(); + + testZipArchive(testJar); + testZipFileIndexArchive(testJar); + testZipFileIndexArchive2(testJar); if (errors > 0) throw new Exception(errors + " error found"); } + File createJar() throws IOException { + File f = new File("test.jar"); + try (JavaFileManager fm = new JavacFileManager(new Context(), false, null)) { + ToolBox tb = new ToolBox(); + tb.new JarTask(f.getPath()) + .files(fm, StandardLocation.PLATFORM_CLASS_PATH, "java.lang.*") + .run(); + } + return f; + } + void testDirectory() throws IOException { String testClassName = "p.A"; - JavaFileManager fm = - getFileManager("test.classes", USE_SYMBOL_FILE, USE_ZIP_FILE_INDEX); - test("testDirectory", - fm, testClassName, "com.sun.tools.javac.file.RegularFileObject"); + List testClasses = Arrays.asList(new File(System.getProperty("test.classes"))); + try (JavaFileManager fm = + getFileManager(testClasses, USE_SYMBOL_FILE, USE_ZIP_FILE_INDEX)) { + test("testDirectory", + fm, testClassName, "com.sun.tools.javac.file.RegularFileObject"); + } } void testSymbolArchive() throws IOException { String testClassName = "java.lang.String"; - JavaFileManager fm = - getFileManager("sun.boot.class.path", USE_SYMBOL_FILE, DONT_USE_ZIP_FILE_INDEX); - test("testSymbolArchive", - fm, testClassName, "com.sun.tools.javac.file.SymbolArchive$SymbolFileObject"); + List path = getPath(System.getProperty("sun.boot.class.path")); + try (JavaFileManager fm = + getFileManager(path, USE_SYMBOL_FILE, DONT_USE_ZIP_FILE_INDEX)) { + test("testSymbolArchive", + fm, testClassName, "com.sun.tools.javac.file.SymbolArchive$SymbolFileObject"); + } } - void testZipArchive() throws IOException { + void testZipArchive(File testJar) throws IOException { String testClassName = "java.lang.String"; - JavaFileManager fm = - getFileManager("sun.boot.class.path", IGNORE_SYMBOL_FILE, DONT_USE_ZIP_FILE_INDEX); - test("testZipArchive", - fm, testClassName, "com.sun.tools.javac.file.ZipArchive$ZipFileObject"); + List path = Arrays.asList(testJar); + try (JavaFileManager fm = + getFileManager(path, IGNORE_SYMBOL_FILE, DONT_USE_ZIP_FILE_INDEX)) { + test("testZipArchive", + fm, testClassName, "com.sun.tools.javac.file.ZipArchive$ZipFileObject"); + } } - void testZipFileIndexArchive() throws IOException { + void testZipFileIndexArchive(File testJar) throws IOException { String testClassName = "java.lang.String"; - JavaFileManager fm = - getFileManager("sun.boot.class.path", USE_SYMBOL_FILE, USE_ZIP_FILE_INDEX); - test("testZipFileIndexArchive", - fm, testClassName, "com.sun.tools.javac.file.ZipFileIndexArchive$ZipFileIndexFileObject"); + List path = Arrays.asList(testJar); + try (JavaFileManager fm = + getFileManager(path, USE_SYMBOL_FILE, USE_ZIP_FILE_INDEX)) { + test("testZipFileIndexArchive", + fm, testClassName, "com.sun.tools.javac.file.ZipFileIndexArchive$ZipFileIndexFileObject"); + } } - void testZipFileIndexArchive2() throws IOException { + void testZipFileIndexArchive2(File testJar) throws IOException { String testClassName = "java.lang.String"; - JavaFileManager fm = - getFileManager("sun.boot.class.path", IGNORE_SYMBOL_FILE, USE_ZIP_FILE_INDEX); - test("testZipFileIndexArchive2", - fm, testClassName, "com.sun.tools.javac.file.ZipFileIndexArchive$ZipFileIndexFileObject"); + List path = Arrays.asList(testJar); + try (JavaFileManager fm = + getFileManager(path, IGNORE_SYMBOL_FILE, USE_ZIP_FILE_INDEX)) { + test("testZipFileIndexArchive2", + fm, testClassName, "com.sun.tools.javac.file.ZipFileIndexArchive$ZipFileIndexFileObject"); + } } /** @@ -133,7 +158,7 @@ public class TestInferBinaryName { System.err.println("OK"); } - JavaFileManager getFileManager(String classpathProperty, + JavaFileManager getFileManager(List path, boolean symFileKind, boolean zipFileIndexKind) throws IOException { @@ -145,7 +170,6 @@ public class TestInferBinaryName { if (symFileKind == IGNORE_SYMBOL_FILE) options.put("ignore.symbol.file", "true"); JavacFileManager fm = new JavacFileManager(ctx, false, null); - List path = getPath(System.getProperty(classpathProperty)); fm.setLocation(CLASS_PATH, path); return fm; } diff --git a/langtools/test/tools/lib/ToolBox.java b/langtools/test/tools/lib/ToolBox.java index c052f463a36..859765b2db1 100644 --- a/langtools/test/tools/lib/ToolBox.java +++ b/langtools/test/tools/lib/ToolBox.java @@ -21,6 +21,7 @@ * questions. */ +import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; @@ -28,6 +29,7 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FilterOutputStream; import java.io.FilterWriter; +import java.io.IOError; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -49,12 +51,15 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumMap; +import java.util.EnumSet; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.ListIterator; import java.util.Locale; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; @@ -64,18 +69,20 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; + import javax.tools.FileObject; import javax.tools.ForwardingJavaFileManager; import javax.tools.JavaCompiler; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; +import javax.tools.JavaFileManager.Location; import javax.tools.SimpleJavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.StandardLocation; import com.sun.tools.javac.api.JavacTaskImpl; import com.sun.tools.javac.api.JavacTool; -import java.io.IOError; /** * Utility methods and classes for writing jtreg tests for @@ -233,7 +240,7 @@ public class ToolBox { public void createDirectories(String... paths) throws IOException { if (paths.length == 0) throw new IllegalArgumentException("no directories specified"); - for (String p: paths) + for (String p : paths) Files.createDirectories(Paths.get(p)); } @@ -248,7 +255,7 @@ public class ToolBox { public void createDirectories(Path... paths) throws IOException { if (paths.length == 0) throw new IllegalArgumentException("no directories specified"); - for (Path p: paths) + for (Path p : paths) Files.createDirectories(p); } @@ -262,7 +269,7 @@ public class ToolBox { public void deleteFiles(String... files) throws IOException { if (files.length == 0) throw new IllegalArgumentException("no files specified"); - for (String file: files) + for (String file : files) Files.delete(Paths.get(file)); } @@ -392,7 +399,7 @@ public class ToolBox { public void writeJavaFiles(Path dir, String... contents) throws IOException { if (contents.length == 0) throw new IllegalArgumentException("no content specified for any files"); - for (String c: contents) { + for (String c : contents) { new JavaSource(c).write(dir); } } @@ -1090,7 +1097,7 @@ public class ToolBox { private List toFiles(String path) { List result = new ArrayList<>(); - for (String s: path.split(File.pathSeparator)) { + for (String s : path.split(File.pathSeparator)) { if (!s.isEmpty()) result.add(new File(s)); } @@ -1108,7 +1115,7 @@ public class ToolBox { if (fileObjects == null) return filesAsFileObjects; List combinedList = new ArrayList<>(); - for (JavaFileObject o: filesAsFileObjects) + for (JavaFileObject o : filesAsFileObjects) combinedList.add(o); combinedList.addAll(fileObjects); return combinedList; @@ -1308,6 +1315,7 @@ public class ToolBox { private String mainClass; private Path baseDir; private List paths; + private Set fileObjects; /** * Creates a task to write jar files, using API mode. @@ -1315,6 +1323,7 @@ public class ToolBox { public JarTask() { super(Mode.API); paths = Collections.emptyList(); + fileObjects = new LinkedHashSet<>(); } /** @@ -1391,6 +1400,53 @@ public class ToolBox { return this; } + /** + * Adds a set of file objects to be written into the jar file, by copying them + * from a Location in a JavaFileManager. + * The file objects to be written are specified by a series of paths; + * each path can be in one of the following forms: + *
    + *
  • The name of a class. For example, java.lang.Object. + * In this case, the corresponding .class file will be written to the jar file. + *
  • the name of a package followed by {@code .*}. For example, {@code java.lang.*}. + * In this case, all the class files in the specified package will be written to + * the jar file. + *
  • the name of a package followed by {@code .**}. For example, {@code java.lang.**}. + * In this case, all the class files in the specified package, and any subpackages + * will be written to the jar file. + *
+ * + * @param fm the file manager in which to find the file objects + * @param l the location in which to find the file objects + * @param paths the paths specifying the file objects to be copied + * @return this task object + * @throws IOException if errors occur while determining the set of file objects + */ + public JarTask files(JavaFileManager fm, Location l, String... paths) + throws IOException { + for (String p : paths) { + if (p.endsWith(".**")) + addPackage(fm, l, p.substring(0, p.length() - 3), true); + else if (p.endsWith(".*")) + addPackage(fm, l, p.substring(0, p.length() - 2), false); + else + addFile(fm, l, p); + } + return this; + } + + private void addPackage(JavaFileManager fm, Location l, String pkg, boolean recurse) + throws IOException { + for (JavaFileObject fo : fm.list(l, pkg, EnumSet.allOf(JavaFileObject.Kind.class), recurse)) { + fileObjects.add(fo); + } + } + + private void addFile(JavaFileManager fm, Location l, String path) throws IOException { + JavaFileObject fo = fm.getJavaFileForInput(l, path, Kind.CLASS); + fileObjects.add(fo); + } + /** * Provides limited jar command-like functionality. * The supported commands are: @@ -1464,42 +1520,19 @@ public class ToolBox { StreamOutput sysOut = new StreamOutput(System.out, System::setOut); StreamOutput sysErr = new StreamOutput(System.err, System::setErr); - int rc; Map outputMap = new HashMap<>(); try (OutputStream os = Files.newOutputStream(jar); JarOutputStream jos = openJar(os, m)) { - Path base = (baseDir == null) ? currDir : baseDir; - for (Path path: paths) { - Files.walkFileTree(base.resolve(path), new SimpleFileVisitor() { - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { - try { - String p = base.relativize(file) - .normalize() - .toString() - .replace(File.separatorChar, '/'); - JarEntry e = new JarEntry(p); - jos.putNextEntry(e); - jos.write(Files.readAllBytes(file)); - jos.closeEntry(); - return FileVisitResult.CONTINUE; - } catch (IOException e) { - System.err.println("Error adding " + file + " to jar file: " + e); - return FileVisitResult.TERMINATE; - } - } - }); - } - rc = 0; + writeFiles(jos); + writeFileObjects(jos); } catch (IOException e) { - System.err.println("Error opening " + jar + ": " + e); - rc = 1; + error("Exception while opening " + jar, e); } finally { outputMap.put(OutputKind.STDOUT, sysOut.close()); outputMap.put(OutputKind.STDERR, sysErr.close()); } - return checkExit(new Result(this, rc, outputMap)); + return checkExit(new Result(this, (errors == 0) ? 0 : 1, outputMap)); } private JarOutputStream openJar(OutputStream os, Manifest m) throws IOException { @@ -1512,6 +1545,79 @@ public class ToolBox { } } + private void writeFiles(JarOutputStream jos) throws IOException { + Path base = (baseDir == null) ? currDir : baseDir; + for (Path path : paths) { + Files.walkFileTree(base.resolve(path), new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + try { + String p = base.relativize(file) + .normalize() + .toString() + .replace(File.separatorChar, '/'); + JarEntry e = new JarEntry(p); + jos.putNextEntry(e); + try { + jos.write(Files.readAllBytes(file)); + } finally { + jos.closeEntry(); + } + return FileVisitResult.CONTINUE; + } catch (IOException e) { + error("Exception while adding " + file + " to jar file", e); + return FileVisitResult.TERMINATE; + } + } + }); + } + } + + private void writeFileObjects(JarOutputStream jos) throws IOException { + for (FileObject fo : fileObjects) { + String p = guessPath(fo); + JarEntry e = new JarEntry(p); + jos.putNextEntry(e); + try { + byte[] buf = new byte[1024]; + try (BufferedInputStream in = new BufferedInputStream(fo.openInputStream())) { + int n; + while ((n = in.read(buf)) > 0) + jos.write(buf, 0, n); + } catch (IOException ex) { + error("Exception while adding " + fo.getName() + " to jar file", ex); + } + } finally { + jos.closeEntry(); + } + } + } + + /* + * A jar: URL is of the form jar:URL!/entry where URL is a URL for the .jar file itself. + * In Symbol files (i.e. ct.sym) the underlying entry is prefixed META-INF/sym/. + */ + private final Pattern jarEntry = Pattern.compile(".*!/(?:META-INF/sym/[^/]+/)?(.*)"); + + private String guessPath(FileObject fo) { + URI u = fo.toUri(); + switch (u.getScheme()) { + case "jar": + Matcher m = jarEntry.matcher(u.getSchemeSpecificPart()); + if (m.matches()) { + return m.group(1); + } + break; + } + throw new IllegalArgumentException(fo.getName()); + } + + private void error(String message, Throwable t) { + out.println("Error: " + message + ": " + t); + errors++; + } + + private int errors; } /** From 1de8cf1b7f6c35f898e8441441b6eeb5d8cc9bb0 Mon Sep 17 00:00:00 2001 From: Maurizio Cimadamore Date: Sat, 8 Nov 2014 22:00:31 +0000 Subject: [PATCH 038/118] 8064367: Fix IntelliJ langtools support to use new dev build Update ant hooks in langtools IntelliJ project Reviewed-by: jlahoda --- langtools/make/intellij/build.xml | 4 +-- .../src/idea/LangtoolsIdeaAntLogger.java | 32 +++++++++++-------- langtools/make/intellij/workspace.xml | 22 ++++++------- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/langtools/make/intellij/build.xml b/langtools/make/intellij/build.xml index ed1db8342df..c67b66555d0 100644 --- a/langtools/make/intellij/build.xml +++ b/langtools/make/intellij/build.xml @@ -2,10 +2,8 @@ diff --git a/langtools/make/intellij/src/idea/LangtoolsIdeaAntLogger.java b/langtools/make/intellij/src/idea/LangtoolsIdeaAntLogger.java index 685b2fa89ec..0e6e9da4d71 100644 --- a/langtools/make/intellij/src/idea/LangtoolsIdeaAntLogger.java +++ b/langtools/make/intellij/src/idea/LangtoolsIdeaAntLogger.java @@ -26,7 +26,9 @@ package idea; import org.apache.tools.ant.BuildEvent; +import org.apache.tools.ant.BuildListener; import org.apache.tools.ant.DefaultLogger; +import org.apache.tools.ant.Project; import java.util.EnumSet; import java.util.Stack; @@ -166,21 +168,17 @@ public final class LangtoolsIdeaAntLogger extends DefaultLogger { } }, /** build bootstrap tool target - executed when bootstrapping javac */ - BUILD_BOOTSTRAP_TOOL("build-bootstrap-.*") { + BUILD_BOOTSTRAP_JAVAC("build-bootstrap-javac-classes") { @Override String getDisplayMessage(BuildEvent e) { - String targetName = e.getTarget().getName(); - String tool = targetName.split("-")[2]; - return "Building bootstrap " + tool + "..."; + return "Building bootstrap javac..."; } }, /** build classes target - executed when building classes of given tool */ - BUILD_TOOL("build-classes-.*") { + BUILD_ALL_CLASSES("build-all-classes") { @Override String getDisplayMessage(BuildEvent e) { - String targetName = e.getTarget().getName(); - String tool = targetName.split("-")[2]; - return "Building " + tool + "..."; + return "Building all classes..."; } }, /** synthetic target catching any other target not in this list */ @@ -195,14 +193,14 @@ public final class LangtoolsIdeaAntLogger extends DefaultLogger { } }; - String targetRegex; + String targetName; - Target(String targetRegex) { - this.targetRegex = targetRegex; + Target(String targetName) { + this.targetName = targetName; } boolean matches(String msg) { - return msg.matches(targetRegex); + return msg.equals(targetName); } abstract String getDisplayMessage(BuildEvent e); @@ -253,8 +251,14 @@ public final class LangtoolsIdeaAntLogger extends DefaultLogger { /** stack of pending tasks */ Stack tasks = new Stack<>(); - public LangtoolsIdeaAntLogger(DefaultLogger logger) { - this.logger = logger; + public LangtoolsIdeaAntLogger(Project project) { + for (Object o : project.getBuildListeners()) { + if (o instanceof DefaultLogger) { + this.logger = (DefaultLogger)o; + project.removeBuildListener((BuildListener)o); + project.addBuildListener(this); + } + } tasks.push(Task.ROOT); } diff --git a/langtools/make/intellij/workspace.xml b/langtools/make/intellij/workspace.xml index eb5e3adb181..6f613f966a6 100644 --- a/langtools/make/intellij/workspace.xml +++ b/langtools/make/intellij/workspace.xml @@ -10,7 +10,7 @@