From 07537703cc96d8eb5a78b7186d60ccab31eba2ad Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Thu, 4 Dec 2014 14:34:11 +0100 Subject: [PATCH 01/58] 8048170: Test closed/java/text/Normalizer/ConformanceTest.java failed PhaseIdealLoop::split_if_with_blocks_post() shoulnd't reorder range checks adjusted by range check smearing Reviewed-by: kvn, jrose --- hotspot/src/share/vm/opto/loopopts.cpp | 17 +++-- .../TestRangeCheckSmearingLoopOpts.java | 76 +++++++++++++++++++ 2 files changed, 87 insertions(+), 6 deletions(-) create mode 100644 hotspot/test/compiler/rangechecks/TestRangeCheckSmearingLoopOpts.java diff --git a/hotspot/src/share/vm/opto/loopopts.cpp b/hotspot/src/share/vm/opto/loopopts.cpp index bfa483c0181..e00a47dbfda 100644 --- a/hotspot/src/share/vm/opto/loopopts.cpp +++ b/hotspot/src/share/vm/opto/loopopts.cpp @@ -241,8 +241,13 @@ void PhaseIdealLoop::dominated_by( Node *prevdom, Node *iff, bool flip, bool exc ProjNode* dp_proj = dp->as_Proj(); ProjNode* unc_proj = iff->as_If()->proj_out(1 - dp_proj->_con)->as_Proj(); if (exclude_loop_predicate && - unc_proj->is_uncommon_trap_proj(Deoptimization::Reason_predicate)) + (unc_proj->is_uncommon_trap_proj(Deoptimization::Reason_predicate) || + unc_proj->is_uncommon_trap_proj(Deoptimization::Reason_range_check))) { + // If this is a range check (IfNode::is_range_check), do not + // reorder because Compile::allow_range_check_smearing might have + // changed the check. return; // Let IGVN transformation change control dependence. + } IdealLoopTree *old_loop = get_loop(dp); @@ -898,23 +903,23 @@ void PhaseIdealLoop::split_if_with_blocks_post( Node *n ) { int n_op = n->Opcode(); // Check for an IF being dominated by another IF same test - if( n_op == Op_If ) { + if (n_op == Op_If) { Node *bol = n->in(1); uint max = bol->outcnt(); // Check for same test used more than once? - if( n_op == Op_If && max > 1 && bol->is_Bool() ) { + if (max > 1 && bol->is_Bool()) { // Search up IDOMs to see if this IF is dominated. Node *cutoff = get_ctrl(bol); // Now search up IDOMs till cutoff, looking for a dominating test Node *prevdom = n; Node *dom = idom(prevdom); - while( dom != cutoff ) { - if( dom->req() > 1 && dom->in(1) == bol && prevdom->in(0) == dom ) { + while (dom != cutoff) { + if (dom->req() > 1 && dom->in(1) == bol && prevdom->in(0) == dom) { // Replace the dominated test with an obvious true or false. // Place it on the IGVN worklist for later cleanup. C->set_major_progress(); - dominated_by( prevdom, n, false, true ); + dominated_by(prevdom, n, false, true); #ifndef PRODUCT if( VerifyLoopOptimizations ) verify(); #endif diff --git a/hotspot/test/compiler/rangechecks/TestRangeCheckSmearingLoopOpts.java b/hotspot/test/compiler/rangechecks/TestRangeCheckSmearingLoopOpts.java new file mode 100644 index 00000000000..699754a8e8c --- /dev/null +++ b/hotspot/test/compiler/rangechecks/TestRangeCheckSmearingLoopOpts.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 8048170 + * @summary Following range check smearing, range check cannot be replaced by dominating identical test. + * @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement TestRangeCheckSmearingLoopOpts + * + */ +public class TestRangeCheckSmearingLoopOpts { + + static int dummy; + + static int m1(int[] array, int i) { + for (;;) { + for (;;) { + if (array[i] < 0) { // range check (i+0) dominates equivalent check below + break; + } + i++; + } + + // A control flow that stops IfNode::up_one_dom() + if ((i % 2)== 0) { + if ((array[i] % 2) == 0) { + dummy = i; + } + } + + // IfNode::Ideal will rewrite some range checks if Compile::allow_range_check_smearing + if (array[i-1] == 9) { // range check (i-1) unchanged + int res = array[i-3]; // range check (i-3) unchanged + res += array[i]; // range check (i+0) unchanged + res += array[i-2]; // removed redundant range check + // the previous access might be hoisted by + // PhaseIdealLoop::split_if_with_blocks_post because + // it appears to have the same guard, but it also + // depends on the previous guards + return res; + } + i++; + } + } + + static public void main(String[] args) { + int[] array = { 0, 1, 2, -3, 4, 5, -2, 7, 8, 9, -1 }; + for (int i = 0; i < 20000; i++) { + m1(array, 0); + } + array[0] = -1; + try { + m1(array, 0); + } catch(ArrayIndexOutOfBoundsException aioobe) {} + } +} From 15dcd41e8753f7d1827f0970baf6d465bfa65c94 Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Tue, 25 Nov 2014 17:33:59 +0100 Subject: [PATCH 02/58] 6898462: The escape analysis with G1 cause crash assertion src/share/vm/runtime/vframeArray.cpp:94 OOM during reallocation of scalar replaced objects in deoptimization causes crashes Reviewed-by: kvn, jrose --- .../vm/interpreter/interpreterRuntime.cpp | 12 + hotspot/src/share/vm/memory/universe.cpp | 9 +- hotspot/src/share/vm/memory/universe.hpp | 2 + hotspot/src/share/vm/opto/macro.cpp | 6 +- .../src/share/vm/runtime/deoptimization.cpp | 141 ++++-- .../src/share/vm/runtime/deoptimization.hpp | 9 +- .../src/share/vm/runtime/sharedRuntime.cpp | 1 + hotspot/src/share/vm/runtime/thread.cpp | 1 + hotspot/src/share/vm/runtime/thread.hpp | 10 + hotspot/src/share/vm/runtime/vframeArray.cpp | 33 +- hotspot/src/share/vm/runtime/vframeArray.hpp | 16 +- .../compiler/uncommontrap/TestDeoptOOM.java | 426 ++++++++++++++++++ 12 files changed, 602 insertions(+), 64 deletions(-) create mode 100644 hotspot/test/compiler/uncommontrap/TestDeoptOOM.java diff --git a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp index 53be23d848b..1363b555132 100644 --- a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp +++ b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp @@ -385,6 +385,18 @@ IRT_ENTRY(address, InterpreterRuntime::exception_handler_for_exception(JavaThrea int handler_bci; int current_bci = bci(thread); + if (thread->frames_to_pop_failed_realloc() > 0) { + // Allocation of scalar replaced object used in this frame + // failed. Unconditionally pop the frame. + thread->dec_frames_to_pop_failed_realloc(); + thread->set_vm_result(h_exception()); + // If the method is synchronized we already unlocked the monitor + // during deoptimization so the interpreter needs to skip it when + // the frame is popped. + thread->set_do_not_unlock_if_synchronized(true); + return Interpreter::remove_activation_entry(); + } + // Need to do this check first since when _do_not_unlock_if_synchronized // is set, we don't want to trigger any classloading which may make calls // into java, or surprisingly find a matching exception handler for bci 0 diff --git a/hotspot/src/share/vm/memory/universe.cpp b/hotspot/src/share/vm/memory/universe.cpp index c731140cd5f..61396a28c2b 100644 --- a/hotspot/src/share/vm/memory/universe.cpp +++ b/hotspot/src/share/vm/memory/universe.cpp @@ -120,6 +120,7 @@ oop Universe::_out_of_memory_error_metaspace = NULL; oop Universe::_out_of_memory_error_class_metaspace = NULL; oop Universe::_out_of_memory_error_array_size = NULL; oop Universe::_out_of_memory_error_gc_overhead_limit = NULL; +oop Universe::_out_of_memory_error_realloc_objects = NULL; objArrayOop Universe::_preallocated_out_of_memory_error_array = NULL; volatile jint Universe::_preallocated_out_of_memory_error_avail_count = 0; bool Universe::_verify_in_progress = false; @@ -191,6 +192,7 @@ void Universe::oops_do(OopClosure* f, bool do_all) { f->do_oop((oop*)&_out_of_memory_error_class_metaspace); f->do_oop((oop*)&_out_of_memory_error_array_size); f->do_oop((oop*)&_out_of_memory_error_gc_overhead_limit); + f->do_oop((oop*)&_out_of_memory_error_realloc_objects); f->do_oop((oop*)&_preallocated_out_of_memory_error_array); f->do_oop((oop*)&_null_ptr_exception_instance); f->do_oop((oop*)&_arithmetic_exception_instance); @@ -575,7 +577,8 @@ bool Universe::should_fill_in_stack_trace(Handle throwable) { (throwable() != Universe::_out_of_memory_error_metaspace) && (throwable() != Universe::_out_of_memory_error_class_metaspace) && (throwable() != Universe::_out_of_memory_error_array_size) && - (throwable() != Universe::_out_of_memory_error_gc_overhead_limit)); + (throwable() != Universe::_out_of_memory_error_gc_overhead_limit) && + (throwable() != Universe::_out_of_memory_error_realloc_objects)); } @@ -1039,6 +1042,7 @@ bool universe_post_init() { Universe::_out_of_memory_error_array_size = k_h->allocate_instance(CHECK_false); Universe::_out_of_memory_error_gc_overhead_limit = k_h->allocate_instance(CHECK_false); + Universe::_out_of_memory_error_realloc_objects = k_h->allocate_instance(CHECK_false); // Setup preallocated NullPointerException // (this is currently used for a cheap & dirty solution in compiler exception handling) @@ -1078,6 +1082,9 @@ bool universe_post_init() { msg = java_lang_String::create_from_str("GC overhead limit exceeded", CHECK_false); java_lang_Throwable::set_message(Universe::_out_of_memory_error_gc_overhead_limit, msg()); + msg = java_lang_String::create_from_str("Java heap space: failed reallocation of scalar replaced objects", CHECK_false); + java_lang_Throwable::set_message(Universe::_out_of_memory_error_realloc_objects, msg()); + msg = java_lang_String::create_from_str("/ by zero", CHECK_false); java_lang_Throwable::set_message(Universe::_arithmetic_exception_instance, msg()); diff --git a/hotspot/src/share/vm/memory/universe.hpp b/hotspot/src/share/vm/memory/universe.hpp index 40e2d03b6d4..52334d0dbce 100644 --- a/hotspot/src/share/vm/memory/universe.hpp +++ b/hotspot/src/share/vm/memory/universe.hpp @@ -157,6 +157,7 @@ class Universe: AllStatic { static oop _out_of_memory_error_class_metaspace; static oop _out_of_memory_error_array_size; static oop _out_of_memory_error_gc_overhead_limit; + static oop _out_of_memory_error_realloc_objects; static Array* _the_empty_int_array; // Canonicalized int array static Array* _the_empty_short_array; // Canonicalized short array @@ -328,6 +329,7 @@ class Universe: AllStatic { static oop out_of_memory_error_class_metaspace() { return gen_out_of_memory_error(_out_of_memory_error_class_metaspace); } static oop out_of_memory_error_array_size() { return gen_out_of_memory_error(_out_of_memory_error_array_size); } static oop out_of_memory_error_gc_overhead_limit() { return gen_out_of_memory_error(_out_of_memory_error_gc_overhead_limit); } + static oop out_of_memory_error_realloc_objects() { return gen_out_of_memory_error(_out_of_memory_error_realloc_objects); } // Accessors needed for fast allocation static Klass** boolArrayKlassObj_addr() { return &_boolArrayKlassObj; } diff --git a/hotspot/src/share/vm/opto/macro.cpp b/hotspot/src/share/vm/opto/macro.cpp index 073945c833d..b393745620b 100644 --- a/hotspot/src/share/vm/opto/macro.cpp +++ b/hotspot/src/share/vm/opto/macro.cpp @@ -971,7 +971,11 @@ void PhaseMacroExpand::process_users_of_allocation(CallNode *alloc) { } bool PhaseMacroExpand::eliminate_allocate_node(AllocateNode *alloc) { - if (!EliminateAllocations || !alloc->_is_non_escaping) { + // Don't do scalar replacement if the frame can be popped by JVMTI: + // if reallocation fails during deoptimization we'll pop all + // interpreter frames for this compiled frame and that won't play + // nice with JVMTI popframe. + if (!EliminateAllocations || JvmtiExport::can_pop_frame() || !alloc->_is_non_escaping) { return false; } Node* klass = alloc->in(AllocateNode::KlassNode); diff --git a/hotspot/src/share/vm/runtime/deoptimization.cpp b/hotspot/src/share/vm/runtime/deoptimization.cpp index 75da1fef019..03e272fd690 100644 --- a/hotspot/src/share/vm/runtime/deoptimization.cpp +++ b/hotspot/src/share/vm/runtime/deoptimization.cpp @@ -176,6 +176,8 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread assert(vf->is_compiled_frame(), "Wrong frame type"); chunk->push(compiledVFrame::cast(vf)); + bool realloc_failures = false; + #ifdef COMPILER2 // Reallocate the non-escaping objects and restore their fields. Then // relock objects if synchronization on them was eliminated. @@ -206,22 +208,19 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread tty->print_cr("SAVED OOP RESULT " INTPTR_FORMAT " in thread " INTPTR_FORMAT, (void *)result, thread); } } - bool reallocated = false; if (objects != NULL) { JRT_BLOCK - reallocated = realloc_objects(thread, &deoptee, objects, THREAD); + realloc_failures = realloc_objects(thread, &deoptee, objects, THREAD); JRT_END + reassign_fields(&deoptee, &map, objects, realloc_failures); } - if (reallocated) { - reassign_fields(&deoptee, &map, objects); #ifndef PRODUCT - if (TraceDeoptimization) { - ttyLocker ttyl; - tty->print_cr("REALLOC OBJECTS in thread " INTPTR_FORMAT, thread); - print_objects(objects); - } -#endif + if (TraceDeoptimization) { + ttyLocker ttyl; + tty->print_cr("REALLOC OBJECTS in thread " INTPTR_FORMAT, thread); + print_objects(objects, realloc_failures); } +#endif if (save_oop_result) { // Restore result. deoptee.set_saved_oop_result(&map, return_value()); @@ -236,7 +235,7 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread assert (cvf->scope() != NULL,"expect only compiled java frames"); GrowableArray* monitors = cvf->monitors(); if (monitors->is_nonempty()) { - relock_objects(monitors, thread); + relock_objects(monitors, thread, realloc_failures); #ifndef PRODUCT if (TraceDeoptimization) { ttyLocker ttyl; @@ -247,7 +246,12 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread first = false; tty->print_cr("RELOCK OBJECTS in thread " INTPTR_FORMAT, thread); } - tty->print_cr(" object <" INTPTR_FORMAT "> locked", (void *)mi->owner()); + if (mi->owner_is_scalar_replaced()) { + Klass* k = java_lang_Class::as_Klass(mi->owner_klass()); + tty->print_cr(" failed reallocation for klass %s", k->external_name()); + } else { + tty->print_cr(" object <" INTPTR_FORMAT "> locked", (void *)mi->owner()); + } } } } @@ -262,9 +266,14 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread // out the java state residing in the vframeArray will be missed. No_Safepoint_Verifier no_safepoint; - vframeArray* array = create_vframeArray(thread, deoptee, &map, chunk); + vframeArray* array = create_vframeArray(thread, deoptee, &map, chunk, realloc_failures); +#ifdef COMPILER2 + if (realloc_failures) { + pop_frames_failed_reallocs(thread, array); + } +#endif - assert(thread->vframe_array_head() == NULL, "Pending deopt!");; + assert(thread->vframe_array_head() == NULL, "Pending deopt!"); thread->set_vframe_array_head(array); // Now that the vframeArray has been created if we have any deferred local writes @@ -718,6 +727,8 @@ bool Deoptimization::realloc_objects(JavaThread* thread, frame* fr, GrowableArra int exception_line = thread->exception_line(); thread->clear_pending_exception(); + bool failures = false; + for (int i = 0; i < objects->length(); i++) { assert(objects->at(i)->is_object(), "invalid debug information"); ObjectValue* sv = (ObjectValue*) objects->at(i); @@ -727,27 +738,34 @@ bool Deoptimization::realloc_objects(JavaThread* thread, frame* fr, GrowableArra if (k->oop_is_instance()) { InstanceKlass* ik = InstanceKlass::cast(k()); - obj = ik->allocate_instance(CHECK_(false)); + obj = ik->allocate_instance(THREAD); } else if (k->oop_is_typeArray()) { TypeArrayKlass* ak = TypeArrayKlass::cast(k()); assert(sv->field_size() % type2size[ak->element_type()] == 0, "non-integral array length"); int len = sv->field_size() / type2size[ak->element_type()]; - obj = ak->allocate(len, CHECK_(false)); + obj = ak->allocate(len, THREAD); } else if (k->oop_is_objArray()) { ObjArrayKlass* ak = ObjArrayKlass::cast(k()); - obj = ak->allocate(sv->field_size(), CHECK_(false)); + obj = ak->allocate(sv->field_size(), THREAD); + } + + if (obj == NULL) { + failures = true; } - assert(obj != NULL, "allocation failed"); assert(sv->value().is_null(), "redundant reallocation"); + assert(obj != NULL || HAS_PENDING_EXCEPTION, "allocation should succeed or we should get an exception"); + CLEAR_PENDING_EXCEPTION; sv->set_value(obj); } - if (pending_exception.not_null()) { + if (failures) { + THROW_OOP_(Universe::out_of_memory_error_realloc_objects(), failures); + } else if (pending_exception.not_null()) { thread->set_pending_exception(pending_exception(), exception_file, exception_line); } - return true; + return failures; } // This assumes that the fields are stored in ObjectValue in the same order @@ -885,12 +903,15 @@ void Deoptimization::reassign_object_array_elements(frame* fr, RegisterMap* reg_ // restore fields of all eliminated objects and arrays -void Deoptimization::reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray* objects) { +void Deoptimization::reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray* objects, bool realloc_failures) { for (int i = 0; i < objects->length(); i++) { ObjectValue* sv = (ObjectValue*) objects->at(i); KlassHandle k(java_lang_Class::as_Klass(sv->klass()->as_ConstantOopReadValue()->value()())); Handle obj = sv->value(); - assert(obj.not_null(), "reallocation was missed"); + assert(obj.not_null() || realloc_failures, "reallocation was missed"); + if (obj.is_null()) { + continue; + } if (k->oop_is_instance()) { InstanceKlass* ik = InstanceKlass::cast(k()); @@ -907,34 +928,36 @@ void Deoptimization::reassign_fields(frame* fr, RegisterMap* reg_map, GrowableAr // relock objects for which synchronization was eliminated -void Deoptimization::relock_objects(GrowableArray* monitors, JavaThread* thread) { +void Deoptimization::relock_objects(GrowableArray* monitors, JavaThread* thread, bool realloc_failures) { for (int i = 0; i < monitors->length(); i++) { MonitorInfo* mon_info = monitors->at(i); if (mon_info->eliminated()) { - assert(mon_info->owner() != NULL, "reallocation was missed"); - Handle obj = Handle(mon_info->owner()); - markOop mark = obj->mark(); - if (UseBiasedLocking && mark->has_bias_pattern()) { - // New allocated objects may have the mark set to anonymously biased. - // Also the deoptimized method may called methods with synchronization - // where the thread-local object is bias locked to the current thread. - assert(mark->is_biased_anonymously() || - mark->biased_locker() == thread, "should be locked to current thread"); - // Reset mark word to unbiased prototype. - markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age()); - obj->set_mark(unbiased_prototype); + assert(!mon_info->owner_is_scalar_replaced() || realloc_failures, "reallocation was missed"); + if (!mon_info->owner_is_scalar_replaced()) { + Handle obj = Handle(mon_info->owner()); + markOop mark = obj->mark(); + if (UseBiasedLocking && mark->has_bias_pattern()) { + // New allocated objects may have the mark set to anonymously biased. + // Also the deoptimized method may called methods with synchronization + // where the thread-local object is bias locked to the current thread. + assert(mark->is_biased_anonymously() || + mark->biased_locker() == thread, "should be locked to current thread"); + // Reset mark word to unbiased prototype. + markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age()); + obj->set_mark(unbiased_prototype); + } + BasicLock* lock = mon_info->lock(); + ObjectSynchronizer::slow_enter(obj, lock, thread); + assert(mon_info->owner()->is_locked(), "object must be locked now"); } - BasicLock* lock = mon_info->lock(); - ObjectSynchronizer::slow_enter(obj, lock, thread); } - assert(mon_info->owner()->is_locked(), "object must be locked now"); } } #ifndef PRODUCT // print information about reallocated objects -void Deoptimization::print_objects(GrowableArray* objects) { +void Deoptimization::print_objects(GrowableArray* objects, bool realloc_failures) { fieldDescriptor fd; for (int i = 0; i < objects->length(); i++) { @@ -944,10 +967,15 @@ void Deoptimization::print_objects(GrowableArray* objects) { tty->print(" object <" INTPTR_FORMAT "> of type ", (void *)sv->value()()); k->print_value(); - tty->print(" allocated (%d bytes)", obj->size() * HeapWordSize); + assert(obj.not_null() || realloc_failures, "reallocation was missed"); + if (obj.is_null()) { + tty->print(" allocation failed"); + } else { + tty->print(" allocated (%d bytes)", obj->size() * HeapWordSize); + } tty->cr(); - if (Verbose) { + if (Verbose && !obj.is_null()) { k->oop_print_on(obj(), tty); } } @@ -955,7 +983,7 @@ void Deoptimization::print_objects(GrowableArray* objects) { #endif #endif // COMPILER2 -vframeArray* Deoptimization::create_vframeArray(JavaThread* thread, frame fr, RegisterMap *reg_map, GrowableArray* chunk) { +vframeArray* Deoptimization::create_vframeArray(JavaThread* thread, frame fr, RegisterMap *reg_map, GrowableArray* chunk, bool realloc_failures) { Events::log(thread, "DEOPT PACKING pc=" INTPTR_FORMAT " sp=" INTPTR_FORMAT, fr.pc(), fr.sp()); #ifndef PRODUCT @@ -998,7 +1026,7 @@ vframeArray* Deoptimization::create_vframeArray(JavaThread* thread, frame fr, Re // Since the Java thread being deoptimized will eventually adjust it's own stack, // the vframeArray containing the unpacking information is allocated in the C heap. // For Compiler1, the caller of the deoptimized frame is saved for use by unpack_frames(). - vframeArray* array = vframeArray::allocate(thread, frame_size, chunk, reg_map, sender, caller, fr); + vframeArray* array = vframeArray::allocate(thread, frame_size, chunk, reg_map, sender, caller, fr, realloc_failures); // Compare the vframeArray to the collected vframes assert(array->structural_compare(thread, chunk), "just checking"); @@ -1013,6 +1041,33 @@ vframeArray* Deoptimization::create_vframeArray(JavaThread* thread, frame fr, Re return array; } +#ifdef COMPILER2 +void Deoptimization::pop_frames_failed_reallocs(JavaThread* thread, vframeArray* array) { + // Reallocation of some scalar replaced objects failed. Record + // that we need to pop all the interpreter frames for the + // deoptimized compiled frame. + assert(thread->frames_to_pop_failed_realloc() == 0, "missed frames to pop?"); + thread->set_frames_to_pop_failed_realloc(array->frames()); + // Unlock all monitors here otherwise the interpreter will see a + // mix of locked and unlocked monitors (because of failed + // reallocations of synchronized objects) and be confused. + for (int i = 0; i < array->frames(); i++) { + MonitorChunk* monitors = array->element(i)->monitors(); + if (monitors != NULL) { + for (int j = 0; j < monitors->number_of_monitors(); j++) { + BasicObjectLock* src = monitors->at(j); + if (src->obj() != NULL) { + ObjectSynchronizer::fast_exit(src->obj(), src->lock(), thread); + } + } + array->element(i)->free_monitors(thread); +#ifdef ASSERT + array->element(i)->set_removed_monitors(); +#endif + } + } +} +#endif static void collect_monitors(compiledVFrame* cvf, GrowableArray* objects_to_revoke) { GrowableArray* monitors = cvf->monitors(); diff --git a/hotspot/src/share/vm/runtime/deoptimization.hpp b/hotspot/src/share/vm/runtime/deoptimization.hpp index ee461196bd0..e04ad5a0043 100644 --- a/hotspot/src/share/vm/runtime/deoptimization.hpp +++ b/hotspot/src/share/vm/runtime/deoptimization.hpp @@ -125,13 +125,14 @@ class Deoptimization : AllStatic { static bool realloc_objects(JavaThread* thread, frame* fr, GrowableArray* objects, TRAPS); static void reassign_type_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, typeArrayOop obj, BasicType type); static void reassign_object_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, objArrayOop obj); - static void reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray* objects); - static void relock_objects(GrowableArray* monitors, JavaThread* thread); - NOT_PRODUCT(static void print_objects(GrowableArray* objects);) + static void reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray* objects, bool realloc_failures); + static void relock_objects(GrowableArray* monitors, JavaThread* thread, bool realloc_failures); + static void pop_frames_failed_reallocs(JavaThread* thread, vframeArray* array); + NOT_PRODUCT(static void print_objects(GrowableArray* objects, bool realloc_failures);) #endif // COMPILER2 public: - static vframeArray* create_vframeArray(JavaThread* thread, frame fr, RegisterMap *reg_map, GrowableArray* chunk); + static vframeArray* create_vframeArray(JavaThread* thread, frame fr, RegisterMap *reg_map, GrowableArray* chunk, bool realloc_failures); // Interface used for unpacking deoptimized frames diff --git a/hotspot/src/share/vm/runtime/sharedRuntime.cpp b/hotspot/src/share/vm/runtime/sharedRuntime.cpp index 409a867afaf..fa1f5672292 100644 --- a/hotspot/src/share/vm/runtime/sharedRuntime.cpp +++ b/hotspot/src/share/vm/runtime/sharedRuntime.cpp @@ -456,6 +456,7 @@ JRT_END address SharedRuntime::raw_exception_handler_for_return_address(JavaThread* thread, address return_address) { assert(frame::verify_return_pc(return_address), err_msg("must be a return address: " INTPTR_FORMAT, return_address)); + assert(thread->frames_to_pop_failed_realloc() == 0 || Interpreter::contains(return_address), "missed frames to pop?"); // Reset method handle flag. thread->set_is_method_handle_return(false); diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp index 6ce71c36001..b6a38e38f05 100644 --- a/hotspot/src/share/vm/runtime/thread.cpp +++ b/hotspot/src/share/vm/runtime/thread.cpp @@ -1448,6 +1448,7 @@ void JavaThread::initialize() { _popframe_condition = popframe_inactive; _popframe_preserved_args = NULL; _popframe_preserved_args_size = 0; + _frames_to_pop_failed_realloc = 0; pd_initialize(); } diff --git a/hotspot/src/share/vm/runtime/thread.hpp b/hotspot/src/share/vm/runtime/thread.hpp index 44e10aae90b..399a2cbe671 100644 --- a/hotspot/src/share/vm/runtime/thread.hpp +++ b/hotspot/src/share/vm/runtime/thread.hpp @@ -908,6 +908,12 @@ class JavaThread: public Thread { // This is set to popframe_pending to signal that top Java frame should be popped immediately int _popframe_condition; + // If reallocation of scalar replaced objects fails, we throw OOM + // and during exception propagation, pop the top + // _frames_to_pop_failed_realloc frames, the ones that reference + // failed reallocations. + int _frames_to_pop_failed_realloc; + #ifndef PRODUCT int _jmp_ring_index; struct { @@ -1567,6 +1573,10 @@ class JavaThread: public Thread { void clr_pop_frame_in_process(void) { _popframe_condition &= ~popframe_processing_bit; } #endif + int frames_to_pop_failed_realloc() const { return _frames_to_pop_failed_realloc; } + void set_frames_to_pop_failed_realloc(int nb) { _frames_to_pop_failed_realloc = nb; } + void dec_frames_to_pop_failed_realloc() { _frames_to_pop_failed_realloc--; } + private: // Saved incoming arguments to popped frame. // Used only when popped interpreted frame returns to deoptimized frame. diff --git a/hotspot/src/share/vm/runtime/vframeArray.cpp b/hotspot/src/share/vm/runtime/vframeArray.cpp index 6a3652c0ea1..d3e989e59e2 100644 --- a/hotspot/src/share/vm/runtime/vframeArray.cpp +++ b/hotspot/src/share/vm/runtime/vframeArray.cpp @@ -57,7 +57,7 @@ void vframeArrayElement::free_monitors(JavaThread* jt) { } } -void vframeArrayElement::fill_in(compiledVFrame* vf) { +void vframeArrayElement::fill_in(compiledVFrame* vf, bool realloc_failures) { // Copy the information from the compiled vframe to the // interpreter frame we will be creating to replace vf @@ -65,6 +65,9 @@ void vframeArrayElement::fill_in(compiledVFrame* vf) { _method = vf->method(); _bci = vf->raw_bci(); _reexecute = vf->should_reexecute(); +#ifdef ASSERT + _removed_monitors = false; +#endif int index; @@ -82,11 +85,15 @@ void vframeArrayElement::fill_in(compiledVFrame* vf) { // Migrate the BasicLocks from the stack to the monitor chunk for (index = 0; index < list->length(); index++) { MonitorInfo* monitor = list->at(index); - assert(!monitor->owner_is_scalar_replaced(), "object should be reallocated already"); - assert(monitor->owner() == NULL || (!monitor->owner()->is_unlocked() && !monitor->owner()->has_bias_pattern()), "object must be null or locked, and unbiased"); + assert(!monitor->owner_is_scalar_replaced() || realloc_failures, "object should be reallocated already"); BasicObjectLock* dest = _monitors->at(index); - dest->set_obj(monitor->owner()); - monitor->lock()->move_to(monitor->owner(), dest->lock()); + if (monitor->owner_is_scalar_replaced()) { + dest->set_obj(NULL); + } else { + assert(monitor->owner() == NULL || (!monitor->owner()->is_unlocked() && !monitor->owner()->has_bias_pattern()), "object must be null or locked, and unbiased"); + dest->set_obj(monitor->owner()); + monitor->lock()->move_to(monitor->owner(), dest->lock()); + } } } @@ -111,7 +118,7 @@ void vframeArrayElement::fill_in(compiledVFrame* vf) { StackValue* value = locs->at(index); switch(value->type()) { case T_OBJECT: - assert(!value->obj_is_scalar_replaced(), "object should be reallocated already"); + assert(!value->obj_is_scalar_replaced() || realloc_failures, "object should be reallocated already"); // preserve object type _locals->add( new StackValue(cast_from_oop((value->get_obj()())), T_OBJECT )); break; @@ -136,7 +143,7 @@ void vframeArrayElement::fill_in(compiledVFrame* vf) { StackValue* value = exprs->at(index); switch(value->type()) { case T_OBJECT: - assert(!value->obj_is_scalar_replaced(), "object should be reallocated already"); + assert(!value->obj_is_scalar_replaced() || realloc_failures, "object should be reallocated already"); // preserve object type _expressions->add( new StackValue(cast_from_oop((value->get_obj()())), T_OBJECT )); break; @@ -287,7 +294,7 @@ void vframeArrayElement::unpack_on_stack(int caller_actual_parameters, _frame.patch_pc(thread, pc); - assert (!method()->is_synchronized() || locks > 0, "synchronized methods must have monitors"); + assert (!method()->is_synchronized() || locks > 0 || _removed_monitors, "synchronized methods must have monitors"); BasicObjectLock* top = iframe()->interpreter_frame_monitor_begin(); for (int index = 0; index < locks; index++) { @@ -439,7 +446,8 @@ int vframeArrayElement::on_stack_size(int callee_parameters, vframeArray* vframeArray::allocate(JavaThread* thread, int frame_size, GrowableArray* chunk, - RegisterMap *reg_map, frame sender, frame caller, frame self) { + RegisterMap *reg_map, frame sender, frame caller, frame self, + bool realloc_failures) { // Allocate the vframeArray vframeArray * result = (vframeArray*) AllocateHeap(sizeof(vframeArray) + // fixed part @@ -451,19 +459,20 @@ vframeArray* vframeArray::allocate(JavaThread* thread, int frame_size, GrowableA result->_caller = caller; result->_original = self; result->set_unroll_block(NULL); // initialize it - result->fill_in(thread, frame_size, chunk, reg_map); + result->fill_in(thread, frame_size, chunk, reg_map, realloc_failures); return result; } void vframeArray::fill_in(JavaThread* thread, int frame_size, GrowableArray* chunk, - const RegisterMap *reg_map) { + const RegisterMap *reg_map, + bool realloc_failures) { // Set owner first, it is used when adding monitor chunks _frame_size = frame_size; for(int i = 0; i < chunk->length(); i++) { - element(i)->fill_in(chunk->at(i)); + element(i)->fill_in(chunk->at(i), realloc_failures); } // Copy registers for callee-saved registers diff --git a/hotspot/src/share/vm/runtime/vframeArray.hpp b/hotspot/src/share/vm/runtime/vframeArray.hpp index e4665c40d36..890c386ba75 100644 --- a/hotspot/src/share/vm/runtime/vframeArray.hpp +++ b/hotspot/src/share/vm/runtime/vframeArray.hpp @@ -58,6 +58,9 @@ class vframeArrayElement : public _ValueObj { MonitorChunk* _monitors; // active monitors for this vframe StackValueCollection* _locals; StackValueCollection* _expressions; +#ifdef ASSERT + bool _removed_monitors; +#endif public: @@ -78,7 +81,7 @@ class vframeArrayElement : public _ValueObj { StackValueCollection* expressions(void) const { return _expressions; } - void fill_in(compiledVFrame* vf); + void fill_in(compiledVFrame* vf, bool realloc_failures); // Formerly part of deoptimizedVFrame @@ -99,6 +102,12 @@ class vframeArrayElement : public _ValueObj { bool is_bottom_frame, int exec_mode); +#ifdef ASSERT + void set_removed_monitors() { + _removed_monitors = true; + } +#endif + #ifndef PRODUCT void print(outputStream* st); #endif /* PRODUCT */ @@ -160,13 +169,14 @@ class vframeArray: public CHeapObj { int frames() const { return _frames; } static vframeArray* allocate(JavaThread* thread, int frame_size, GrowableArray* chunk, - RegisterMap* reg_map, frame sender, frame caller, frame self); + RegisterMap* reg_map, frame sender, frame caller, frame self, + bool realloc_failures); vframeArrayElement* element(int index) { assert(is_within_bounds(index), "Bad index"); return &_elements[index]; } // Allocates a new vframe in the array and fills the array with vframe information in chunk - void fill_in(JavaThread* thread, int frame_size, GrowableArray* chunk, const RegisterMap *reg_map); + void fill_in(JavaThread* thread, int frame_size, GrowableArray* chunk, const RegisterMap *reg_map, bool realloc_failures); // Returns the owner of this vframeArray JavaThread* owner_thread() const { return _owner_thread; } diff --git a/hotspot/test/compiler/uncommontrap/TestDeoptOOM.java b/hotspot/test/compiler/uncommontrap/TestDeoptOOM.java new file mode 100644 index 00000000000..5342582fb00 --- /dev/null +++ b/hotspot/test/compiler/uncommontrap/TestDeoptOOM.java @@ -0,0 +1,426 @@ +/* + * 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 6898462 + * @summary failed reallocations of scalar replaced objects during deoptimization causes crash + * @run main/othervm -XX:-BackgroundCompilation -XX:CompileCommand=exclude,TestDeoptOOM::main -XX:CompileCommand=exclude,TestDeoptOOM::m9_1 -Xmx128M TestDeoptOOM + * + */ + +public class TestDeoptOOM { + + long f1; + long f2; + long f3; + long f4; + long f5; + + static class LinkedList { + LinkedList l; + long[] array; + LinkedList(LinkedList l, int size) { + array = new long[size]; + this.l = l; + } + } + + static LinkedList ll; + + static void consume_all_memory() { + int size = 128 * 1024 * 1024; + while(size > 0) { + try { + while(true) { + ll = new LinkedList(ll, size); + } + } catch(OutOfMemoryError oom) { + } + size = size / 2; + } + } + + static void free_memory() { + ll = null; + } + + static TestDeoptOOM m1(boolean deopt) { + try { + TestDeoptOOM tdoom = new TestDeoptOOM(); + if (deopt) { + return tdoom; + } + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m1"); + } + return null; + } + + static TestDeoptOOM m2_1(boolean deopt) { + try { + TestDeoptOOM tdoom = new TestDeoptOOM(); + if (deopt) { + return tdoom; + } + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m2_1"); + } + return null; + } + + static TestDeoptOOM m2(boolean deopt) { + try { + return m2_1(deopt); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m2"); + } + return null; + } + + static TestDeoptOOM m3_3(boolean deopt) { + try { + TestDeoptOOM tdoom = new TestDeoptOOM(); + if (deopt) { + return tdoom; + } + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m3_3"); + } + return null; + } + + static boolean m3_2(boolean deopt) { + try { + return m3_3(deopt) != null; + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m3_2"); + } + return false; + } + + static TestDeoptOOM m3_1(boolean deopt) { + try { + TestDeoptOOM tdoom = new TestDeoptOOM(); + if (m3_2(deopt)) { + return tdoom; + } + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m3_1"); + } + return null; + } + + static TestDeoptOOM m3(boolean deopt) { + try { + return m3_1(deopt); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m3"); + } + return null; + } + + static TestDeoptOOM m4(boolean deopt) { + try { + TestDeoptOOM tdoom = new TestDeoptOOM(); + if (deopt) { + tdoom.f1 = 1l; + tdoom.f2 = 2l; + tdoom.f3 = 3l; + return tdoom; + } + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m4"); + } + return null; + } + + static TestDeoptOOM m5(boolean deopt) { + try { + TestDeoptOOM tdoom = new TestDeoptOOM(); + synchronized(tdoom) { + if (deopt) { + return tdoom; + } + } + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m5"); + } + return null; + } + + synchronized TestDeoptOOM m6_1(boolean deopt) { + if (deopt) { + return this; + } + return null; + } + + static TestDeoptOOM m6(boolean deopt) { + try { + TestDeoptOOM tdoom = new TestDeoptOOM(); + return tdoom.m6_1(deopt); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m6"); + } + return null; + } + + static TestDeoptOOM m7_1(boolean deopt, Object lock) { + try { + synchronized(lock) { + TestDeoptOOM tdoom = new TestDeoptOOM(); + if (deopt) { + return tdoom; + } + } + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m7_1"); + } + return null; + } + + static TestDeoptOOM m7(boolean deopt, Object lock) { + try { + return m7_1(deopt, lock); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m7"); + } + return null; + } + + static class A { + long f1; + long f2; + long f3; + long f4; + long f5; + } + + static class B { + long f1; + long f2; + long f3; + long f4; + long f5; + + A a; + } + + static B m8(boolean deopt) { + try { + A a = new A(); + B b = new B(); + b.a = a; + if (deopt) { + return b; + } + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m8"); + } + return null; + } + + static void m9_1(int i) { + if (i > 90000) { + consume_all_memory(); + } + } + + static TestDeoptOOM m9() { + try { + for (int i = 0; i < 100000; i++) { + TestDeoptOOM tdoom = new TestDeoptOOM(); + m9_1(i); + if (i > 90000) { + return tdoom; + } + } + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m1"); + } + return null; + } + + public static void main(String[] args) { + for (int i = 0; i < 20000; i++) { + m1(false); + } + + consume_all_memory(); + + try { + m1(true); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in main " + oom.getMessage()); + } + + free_memory(); + + for (int i = 0; i < 20000; i++) { + m2(false); + } + + consume_all_memory(); + + try { + m2(true); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in main"); + } + + free_memory(); + + for (int i = 0; i < 20000; i++) { + m3(false); + } + + consume_all_memory(); + + try { + m3(true); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in main"); + } + + free_memory(); + + for (int i = 0; i < 20000; i++) { + m4(false); + } + + consume_all_memory(); + + try { + m4(true); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in main"); + } + + free_memory(); + + for (int i = 0; i < 20000; i++) { + m5(false); + } + + consume_all_memory(); + + try { + m5(true); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in main"); + } + + free_memory(); + + for (int i = 0; i < 20000; i++) { + m6(false); + } + + consume_all_memory(); + + try { + m6(true); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in main"); + } + + free_memory(); + + final Object lock = new Object(); + + for (int i = 0; i < 20000; i++) { + m7(false, lock); + } + + consume_all_memory(); + + try { + m7(true, lock); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in main"); + } + + free_memory(); + + Thread thread = new Thread() { + public void run() { + System.out.println("Acquiring lock"); + synchronized(lock) { + System.out.println("Lock acquired"); + } + System.out.println("Lock released"); + } + }; + thread.start(); + try { + thread.join(); + } catch(InterruptedException ie) { + } + + for (int i = 0; i < 20000; i++) { + m8(false); + } + + consume_all_memory(); + + try { + m8(true); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in main"); + } + + free_memory(); + + try { + m9(); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in main"); + } + + free_memory(); + } +} From cbe8efabfe94969f038378a4055d55364c06a7a3 Mon Sep 17 00:00:00 2001 From: Erik Helin Date: Tue, 2 Dec 2014 09:53:30 +0100 Subject: [PATCH 03/58] 8059066: CardTableModRefBS might commit the same page twice Reviewed-by: tschatzl, kbarrett, jmasa --- .../src/share/vm/memory/cardTableModRefBS.cpp | 43 ++++++++-------- hotspot/test/gc/TestCardTablePageCommits.java | 49 +++++++++++++++++++ 2 files changed, 69 insertions(+), 23 deletions(-) create mode 100644 hotspot/test/gc/TestCardTablePageCommits.java diff --git a/hotspot/src/share/vm/memory/cardTableModRefBS.cpp b/hotspot/src/share/vm/memory/cardTableModRefBS.cpp index fb67888e4a2..9bf9786c9a9 100644 --- a/hotspot/src/share/vm/memory/cardTableModRefBS.cpp +++ b/hotspot/src/share/vm/memory/cardTableModRefBS.cpp @@ -275,29 +275,26 @@ void CardTableModRefBS::resize_covered_region(MemRegion new_region) { // the new_end_aligned does not intrude onto the committed // space of another region. int ri = 0; - for (ri = 0; ri < _cur_covered_regions; ri++) { - if (ri != ind) { - if (_committed[ri].contains(new_end_aligned)) { - // The prior check included in the assert - // (new_end_aligned >= _committed[ri].start()) - // is redundant with the "contains" test. - // Any region containing the new end - // should start at or beyond the region found (ind) - // for the new end (committed regions are not expected to - // be proper subsets of other committed regions). - assert(_committed[ri].start() >= _committed[ind].start(), - "New end of committed region is inconsistent"); - new_end_aligned = _committed[ri].start(); - // new_end_aligned can be equal to the start of its - // committed region (i.e., of "ind") if a second - // region following "ind" also start at the same location - // as "ind". - assert(new_end_aligned >= _committed[ind].start(), - "New end of committed region is before start"); - debug_only(collided = true;) - // Should only collide with 1 region - break; - } + for (ri = ind + 1; ri < _cur_covered_regions; ri++) { + if (new_end_aligned > _committed[ri].start()) { + assert(new_end_aligned <= _committed[ri].end(), + "An earlier committed region can't cover a later committed region"); + // Any region containing the new end + // should start at or beyond the region found (ind) + // for the new end (committed regions are not expected to + // be proper subsets of other committed regions). + assert(_committed[ri].start() >= _committed[ind].start(), + "New end of committed region is inconsistent"); + new_end_aligned = _committed[ri].start(); + // new_end_aligned can be equal to the start of its + // committed region (i.e., of "ind") if a second + // region following "ind" also start at the same location + // as "ind". + assert(new_end_aligned >= _committed[ind].start(), + "New end of committed region is before start"); + debug_only(collided = true;) + // Should only collide with 1 region + break; } } #ifdef ASSERT diff --git a/hotspot/test/gc/TestCardTablePageCommits.java b/hotspot/test/gc/TestCardTablePageCommits.java new file mode 100644 index 00000000000..0dec3d83b93 --- /dev/null +++ b/hotspot/test/gc/TestCardTablePageCommits.java @@ -0,0 +1,49 @@ +/* +* 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. +*/ + +import com.oracle.java.testlibrary.JDKToolFinder; +import com.oracle.java.testlibrary.OutputAnalyzer; +import com.oracle.java.testlibrary.ProcessTools; +import com.oracle.java.testlibrary.Platform; + +/* + * @test TestCardTablePageCommits + * @key gc + * @bug 8059066 + * @summary Tests that the card table does not commit the same page twice + * @library /testlibrary + * @run driver TestCardTablePageCommits + */ +public class TestCardTablePageCommits { + public static void main(String args[]) throws Exception { + // The test is run with a small heap to make sure all pages in the card + // table gets committed. Need 8 MB heap to trigger the bug on SPARC + // because of 8kB pages, assume 4 KB pages for all other CPUs. + String Xmx = Platform.isSparc() ? "-Xmx8m" : "-Xmx4m"; + + String[] opts = {Xmx, "-XX:NativeMemoryTracking=detail", "-XX:+UseParallelGC", "-version"}; + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(opts); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + } +} From 7a2bd82482e8a6dd00fac11f714a71007d034d54 Mon Sep 17 00:00:00 2001 From: Thomas Stuefe Date: Wed, 3 Dec 2014 17:12:25 +0100 Subject: [PATCH 04/58] 8065788: os::reserve_memory() on Windows should not assert that allocation size is aligned to OS allocation granularity Reviewed-by: mgronlun, simonis --- hotspot/src/os/windows/vm/os_windows.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hotspot/src/os/windows/vm/os_windows.cpp b/hotspot/src/os/windows/vm/os_windows.cpp index 5677e63a058..5241b15f495 100644 --- a/hotspot/src/os/windows/vm/os_windows.cpp +++ b/hotspot/src/os/windows/vm/os_windows.cpp @@ -3087,7 +3087,7 @@ char* os::reserve_memory_aligned(size_t size, size_t alignment) { char* os::pd_reserve_memory(size_t bytes, char* addr, size_t alignment_hint) { assert((size_t)addr % os::vm_allocation_granularity() == 0, "reserve alignment"); - assert(bytes % os::vm_allocation_granularity() == 0, "reserve block size"); + assert(bytes % os::vm_page_size() == 0, "reserve page size"); char* res; // note that if UseLargePages is on, all the areas that require interleaving // will go thru reserve_memory_special rather than thru here. From 4b3a02ee0b3d358db39b2f1d37fe71cf4411abdf Mon Sep 17 00:00:00 2001 From: Staffan Friberg Date: Wed, 3 Dec 2014 09:27:24 -0800 Subject: [PATCH 05/58] 8066441: Add PLAB trace event Reviewed-by: tschatzl, ehelin, egahlin --- .../vm/gc_implementation/shared/gcTrace.cpp | 21 ++++++++++ .../vm/gc_implementation/shared/gcTrace.hpp | 29 ++++++++++++++ .../gc_implementation/shared/gcTraceSend.cpp | 38 +++++++++++++++++++ hotspot/src/share/vm/trace/trace.xml | 22 +++++++++++ 4 files changed, 110 insertions(+) diff --git a/hotspot/src/share/vm/gc_implementation/shared/gcTrace.cpp b/hotspot/src/share/vm/gc_implementation/shared/gcTrace.cpp index dba0c5f709b..79af8d24eb4 100644 --- a/hotspot/src/share/vm/gc_implementation/shared/gcTrace.cpp +++ b/hotspot/src/share/vm/gc_implementation/shared/gcTrace.cpp @@ -172,6 +172,27 @@ void YoungGCTracer::report_tenuring_threshold(const uint tenuring_threshold) { _tenuring_threshold = tenuring_threshold; } +bool YoungGCTracer::should_report_promotion_in_new_plab_event() const { + return should_send_promotion_in_new_plab_event(); +} + +bool YoungGCTracer::should_report_promotion_outside_plab_event() const { + return should_send_promotion_outside_plab_event(); +} + +void YoungGCTracer::report_promotion_in_new_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured, + size_t plab_size) const { + assert_set_gc_id(); + send_promotion_in_new_plab_event(klass, obj_size, age, tenured, plab_size); +} + +void YoungGCTracer::report_promotion_outside_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured) const { + assert_set_gc_id(); + send_promotion_outside_plab_event(klass, obj_size, age, tenured); +} + void OldGCTracer::report_gc_end_impl(const Ticks& timestamp, TimePartitions* time_partitions) { assert_set_gc_id(); diff --git a/hotspot/src/share/vm/gc_implementation/shared/gcTrace.hpp b/hotspot/src/share/vm/gc_implementation/shared/gcTrace.hpp index 20342ebc244..9774dcb9537 100644 --- a/hotspot/src/share/vm/gc_implementation/shared/gcTrace.hpp +++ b/hotspot/src/share/vm/gc_implementation/shared/gcTrace.hpp @@ -156,9 +156,38 @@ class YoungGCTracer : public GCTracer { void report_promotion_failed(const PromotionFailedInfo& pf_info); void report_tenuring_threshold(const uint tenuring_threshold); + /* + * Methods for reporting Promotion in new or outside PLAB Events. + * + * The object age is always required as it is not certain that the mark word + * of the oop can be trusted at this stage. + * + * obj_size is the size of the promoted object in bytes. + * + * tenured should be true if the object has been promoted to the old + * space during this GC, if the object is copied to survivor space + * from young space or survivor space (aging) tenured should be false. + * + * plab_size is the size of the newly allocated PLAB in bytes. + */ + bool should_report_promotion_in_new_plab_event() const; + bool should_report_promotion_outside_plab_event() const; + void report_promotion_in_new_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured, + size_t plab_size) const; + void report_promotion_outside_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured) const; + private: void send_young_gc_event() const; void send_promotion_failed_event(const PromotionFailedInfo& pf_info) const; + bool should_send_promotion_in_new_plab_event() const; + bool should_send_promotion_outside_plab_event() const; + void send_promotion_in_new_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured, + size_t plab_size) const; + void send_promotion_outside_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured) const; }; class OldGCTracer : public GCTracer { diff --git a/hotspot/src/share/vm/gc_implementation/shared/gcTraceSend.cpp b/hotspot/src/share/vm/gc_implementation/shared/gcTraceSend.cpp index 97055694fae..326625bf1a8 100644 --- a/hotspot/src/share/vm/gc_implementation/shared/gcTraceSend.cpp +++ b/hotspot/src/share/vm/gc_implementation/shared/gcTraceSend.cpp @@ -111,6 +111,44 @@ void YoungGCTracer::send_young_gc_event() const { } } +bool YoungGCTracer::should_send_promotion_in_new_plab_event() const { + return EventPromoteObjectInNewPLAB::is_enabled(); +} + +bool YoungGCTracer::should_send_promotion_outside_plab_event() const { + return EventPromoteObjectOutsidePLAB::is_enabled(); +} + +void YoungGCTracer::send_promotion_in_new_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured, + size_t plab_size) const { + + EventPromoteObjectInNewPLAB event; + if (event.should_commit()) { + event.set_gcId(_shared_gc_info.gc_id().id()); + event.set_class(klass); + event.set_objectSize(obj_size); + event.set_tenured(tenured); + event.set_tenuringAge(age); + event.set_plabSize(plab_size); + event.commit(); + } +} + +void YoungGCTracer::send_promotion_outside_plab_event(Klass* klass, size_t obj_size, + uint age, bool tenured) const { + + EventPromoteObjectOutsidePLAB event; + if (event.should_commit()) { + event.set_gcId(_shared_gc_info.gc_id().id()); + event.set_class(klass); + event.set_objectSize(obj_size); + event.set_tenured(tenured); + event.set_tenuringAge(age); + event.commit(); + } +} + void OldGCTracer::send_old_gc_event() const { EventGCOldGarbageCollection e(UNTIMED); if (e.should_commit()) { diff --git a/hotspot/src/share/vm/trace/trace.xml b/hotspot/src/share/vm/trace/trace.xml index 120d27f4d4d..fc7acd38e95 100644 --- a/hotspot/src/share/vm/trace/trace.xml +++ b/hotspot/src/share/vm/trace/trace.xml @@ -314,6 +314,28 @@ Declares a structure type that can be used in other events. + + + + + + + + + + + + + + + + + + From 5c410f0bb584f4e8fadf298cb5d0814e423d5c73 Mon Sep 17 00:00:00 2001 From: Kevin Walls Date: Thu, 4 Dec 2014 12:43:45 +0000 Subject: [PATCH 06/58] 8061785: [TEST_BUG] serviceability/sa/jmap-hashcode/Test8028623.java has utf8 character corrupted by earlier merge Reviewed-by: sla, dsamersoff --- hotspot/test/serviceability/sa/jmap-hashcode/Test8028623.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hotspot/test/serviceability/sa/jmap-hashcode/Test8028623.java b/hotspot/test/serviceability/sa/jmap-hashcode/Test8028623.java index 10877ba86d9..169c0432999 100644 --- a/hotspot/test/serviceability/sa/jmap-hashcode/Test8028623.java +++ b/hotspot/test/serviceability/sa/jmap-hashcode/Test8028623.java @@ -41,12 +41,12 @@ import java.io.File; public class Test8028623 { - public static int à = 1; + public static int \u00CB = 1; public static String dumpFile = "heap.out"; public static void main (String[] args) { - System.out.println(Ã); + System.out.println(\u00CB); try { if (!Platform.shouldSAAttach()) { From ea256a978fd6971f187b5c9292779683523e8dee Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Thu, 4 Dec 2014 15:20:09 -0800 Subject: [PATCH 07/58] 8066670: PrintSharedArchiveAndExit does not exit the VM when the archive is invalid In FileMapInfo::fail_continue do not set UseSharedSpaces = false Reviewed-by: dholmes, ccheung --- hotspot/src/share/vm/memory/filemap.cpp | 6 +- .../src/share/vm/memory/metaspaceShared.cpp | 2 +- .../PrintSharedArchiveAndExit.java | 83 +++++++++++++++++++ 3 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 hotspot/test/runtime/SharedArchiveFile/PrintSharedArchiveAndExit.java diff --git a/hotspot/src/share/vm/memory/filemap.cpp b/hotspot/src/share/vm/memory/filemap.cpp index bac26f1b802..3a971b068ff 100644 --- a/hotspot/src/share/vm/memory/filemap.cpp +++ b/hotspot/src/share/vm/memory/filemap.cpp @@ -98,11 +98,11 @@ void FileMapInfo::fail_continue(const char *msg, ...) { tty->print_cr("UseSharedSpaces: %s", msg); } } + UseSharedSpaces = false; + assert(current_info() != NULL, "singleton must be registered"); + current_info()->close(); } va_end(ap); - UseSharedSpaces = false; - assert(current_info() != NULL, "singleton must be registered"); - current_info()->close(); } // Fill in the fileMapInfo structure with data about this VM instance. diff --git a/hotspot/src/share/vm/memory/metaspaceShared.cpp b/hotspot/src/share/vm/memory/metaspaceShared.cpp index b6edbae988a..26729be6efd 100644 --- a/hotspot/src/share/vm/memory/metaspaceShared.cpp +++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp @@ -969,7 +969,7 @@ bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) { #endif // If -Xshare:on is specified, print out the error message and exit VM, // otherwise, set UseSharedSpaces to false and continue. - if (RequireSharedSpaces) { + if (RequireSharedSpaces || PrintSharedArchiveAndExit) { vm_exit_during_initialization("Unable to use shared archive.", "Failed map_region for using -Xshare:on."); } else { FLAG_SET_DEFAULT(UseSharedSpaces, false); diff --git a/hotspot/test/runtime/SharedArchiveFile/PrintSharedArchiveAndExit.java b/hotspot/test/runtime/SharedArchiveFile/PrintSharedArchiveAndExit.java new file mode 100644 index 00000000000..e3e81ca9d43 --- /dev/null +++ b/hotspot/test/runtime/SharedArchiveFile/PrintSharedArchiveAndExit.java @@ -0,0 +1,83 @@ +/* + * 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 8066670 + * @summary Testing -XX:+PrintSharedArchiveAndExit option + * @library /testlibrary + */ + +import com.oracle.java.testlibrary.*; + +public class PrintSharedArchiveAndExit { + public static void main(String[] args) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=./sample.jsa", "-Xshare:dump"); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + try { + output.shouldContain("Loading classes to share"); + output.shouldHaveExitValue(0); + + // (1) With a valid archive + pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=./sample.jsa", + "-XX:+PrintSharedArchiveAndExit", "-version"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("archive is valid"); + output.shouldNotContain("java version"); // Should not print JVM version + output.shouldHaveExitValue(0); // Should report success in error code. + + pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=./sample.jsa", + "-XX:+PrintSharedArchiveAndExit"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("archive is valid"); + output.shouldNotContain("Usage:"); // Should not print JVM help message + output.shouldHaveExitValue(0); // Should report success in error code. + + // (2) With an invalid archive (boot class path has been prepended) + pb = ProcessTools.createJavaProcessBuilder( + "-Xbootclasspath/p:foo.jar", + "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=./sample.jsa", + "-XX:+PrintSharedArchiveAndExit", "-version"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("archive is invalid"); + output.shouldNotContain("java version"); // Should not print JVM version + output.shouldHaveExitValue(1); // Should report failure in error code. + + pb = ProcessTools.createJavaProcessBuilder( + "-Xbootclasspath/p:foo.jar", + "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=./sample.jsa", + "-XX:+PrintSharedArchiveAndExit"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("archive is invalid"); + output.shouldNotContain("Usage:"); // Should not print JVM help message + output.shouldHaveExitValue(1); // Should report failure in error code. + } catch (RuntimeException e) { + e.printStackTrace(); + output.shouldContain("Unable to use shared archive"); + output.shouldHaveExitValue(1); + } + } +} From 5974dd3005bedaaeedd53ae20ed1eac0f6acdf23 Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Fri, 5 Dec 2014 18:03:15 +0100 Subject: [PATCH 08/58] 8066775: opto/node.hpp:355, assert(i < _max) failed: oob: i=1, _max=1 Bad assumption on graph shape in CastIINode::Value if that part of the graph is becoming dead. Reviewed-by: kvn --- hotspot/src/share/vm/opto/castnode.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hotspot/src/share/vm/opto/castnode.cpp b/hotspot/src/share/vm/opto/castnode.cpp index 3f3c5cab2ed..48aab726417 100644 --- a/hotspot/src/share/vm/opto/castnode.cpp +++ b/hotspot/src/share/vm/opto/castnode.cpp @@ -104,7 +104,8 @@ const Type *CastIINode::Value(PhaseTransform *phase) const { // Try to improve the type of the CastII if we recognize a CmpI/If // pattern. if (_carry_dependency) { - if (in(0) != NULL && (in(0)->is_IfFalse() || in(0)->is_IfTrue())) { + if (in(0) != NULL && in(0)->in(0) != NULL && in(0)->in(0)->is_If()) { + assert(in(0)->is_IfFalse() || in(0)->is_IfTrue(), "should be If proj"); Node* proj = in(0); if (proj->in(0)->in(1)->is_Bool()) { Node* b = proj->in(0)->in(1); From 4b628f1aee26876fd5c1d381ec5fb0bbadd7139a Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Fri, 5 Dec 2014 15:15:13 -0500 Subject: [PATCH 09/58] 8066171: Out of order with Metaspace allocation lock Lock resolved_references instead. Reviewed-by: twisti, sspitsyn --- hotspot/src/share/vm/oops/cpCache.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/hotspot/src/share/vm/oops/cpCache.cpp b/hotspot/src/share/vm/oops/cpCache.cpp index 64f16f78467..02c642f13a4 100644 --- a/hotspot/src/share/vm/oops/cpCache.cpp +++ b/hotspot/src/share/vm/oops/cpCache.cpp @@ -287,9 +287,13 @@ void ConstantPoolCacheEntry::set_method_handle_common(constantPoolHandle cpool, // the lock, so that when the losing writer returns, he can use the linked // cache entry. - // Use the lock from the metaspace for this, which cannot stop for safepoint. - Mutex* metaspace_lock = cpool->pool_holder()->class_loader_data()->metaspace_lock(); - MutexLockerEx ml(metaspace_lock, Mutex::_no_safepoint_check_flag); + objArrayHandle resolved_references = cpool->resolved_references(); + // Use the resolved_references() lock for this cpCache entry. + // resolved_references are created for all classes with Invokedynamic, MethodHandle + // or MethodType constant pool cache entries. + assert(resolved_references() != NULL, + "a resolved_references array should have been created for this class"); + ObjectLocker ol(resolved_references, Thread::current()); if (!is_f1_null()) { return; } @@ -336,7 +340,6 @@ void ConstantPoolCacheEntry::set_method_handle_common(constantPoolHandle cpool, // This allows us to create fewer Methods, while keeping type safety. // - objArrayHandle resolved_references = cpool->resolved_references(); // Store appendix, if any. if (has_appendix) { const int appendix_index = f2_as_index() + _indy_resolved_references_appendix_offset; From e9b92ce02462c63f73501fb331a8fb0c606c44f3 Mon Sep 17 00:00:00 2001 From: Stefan Karlsson Date: Fri, 5 Dec 2014 21:16:45 +0100 Subject: [PATCH 10/58] 8065634: Crash in InstanceKlass::clean_method_data when _method is NULL Reviewed-by: coleenp, hseigel, poonam --- .../share/vm/classfile/classFileParser.cpp | 66 +++++++++++++++---- .../share/vm/classfile/classFileParser.hpp | 4 ++ 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/hotspot/src/share/vm/classfile/classFileParser.cpp b/hotspot/src/share/vm/classfile/classFileParser.cpp index 7d54c6e2ed6..f80d0fcc00e 100644 --- a/hotspot/src/share/vm/classfile/classFileParser.cpp +++ b/hotspot/src/share/vm/classfile/classFileParser.cpp @@ -3108,21 +3108,39 @@ void ClassFileParser::apply_parsed_class_attributes(instanceKlassHandle k) { } } -// 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) { +// Create the Annotations object that will +// hold the annotations array for the Klass. +void ClassFileParser::create_combined_annotations(TRAPS) { + if (_annotations == NULL && + _type_annotations == NULL && + _fields_annotations == NULL && + _fields_type_annotations == NULL) { + // Don't create the Annotations object unnecessarily. + return; + } + 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); - } + // This is the Annotations object that will be + // assigned to InstanceKlass being constructed. + _combined_annotations = annotations; + + // The annotations arrays below has been transfered the + // _combined_annotations so these fields can now be cleared. + _annotations = NULL; + _type_annotations = NULL; + _fields_annotations = NULL; + _fields_type_annotations = NULL; +} + +// Transfer ownership of metadata allocated to the InstanceKlass. +void ClassFileParser::apply_parsed_class_metadata( + instanceKlassHandle this_klass, + int java_fields_count, TRAPS) { _cp->set_pool_holder(this_klass()); this_klass->set_constants(_cp); this_klass->set_fields(_fields, java_fields_count); @@ -3130,6 +3148,7 @@ void ClassFileParser::apply_parsed_class_metadata( this_klass->set_inner_classes(_inner_classes); this_klass->set_local_interfaces(_local_interfaces); this_klass->set_transitive_interfaces(_transitive_interfaces); + this_klass->set_annotations(_combined_annotations); // Clear out these fields so they don't get deallocated by the destructor clear_class_metadata(); @@ -4002,6 +4021,10 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, ClassAnnotationCollector parsed_annotations; parse_classfile_attributes(&parsed_annotations, CHECK_(nullHandle)); + // Finalize the Annotations metadata object, + // now that all annotation arrays have been created. + create_combined_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)); @@ -4302,10 +4325,27 @@ ClassFileParser::~ClassFileParser() { 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); + if (_combined_annotations != NULL) { + // After all annotations arrays have been created, they are installed into the + // Annotations object that will be assigned to the InstanceKlass being created. + + // Deallocate the Annotations object and the installed annotations arrays. + _combined_annotations->deallocate_contents(_loader_data); + + // If the _combined_annotations pointer is non-NULL, + // then the other annotations fields should have been cleared. + assert(_annotations == NULL, "Should have been cleared"); + assert(_type_annotations == NULL, "Should have been cleared"); + assert(_fields_annotations == NULL, "Should have been cleared"); + assert(_fields_type_annotations == NULL, "Should have been cleared"); + } else { + // If the annotations arrays were not installed into the Annotations object, + // then they have to be deallocated explicitly. + 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(); diff --git a/hotspot/src/share/vm/classfile/classFileParser.hpp b/hotspot/src/share/vm/classfile/classFileParser.hpp index b2efa8ceba3..c3d23df881c 100644 --- a/hotspot/src/share/vm/classfile/classFileParser.hpp +++ b/hotspot/src/share/vm/classfile/classFileParser.hpp @@ -75,6 +75,7 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC { Array* _inner_classes; Array* _local_interfaces; Array* _transitive_interfaces; + Annotations* _combined_annotations; AnnotationArray* _annotations; AnnotationArray* _type_annotations; Array* _fields_annotations; @@ -86,6 +87,8 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC { void set_class_generic_signature_index(u2 x) { _generic_signature_index = x; } void set_class_sde_buffer(char* x, int len) { _sde_buffer = x; _sde_length = len; } + void create_combined_annotations(TRAPS); + void init_parsed_class_attributes(ClassLoaderData* loader_data) { _loader_data = loader_data; _synthetic_flag = false; @@ -110,6 +113,7 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC { _inner_classes = NULL; _local_interfaces = NULL; _transitive_interfaces = NULL; + _combined_annotations = NULL; _annotations = _type_annotations = NULL; _fields_annotations = _fields_type_annotations = NULL; } From 701b666284fc08aeb664ce4481503d64f4ddd20c Mon Sep 17 00:00:00 2001 From: Calvin Cheung Date: Fri, 5 Dec 2014 12:24:10 -0800 Subject: [PATCH 11/58] 8065050: vm crashes during CDS dump when very small SharedMiscDataSize is specified Define minimum required sizes for the ro, rw, and md regions and make sure the specified sizes are not less than the minimum sizes Reviewed-by: jiangli, dholmes, mseledtsov --- hotspot/src/share/vm/memory/metaspace.cpp | 20 ++- .../src/share/vm/memory/metaspaceShared.hpp | 5 + .../SharedArchiveFile/LimitSharedSizes.java | 125 +++++++++++++++--- 3 files changed, 129 insertions(+), 21 deletions(-) diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp index 2f5d5843ee2..f0db64651df 100644 --- a/hotspot/src/share/vm/memory/metaspace.cpp +++ b/hotspot/src/share/vm/memory/metaspace.cpp @@ -3157,7 +3157,25 @@ 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() + // make sure SharedReadOnlySize and SharedReadWriteSize are not less than + // the minimum values. + if (SharedReadOnlySize < MetaspaceShared::min_ro_size){ + report_out_of_shared_space(SharedReadOnly); + } + + if (SharedReadWriteSize < MetaspaceShared::min_rw_size){ + report_out_of_shared_space(SharedReadWrite); + } + + // the min_misc_data_size and min_misc_code_size estimates are based on + // MetaspaceShared::generate_vtable_methods() + uint min_misc_data_size = align_size_up( + MetaspaceShared::num_virtuals * MetaspaceShared::vtbl_list_size * sizeof(void*), max_alignment); + + if (SharedMiscDataSize < min_misc_data_size) { + report_out_of_shared_space(SharedMiscData); + } + 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, diff --git a/hotspot/src/share/vm/memory/metaspaceShared.hpp b/hotspot/src/share/vm/memory/metaspaceShared.hpp index db344359082..fc29f9c768b 100644 --- a/hotspot/src/share/vm/memory/metaspaceShared.hpp +++ b/hotspot/src/share/vm/memory/metaspaceShared.hpp @@ -69,6 +69,11 @@ class MetaspaceShared : AllStatic { vtbl_common_code_size = (1*K) // conservative size of the "common_code" for the x64 platform }; + enum { + min_ro_size = NOT_LP64(8*M) LP64_ONLY(9*M), // minimum ro and rw regions sizes based on dumping + min_rw_size = NOT_LP64(7*M) LP64_ONLY(12*M) // of a shared archive using the default classlist + }; + enum { ro = 0, // read-only shared space in the heap rw = 1, // read-write shared space in the heap diff --git a/hotspot/test/runtime/SharedArchiveFile/LimitSharedSizes.java b/hotspot/test/runtime/SharedArchiveFile/LimitSharedSizes.java index b2505be0d7d..f38b85ad9af 100644 --- a/hotspot/test/runtime/SharedArchiveFile/LimitSharedSizes.java +++ b/hotspot/test/runtime/SharedArchiveFile/LimitSharedSizes.java @@ -30,40 +30,96 @@ import com.oracle.java.testlibrary.*; public class LimitSharedSizes { + static enum Region { + RO, RW, MD, MC + } + private static class SharedSizeTestData { public String optionName; public String optionValue; public String expectedErrorMsg; - public SharedSizeTestData(String name, String value, String msg) { - optionName = name; + public SharedSizeTestData(Region region, String value, String msg) { + optionName = getName(region); optionValue = value; expectedErrorMsg = msg; } + + public SharedSizeTestData(Region region, String msg) { + optionName = getName(region); + optionValue = getValue(region); + expectedErrorMsg = msg; + } + + private String getName(Region region) { + String name; + switch (region) { + case RO: + name = "-XX:SharedReadOnlySize"; + break; + case RW: + name = "-XX:SharedReadWriteSize"; + break; + case MD: + name = "-XX:SharedMiscDataSize"; + break; + case MC: + name = "-XX:SharedMiscCodeSize"; + break; + default: + name = "Unknown"; + break; + } + return name; + } + + private String getValue(Region region) { + String value; + switch (region) { + case RO: + value = Platform.is64bit() ? "9M" : "8M"; + break; + case RW: + value = Platform.is64bit() ? "12M" : "7M"; + break; + case MD: + value = Platform.is64bit() ? "4M" : "2M"; + break; + case MC: + value = "120k"; + break; + default: + value = "0M"; + break; + } + return value; + } } private static final SharedSizeTestData[] testTable = { - // values in this part of the test table should cause failure - // (shared space sizes are deliberately too small) - new SharedSizeTestData("-XX:SharedReadOnlySize", "4M", "read only"), - new SharedSizeTestData("-XX:SharedReadWriteSize","4M", "read write"), - - // Known issue, JDK-8038422 (assert() on Windows) - // new SharedSizeTestData("-XX:SharedMiscDataSize", "500k", "miscellaneous data"), - - // Too small of a misc code size should not cause a vm crash. - // It should result in the following error message: + // Too small of a region size should not cause a vm crash. + // It should result in an error message like the following: // 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"), + new SharedSizeTestData(Region.RO, "4M", "read only"), + new SharedSizeTestData(Region.RW, "4M", "read write"), + new SharedSizeTestData(Region.MD, "50k", "miscellaneous data"), + new SharedSizeTestData(Region.MC, "20k", "miscellaneous code"), // these values are larger than default ones, but should // be acceptable and not cause failure - new SharedSizeTestData("-XX:SharedReadOnlySize", "20M", null), - new SharedSizeTestData("-XX:SharedReadWriteSize", "20M", null), - new SharedSizeTestData("-XX:SharedMiscDataSize", "20M", null), - new SharedSizeTestData("-XX:SharedMiscCodeSize", "20M", null) + new SharedSizeTestData(Region.RO, "20M", null), + new SharedSizeTestData(Region.RW, "20M", null), + new SharedSizeTestData(Region.MD, "20M", null), + new SharedSizeTestData(Region.MC, "20M", null), + + // test with sizes which just meet the minimum required sizes + // the following tests also attempt to use the shared archive + new SharedSizeTestData(Region.RO, "UseArchive"), + new SharedSizeTestData(Region.RW, "UseArchive"), + new SharedSizeTestData(Region.MD, "UseArchive"), + new SharedSizeTestData(Region.MC, "UseArchive") }; public static void main(String[] args) throws Exception { @@ -82,10 +138,39 @@ public class LimitSharedSizes { OutputAnalyzer output = new OutputAnalyzer(pb.start()); if (td.expectedErrorMsg != null) { - output.shouldContain("The shared " + td.expectedErrorMsg - + " space is not large enough"); + if (!td.expectedErrorMsg.equals("UseArchive")) { + output.shouldContain("The shared " + td.expectedErrorMsg + + " space is not large enough"); - output.shouldHaveExitValue(2); + output.shouldHaveExitValue(2); + } else { + output.shouldNotContain("space is not large enough"); + output.shouldHaveExitValue(0); + + // try to use the archive + pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:SharedArchiveFile=./" + fileName, + "-XX:+PrintSharedArchiveAndExit", + "-version"); + + try { + output = new OutputAnalyzer(pb.start()); + output.shouldContain("archive is valid"); + } catch (RuntimeException e) { + // if sharing failed due to ASLR or similar reasons, + // check whether sharing was attempted at all (UseSharedSpaces) + if ((output.getOutput().contains("Unable to use shared archive") || + output.getOutput().contains("Unable to map ReadOnly shared space at required address.") || + output.getOutput().contains("Unable to map ReadWrite shared space at required address.") || + output.getOutput().contains("Unable to reserve shared space at required address")) && + output.getExitValue() == 1) { + System.out.println("Unable to use shared archive: test not executed; assumed passed"); + return; + } + } + output.shouldHaveExitValue(0); + } } else { output.shouldNotContain("space is not large enough"); output.shouldHaveExitValue(0); From 8bb3ef2ca02f8b0ab9d52b28e15df7f08bdeacbb Mon Sep 17 00:00:00 2001 From: Chris Plummer Date: Fri, 5 Dec 2014 15:41:51 -0800 Subject: [PATCH 12/58] 8066508: JTReg tests timeout on slow devices when run using JPRT Fixed by increasing timeoutFactor from 1 to 4 as is done already in jdk/test/Makefile. Reviewed-by: dholmes, dcubed --- hotspot/test/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hotspot/test/Makefile b/hotspot/test/Makefile index 479851a3e5a..7dcbda008a4 100644 --- a/hotspot/test/Makefile +++ b/hotspot/test/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1995, 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 @@ -271,6 +271,9 @@ JTREG_BASIC_OPTIONS += -retain:fail,error # Ignore tests are not run and completely silent about it JTREG_IGNORE_OPTION = -ignore:quiet JTREG_BASIC_OPTIONS += $(JTREG_IGNORE_OPTION) +# Multiply by 4 the timeout factor +JTREG_TIMEOUT_OPTION = -timeoutFactor:4 +JTREG_BASIC_OPTIONS += $(JTREG_TIMEOUT_OPTION) # Add any extra options JTREG_BASIC_OPTIONS += $(EXTRA_JTREG_OPTIONS) # Set other vm and test options From a7ebb442cf3680271ee6cf102345bf71c4ecf339 Mon Sep 17 00:00:00 2001 From: Tatiana Pivovarova Date: Mon, 8 Dec 2014 18:21:02 +0300 Subject: [PATCH 13/58] 8066250: compiler/dependencies/MonomorphicObjectCall/TestMonomorphicObjectCall.java fails product Reviewed-by: kvn, thartmann --- .../MonomorphicObjectCall/TestMonomorphicObjectCall.java | 1 + 1 file changed, 1 insertion(+) diff --git a/hotspot/test/compiler/dependencies/MonomorphicObjectCall/TestMonomorphicObjectCall.java b/hotspot/test/compiler/dependencies/MonomorphicObjectCall/TestMonomorphicObjectCall.java index 932599013cf..8884ce404c5 100644 --- a/hotspot/test/compiler/dependencies/MonomorphicObjectCall/TestMonomorphicObjectCall.java +++ b/hotspot/test/compiler/dependencies/MonomorphicObjectCall/TestMonomorphicObjectCall.java @@ -61,6 +61,7 @@ public class TestMonomorphicObjectCall { String[] vmOpts = new String[] { "-Xbootclasspath/p:" + testClasses, "-Xcomp", + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-VerifyDependencies", "-XX:CompileOnly=TestMonomorphicObjectCall::callFinalize", "-XX:CompileOnly=Object::finalizeObject", From 5d868d4e0fc0ec59be883d8b52a4b92a6b175930 Mon Sep 17 00:00:00 2001 From: Thomas Schatzl Date: Tue, 9 Dec 2014 12:47:19 +0100 Subject: [PATCH 14/58] 8066102: Clean up HeapRegionRemSet files Remove dead code, tighten public interfaces and improve documentation in the HeapRegionRemSet implementation. Reviewed-by: mgerdin, kbarrett --- .../gc_implementation/g1/g1CollectedHeap.cpp | 2 +- .../gc_implementation/g1/heapRegionRemSet.cpp | 55 +++------------- .../gc_implementation/g1/heapRegionRemSet.hpp | 64 ++++++++----------- 3 files changed, 36 insertions(+), 85 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp index 875ffdbc311..d50c456c3fc 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp @@ -352,7 +352,7 @@ void YoungList::print() { } void G1RegionMappingChangedListener::reset_from_card_cache(uint start_idx, size_t num_regions) { - OtherRegionsTable::invalidate(start_idx, num_regions); + HeapRegionRemSet::invalidate_from_card_cache(start_idx, num_regions); } void G1RegionMappingChangedListener::on_commit(uint start_idx, size_t num_regions, bool zero_filled) { diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp index 058426f4603..e04cc8ead7b 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.cpp @@ -407,20 +407,8 @@ void FromCardCache::clear(uint region_idx) { } } -void OtherRegionsTable::initialize(uint max_regions) { - FromCardCache::initialize(HeapRegionRemSet::num_par_rem_sets(), max_regions); -} - -void OtherRegionsTable::invalidate(uint start_idx, size_t num_regions) { - FromCardCache::invalidate(start_idx, num_regions); -} - -void OtherRegionsTable::print_from_card_cache() { - FromCardCache::print(); -} - void OtherRegionsTable::add_reference(OopOrNarrowOopStar from, uint tid) { - uint cur_hrm_ind = hr()->hrm_index(); + uint cur_hrm_ind = _hr->hrm_index(); if (G1TraceHeapRegionRememberedSet) { gclog_or_tty->print_cr("ORT::add_reference_work(" PTR_FORMAT "->" PTR_FORMAT ").", @@ -434,7 +422,7 @@ void OtherRegionsTable::add_reference(OopOrNarrowOopStar from, uint tid) { if (G1TraceHeapRegionRememberedSet) { gclog_or_tty->print_cr("Table for [" PTR_FORMAT "...): card %d (cache = %d)", - hr()->bottom(), from_card, + _hr->bottom(), from_card, FromCardCache::at(tid, cur_hrm_ind)); } @@ -477,13 +465,13 @@ void OtherRegionsTable::add_reference(OopOrNarrowOopStar from, uint tid) { if (G1HRRSUseSparseTable && _sparse_table.add_card(from_hrm_ind, card_index)) { if (G1RecordHRRSOops) { - HeapRegionRemSet::record(hr(), from); + HeapRegionRemSet::record(_hr, from); if (G1TraceHeapRegionRememberedSet) { gclog_or_tty->print(" Added card " PTR_FORMAT " to region " "[" PTR_FORMAT "...) for ref " PTR_FORMAT ".\n", align_size_down(uintptr_t(from), CardTableModRefBS::card_size), - hr()->bottom(), from); + _hr->bottom(), from); } } if (G1TraceHeapRegionRememberedSet) { @@ -539,13 +527,13 @@ void OtherRegionsTable::add_reference(OopOrNarrowOopStar from, uint tid) { prt->add_reference(from); if (G1RecordHRRSOops) { - HeapRegionRemSet::record(hr(), from); + HeapRegionRemSet::record(_hr, from); if (G1TraceHeapRegionRememberedSet) { gclog_or_tty->print("Added card " PTR_FORMAT " to region " "[" PTR_FORMAT "...) for ref " PTR_FORMAT ".\n", align_size_down(uintptr_t(from), CardTableModRefBS::card_size), - hr()->bottom(), from); + _hr->bottom(), from); } } assert(contains_reference(from), "We just added it!"); @@ -614,7 +602,7 @@ PerRegionTable* OtherRegionsTable::delete_region_table() { if (G1TraceHeapRegionRememberedSet) { gclog_or_tty->print("Coarsened entry in region [" PTR_FORMAT "...] " "for region [" PTR_FORMAT "...] (" SIZE_FORMAT " coarse entries).\n", - hr()->bottom(), + _hr->bottom(), max->hr()->bottom(), _n_coarse_entries); } @@ -627,13 +615,11 @@ PerRegionTable* OtherRegionsTable::delete_region_table() { return max; } - -// At present, this must be called stop-world single-threaded. void OtherRegionsTable::scrub(CardTableModRefBS* ctbs, BitMap* region_bm, BitMap* card_bm) { // First eliminated garbage regions from the coarse map. if (G1RSScrubVerbose) { - gclog_or_tty->print_cr("Scrubbing region %u:", hr()->hrm_index()); + gclog_or_tty->print_cr("Scrubbing region %u:", _hr->hrm_index()); } assert(_coarse_map.size() == region_bm->size(), "Precondition"); @@ -752,7 +738,7 @@ size_t OtherRegionsTable::fl_mem_size() { } void OtherRegionsTable::clear_fcc() { - FromCardCache::clear(hr()->hrm_index()); + FromCardCache::clear(_hr->hrm_index()); } void OtherRegionsTable::clear() { @@ -774,27 +760,6 @@ void OtherRegionsTable::clear() { clear_fcc(); } -bool OtherRegionsTable::del_single_region_table(size_t ind, - HeapRegion* hr) { - assert(0 <= ind && ind < _max_fine_entries, "Preconditions."); - PerRegionTable** prev_addr = &_fine_grain_regions[ind]; - PerRegionTable* prt = *prev_addr; - while (prt != NULL && prt->hr() != hr) { - prev_addr = prt->collision_list_next_addr(); - prt = prt->collision_list_next(); - } - if (prt != NULL) { - assert(prt->hr() == hr, "Loop postcondition."); - *prev_addr = prt->collision_list_next(); - unlink_from_all(prt); - PerRegionTable::free(prt); - _n_fine_entries--; - return true; - } else { - return false; - } -} - bool OtherRegionsTable::contains_reference(OopOrNarrowOopStar from) const { // Cast away const in this case. MutexLockerEx x((Mutex*)_m, Mutex::_no_safepoint_check_flag); @@ -975,7 +940,7 @@ HeapRegionRemSetIterator:: HeapRegionRemSetIterator(HeapRegionRemSet* hrrs) : _hrrs(hrrs), _g1h(G1CollectedHeap::heap()), _coarse_map(&hrrs->_other_regions._coarse_map), - _bosa(hrrs->bosa()), + _bosa(hrrs->_bosa), _is(Sparse), // Set these values so that we increment to the first region. _coarse_cur_region_index(-1), diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp index d55fe1858df..bfff90abaef 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegionRemSet.hpp @@ -162,32 +162,36 @@ class OtherRegionsTable VALUE_OBJ_CLASS_SPEC { // to hold _m, and the fine-grain table to be full. PerRegionTable* delete_region_table(); - // If a PRT for "hr" is in the bucket list indicated by "ind" (which must - // be the correct index for "hr"), delete it and return true; else return - // false. - bool del_single_region_table(size_t ind, HeapRegion* hr); - // link/add the given fine grain remembered set into the "all" list void link_to_all(PerRegionTable * prt); // unlink/remove the given fine grain remembered set into the "all" list void unlink_from_all(PerRegionTable * prt); -public: - OtherRegionsTable(HeapRegion* hr, Mutex* m); + bool contains_reference_locked(OopOrNarrowOopStar from) const; - HeapRegion* hr() const { return _hr; } + // Clear the from_card_cache entries for this region. + void clear_fcc(); +public: + // Create a new remembered set for the given heap region. The given mutex should + // be used to ensure consistency. + OtherRegionsTable(HeapRegion* hr, Mutex* m); // For now. Could "expand" some tables in the future, so that this made // sense. void add_reference(OopOrNarrowOopStar from, uint tid); + // Returns whether the remembered set contains the given reference. + bool contains_reference(OopOrNarrowOopStar from) const; + // Removes any entries shown by the given bitmaps to contain only dead - // objects. + // objects. Not thread safe. + // Set bits in the bitmaps indicate that the given region or card is live. void scrub(CardTableModRefBS* ctbs, BitMap* region_bm, BitMap* card_bm); - // Returns whether this remembered set (and all sub-sets) contain no entries. + // Returns whether this remembered set (and all sub-sets) does not contain any entry. bool is_empty() const; + // Returns the number of cards contained in this remembered set. size_t occupied() const; size_t occ_fine() const; size_t occ_coarse() const; @@ -195,31 +199,17 @@ public: static jint n_coarsenings() { return _n_coarsenings; } - // Returns size in bytes. - // Not const because it takes a lock. + // Returns size of the actual remembered set containers in bytes. size_t mem_size() const; + // Returns the size of static data in bytes. static size_t static_mem_size(); + // Returns the size of the free list content in bytes. static size_t fl_mem_size(); - bool contains_reference(OopOrNarrowOopStar from) const; - bool contains_reference_locked(OopOrNarrowOopStar from) const; - + // Clear the entire contents of this remembered set. void clear(); - // Specifically clear the from_card_cache. - void clear_fcc(); - void do_cleanup_work(HRRSCleanupTask* hrrs_cleanup_task); - - // Declare the heap size (in # of regions) to the OtherRegionsTable. - // (Uses it to initialize from_card_cache). - static void initialize(uint max_regions); - - // Declares that regions between start_idx <= i < start_idx + num_regions are - // not in use. Make sure that any entries for these regions are invalid. - static void invalidate(uint start_idx, size_t num_regions); - - static void print_from_card_cache(); }; class HeapRegionRemSet : public CHeapObj { @@ -233,7 +223,6 @@ public: private: G1BlockOffsetSharedArray* _bosa; - G1BlockOffsetSharedArray* bosa() const { return _bosa; } // A set of code blobs (nmethods) whose code contains pointers into // the region that owns this RSet. @@ -268,10 +257,6 @@ public: static uint num_par_rem_sets(); static void setup_remset_size(); - HeapRegion* hr() const { - return _other_regions.hr(); - } - bool is_empty() const { return (strong_code_roots_list_length() == 0) && _other_regions.is_empty(); } @@ -305,8 +290,9 @@ public: _other_regions.add_reference(from, tid); } - // Removes any entries shown by the given bitmaps to contain only dead - // objects. + // Removes any entries in the remembered set shown by the given bitmaps to + // contain only dead objects. Not thread safe. + // One bits in the bitmaps indicate that the given region or card is live. void scrub(CardTableModRefBS* ctbs, BitMap* region_bm, BitMap* card_bm); // The region is being reclaimed; clear its remset, and any mention of @@ -397,16 +383,16 @@ public: // Declare the heap size (in # of regions) to the HeapRegionRemSet(s). // (Uses it to initialize from_card_cache). static void init_heap(uint max_regions) { - OtherRegionsTable::initialize(max_regions); + FromCardCache::initialize(num_par_rem_sets(), max_regions); } - static void invalidate(uint start_idx, uint num_regions) { - OtherRegionsTable::invalidate(start_idx, num_regions); + static void invalidate_from_card_cache(uint start_idx, size_t num_regions) { + FromCardCache::invalidate(start_idx, num_regions); } #ifndef PRODUCT static void print_from_card_cache() { - OtherRegionsTable::print_from_card_cache(); + FromCardCache::print(); } #endif From 79ed4de1e1ad8fc68a64ccb3c2f05c68b8aa8520 Mon Sep 17 00:00:00 2001 From: Tatiana Pivovarova Date: Tue, 9 Dec 2014 17:31:40 +0300 Subject: [PATCH 15/58] 8065134: Need WhiteBox::allocateCodeBlob(long, int) method to be implemented Reviewed-by: kvn, dholmes, iignatyev --- hotspot/src/share/vm/prims/whitebox.hpp | 2 +- .../test/testlibrary/whitebox/sun/hotspot/WhiteBox.java | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/hotspot/src/share/vm/prims/whitebox.hpp b/hotspot/src/share/vm/prims/whitebox.hpp index 4ead33f7fec..59754270e35 100644 --- a/hotspot/src/share/vm/prims/whitebox.hpp +++ b/hotspot/src/share/vm/prims/whitebox.hpp @@ -74,7 +74,7 @@ class WhiteBox : public AllStatic { static JavaThread* create_sweeper_thread(TRAPS); static int get_blob_type(const CodeBlob* code); static CodeHeap* get_code_heap(int blob_type); - static CodeBlob* allocate_code_blob(int blob_type, int size); + static CodeBlob* allocate_code_blob(int size, int blob_type); static int array_bytes_to_length(size_t bytes); static void register_methods(JNIEnv* env, jclass wbclass, JavaThread* thread, JNINativeMethod* method_array, int method_count); diff --git a/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java b/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java index 35f5a9c1109..c5c96cb5c80 100644 --- a/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java +++ b/hotspot/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java @@ -153,6 +153,14 @@ public class WhiteBox { public native int getMethodEntryBci(Executable method); public native Object[] getNMethod(Executable method, boolean isOsr); public native long allocateCodeBlob(int size, int type); + public long allocateCodeBlob(long size, int type) { + int intSize = (int) size; + if ((long) intSize != size || size < 0) { + throw new IllegalArgumentException( + "size argument has illegal value " + size); + } + return allocateCodeBlob( intSize, type); + } public native void freeCodeBlob(long addr); public void forceNMethodSweep() { try { From caa8dcea6450114ca0627c0814ca720e9b9a2d6a Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Tue, 9 Dec 2014 18:49:13 +0100 Subject: [PATCH 16/58] 8066103: C2's range check smearing allows out of bound array accesses Range check smearing uncorrectly adjust first range check in a list of range checks to cover all of them Reviewed-by: jrose, kvn, iveresov --- hotspot/src/share/vm/opto/ifnode.cpp | 163 +++++-- .../rangechecks/TestRangeCheckSmearing.java | 436 ++++++++++++++++++ 2 files changed, 549 insertions(+), 50 deletions(-) create mode 100644 hotspot/test/compiler/rangechecks/TestRangeCheckSmearing.java diff --git a/hotspot/src/share/vm/opto/ifnode.cpp b/hotspot/src/share/vm/opto/ifnode.cpp index c0080c81d57..7b5f0d7b4be 100644 --- a/hotspot/src/share/vm/opto/ifnode.cpp +++ b/hotspot/src/share/vm/opto/ifnode.cpp @@ -820,6 +820,11 @@ static Node *remove_useless_bool(IfNode *iff, PhaseGVN *phase) { static IfNode* idealize_test(PhaseGVN* phase, IfNode* iff); +struct RangeCheck { + Node* ctl; + jint off; +}; + //------------------------------Ideal------------------------------------------ // Return a node which is more "ideal" than the current node. Strip out // control copies @@ -861,83 +866,141 @@ Node *IfNode::Ideal(PhaseGVN *phase, bool can_reshape) { jint offset1; int flip1 = is_range_check(range1, index1, offset1); if( flip1 ) { - Node *first_prev_dom = NULL; - // Try to remove extra range checks. All 'up_one_dom' gives up at merges // so all checks we inspect post-dominate the top-most check we find. // If we are going to fail the current check and we reach the top check // then we are guaranteed to fail, so just start interpreting there. - // We 'expand' the top 2 range checks to include all post-dominating + // We 'expand' the top 3 range checks to include all post-dominating // checks. - // The top 2 range checks seen - Node *prev_chk1 = NULL; - Node *prev_chk2 = NULL; + // The top 3 range checks seen + const int NRC =3; + RangeCheck prev_checks[NRC]; + int nb_checks = 0; + // Low and high offsets seen so far jint off_lo = offset1; jint off_hi = offset1; - // Scan for the top 2 checks and collect range of offsets - for( int dist = 0; dist < 999; dist++ ) { // Range-Check scan limit - if( dom->Opcode() == Op_If && // Not same opcode? - prev_dom->in(0) == dom ) { // One path of test does dominate? - if( dom == this ) return NULL; // dead loop + bool found_immediate_dominator = false; + + // Scan for the top checks and collect range of offsets + for (int dist = 0; dist < 999; dist++) { // Range-Check scan limit + if (dom->Opcode() == Op_If && // Not same opcode? + prev_dom->in(0) == dom) { // One path of test does dominate? + if (dom == this) return NULL; // dead loop // See if this is a range check Node *index2, *range2; jint offset2; int flip2 = dom->as_If()->is_range_check(range2, index2, offset2); // See if this is a _matching_ range check, checking against // the same array bounds. - if( flip2 == flip1 && range2 == range1 && index2 == index1 && - dom->outcnt() == 2 ) { + if (flip2 == flip1 && range2 == range1 && index2 == index1 && + dom->outcnt() == 2) { + if (nb_checks == 0 && dom->in(1) == in(1)) { + // Found an immediately dominating test at the same offset. + // This kind of back-to-back test can be eliminated locally, + // and there is no need to search further for dominating tests. + assert(offset2 == offset1, "Same test but different offsets"); + found_immediate_dominator = true; + break; + } // Gather expanded bounds off_lo = MIN2(off_lo,offset2); off_hi = MAX2(off_hi,offset2); - // Record top 2 range checks - prev_chk2 = prev_chk1; - prev_chk1 = prev_dom; - // If we match the test exactly, then the top test covers - // both our lower and upper bounds. - if( dom->in(1) == in(1) ) - prev_chk2 = prev_chk1; + // Record top NRC range checks + prev_checks[nb_checks%NRC].ctl = prev_dom; + prev_checks[nb_checks%NRC].off = offset2; + nb_checks++; } } prev_dom = dom; - dom = up_one_dom( dom ); - if( !dom ) break; + dom = up_one_dom(dom); + if (!dom) break; } + if (!found_immediate_dominator) { + // Attempt to widen the dominating range check to cover some later + // ones. Since range checks "fail" by uncommon-trapping to the + // interpreter, widening a check can make us speculatively enter + // the interpreter. If we see range-check deopt's, do not widen! + if (!phase->C->allow_range_check_smearing()) return NULL; - // Attempt to widen the dominating range check to cover some later - // ones. Since range checks "fail" by uncommon-trapping to the - // interpreter, widening a check can make us speculative enter the - // interpreter. If we see range-check deopt's, do not widen! - if (!phase->C->allow_range_check_smearing()) return NULL; - - // Constant indices only need to check the upper bound. - // Non-constance indices must check both low and high. - if( index1 ) { - // Didn't find 2 prior covering checks, so cannot remove anything. - if( !prev_chk2 ) return NULL; - // 'Widen' the offsets of the 1st and 2nd covering check - adjust_check( prev_chk1, range1, index1, flip1, off_lo, igvn ); - // Do not call adjust_check twice on the same projection - // as the first call may have transformed the BoolNode to a ConI - if( prev_chk1 != prev_chk2 ) { - adjust_check( prev_chk2, range1, index1, flip1, off_hi, igvn ); - } - // Test is now covered by prior checks, dominate it out - prev_dom = prev_chk2; - } else { // Didn't find prior covering check, so cannot remove anything. - if( !prev_chk1 ) return NULL; - // 'Widen' the offset of the 1st and only covering check - adjust_check( prev_chk1, range1, index1, flip1, off_hi, igvn ); - // Test is now covered by prior checks, dominate it out - prev_dom = prev_chk1; + if (nb_checks == 0) { + return NULL; + } + // Constant indices only need to check the upper bound. + // Non-constant indices must check both low and high. + int chk0 = (nb_checks - 1) % NRC; + if (index1) { + if (nb_checks == 1) { + return NULL; + } else { + // If the top range check's constant is the min or max of + // all constants we widen the next one to cover the whole + // range of constants. + RangeCheck rc0 = prev_checks[chk0]; + int chk1 = (nb_checks - 2) % NRC; + RangeCheck rc1 = prev_checks[chk1]; + if (rc0.off == off_lo) { + adjust_check(rc1.ctl, range1, index1, flip1, off_hi, igvn); + prev_dom = rc1.ctl; + } else if (rc0.off == off_hi) { + adjust_check(rc1.ctl, range1, index1, flip1, off_lo, igvn); + prev_dom = rc1.ctl; + } else { + // If the top test's constant is not the min or max of all + // constants, we need 3 range checks. We must leave the + // top test unchanged because widening it would allow the + // accesses it protects to successfully read/write out of + // bounds. + if (nb_checks == 2) { + return NULL; + } + int chk2 = (nb_checks - 3) % NRC; + RangeCheck rc2 = prev_checks[chk2]; + // The top range check a+i covers interval: -a <= i < length-a + // The second range check b+i covers interval: -b <= i < length-b + if (rc1.off <= rc0.off) { + // if b <= a, we change the second range check to: + // -min_of_all_constants <= i < length-min_of_all_constants + // Together top and second range checks now cover: + // -min_of_all_constants <= i < length-a + // which is more restrictive than -b <= i < length-b: + // -b <= -min_of_all_constants <= i < length-a <= length-b + // The third check is then changed to: + // -max_of_all_constants <= i < length-max_of_all_constants + // so 2nd and 3rd checks restrict allowed values of i to: + // -min_of_all_constants <= i < length-max_of_all_constants + adjust_check(rc1.ctl, range1, index1, flip1, off_lo, igvn); + adjust_check(rc2.ctl, range1, index1, flip1, off_hi, igvn); + } else { + // if b > a, we change the second range check to: + // -max_of_all_constants <= i < length-max_of_all_constants + // Together top and second range checks now cover: + // -a <= i < length-max_of_all_constants + // which is more restrictive than -b <= i < length-b: + // -b < -a <= i < length-max_of_all_constants <= length-b + // The third check is then changed to: + // -max_of_all_constants <= i < length-max_of_all_constants + // so 2nd and 3rd checks restrict allowed values of i to: + // -min_of_all_constants <= i < length-max_of_all_constants + adjust_check(rc1.ctl, range1, index1, flip1, off_hi, igvn); + adjust_check(rc2.ctl, range1, index1, flip1, off_lo, igvn); + } + prev_dom = rc2.ctl; + } + } + } else { + RangeCheck rc0 = prev_checks[chk0]; + // 'Widen' the offset of the 1st and only covering check + adjust_check(rc0.ctl, range1, index1, flip1, off_hi, igvn); + // Test is now covered by prior checks, dominate it out + prev_dom = rc0.ctl; + } } - } else { // Scan for an equivalent test Node *cmp; @@ -1019,7 +1082,7 @@ void IfNode::dominated_by( Node *prev_dom, PhaseIterGVN *igvn ) { // for lower and upper bounds. ProjNode* unc_proj = proj_out(1 - prev_dom->as_Proj()->_con)->as_Proj(); if (unc_proj->is_uncommon_trap_proj(Deoptimization::Reason_predicate)) - prev_dom = idom; + prev_dom = idom; // Now walk the current IfNode's projections. // Loop ends when 'this' has no more uses. diff --git a/hotspot/test/compiler/rangechecks/TestRangeCheckSmearing.java b/hotspot/test/compiler/rangechecks/TestRangeCheckSmearing.java new file mode 100644 index 00000000000..204d276f293 --- /dev/null +++ b/hotspot/test/compiler/rangechecks/TestRangeCheckSmearing.java @@ -0,0 +1,436 @@ +/* + * 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 8066103 + * @summary C2's range check smearing allows out of bound array accesses + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox /testlibrary/com/oracle/java/testlibrary + * @build TestRangeCheckSmearing + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main ClassFileInstaller com.oracle.java.testlibrary.Platform + * @run main/othervm -ea -Xmixed -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:-BackgroundCompilation -XX:-UseOnStackReplacement TestRangeCheckSmearing + * + */ + +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.util.*; +import sun.hotspot.WhiteBox; +import sun.hotspot.code.NMethod; +import com.oracle.java.testlibrary.Platform; + +public class TestRangeCheckSmearing { + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + + @Retention(RetentionPolicy.RUNTIME) + @interface Args { int[] value(); } + + // first range check is i + max of all constants + @Args({0, 8}) + static int m1(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+9]; + if (allaccesses) { + res += array[i+8]; + res += array[i+7]; + res += array[i+6]; + res += array[i+5]; + res += array[i+4]; + res += array[i+3]; + res += array[i+2]; + res += array[i+1]; + } + return res; + } + + // first range check is i + min of all constants + @Args({0, -9}) + static int m2(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+1]; + if (allaccesses) { + res += array[i+2]; + res += array[i+3]; + res += array[i+4]; + res += array[i+5]; + res += array[i+6]; + res += array[i+7]; + res += array[i+8]; + res += array[i+9]; + } + return res; + } + + // first range check is not i + min/max of all constants + @Args({0, 8}) + static int m3(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+3]; + if (allaccesses) { + res += array[i+2]; + res += array[i+1]; + res += array[i+4]; + res += array[i+5]; + res += array[i+6]; + res += array[i+7]; + res += array[i+8]; + res += array[i+9]; + } + return res; + } + + @Args({0, -9}) + static int m4(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+3]; + if (allaccesses) { + res += array[i+4]; + res += array[i+1]; + res += array[i+2]; + res += array[i+5]; + res += array[i+6]; + res += array[i+7]; + res += array[i+8]; + res += array[i+9]; + } + return res; + } + + @Args({0, -3}) + static int m5(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+3]; + res += array[i+2]; + if (allaccesses) { + res += array[i+1]; + res += array[i+4]; + res += array[i+5]; + res += array[i+6]; + res += array[i+7]; + res += array[i+8]; + res += array[i+9]; + } + return res; + } + + @Args({0, 6}) + static int m6(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+3]; + res += array[i+4]; + if (allaccesses) { + res += array[i+2]; + res += array[i+1]; + res += array[i+5]; + res += array[i+6]; + res += array[i+7]; + res += array[i+8]; + res += array[i+9]; + } + return res; + } + + @Args({0, 6}) + static int m7(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+3]; + res += array[i+2]; + res += array[i+4]; + if (allaccesses) { + res += array[i+1]; + res += array[i+5]; + res += array[i+6]; + res += array[i+7]; + res += array[i+8]; + res += array[i+9]; + } + return res; + } + + @Args({0, -3}) + static int m8(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+3]; + res += array[i+4]; + res += array[i+2]; + if (allaccesses) { + res += array[i+1]; + res += array[i+5]; + res += array[i+6]; + res += array[i+7]; + res += array[i+8]; + res += array[i+9]; + } + return res; + } + + @Args({6, 15}) + static int m9(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+3]; + if (allaccesses) { + res += array[i-2]; + res += array[i-1]; + res += array[i-4]; + res += array[i-5]; + res += array[i-6]; + } + return res; + } + + @Args({3, 12}) + static int m10(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+3]; + if (allaccesses) { + res += array[i-2]; + res += array[i-1]; + res += array[i-3]; + res += array[i+4]; + res += array[i+5]; + res += array[i+6]; + } + return res; + } + + @Args({3, -3}) + static int m11(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+3]; + res += array[i-2]; + if (allaccesses) { + res += array[i+5]; + res += array[i+6]; + } + return res; + } + + @Args({3, 6}) + static int m12(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+3]; + res += array[i+6]; + if (allaccesses) { + res += array[i-2]; + res += array[i-3]; + } + return res; + } + + // check that identical range check is replaced by dominating one + // only when correct + @Args({0}) + static int m13(int[] array, int i, boolean ignore) { + int res = 0; + res += array[i+3]; + res += array[i+3]; + return res; + } + + @Args({2, 0}) + static int m14(int[] array, int i, boolean ignore) { + int res = 0; + + res += array[i]; + res += array[i-2]; + res += array[i]; // If range check below were to be removed first this cannot be considered identical to first range check + res += array[i-1]; // range check removed so i-1 array access depends on previous check + + return res; + } + + static int[] m15_dummy = new int[10]; + @Args({2, 0}) + static int m15(int[] array, int i, boolean ignore) { + int res = 0; + res += array[i]; + + // When the loop is optimized out we don't want the + // array[i-1] access which is dependent on array[i]'s + // range check to become dependent on the identical range + // check above. + + int[] array2 = m15_dummy; + int j = 0; + for (; j < 10; j++); + if (j == 10) { + array2 = array; + } + + res += array2[i-2]; + res += array2[i]; + res += array2[i-1]; // range check removed so i-1 array access depends on previous check + + return res; + } + + @Args({2, 0}) + static int m16(int[] array, int i, boolean ignore) { + int res = 0; + + res += array[i]; + res += array[i-1]; + res += array[i-1]; + res += array[i-2]; + + return res; + } + + @Args({2, 0}) + static int m17(int[] array, int i, boolean ignore) { + int res = 0; + + res += array[i]; + res += array[i-2]; + res += array[i-2]; + res += array[i+2]; + res += array[i+2]; + res += array[i-1]; + res += array[i-1]; + + return res; + } + + static public void main(String[] args) { + if (WHITE_BOX.getBooleanVMFlag("BackgroundCompilation")) { + throw new AssertionError("Background compilation enabled"); + } + new TestRangeCheckSmearing().doTests(); + } + boolean success = true; + boolean exception = false; + final int[] array = new int[10]; + final HashMap tests = new HashMap<>(); + { + final Class TEST_PARAM_TYPES[] = { int[].class, int.class, boolean.class }; + for (Method m : this.getClass().getDeclaredMethods()) { + if (m.getName().matches("m[0-9]+")) { + assert(Modifier.isStatic(m.getModifiers())) : m; + assert(m.getReturnType() == int.class) : m; + assert(Arrays.equals(m.getParameterTypes(), TEST_PARAM_TYPES)) : m; + tests.put(m.getName(), m); + } + } + } + + void invokeTest(Method m, int[] array, int index, boolean z) { + try { + m.invoke(null, array, index, z); + } catch (ReflectiveOperationException roe) { + Throwable ex = roe.getCause(); + if (ex instanceof ArrayIndexOutOfBoundsException) + throw (ArrayIndexOutOfBoundsException) ex; + throw new AssertionError(roe); + } + } + + void doTest(String name) { + Method m = tests.get(name); + tests.remove(name); + int[] args = m.getAnnotation(Args.class).value(); + int index0 = args[0], index1; + boolean exceptionRequired = true; + if (args.length == 2) { + index1 = args[1]; + } else { + // no negative test for this one + assert(args.length == 1); + assert(name.equals("m13")); + exceptionRequired = false; + index1 = index0; + } + // Get the method compiled. + if (!WHITE_BOX.isMethodCompiled(m)) { + // If not, try to compile it with C2 + if(!WHITE_BOX.enqueueMethodForCompilation(m, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION)) { + // C2 compiler not available, try to compile with C1 + WHITE_BOX.enqueueMethodForCompilation(m, CompilerWhiteBoxTest.COMP_LEVEL_SIMPLE); + } + } + if (!WHITE_BOX.isMethodCompiled(m)) { + throw new RuntimeException(m + " not compiled"); + } + + // valid access + invokeTest(m, array, index0, true); + + if (!WHITE_BOX.isMethodCompiled(m)) { + throw new RuntimeException(m + " deoptimized on valid array access"); + } + + exception = false; + boolean test_success = true; + try { + invokeTest(m, array, index1, false); + } catch(ArrayIndexOutOfBoundsException aioob) { + exception = true; + System.out.println("ArrayIndexOutOfBoundsException thrown in "+name); + } + if (!exception) { + System.out.println("ArrayIndexOutOfBoundsException was not thrown in "+name); + } + + if (Platform.isServer()) { + if (exceptionRequired == WHITE_BOX.isMethodCompiled(m)) { + System.out.println((exceptionRequired?"Didn't deoptimized":"deoptimized") + " in "+name); + test_success = false; + } + } + + if (exception != exceptionRequired) { + System.out.println((exceptionRequired?"exception required but not thrown":"not exception required but thrown") + " in "+name); + test_success = false; + } + + if (!test_success) { + success = false; + System.out.println("TEST FAILED: "+name); + } + + } + void doTests() { + doTest("m1"); + doTest("m2"); + doTest("m3"); + doTest("m4"); + doTest("m5"); + doTest("m6"); + doTest("m7"); + doTest("m8"); + doTest("m9"); + doTest("m10"); + doTest("m11"); + doTest("m12"); + doTest("m13"); + doTest("m14"); + doTest("m15"); + doTest("m16"); + doTest("m17"); + if (!success) { + throw new RuntimeException("Some tests failed"); + } + assert(tests.isEmpty()) : tests; + } +} From 5af5b25f986b76196d409ba243693db11cbf7a38 Mon Sep 17 00:00:00 2001 From: Igor Veresov Date: Tue, 9 Dec 2014 12:25:38 -0800 Subject: [PATCH 17/58] 8066900: Array Out Of Bounds Exception causes variable corruption Fix FP registers save/restore during exception handling Reviewed-by: kvn, vlivanov --- hotspot/src/cpu/x86/vm/c1_Runtime1_x86.cpp | 4 +- hotspot/test/compiler/exceptions/SumTest.java | 86 +++++++++++++++++++ 2 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 hotspot/test/compiler/exceptions/SumTest.java diff --git a/hotspot/src/cpu/x86/vm/c1_Runtime1_x86.cpp b/hotspot/src/cpu/x86/vm/c1_Runtime1_x86.cpp index a0d54665375..63da256a7a9 100644 --- a/hotspot/src/cpu/x86/vm/c1_Runtime1_x86.cpp +++ b/hotspot/src/cpu/x86/vm/c1_Runtime1_x86.cpp @@ -675,7 +675,7 @@ OopMapSet* Runtime1::generate_handle_exception(StubID id, StubAssembler *sasm) { case handle_exception_nofpu_id: case handle_exception_id: // At this point all registers MAY be live. - oop_map = save_live_registers(sasm, 1 /*thread*/, id == handle_exception_nofpu_id); + oop_map = save_live_registers(sasm, 1 /*thread*/, id != handle_exception_nofpu_id); break; case handle_exception_from_callee_id: { // At this point all registers except exception oop (RAX) and @@ -748,7 +748,7 @@ OopMapSet* Runtime1::generate_handle_exception(StubID id, StubAssembler *sasm) { case handle_exception_nofpu_id: case handle_exception_id: // Restore the registers that were saved at the beginning. - restore_live_registers(sasm, id == handle_exception_nofpu_id); + restore_live_registers(sasm, id != handle_exception_nofpu_id); break; case handle_exception_from_callee_id: // WIN64_ONLY: No need to add frame::arg_reg_save_area_bytes to SP diff --git a/hotspot/test/compiler/exceptions/SumTest.java b/hotspot/test/compiler/exceptions/SumTest.java new file mode 100644 index 00000000000..d4555aa55aa --- /dev/null +++ b/hotspot/test/compiler/exceptions/SumTest.java @@ -0,0 +1,86 @@ +/* + * 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 8066900 + * @summary FP registers are not properly restored by C1 when handling exceptions + * @run main/othervm -Xbatch SumTest + * + */ +public class SumTest { + private static class Sum { + + double[] sums; + + /** + * Construct empty Sum + */ + public Sum() { + sums = new double[0]; + } + + /** + * Return the sum of all numbers added to this Sum + * + * @return the sum + */ + final public double getSum() { + double sum = 0; + for (final double s : sums) { + sum += s; + } + + return sum; + } + + /** + * Add a new number to this Sum + * + * @param a number to be added. + */ + final public void add(double a) { + try { + sums[sums.length] = -1; // Cause IndexOutOfBoundsException + } catch (final IndexOutOfBoundsException e) { + final double[] oldSums = sums; + sums = new double[oldSums.length + 1]; // Extend sums + System.arraycopy(oldSums, 0, sums, 0, oldSums.length); + sums[oldSums.length] = a; // Append a + } + } + } + + public static void main(String[] args) throws Exception { + final Sum sum = new Sum(); + for (int i = 1; i <= 10000; ++i) { + sum.add(1); + double ii = sum.getSum(); + if (i != ii) { + throw new Exception("Failure: computed = " + ii + ", expected = " + i); + } + } + } + +} + From ef7d6c3b9de26fdbf47709798d078124af65c92b Mon Sep 17 00:00:00 2001 From: Bengt Rutisson Date: Wed, 10 Dec 2014 11:30:46 +0100 Subject: [PATCH 18/58] 8066780: Split CardGeneration out to its own file Reviewed-by: kbarrett, tschatzl --- .../concurrentMarkSweepGeneration.hpp | 2 +- .../src/share/vm/memory/cardGeneration.cpp | 271 ++++++++++++++++++ .../src/share/vm/memory/cardGeneration.hpp | 81 ++++++ hotspot/src/share/vm/memory/generation.cpp | 241 ---------------- hotspot/src/share/vm/memory/generation.hpp | 53 ---- .../src/share/vm/memory/tenuredGeneration.hpp | 2 +- 6 files changed, 354 insertions(+), 296 deletions(-) create mode 100644 hotspot/src/share/vm/memory/cardGeneration.cpp create mode 100644 hotspot/src/share/vm/memory/cardGeneration.hpp diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp index 826cfd5def2..83364976bfc 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp @@ -30,8 +30,8 @@ #include "gc_implementation/shared/gcStats.hpp" #include "gc_implementation/shared/gcWhen.hpp" #include "gc_implementation/shared/generationCounters.hpp" +#include "memory/cardGeneration.hpp" #include "memory/freeBlockDictionary.hpp" -#include "memory/generation.hpp" #include "memory/iterator.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/virtualspace.hpp" diff --git a/hotspot/src/share/vm/memory/cardGeneration.cpp b/hotspot/src/share/vm/memory/cardGeneration.cpp new file mode 100644 index 00000000000..12faf829986 --- /dev/null +++ b/hotspot/src/share/vm/memory/cardGeneration.cpp @@ -0,0 +1,271 @@ +/* + * 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. + * + */ + +#include "precompiled.hpp" +#include "memory/blockOffsetTable.inline.hpp" +#include "memory/gcLocker.hpp" +#include "memory/generationSpec.hpp" +#include "memory/genOopClosures.inline.hpp" +#include "memory/genRemSet.hpp" +#include "memory/iterator.hpp" +#include "memory/memRegion.hpp" +#include "memory/space.inline.hpp" +#include "runtime/java.hpp" + +CardGeneration::CardGeneration(ReservedSpace rs, size_t initial_byte_size, + int level, + GenRemSet* remset) : + Generation(rs, initial_byte_size, level), _rs(remset), + _shrink_factor(0), _min_heap_delta_bytes(), _capacity_at_prologue(), + _used_at_prologue() +{ + HeapWord* start = (HeapWord*)rs.base(); + size_t reserved_byte_size = rs.size(); + assert((uintptr_t(start) & 3) == 0, "bad alignment"); + assert((reserved_byte_size & 3) == 0, "bad alignment"); + MemRegion reserved_mr(start, heap_word_size(reserved_byte_size)); + _bts = new BlockOffsetSharedArray(reserved_mr, + heap_word_size(initial_byte_size)); + MemRegion committed_mr(start, heap_word_size(initial_byte_size)); + _rs->resize_covered_region(committed_mr); + if (_bts == NULL) + vm_exit_during_initialization("Could not allocate a BlockOffsetArray"); + + // Verify that the start and end of this generation is the start of a card. + // If this wasn't true, a single card could span more than on generation, + // which would cause problems when we commit/uncommit memory, and when we + // clear and dirty cards. + guarantee(_rs->is_aligned(reserved_mr.start()), "generation must be card aligned"); + if (reserved_mr.end() != Universe::heap()->reserved_region().end()) { + // Don't check at the very end of the heap as we'll assert that we're probing off + // the end if we try. + guarantee(_rs->is_aligned(reserved_mr.end()), "generation must be card aligned"); + } + _min_heap_delta_bytes = MinHeapDeltaBytes; + _capacity_at_prologue = initial_byte_size; + _used_at_prologue = 0; +} + +bool CardGeneration::expand(size_t bytes, size_t expand_bytes) { + assert_locked_or_safepoint(Heap_lock); + if (bytes == 0) { + return true; // That's what grow_by(0) would return + } + size_t aligned_bytes = ReservedSpace::page_align_size_up(bytes); + if (aligned_bytes == 0){ + // The alignment caused the number of bytes to wrap. An expand_by(0) will + // return true with the implication that an expansion was done when it + // was not. A call to expand implies a best effort to expand by "bytes" + // but not a guarantee. Align down to give a best effort. This is likely + // the most that the generation can expand since it has some capacity to + // start with. + aligned_bytes = ReservedSpace::page_align_size_down(bytes); + } + size_t aligned_expand_bytes = ReservedSpace::page_align_size_up(expand_bytes); + bool success = false; + if (aligned_expand_bytes > aligned_bytes) { + success = grow_by(aligned_expand_bytes); + } + if (!success) { + success = grow_by(aligned_bytes); + } + if (!success) { + success = grow_to_reserved(); + } + if (PrintGC && Verbose) { + if (success && GC_locker::is_active_and_needs_gc()) { + gclog_or_tty->print_cr("Garbage collection disabled, expanded heap instead"); + } + } + + return success; +} + +// No young generation references, clear this generation's cards. +void CardGeneration::clear_remembered_set() { + _rs->clear(reserved()); +} + +// Objects in this generation may have moved, invalidate this +// generation's cards. +void CardGeneration::invalidate_remembered_set() { + _rs->invalidate(used_region()); +} + +void CardGeneration::compute_new_size() { + assert(_shrink_factor <= 100, "invalid shrink factor"); + size_t current_shrink_factor = _shrink_factor; + _shrink_factor = 0; + + // We don't have floating point command-line arguments + // Note: argument processing ensures that MinHeapFreeRatio < 100. + const double minimum_free_percentage = MinHeapFreeRatio / 100.0; + const double maximum_used_percentage = 1.0 - minimum_free_percentage; + + // Compute some numbers about the state of the heap. + const size_t used_after_gc = used(); + const size_t capacity_after_gc = capacity(); + + const double min_tmp = used_after_gc / maximum_used_percentage; + size_t minimum_desired_capacity = (size_t)MIN2(min_tmp, double(max_uintx)); + // Don't shrink less than the initial generation size + minimum_desired_capacity = MAX2(minimum_desired_capacity, + spec()->init_size()); + assert(used_after_gc <= minimum_desired_capacity, "sanity check"); + + if (PrintGC && Verbose) { + const size_t free_after_gc = free(); + const double free_percentage = ((double)free_after_gc) / capacity_after_gc; + gclog_or_tty->print_cr("TenuredGeneration::compute_new_size: "); + gclog_or_tty->print_cr(" " + " minimum_free_percentage: %6.2f" + " maximum_used_percentage: %6.2f", + minimum_free_percentage, + maximum_used_percentage); + gclog_or_tty->print_cr(" " + " free_after_gc : %6.1fK" + " used_after_gc : %6.1fK" + " capacity_after_gc : %6.1fK", + free_after_gc / (double) K, + used_after_gc / (double) K, + capacity_after_gc / (double) K); + gclog_or_tty->print_cr(" " + " free_percentage: %6.2f", + free_percentage); + } + + if (capacity_after_gc < minimum_desired_capacity) { + // If we have less free space than we want then expand + size_t expand_bytes = minimum_desired_capacity - capacity_after_gc; + // Don't expand unless it's significant + if (expand_bytes >= _min_heap_delta_bytes) { + expand(expand_bytes, 0); // safe if expansion fails + } + if (PrintGC && Verbose) { + gclog_or_tty->print_cr(" expanding:" + " minimum_desired_capacity: %6.1fK" + " expand_bytes: %6.1fK" + " _min_heap_delta_bytes: %6.1fK", + minimum_desired_capacity / (double) K, + expand_bytes / (double) K, + _min_heap_delta_bytes / (double) K); + } + return; + } + + // No expansion, now see if we want to shrink + size_t shrink_bytes = 0; + // We would never want to shrink more than this + size_t max_shrink_bytes = capacity_after_gc - minimum_desired_capacity; + + if (MaxHeapFreeRatio < 100) { + const double maximum_free_percentage = MaxHeapFreeRatio / 100.0; + const double minimum_used_percentage = 1.0 - maximum_free_percentage; + const double max_tmp = used_after_gc / minimum_used_percentage; + size_t maximum_desired_capacity = (size_t)MIN2(max_tmp, double(max_uintx)); + maximum_desired_capacity = MAX2(maximum_desired_capacity, + spec()->init_size()); + if (PrintGC && Verbose) { + gclog_or_tty->print_cr(" " + " maximum_free_percentage: %6.2f" + " minimum_used_percentage: %6.2f", + maximum_free_percentage, + minimum_used_percentage); + gclog_or_tty->print_cr(" " + " _capacity_at_prologue: %6.1fK" + " minimum_desired_capacity: %6.1fK" + " maximum_desired_capacity: %6.1fK", + _capacity_at_prologue / (double) K, + minimum_desired_capacity / (double) K, + maximum_desired_capacity / (double) K); + } + assert(minimum_desired_capacity <= maximum_desired_capacity, + "sanity check"); + + if (capacity_after_gc > maximum_desired_capacity) { + // Capacity too large, compute shrinking size + shrink_bytes = capacity_after_gc - maximum_desired_capacity; + // We don't want shrink all the way back to initSize if people call + // System.gc(), because some programs do that between "phases" and then + // we'd just have to grow the heap up again for the next phase. So we + // damp the shrinking: 0% on the first call, 10% on the second call, 40% + // on the third call, and 100% by the fourth call. But if we recompute + // size without shrinking, it goes back to 0%. + shrink_bytes = shrink_bytes / 100 * current_shrink_factor; + assert(shrink_bytes <= max_shrink_bytes, "invalid shrink size"); + if (current_shrink_factor == 0) { + _shrink_factor = 10; + } else { + _shrink_factor = MIN2(current_shrink_factor * 4, (size_t) 100); + } + if (PrintGC && Verbose) { + gclog_or_tty->print_cr(" " + " shrinking:" + " initSize: %.1fK" + " maximum_desired_capacity: %.1fK", + spec()->init_size() / (double) K, + maximum_desired_capacity / (double) K); + gclog_or_tty->print_cr(" " + " shrink_bytes: %.1fK" + " current_shrink_factor: " SIZE_FORMAT + " new shrink factor: " SIZE_FORMAT + " _min_heap_delta_bytes: %.1fK", + shrink_bytes / (double) K, + current_shrink_factor, + _shrink_factor, + _min_heap_delta_bytes / (double) K); + } + } + } + + if (capacity_after_gc > _capacity_at_prologue) { + // We might have expanded for promotions, in which case we might want to + // take back that expansion if there's room after GC. That keeps us from + // stretching the heap with promotions when there's plenty of room. + size_t expansion_for_promotion = capacity_after_gc - _capacity_at_prologue; + expansion_for_promotion = MIN2(expansion_for_promotion, max_shrink_bytes); + // We have two shrinking computations, take the largest + shrink_bytes = MAX2(shrink_bytes, expansion_for_promotion); + assert(shrink_bytes <= max_shrink_bytes, "invalid shrink size"); + if (PrintGC && Verbose) { + gclog_or_tty->print_cr(" " + " aggressive shrinking:" + " _capacity_at_prologue: %.1fK" + " capacity_after_gc: %.1fK" + " expansion_for_promotion: %.1fK" + " shrink_bytes: %.1fK", + capacity_after_gc / (double) K, + _capacity_at_prologue / (double) K, + expansion_for_promotion / (double) K, + shrink_bytes / (double) K); + } + } + // Don't shrink unless it's significant + if (shrink_bytes >= _min_heap_delta_bytes) { + shrink(shrink_bytes); + } +} + +// Currently nothing to do. +void CardGeneration::prepare_for_verify() {} diff --git a/hotspot/src/share/vm/memory/cardGeneration.hpp b/hotspot/src/share/vm/memory/cardGeneration.hpp new file mode 100644 index 00000000000..3fb43add866 --- /dev/null +++ b/hotspot/src/share/vm/memory/cardGeneration.hpp @@ -0,0 +1,81 @@ +/* + * 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. + * + */ + +#ifndef SHARE_VM_MEMORY_CARDGENERATION_HPP +#define SHARE_VM_MEMORY_CARDGENERATION_HPP + +// Class CardGeneration is a generation that is covered by a card table, +// and uses a card-size block-offset array to implement block_start. + +#include "memory/generation.hpp" + +class BlockOffsetSharedArray; + +class CardGeneration: public Generation { + friend class VMStructs; + protected: + // This is shared with other generations. + GenRemSet* _rs; + // This is local to this generation. + BlockOffsetSharedArray* _bts; + + // current shrinking effect: this damps shrinking when the heap gets empty. + size_t _shrink_factor; + + size_t _min_heap_delta_bytes; // Minimum amount to expand. + + // Some statistics from before gc started. + // These are gathered in the gc_prologue (and should_collect) + // to control growing/shrinking policy in spite of promotions. + size_t _capacity_at_prologue; + size_t _used_at_prologue; + + CardGeneration(ReservedSpace rs, size_t initial_byte_size, int level, + GenRemSet* remset); + + public: + + // Attempt to expand the generation by "bytes". Expand by at a + // minimum "expand_bytes". Return true if some amount (not + // necessarily the full "bytes") was done. + virtual bool expand(size_t bytes, size_t expand_bytes); + + // Shrink generation with specified size + virtual void shrink(size_t bytes) = 0; + + virtual void compute_new_size(); + + virtual void clear_remembered_set(); + + virtual void invalidate_remembered_set(); + + virtual void prepare_for_verify(); + + // Grow generation with specified size (returns false if unable to grow) + virtual bool grow_by(size_t bytes) = 0; + // Grow generation to reserved size. + virtual bool grow_to_reserved() = 0; +}; + +#endif // SHARE_VM_MEMORY_CARDGENERATION_HPP diff --git a/hotspot/src/share/vm/memory/generation.cpp b/hotspot/src/share/vm/memory/generation.cpp index 87880cf0e31..617ed682cfc 100644 --- a/hotspot/src/share/vm/memory/generation.cpp +++ b/hotspot/src/share/vm/memory/generation.cpp @@ -361,244 +361,3 @@ void Generation::compact() { sp = sp->next_compaction_space(); } } - -CardGeneration::CardGeneration(ReservedSpace rs, size_t initial_byte_size, - int level, - GenRemSet* remset) : - Generation(rs, initial_byte_size, level), _rs(remset), - _shrink_factor(0), _min_heap_delta_bytes(), _capacity_at_prologue(), - _used_at_prologue() -{ - HeapWord* start = (HeapWord*)rs.base(); - size_t reserved_byte_size = rs.size(); - assert((uintptr_t(start) & 3) == 0, "bad alignment"); - assert((reserved_byte_size & 3) == 0, "bad alignment"); - MemRegion reserved_mr(start, heap_word_size(reserved_byte_size)); - _bts = new BlockOffsetSharedArray(reserved_mr, - heap_word_size(initial_byte_size)); - MemRegion committed_mr(start, heap_word_size(initial_byte_size)); - _rs->resize_covered_region(committed_mr); - if (_bts == NULL) - vm_exit_during_initialization("Could not allocate a BlockOffsetArray"); - - // Verify that the start and end of this generation is the start of a card. - // If this wasn't true, a single card could span more than on generation, - // which would cause problems when we commit/uncommit memory, and when we - // clear and dirty cards. - guarantee(_rs->is_aligned(reserved_mr.start()), "generation must be card aligned"); - if (reserved_mr.end() != Universe::heap()->reserved_region().end()) { - // Don't check at the very end of the heap as we'll assert that we're probing off - // the end if we try. - guarantee(_rs->is_aligned(reserved_mr.end()), "generation must be card aligned"); - } - _min_heap_delta_bytes = MinHeapDeltaBytes; - _capacity_at_prologue = initial_byte_size; - _used_at_prologue = 0; -} - -bool CardGeneration::expand(size_t bytes, size_t expand_bytes) { - assert_locked_or_safepoint(Heap_lock); - if (bytes == 0) { - return true; // That's what grow_by(0) would return - } - size_t aligned_bytes = ReservedSpace::page_align_size_up(bytes); - if (aligned_bytes == 0){ - // The alignment caused the number of bytes to wrap. An expand_by(0) will - // return true with the implication that an expansion was done when it - // was not. A call to expand implies a best effort to expand by "bytes" - // but not a guarantee. Align down to give a best effort. This is likely - // the most that the generation can expand since it has some capacity to - // start with. - aligned_bytes = ReservedSpace::page_align_size_down(bytes); - } - size_t aligned_expand_bytes = ReservedSpace::page_align_size_up(expand_bytes); - bool success = false; - if (aligned_expand_bytes > aligned_bytes) { - success = grow_by(aligned_expand_bytes); - } - if (!success) { - success = grow_by(aligned_bytes); - } - if (!success) { - success = grow_to_reserved(); - } - if (PrintGC && Verbose) { - if (success && GC_locker::is_active_and_needs_gc()) { - gclog_or_tty->print_cr("Garbage collection disabled, expanded heap instead"); - } - } - - return success; -} - - -// No young generation references, clear this generation's cards. -void CardGeneration::clear_remembered_set() { - _rs->clear(reserved()); -} - - -// Objects in this generation may have moved, invalidate this -// generation's cards. -void CardGeneration::invalidate_remembered_set() { - _rs->invalidate(used_region()); -} - - -void CardGeneration::compute_new_size() { - assert(_shrink_factor <= 100, "invalid shrink factor"); - size_t current_shrink_factor = _shrink_factor; - _shrink_factor = 0; - - // We don't have floating point command-line arguments - // Note: argument processing ensures that MinHeapFreeRatio < 100. - const double minimum_free_percentage = MinHeapFreeRatio / 100.0; - const double maximum_used_percentage = 1.0 - minimum_free_percentage; - - // Compute some numbers about the state of the heap. - const size_t used_after_gc = used(); - const size_t capacity_after_gc = capacity(); - - const double min_tmp = used_after_gc / maximum_used_percentage; - size_t minimum_desired_capacity = (size_t)MIN2(min_tmp, double(max_uintx)); - // Don't shrink less than the initial generation size - minimum_desired_capacity = MAX2(minimum_desired_capacity, - spec()->init_size()); - assert(used_after_gc <= minimum_desired_capacity, "sanity check"); - - if (PrintGC && Verbose) { - const size_t free_after_gc = free(); - const double free_percentage = ((double)free_after_gc) / capacity_after_gc; - gclog_or_tty->print_cr("TenuredGeneration::compute_new_size: "); - gclog_or_tty->print_cr(" " - " minimum_free_percentage: %6.2f" - " maximum_used_percentage: %6.2f", - minimum_free_percentage, - maximum_used_percentage); - gclog_or_tty->print_cr(" " - " free_after_gc : %6.1fK" - " used_after_gc : %6.1fK" - " capacity_after_gc : %6.1fK", - free_after_gc / (double) K, - used_after_gc / (double) K, - capacity_after_gc / (double) K); - gclog_or_tty->print_cr(" " - " free_percentage: %6.2f", - free_percentage); - } - - if (capacity_after_gc < minimum_desired_capacity) { - // If we have less free space than we want then expand - size_t expand_bytes = minimum_desired_capacity - capacity_after_gc; - // Don't expand unless it's significant - if (expand_bytes >= _min_heap_delta_bytes) { - expand(expand_bytes, 0); // safe if expansion fails - } - if (PrintGC && Verbose) { - gclog_or_tty->print_cr(" expanding:" - " minimum_desired_capacity: %6.1fK" - " expand_bytes: %6.1fK" - " _min_heap_delta_bytes: %6.1fK", - minimum_desired_capacity / (double) K, - expand_bytes / (double) K, - _min_heap_delta_bytes / (double) K); - } - return; - } - - // No expansion, now see if we want to shrink - size_t shrink_bytes = 0; - // We would never want to shrink more than this - size_t max_shrink_bytes = capacity_after_gc - minimum_desired_capacity; - - if (MaxHeapFreeRatio < 100) { - const double maximum_free_percentage = MaxHeapFreeRatio / 100.0; - const double minimum_used_percentage = 1.0 - maximum_free_percentage; - const double max_tmp = used_after_gc / minimum_used_percentage; - size_t maximum_desired_capacity = (size_t)MIN2(max_tmp, double(max_uintx)); - maximum_desired_capacity = MAX2(maximum_desired_capacity, - spec()->init_size()); - if (PrintGC && Verbose) { - gclog_or_tty->print_cr(" " - " maximum_free_percentage: %6.2f" - " minimum_used_percentage: %6.2f", - maximum_free_percentage, - minimum_used_percentage); - gclog_or_tty->print_cr(" " - " _capacity_at_prologue: %6.1fK" - " minimum_desired_capacity: %6.1fK" - " maximum_desired_capacity: %6.1fK", - _capacity_at_prologue / (double) K, - minimum_desired_capacity / (double) K, - maximum_desired_capacity / (double) K); - } - assert(minimum_desired_capacity <= maximum_desired_capacity, - "sanity check"); - - if (capacity_after_gc > maximum_desired_capacity) { - // Capacity too large, compute shrinking size - shrink_bytes = capacity_after_gc - maximum_desired_capacity; - // We don't want shrink all the way back to initSize if people call - // System.gc(), because some programs do that between "phases" and then - // we'd just have to grow the heap up again for the next phase. So we - // damp the shrinking: 0% on the first call, 10% on the second call, 40% - // on the third call, and 100% by the fourth call. But if we recompute - // size without shrinking, it goes back to 0%. - shrink_bytes = shrink_bytes / 100 * current_shrink_factor; - assert(shrink_bytes <= max_shrink_bytes, "invalid shrink size"); - if (current_shrink_factor == 0) { - _shrink_factor = 10; - } else { - _shrink_factor = MIN2(current_shrink_factor * 4, (size_t) 100); - } - if (PrintGC && Verbose) { - gclog_or_tty->print_cr(" " - " shrinking:" - " initSize: %.1fK" - " maximum_desired_capacity: %.1fK", - spec()->init_size() / (double) K, - maximum_desired_capacity / (double) K); - gclog_or_tty->print_cr(" " - " shrink_bytes: %.1fK" - " current_shrink_factor: " SIZE_FORMAT - " new shrink factor: " SIZE_FORMAT - " _min_heap_delta_bytes: %.1fK", - shrink_bytes / (double) K, - current_shrink_factor, - _shrink_factor, - _min_heap_delta_bytes / (double) K); - } - } - } - - if (capacity_after_gc > _capacity_at_prologue) { - // We might have expanded for promotions, in which case we might want to - // take back that expansion if there's room after GC. That keeps us from - // stretching the heap with promotions when there's plenty of room. - size_t expansion_for_promotion = capacity_after_gc - _capacity_at_prologue; - expansion_for_promotion = MIN2(expansion_for_promotion, max_shrink_bytes); - // We have two shrinking computations, take the largest - shrink_bytes = MAX2(shrink_bytes, expansion_for_promotion); - assert(shrink_bytes <= max_shrink_bytes, "invalid shrink size"); - if (PrintGC && Verbose) { - gclog_or_tty->print_cr(" " - " aggressive shrinking:" - " _capacity_at_prologue: %.1fK" - " capacity_after_gc: %.1fK" - " expansion_for_promotion: %.1fK" - " shrink_bytes: %.1fK", - capacity_after_gc / (double) K, - _capacity_at_prologue / (double) K, - expansion_for_promotion / (double) K, - shrink_bytes / (double) K); - } - } - // Don't shrink unless it's significant - if (shrink_bytes >= _min_heap_delta_bytes) { - shrink(shrink_bytes); - } -} - -// Currently nothing to do. -void CardGeneration::prepare_for_verify() {} - diff --git a/hotspot/src/share/vm/memory/generation.hpp b/hotspot/src/share/vm/memory/generation.hpp index e9c352e10b4..058f757de01 100644 --- a/hotspot/src/share/vm/memory/generation.hpp +++ b/hotspot/src/share/vm/memory/generation.hpp @@ -584,57 +584,4 @@ public: virtual CollectorCounters* counters() { return _gc_counters; } }; -// Class CardGeneration is a generation that is covered by a card table, -// and uses a card-size block-offset array to implement block_start. - -// class BlockOffsetArray; -// class BlockOffsetArrayContigSpace; -class BlockOffsetSharedArray; - -class CardGeneration: public Generation { - friend class VMStructs; - protected: - // This is shared with other generations. - GenRemSet* _rs; - // This is local to this generation. - BlockOffsetSharedArray* _bts; - - // current shrinking effect: this damps shrinking when the heap gets empty. - size_t _shrink_factor; - - size_t _min_heap_delta_bytes; // Minimum amount to expand. - - // Some statistics from before gc started. - // These are gathered in the gc_prologue (and should_collect) - // to control growing/shrinking policy in spite of promotions. - size_t _capacity_at_prologue; - size_t _used_at_prologue; - - CardGeneration(ReservedSpace rs, size_t initial_byte_size, int level, - GenRemSet* remset); - - public: - - // Attempt to expand the generation by "bytes". Expand by at a - // minimum "expand_bytes". Return true if some amount (not - // necessarily the full "bytes") was done. - virtual bool expand(size_t bytes, size_t expand_bytes); - - // Shrink generation with specified size (returns false if unable to shrink) - virtual void shrink(size_t bytes) = 0; - - virtual void compute_new_size(); - - virtual void clear_remembered_set(); - - virtual void invalidate_remembered_set(); - - virtual void prepare_for_verify(); - - // Grow generation with specified size (returns false if unable to grow) - virtual bool grow_by(size_t bytes) = 0; - // Grow generation to reserved size. - virtual bool grow_to_reserved() = 0; -}; - #endif // SHARE_VM_MEMORY_GENERATION_HPP diff --git a/hotspot/src/share/vm/memory/tenuredGeneration.hpp b/hotspot/src/share/vm/memory/tenuredGeneration.hpp index 0ecd54dd55f..3ee299d3f93 100644 --- a/hotspot/src/share/vm/memory/tenuredGeneration.hpp +++ b/hotspot/src/share/vm/memory/tenuredGeneration.hpp @@ -28,7 +28,7 @@ #include "gc_implementation/shared/cSpaceCounters.hpp" #include "gc_implementation/shared/gcStats.hpp" #include "gc_implementation/shared/generationCounters.hpp" -#include "memory/generation.hpp" +#include "memory/cardGeneration.hpp" #include "utilities/macros.hpp" // TenuredGeneration models the heap containing old (promoted/tenured) objects From b26180dfe95ae44b284e7cefc86b541ca349522a Mon Sep 17 00:00:00 2001 From: Bengt Rutisson Date: Wed, 10 Dec 2014 11:31:43 +0100 Subject: [PATCH 19/58] 8066781: Minor cleanups to TenuredGeneration Reviewed-by: kbarrett, tschatzl --- hotspot/src/share/vm/memory/tenuredGeneration.cpp | 2 -- hotspot/src/share/vm/memory/tenuredGeneration.hpp | 6 ------ .../share/vm/memory/tenuredGeneration.inline.hpp | 15 +++------------ hotspot/src/share/vm/runtime/vmStructs.cpp | 3 --- 4 files changed, 3 insertions(+), 23 deletions(-) diff --git a/hotspot/src/share/vm/memory/tenuredGeneration.cpp b/hotspot/src/share/vm/memory/tenuredGeneration.cpp index 37cd32c8585..4191d99237b 100644 --- a/hotspot/src/share/vm/memory/tenuredGeneration.cpp +++ b/hotspot/src/share/vm/memory/tenuredGeneration.cpp @@ -389,8 +389,6 @@ ALL_SINCE_SAVE_MARKS_CLOSURES(TenuredGen_SINCE_SAVE_MARKS_ITERATE_DEFN) void TenuredGeneration::gc_epilogue(bool full) { - _last_gc = WaterMark(the_space(), the_space()->top()); - // update the generation and space performance counters update_counters(); if (ZapUnusedHeapArea) { diff --git a/hotspot/src/share/vm/memory/tenuredGeneration.hpp b/hotspot/src/share/vm/memory/tenuredGeneration.hpp index 3ee299d3f93..2aa992cccd1 100644 --- a/hotspot/src/share/vm/memory/tenuredGeneration.hpp +++ b/hotspot/src/share/vm/memory/tenuredGeneration.hpp @@ -43,8 +43,6 @@ class TenuredGeneration: public CardGeneration { protected: ContiguousSpace* _the_space; // actual space holding objects - WaterMark _last_gc; // watermark between objects allocated before - // and after last GC. GenerationCounters* _gen_counters; CSpaceCounters* _space_counters; @@ -104,10 +102,6 @@ class TenuredGeneration: public CardGeneration { virtual inline HeapWord* allocate(size_t word_size, bool is_tlab); virtual inline HeapWord* par_allocate(size_t word_size, bool is_tlab); - // Accessing marks - inline WaterMark top_mark(); - inline WaterMark bottom_mark(); - #define TenuredGen_SINCE_SAVE_MARKS_DECL(OopClosureType, nv_suffix) \ void oop_since_save_marks_iterate##nv_suffix(OopClosureType* cl); TenuredGen_SINCE_SAVE_MARKS_DECL(OopsInGenClosure,_v) diff --git a/hotspot/src/share/vm/memory/tenuredGeneration.inline.hpp b/hotspot/src/share/vm/memory/tenuredGeneration.inline.hpp index 0aa4c6d2d3b..d7767bf8208 100644 --- a/hotspot/src/share/vm/memory/tenuredGeneration.inline.hpp +++ b/hotspot/src/share/vm/memory/tenuredGeneration.inline.hpp @@ -22,8 +22,8 @@ * */ -#ifndef SHARE_VM_MEMORY_GENERATION_INLINE_HPP -#define SHARE_VM_MEMORY_GENERATION_INLINE_HPP +#ifndef SHARE_VM_MEMORY_TENUREDGENERATION_INLINE_HPP +#define SHARE_VM_MEMORY_TENUREDGENERATION_INLINE_HPP #include "memory/genCollectedHeap.hpp" #include "memory/space.hpp" @@ -33,11 +33,6 @@ bool TenuredGeneration::is_in(const void* p) const { return the_space()->is_in(p); } - -WaterMark TenuredGeneration::top_mark() { - return the_space()->top_mark(); -} - CompactibleSpace* TenuredGeneration::first_compaction_space() const { return the_space(); @@ -55,10 +50,6 @@ HeapWord* TenuredGeneration::par_allocate(size_t word_size, return the_space()->par_allocate(word_size); } -WaterMark TenuredGeneration::bottom_mark() { - return the_space()->bottom_mark(); -} - size_t TenuredGeneration::block_size(const HeapWord* addr) const { if (addr < the_space()->top()) return oop(addr)->size(); else { @@ -71,4 +62,4 @@ bool TenuredGeneration::block_is_obj(const HeapWord* addr) const { return addr < the_space()->top(); } -#endif // SHARE_VM_MEMORY_GENERATION_INLINE_HPP +#endif // SHARE_VM_MEMORY_TENUREDGENERATION_INLINE_HPP diff --git a/hotspot/src/share/vm/runtime/vmStructs.cpp b/hotspot/src/share/vm/runtime/vmStructs.cpp index 4df238925dd..327e0f0009c 100644 --- a/hotspot/src/share/vm/runtime/vmStructs.cpp +++ b/hotspot/src/share/vm/runtime/vmStructs.cpp @@ -556,9 +556,6 @@ typedef TwoOopHashtable SymbolTwoOopHashtable; \ nonstatic_field(TenuredGeneration, _min_heap_delta_bytes, size_t) \ nonstatic_field(TenuredGeneration, _the_space, ContiguousSpace*) \ - nonstatic_field(TenuredGeneration, _last_gc, WaterMark) \ - \ - \ \ nonstatic_field(Space, _bottom, HeapWord*) \ nonstatic_field(Space, _end, HeapWord*) \ From bdb2636f9e80e4f4b06fb9be89fc66f58ec7f52a Mon Sep 17 00:00:00 2001 From: Bengt Rutisson Date: Wed, 10 Dec 2014 11:32:22 +0100 Subject: [PATCH 20/58] 8066782: Move common code from CMSGeneration and TenuredGeneration to CardGeneration Reviewed-by: kbarrett, tschatzl --- .../concurrentMarkSweepGeneration.cpp | 110 ++-------------- .../concurrentMarkSweepGeneration.hpp | 30 ++--- .../concurrentMarkSweepGeneration.inline.hpp | 18 +-- .../src/share/vm/memory/cardGeneration.cpp | 91 ++++++++++++- .../src/share/vm/memory/cardGeneration.hpp | 26 +++- .../share/vm/memory/cardGeneration.inline.hpp | 55 ++++++++ .../src/share/vm/memory/tenuredGeneration.cpp | 120 +----------------- .../src/share/vm/memory/tenuredGeneration.hpp | 29 +---- .../vm/memory/tenuredGeneration.inline.hpp | 27 ++-- 9 files changed, 208 insertions(+), 298 deletions(-) create mode 100644 hotspot/src/share/vm/memory/cardGeneration.inline.hpp diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp index d6d054d184e..b687ab40096 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp @@ -793,11 +793,6 @@ void ConcurrentMarkSweepGeneration::promotion_failure_occurred() { } } -CompactibleSpace* -ConcurrentMarkSweepGeneration::first_compaction_space() const { - return _cmsSpace; -} - void ConcurrentMarkSweepGeneration::reset_after_compaction() { // Clear the promotion information. These pointers can be adjusted // along with all the other pointers into the heap but @@ -808,10 +803,6 @@ void ConcurrentMarkSweepGeneration::reset_after_compaction() { } } -void ConcurrentMarkSweepGeneration::space_iterate(SpaceClosure* blk, bool usedOnly) { - blk->do_space(_cmsSpace); -} - void ConcurrentMarkSweepGeneration::compute_new_size() { assert_locked_or_safepoint(Heap_lock); @@ -882,7 +873,7 @@ void ConcurrentMarkSweepGeneration::compute_new_size_free_list() { expand_bytes); } // safe if expansion fails - expand(expand_bytes, 0, CMSExpansionCause::_satisfy_free_ratio); + expand_for_gc_cause(expand_bytes, 0, CMSExpansionCause::_satisfy_free_ratio); if (PrintGCDetails && Verbose) { gclog_or_tty->print_cr(" Expanded free fraction %f", ((double) free()) / capacity()); @@ -1048,8 +1039,7 @@ oop ConcurrentMarkSweepGeneration::promote(oop obj, size_t obj_size) { if (res == NULL) { // expand and retry size_t s = _cmsSpace->expansionSpaceRequired(obj_size); // HeapWords - expand(s*HeapWordSize, MinHeapDeltaBytes, - CMSExpansionCause::_satisfy_promotion); + expand_for_gc_cause(s*HeapWordSize, MinHeapDeltaBytes, CMSExpansionCause::_satisfy_promotion); // Since there's currently no next generation, we don't try to promote // into a more senior generation. assert(next_gen() == NULL, "assumption, based upon which no attempt " @@ -2624,13 +2614,6 @@ oop_since_save_marks_iterate##nv_suffix(OopClosureType* cl) { \ ALL_SINCE_SAVE_MARKS_CLOSURES(CMS_SINCE_SAVE_MARKS_DEFN) -void -ConcurrentMarkSweepGeneration::younger_refs_iterate(OopsInGenClosure* cl) { - cl->set_generation(this); - younger_refs_in_space_iterate(_cmsSpace, cl); - cl->reset_generation(); -} - void ConcurrentMarkSweepGeneration::oop_iterate(ExtendedOopClosure* cl) { if (freelistLock()->owned_by_self()) { @@ -2803,23 +2786,17 @@ ConcurrentMarkSweepGeneration::expand_and_allocate(size_t word_size, CMSSynchronousYieldRequest yr; assert(!tlab, "Can't deal with TLAB allocation"); MutexLockerEx x(freelistLock(), Mutex::_no_safepoint_check_flag); - expand(word_size*HeapWordSize, MinHeapDeltaBytes, - CMSExpansionCause::_satisfy_allocation); + expand_for_gc_cause(word_size*HeapWordSize, MinHeapDeltaBytes, CMSExpansionCause::_satisfy_allocation); if (GCExpandToAllocateDelayMillis > 0) { os::sleep(Thread::current(), GCExpandToAllocateDelayMillis, false); } return have_lock_and_allocate(word_size, tlab); } -// YSR: All of this generation expansion/shrinking stuff is an exact copy of -// TenuredGeneration, which makes me wonder if we should move this -// to CardGeneration and share it... -bool ConcurrentMarkSweepGeneration::expand(size_t bytes, size_t expand_bytes) { - return CardGeneration::expand(bytes, expand_bytes); -} - -void ConcurrentMarkSweepGeneration::expand(size_t bytes, size_t expand_bytes, - CMSExpansionCause::Cause cause) +void ConcurrentMarkSweepGeneration::expand_for_gc_cause( + size_t bytes, + size_t expand_bytes, + CMSExpansionCause::Cause cause) { bool success = expand(bytes, expand_bytes); @@ -2848,8 +2825,7 @@ HeapWord* ConcurrentMarkSweepGeneration::expand_and_par_lab_allocate(CMSParGCThr return NULL; } // Otherwise, we try expansion. - expand(word_sz*HeapWordSize, MinHeapDeltaBytes, - CMSExpansionCause::_allocate_par_lab); + expand_for_gc_cause(word_sz*HeapWordSize, MinHeapDeltaBytes, CMSExpansionCause::_allocate_par_lab); // Now go around the loop and try alloc again; // A competing par_promote might beat us to the expansion space, // so we may go around the loop again if promotion fails again. @@ -2876,8 +2852,7 @@ bool ConcurrentMarkSweepGeneration::expand_and_ensure_spooling_space( return false; } // Otherwise, we try expansion. - expand(refill_size_bytes, MinHeapDeltaBytes, - CMSExpansionCause::_allocate_par_spooling_space); + expand_for_gc_cause(refill_size_bytes, MinHeapDeltaBytes, CMSExpansionCause::_allocate_par_spooling_space); // Now go around the loop and try alloc again; // A competing allocation might beat us to the expansion space, // so we may go around the loop again if allocation fails again. @@ -2887,77 +2862,16 @@ bool ConcurrentMarkSweepGeneration::expand_and_ensure_spooling_space( } } - -void ConcurrentMarkSweepGeneration::shrink_by(size_t bytes) { - assert_locked_or_safepoint(ExpandHeap_lock); - // Shrink committed space - _virtual_space.shrink_by(bytes); - // Shrink space; this also shrinks the space's BOT - _cmsSpace->set_end((HeapWord*) _virtual_space.high()); - size_t new_word_size = heap_word_size(_cmsSpace->capacity()); - // Shrink the shared block offset array - _bts->resize(new_word_size); - MemRegion mr(_cmsSpace->bottom(), new_word_size); - // Shrink the card table - Universe::heap()->barrier_set()->resize_covered_region(mr); - - if (Verbose && PrintGC) { - size_t new_mem_size = _virtual_space.committed_size(); - size_t old_mem_size = new_mem_size + bytes; - gclog_or_tty->print_cr("Shrinking %s from " SIZE_FORMAT "K to " SIZE_FORMAT "K", - name(), old_mem_size/K, new_mem_size/K); - } -} - void ConcurrentMarkSweepGeneration::shrink(size_t bytes) { - assert_locked_or_safepoint(Heap_lock); - size_t size = ReservedSpace::page_align_size_down(bytes); // Only shrink if a compaction was done so that all the free space // in the generation is in a contiguous block at the end. - if (size > 0 && did_compact()) { - shrink_by(size); + if (did_compact()) { + CardGeneration::shrink(bytes); } } -bool ConcurrentMarkSweepGeneration::grow_by(size_t bytes) { +void ConcurrentMarkSweepGeneration::assert_correct_size_change_locking() { assert_locked_or_safepoint(Heap_lock); - bool result = _virtual_space.expand_by(bytes); - if (result) { - size_t new_word_size = - heap_word_size(_virtual_space.committed_size()); - MemRegion mr(_cmsSpace->bottom(), new_word_size); - _bts->resize(new_word_size); // resize the block offset shared array - Universe::heap()->barrier_set()->resize_covered_region(mr); - // Hmmmm... why doesn't CFLS::set_end verify locking? - // This is quite ugly; FIX ME XXX - _cmsSpace->assert_locked(freelistLock()); - _cmsSpace->set_end((HeapWord*)_virtual_space.high()); - - // update the space and generation capacity counters - if (UsePerfData) { - _space_counters->update_capacity(); - _gen_counters->update_all(); - } - - if (Verbose && PrintGC) { - size_t new_mem_size = _virtual_space.committed_size(); - size_t old_mem_size = new_mem_size - bytes; - gclog_or_tty->print_cr("Expanding %s from " SIZE_FORMAT "K by " SIZE_FORMAT "K to " SIZE_FORMAT "K", - name(), old_mem_size/K, bytes/K, new_mem_size/K); - } - } - return result; -} - -bool ConcurrentMarkSweepGeneration::grow_to_reserved() { - assert_locked_or_safepoint(Heap_lock); - bool success = true; - const size_t remaining_bytes = _virtual_space.uncommitted_size(); - if (remaining_bytes > 0) { - success = grow_by(remaining_bytes); - DEBUG_ONLY(if (!success) warning("grow to reserved failed");) - } - return success; } void ConcurrentMarkSweepGeneration::shrink_free_list_by(size_t bytes) { diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp index 83364976bfc..57402455a82 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -33,6 +33,7 @@ #include "memory/cardGeneration.hpp" #include "memory/freeBlockDictionary.hpp" #include "memory/iterator.hpp" +#include "memory/space.hpp" #include "runtime/mutexLocker.hpp" #include "runtime/virtualspace.hpp" #include "services/memoryService.hpp" @@ -171,9 +172,7 @@ class CMSBitMap VALUE_OBJ_CLASS_SPEC { // Represents a marking stack used by the CMS collector. // Ideally this should be GrowableArray<> just like MSC's marking stack(s). class CMSMarkStack: public CHeapObj { - // friend class CMSCollector; // To get at expansion stats further below. - // VirtualSpace _virtual_space; // Space for the stack oop* _base; // Bottom of stack @@ -1031,6 +1030,9 @@ class ConcurrentMarkSweepGeneration: public CardGeneration { void set_expansion_cause(CMSExpansionCause::Cause v) { _expansion_cause = v;} CMSExpansionCause::Cause expansion_cause() const { return _expansion_cause; } + // Accessing spaces + CompactibleSpace* space() const { return (CompactibleSpace*)_cmsSpace; } + private: // For parallel young-gen GC support. CMSParGCThreadState** _par_gc_thread_states; @@ -1064,6 +1066,10 @@ class ConcurrentMarkSweepGeneration: public CardGeneration { double initiating_occupancy() const { return _initiating_occupancy; } void init_initiating_occupancy(intx io, uintx tr); + void expand_for_gc_cause(size_t bytes, size_t expand_bytes, CMSExpansionCause::Cause cause); + + void assert_correct_size_change_locking(); + public: ConcurrentMarkSweepGeneration(ReservedSpace rs, size_t initial_byte_size, int level, CardTableRS* ct, @@ -1100,23 +1106,14 @@ class ConcurrentMarkSweepGeneration: public CardGeneration { // Override virtual void ref_processor_init(); - // Grow generation by specified size (returns false if unable to grow) - bool grow_by(size_t bytes); - // Grow generation to reserved size. - bool grow_to_reserved(); - void clear_expansion_cause() { _expansion_cause = CMSExpansionCause::_no_expansion; } // Space enquiries - size_t capacity() const; - size_t used() const; - size_t free() const; double occupancy() const { return ((double)used())/((double)capacity()); } size_t contiguous_available() const; size_t unsafe_max_alloc_nogc() const; // over-rides - MemRegion used_region() const; MemRegion used_region_at_save_marks() const; // Does a "full" (forced) collection invoked on this generation collect @@ -1127,10 +1124,6 @@ class ConcurrentMarkSweepGeneration: public CardGeneration { return !ScavengeBeforeFullGC; } - void space_iterate(SpaceClosure* blk, bool usedOnly = false); - - // Support for compaction - CompactibleSpace* first_compaction_space() const; // Adjust quantities in the generation affected by // the compaction. void reset_after_compaction(); @@ -1190,18 +1183,13 @@ class ConcurrentMarkSweepGeneration: public CardGeneration { } // Allocation failure - void expand(size_t bytes, size_t expand_bytes, - CMSExpansionCause::Cause cause); - virtual bool expand(size_t bytes, size_t expand_bytes); void shrink(size_t bytes); - void shrink_by(size_t bytes); HeapWord* expand_and_par_lab_allocate(CMSParGCThreadState* ps, size_t word_sz); bool expand_and_ensure_spooling_space(PromotionInfo* promo); // Iteration support and related enquiries void save_marks(); bool no_allocs_since_save_marks(); - void younger_refs_iterate(OopsInGenClosure* cl); // Iteration support specific to CMS generations void save_sweep_limit(); diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.inline.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.inline.hpp index dc76d1bc884..bc552f78420 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.inline.hpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -369,22 +369,6 @@ inline void ConcurrentMarkSweepGeneration::save_sweep_limit() { cmsSpace()->save_sweep_limit(); } -inline size_t ConcurrentMarkSweepGeneration::capacity() const { - return _cmsSpace->capacity(); -} - -inline size_t ConcurrentMarkSweepGeneration::used() const { - return _cmsSpace->used(); -} - -inline size_t ConcurrentMarkSweepGeneration::free() const { - return _cmsSpace->free(); -} - -inline MemRegion ConcurrentMarkSweepGeneration::used_region() const { - return _cmsSpace->used_region(); -} - inline MemRegion ConcurrentMarkSweepGeneration::used_region_at_save_marks() const { return _cmsSpace->used_region_at_save_marks(); } diff --git a/hotspot/src/share/vm/memory/cardGeneration.cpp b/hotspot/src/share/vm/memory/cardGeneration.cpp index 12faf829986..21e8d9e51a7 100644 --- a/hotspot/src/share/vm/memory/cardGeneration.cpp +++ b/hotspot/src/share/vm/memory/cardGeneration.cpp @@ -23,7 +23,9 @@ */ #include "precompiled.hpp" + #include "memory/blockOffsetTable.inline.hpp" +#include "memory/cardGeneration.inline.hpp" #include "memory/gcLocker.hpp" #include "memory/generationSpec.hpp" #include "memory/genOopClosures.inline.hpp" @@ -49,8 +51,9 @@ CardGeneration::CardGeneration(ReservedSpace rs, size_t initial_byte_size, heap_word_size(initial_byte_size)); MemRegion committed_mr(start, heap_word_size(initial_byte_size)); _rs->resize_covered_region(committed_mr); - if (_bts == NULL) + if (_bts == NULL) { vm_exit_during_initialization("Could not allocate a BlockOffsetArray"); + } // Verify that the start and end of this generation is the start of a card. // If this wasn't true, a single card could span more than on generation, @@ -67,6 +70,43 @@ CardGeneration::CardGeneration(ReservedSpace rs, size_t initial_byte_size, _used_at_prologue = 0; } +bool CardGeneration::grow_by(size_t bytes) { + assert_correct_size_change_locking(); + bool result = _virtual_space.expand_by(bytes); + if (result) { + size_t new_word_size = + heap_word_size(_virtual_space.committed_size()); + MemRegion mr(space()->bottom(), new_word_size); + // Expand card table + Universe::heap()->barrier_set()->resize_covered_region(mr); + // Expand shared block offset array + _bts->resize(new_word_size); + + // Fix for bug #4668531 + if (ZapUnusedHeapArea) { + MemRegion mangle_region(space()->end(), + (HeapWord*)_virtual_space.high()); + SpaceMangler::mangle_region(mangle_region); + } + + // Expand space -- also expands space's BOT + // (which uses (part of) shared array above) + space()->set_end((HeapWord*)_virtual_space.high()); + + // update the space and generation capacity counters + update_counters(); + + if (Verbose && PrintGC) { + size_t new_mem_size = _virtual_space.committed_size(); + size_t old_mem_size = new_mem_size - bytes; + gclog_or_tty->print_cr("Expanding %s from " SIZE_FORMAT "K by " + SIZE_FORMAT "K to " SIZE_FORMAT "K", + name(), old_mem_size/K, bytes/K, new_mem_size/K); + } + } + return result; +} + bool CardGeneration::expand(size_t bytes, size_t expand_bytes) { assert_locked_or_safepoint(Heap_lock); if (bytes == 0) { @@ -102,6 +142,44 @@ bool CardGeneration::expand(size_t bytes, size_t expand_bytes) { return success; } +bool CardGeneration::grow_to_reserved() { + assert_correct_size_change_locking(); + bool success = true; + const size_t remaining_bytes = _virtual_space.uncommitted_size(); + if (remaining_bytes > 0) { + success = grow_by(remaining_bytes); + DEBUG_ONLY(if (!success) warning("grow to reserved failed");) + } + return success; +} + +void CardGeneration::shrink(size_t bytes) { + assert_correct_size_change_locking(); + + size_t size = ReservedSpace::page_align_size_down(bytes); + if (size == 0) { + return; + } + + // Shrink committed space + _virtual_space.shrink_by(size); + // Shrink space; this also shrinks the space's BOT + space()->set_end((HeapWord*) _virtual_space.high()); + size_t new_word_size = heap_word_size(space()->capacity()); + // Shrink the shared block offset array + _bts->resize(new_word_size); + MemRegion mr(space()->bottom(), new_word_size); + // Shrink the card table + Universe::heap()->barrier_set()->resize_covered_region(mr); + + if (Verbose && PrintGC) { + size_t new_mem_size = _virtual_space.committed_size(); + size_t old_mem_size = new_mem_size + size; + gclog_or_tty->print_cr("Shrinking %s from " SIZE_FORMAT "K to " SIZE_FORMAT "K", + name(), old_mem_size/K, new_mem_size/K); + } +} + // No young generation references, clear this generation's cards. void CardGeneration::clear_remembered_set() { _rs->clear(reserved()); @@ -269,3 +347,14 @@ void CardGeneration::compute_new_size() { // Currently nothing to do. void CardGeneration::prepare_for_verify() {} + +void CardGeneration::space_iterate(SpaceClosure* blk, + bool usedOnly) { + blk->do_space(space()); +} + +void CardGeneration::younger_refs_iterate(OopsInGenClosure* blk) { + blk->set_generation(this); + younger_refs_in_space_iterate(space(), blk); + blk->reset_generation(); +} diff --git a/hotspot/src/share/vm/memory/cardGeneration.hpp b/hotspot/src/share/vm/memory/cardGeneration.hpp index 3fb43add866..24d0fa0f2c2 100644 --- a/hotspot/src/share/vm/memory/cardGeneration.hpp +++ b/hotspot/src/share/vm/memory/cardGeneration.hpp @@ -31,6 +31,7 @@ #include "memory/generation.hpp" class BlockOffsetSharedArray; +class CompactibleSpace; class CardGeneration: public Generation { friend class VMStructs; @@ -40,7 +41,7 @@ class CardGeneration: public Generation { // This is local to this generation. BlockOffsetSharedArray* _bts; - // current shrinking effect: this damps shrinking when the heap gets empty. + // Current shrinking effect: this damps shrinking when the heap gets empty. size_t _shrink_factor; size_t _min_heap_delta_bytes; // Minimum amount to expand. @@ -54,6 +55,10 @@ class CardGeneration: public Generation { CardGeneration(ReservedSpace rs, size_t initial_byte_size, int level, GenRemSet* remset); + virtual void assert_correct_size_change_locking() = 0; + + virtual CompactibleSpace* space() const = 0; + public: // Attempt to expand the generation by "bytes". Expand by at a @@ -62,7 +67,7 @@ class CardGeneration: public Generation { virtual bool expand(size_t bytes, size_t expand_bytes); // Shrink generation with specified size - virtual void shrink(size_t bytes) = 0; + virtual void shrink(size_t bytes); virtual void compute_new_size(); @@ -73,9 +78,22 @@ class CardGeneration: public Generation { virtual void prepare_for_verify(); // Grow generation with specified size (returns false if unable to grow) - virtual bool grow_by(size_t bytes) = 0; + bool grow_by(size_t bytes); // Grow generation to reserved size. - virtual bool grow_to_reserved() = 0; + bool grow_to_reserved(); + + size_t capacity() const; + size_t used() const; + size_t free() const; + MemRegion used_region() const; + + void space_iterate(SpaceClosure* blk, bool usedOnly = false); + + void younger_refs_iterate(OopsInGenClosure* blk); + + bool is_in(const void* p) const; + + CompactibleSpace* first_compaction_space() const; }; #endif // SHARE_VM_MEMORY_CARDGENERATION_HPP diff --git a/hotspot/src/share/vm/memory/cardGeneration.inline.hpp b/hotspot/src/share/vm/memory/cardGeneration.inline.hpp new file mode 100644 index 00000000000..fb49d0d0718 --- /dev/null +++ b/hotspot/src/share/vm/memory/cardGeneration.inline.hpp @@ -0,0 +1,55 @@ +/* + * 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. + * + */ + +#ifndef SHARE_VM_MEMORY_CARDGENERATION_INLINE_HPP +#define SHARE_VM_MEMORY_CARDGENERATION_INLINE_HPP + +#include "memory/cardGeneration.hpp" +#include "memory/space.hpp" + +inline size_t CardGeneration::capacity() const { + return space()->capacity(); +} + +inline size_t CardGeneration::used() const { + return space()->used(); +} + +inline size_t CardGeneration::free() const { + return space()->free(); +} + +inline MemRegion CardGeneration::used_region() const { + return space()->used_region(); +} + +inline bool CardGeneration::is_in(const void* p) const { + return space()->is_in(p); +} + +inline CompactibleSpace* CardGeneration::first_compaction_space() const { + return space(); +} + +#endif // SHARE_VM_MEMORY_CARDGENERATION_INLINE_HPP diff --git a/hotspot/src/share/vm/memory/tenuredGeneration.cpp b/hotspot/src/share/vm/memory/tenuredGeneration.cpp index 4191d99237b..aa1c51237ed 100644 --- a/hotspot/src/share/vm/memory/tenuredGeneration.cpp +++ b/hotspot/src/share/vm/memory/tenuredGeneration.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -235,34 +235,6 @@ bool TenuredGeneration::expand(size_t bytes, size_t expand_bytes) { return CardGeneration::expand(bytes, expand_bytes); } - -void TenuredGeneration::shrink(size_t bytes) { - assert_locked_or_safepoint(ExpandHeap_lock); - size_t size = ReservedSpace::page_align_size_down(bytes); - if (size > 0) { - shrink_by(size); - } -} - - -size_t TenuredGeneration::capacity() const { - return _the_space->capacity(); -} - - -size_t TenuredGeneration::used() const { - return _the_space->used(); -} - - -size_t TenuredGeneration::free() const { - return _the_space->free(); -} - -MemRegion TenuredGeneration::used_region() const { - return the_space()->used_region(); -} - size_t TenuredGeneration::unsafe_max_alloc_nogc() const { return _the_space->free(); } @@ -271,74 +243,8 @@ size_t TenuredGeneration::contiguous_available() const { return _the_space->free() + _virtual_space.uncommitted_size(); } -bool TenuredGeneration::grow_by(size_t bytes) { +void TenuredGeneration::assert_correct_size_change_locking() { assert_locked_or_safepoint(ExpandHeap_lock); - bool result = _virtual_space.expand_by(bytes); - if (result) { - size_t new_word_size = - heap_word_size(_virtual_space.committed_size()); - MemRegion mr(_the_space->bottom(), new_word_size); - // Expand card table - Universe::heap()->barrier_set()->resize_covered_region(mr); - // Expand shared block offset array - _bts->resize(new_word_size); - - // Fix for bug #4668531 - if (ZapUnusedHeapArea) { - MemRegion mangle_region(_the_space->end(), - (HeapWord*)_virtual_space.high()); - SpaceMangler::mangle_region(mangle_region); - } - - // Expand space -- also expands space's BOT - // (which uses (part of) shared array above) - _the_space->set_end((HeapWord*)_virtual_space.high()); - - // update the space and generation capacity counters - update_counters(); - - if (Verbose && PrintGC) { - size_t new_mem_size = _virtual_space.committed_size(); - size_t old_mem_size = new_mem_size - bytes; - gclog_or_tty->print_cr("Expanding %s from " SIZE_FORMAT "K by " - SIZE_FORMAT "K to " SIZE_FORMAT "K", - name(), old_mem_size/K, bytes/K, new_mem_size/K); - } - } - return result; -} - - -bool TenuredGeneration::grow_to_reserved() { - assert_locked_or_safepoint(ExpandHeap_lock); - bool success = true; - const size_t remaining_bytes = _virtual_space.uncommitted_size(); - if (remaining_bytes > 0) { - success = grow_by(remaining_bytes); - DEBUG_ONLY(if (!success) warning("grow to reserved failed");) - } - return success; -} - -void TenuredGeneration::shrink_by(size_t bytes) { - assert_locked_or_safepoint(ExpandHeap_lock); - // Shrink committed space - _virtual_space.shrink_by(bytes); - // Shrink space; this also shrinks the space's BOT - _the_space->set_end((HeapWord*) _virtual_space.high()); - size_t new_word_size = heap_word_size(_the_space->capacity()); - // Shrink the shared block offset array - _bts->resize(new_word_size); - MemRegion mr(_the_space->bottom(), new_word_size); - // Shrink the card table - Universe::heap()->barrier_set()->resize_covered_region(mr); - - if (Verbose && PrintGC) { - size_t new_mem_size = _virtual_space.committed_size(); - size_t old_mem_size = new_mem_size + bytes; - gclog_or_tty->print_cr("Shrinking %s from " SIZE_FORMAT "K to " SIZE_FORMAT "K", - name(), old_mem_size/K, new_mem_size/K); - } } // Currently nothing to do. @@ -348,27 +254,14 @@ void TenuredGeneration::object_iterate(ObjectClosure* blk) { _the_space->object_iterate(blk); } -void TenuredGeneration::space_iterate(SpaceClosure* blk, - bool usedOnly) { - blk->do_space(_the_space); -} - -void TenuredGeneration::younger_refs_iterate(OopsInGenClosure* blk) { - blk->set_generation(this); - younger_refs_in_space_iterate(_the_space, blk); - blk->reset_generation(); -} - void TenuredGeneration::save_marks() { _the_space->set_saved_mark(); } - void TenuredGeneration::reset_saved_marks() { _the_space->reset_saved_mark(); } - bool TenuredGeneration::no_allocs_since_save_marks() { return _the_space->saved_mark_at_top(); } @@ -387,26 +280,25 @@ ALL_SINCE_SAVE_MARKS_CLOSURES(TenuredGen_SINCE_SAVE_MARKS_ITERATE_DEFN) #undef TenuredGen_SINCE_SAVE_MARKS_ITERATE_DEFN - void TenuredGeneration::gc_epilogue(bool full) { // update the generation and space performance counters update_counters(); if (ZapUnusedHeapArea) { - the_space()->check_mangled_unused_area_complete(); + _the_space->check_mangled_unused_area_complete(); } } void TenuredGeneration::record_spaces_top() { assert(ZapUnusedHeapArea, "Not mangling unused space"); - the_space()->set_top_for_allocations(); + _the_space->set_top_for_allocations(); } void TenuredGeneration::verify() { - the_space()->verify(); + _the_space->verify(); } void TenuredGeneration::print_on(outputStream* st) const { Generation::print_on(st); st->print(" the"); - the_space()->print_on(st); + _the_space->print_on(st); } diff --git a/hotspot/src/share/vm/memory/tenuredGeneration.hpp b/hotspot/src/share/vm/memory/tenuredGeneration.hpp index 2aa992cccd1..5417e69cb7f 100644 --- a/hotspot/src/share/vm/memory/tenuredGeneration.hpp +++ b/hotspot/src/share/vm/memory/tenuredGeneration.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -42,25 +42,18 @@ class TenuredGeneration: public CardGeneration { friend class VM_PopulateDumpSharedSpace; protected: - ContiguousSpace* _the_space; // actual space holding objects + ContiguousSpace* _the_space; // Actual space holding objects GenerationCounters* _gen_counters; CSpaceCounters* _space_counters; - // Grow generation with specified size (returns false if unable to grow) - virtual bool grow_by(size_t bytes); - // Grow generation to reserved size. - virtual bool grow_to_reserved(); - // Shrink generation with specified size (returns false if unable to shrink) - void shrink_by(size_t bytes); - // Allocation failure virtual bool expand(size_t bytes, size_t expand_bytes); - void shrink(size_t bytes); // Accessing spaces - ContiguousSpace* the_space() const { return _the_space; } + ContiguousSpace* space() const { return _the_space; } + void assert_correct_size_change_locking(); public: TenuredGeneration(ReservedSpace rs, size_t initial_byte_size, int level, GenRemSet* remset); @@ -79,25 +72,11 @@ class TenuredGeneration: public CardGeneration { return !ScavengeBeforeFullGC; } - inline bool is_in(const void* p) const; - - // Space enquiries - size_t capacity() const; - size_t used() const; - size_t free() const; - - MemRegion used_region() const; - size_t unsafe_max_alloc_nogc() const; size_t contiguous_available() const; // Iteration void object_iterate(ObjectClosure* blk); - void space_iterate(SpaceClosure* blk, bool usedOnly = false); - - void younger_refs_iterate(OopsInGenClosure* blk); - - inline CompactibleSpace* first_compaction_space() const; virtual inline HeapWord* allocate(size_t word_size, bool is_tlab); virtual inline HeapWord* par_allocate(size_t word_size, bool is_tlab); diff --git a/hotspot/src/share/vm/memory/tenuredGeneration.inline.hpp b/hotspot/src/share/vm/memory/tenuredGeneration.inline.hpp index d7767bf8208..aea97ea317a 100644 --- a/hotspot/src/share/vm/memory/tenuredGeneration.inline.hpp +++ b/hotspot/src/share/vm/memory/tenuredGeneration.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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,41 +25,32 @@ #ifndef SHARE_VM_MEMORY_TENUREDGENERATION_INLINE_HPP #define SHARE_VM_MEMORY_TENUREDGENERATION_INLINE_HPP -#include "memory/genCollectedHeap.hpp" #include "memory/space.hpp" #include "memory/tenuredGeneration.hpp" -bool TenuredGeneration::is_in(const void* p) const { - return the_space()->is_in(p); -} - -CompactibleSpace* -TenuredGeneration::first_compaction_space() const { - return the_space(); -} - HeapWord* TenuredGeneration::allocate(size_t word_size, bool is_tlab) { assert(!is_tlab, "TenuredGeneration does not support TLAB allocation"); - return the_space()->allocate(word_size); + return _the_space->allocate(word_size); } HeapWord* TenuredGeneration::par_allocate(size_t word_size, bool is_tlab) { assert(!is_tlab, "TenuredGeneration does not support TLAB allocation"); - return the_space()->par_allocate(word_size); + return _the_space->par_allocate(word_size); } size_t TenuredGeneration::block_size(const HeapWord* addr) const { - if (addr < the_space()->top()) return oop(addr)->size(); - else { - assert(addr == the_space()->top(), "non-block head arg to block_size"); - return the_space()->end() - the_space()->top(); + if (addr < _the_space->top()) { + return oop(addr)->size(); + } else { + assert(addr == _the_space->top(), "non-block head arg to block_size"); + return _the_space->end() - _the_space->top(); } } bool TenuredGeneration::block_is_obj(const HeapWord* addr) const { - return addr < the_space()->top(); + return addr < _the_space ->top(); } #endif // SHARE_VM_MEMORY_TENUREDGENERATION_INLINE_HPP From 0dbf9d71616e62d0a2fabc6a5e50d2c4faeea21e Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Wed, 10 Dec 2014 16:45:55 +0100 Subject: [PATCH 21/58] 8067144: SIGSEGV with +TraceDeoptimization in Deoptimization::print_objects -XX:+TraceDeoptimization tries to print realloc'ed objects even when there are none Reviewed-by: kvn --- .../src/share/vm/runtime/deoptimization.cpp | 12 ++--- .../TraceDeoptimizationNoRealloc.java | 47 +++++++++++++++++++ 2 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 hotspot/test/compiler/uncommontrap/TraceDeoptimizationNoRealloc.java diff --git a/hotspot/src/share/vm/runtime/deoptimization.cpp b/hotspot/src/share/vm/runtime/deoptimization.cpp index 03e272fd690..90156bfd4ca 100644 --- a/hotspot/src/share/vm/runtime/deoptimization.cpp +++ b/hotspot/src/share/vm/runtime/deoptimization.cpp @@ -213,14 +213,14 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread realloc_failures = realloc_objects(thread, &deoptee, objects, THREAD); JRT_END reassign_fields(&deoptee, &map, objects, realloc_failures); - } #ifndef PRODUCT - if (TraceDeoptimization) { - ttyLocker ttyl; - tty->print_cr("REALLOC OBJECTS in thread " INTPTR_FORMAT, thread); - print_objects(objects, realloc_failures); - } + if (TraceDeoptimization) { + ttyLocker ttyl; + tty->print_cr("REALLOC OBJECTS in thread " INTPTR_FORMAT, thread); + print_objects(objects, realloc_failures); + } #endif + } if (save_oop_result) { // Restore result. deoptee.set_saved_oop_result(&map, return_value()); diff --git a/hotspot/test/compiler/uncommontrap/TraceDeoptimizationNoRealloc.java b/hotspot/test/compiler/uncommontrap/TraceDeoptimizationNoRealloc.java new file mode 100644 index 00000000000..563bbbbe00e --- /dev/null +++ b/hotspot/test/compiler/uncommontrap/TraceDeoptimizationNoRealloc.java @@ -0,0 +1,47 @@ +/* + * 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 8067144 + * @summary -XX:+TraceDeoptimization tries to print realloc'ed objects even when there are none + * @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:+IgnoreUnrecognizedVMOptions -XX:+TraceDeoptimization TraceDeoptimizationNoRealloc + * + */ + +public class TraceDeoptimizationNoRealloc { + + static void m(boolean some_condition) { + if (some_condition) { + return; + } + } + + + static public void main(String[] args) { + for (int i = 0; i < 20000; i++) { + m(false); + } + m(true); + } +} From f101b301995d30bc3e9e987c9a4a31c03bd313e2 Mon Sep 17 00:00:00 2001 From: Jesper Wilhelmsson Date: Thu, 11 Dec 2014 02:43:50 +0100 Subject: [PATCH 22/58] 6522873: Java not print "Unrecognized option" when it is invalid option Introduced a new version of match_option() that don't allow a tail after the flag name and used it for flags without extra arguments Reviewed-by: dholmes, dcubed --- hotspot/src/share/vm/runtime/arguments.cpp | 147 +++++++++++---------- 1 file changed, 79 insertions(+), 68 deletions(-) diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp index d3dcac3c2b0..c94770f9195 100644 --- a/hotspot/src/share/vm/runtime/arguments.cpp +++ b/hotspot/src/share/vm/runtime/arguments.cpp @@ -125,8 +125,8 @@ char* Arguments::_meta_index_path = NULL; char* Arguments::_meta_index_dir = NULL; char* Arguments::_ext_dirs = NULL; -// Check if head of 'option' matches 'name', and sets 'tail' remaining part of option string - +// Check if head of 'option' matches 'name', and sets 'tail' to the remaining +// part of the option string. static bool match_option(const JavaVMOption *option, const char* name, const char** tail) { int len = (int)strlen(name); @@ -138,6 +138,32 @@ static bool match_option(const JavaVMOption *option, const char* name, } } +// Check if 'option' matches 'name'. No "tail" is allowed. +static bool match_option(const JavaVMOption *option, const char* name) { + const char* tail = NULL; + bool result = match_option(option, name, &tail); + if (tail != NULL && *tail == '\0') { + return result; + } else { + return false; + } +} + +// Return true if any of the strings in null-terminated array 'names' matches. +// If tail_allowed is true, then the tail must begin with a colon; otherwise, +// the option must match exactly. +static bool match_option(const JavaVMOption* option, const char** names, const char** tail, + bool tail_allowed) { + for (/* empty */; *names != NULL; ++names) { + if (match_option(option, *names, tail)) { + if (**tail == '\0' || tail_allowed && **tail == ':') { + return true; + } + } + } + return false; +} + static void logOption(const char* opt) { if (PrintVMOptions) { jio_fprintf(defaultStream::output_stream(), "VM option '%s'\n", opt); @@ -2526,21 +2552,6 @@ static const char* system_assertion_options[] = { "-dsa", "-esa", "-disablesystemassertions", "-enablesystemassertions", 0 }; -// Return true if any of the strings in null-terminated array 'names' matches. -// If tail_allowed is true, then the tail must begin with a colon; otherwise, -// the option must match exactly. -static bool match_option(const JavaVMOption* option, const char** names, const char** tail, - bool tail_allowed) { - for (/* empty */; *names != NULL; ++names) { - if (match_option(option, *names, tail)) { - if (**tail == '\0' || tail_allowed && **tail == ':') { - return true; - } - } - } - return false; -} - bool Arguments::parse_uintx(const char* value, uintx* uintx_arg, uintx min_size) { @@ -2782,16 +2793,16 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, } #endif // !INCLUDE_JVMTI // -Xnoclassgc - } else if (match_option(option, "-Xnoclassgc", &tail)) { + } else if (match_option(option, "-Xnoclassgc")) { FLAG_SET_CMDLINE(bool, ClassUnloading, false); // -Xconcgc - } else if (match_option(option, "-Xconcgc", &tail)) { + } else if (match_option(option, "-Xconcgc")) { FLAG_SET_CMDLINE(bool, UseConcMarkSweepGC, true); // -Xnoconcgc - } else if (match_option(option, "-Xnoconcgc", &tail)) { + } else if (match_option(option, "-Xnoconcgc")) { FLAG_SET_CMDLINE(bool, UseConcMarkSweepGC, false); // -Xbatch - } else if (match_option(option, "-Xbatch", &tail)) { + } else if (match_option(option, "-Xbatch")) { FLAG_SET_CMDLINE(bool, BackgroundCompilation, false); // -Xmn for compatibility with other JVM vendors } else if (match_option(option, "-Xmn", &tail)) { @@ -2936,28 +2947,28 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, } FLAG_SET_CMDLINE(uintx, IncreaseFirstTierCompileThresholdAt, (uintx)uint_IncreaseFirstTierCompileThresholdAt); // -green - } else if (match_option(option, "-green", &tail)) { + } else if (match_option(option, "-green")) { jio_fprintf(defaultStream::error_stream(), "Green threads support not available\n"); return JNI_EINVAL; // -native - } else if (match_option(option, "-native", &tail)) { + } else if (match_option(option, "-native")) { // HotSpot always uses native threads, ignore silently for compatibility // -Xsqnopause - } else if (match_option(option, "-Xsqnopause", &tail)) { + } else if (match_option(option, "-Xsqnopause")) { // EVM option, ignore silently for compatibility // -Xrs - } else if (match_option(option, "-Xrs", &tail)) { + } else if (match_option(option, "-Xrs")) { // Classic/EVM option, new functionality FLAG_SET_CMDLINE(bool, ReduceSignalUsage, true); - } else if (match_option(option, "-Xusealtsigs", &tail)) { + } else if (match_option(option, "-Xusealtsigs")) { // change default internal VM signals used - lower case for back compat FLAG_SET_CMDLINE(bool, UseAltSigs, true); // -Xoptimize - } else if (match_option(option, "-Xoptimize", &tail)) { + } else if (match_option(option, "-Xoptimize")) { // EVM option, ignore silently for compatibility // -Xprof - } else if (match_option(option, "-Xprof", &tail)) { + } else if (match_option(option, "-Xprof")) { #if INCLUDE_FPROF _has_profile = true; #else // INCLUDE_FPROF @@ -2966,7 +2977,7 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, return JNI_ERR; #endif // INCLUDE_FPROF // -Xconcurrentio - } else if (match_option(option, "-Xconcurrentio", &tail)) { + } else if (match_option(option, "-Xconcurrentio")) { FLAG_SET_CMDLINE(bool, UseLWPSynchronization, true); FLAG_SET_CMDLINE(bool, BackgroundCompilation, false); FLAG_SET_CMDLINE(intx, DeferThrSuspendLoopCount, 1); @@ -2974,13 +2985,13 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, FLAG_SET_CMDLINE(uintx, NewSizeThreadIncrease, 16 * K); // 20Kb per thread added to new generation // -Xinternalversion - } else if (match_option(option, "-Xinternalversion", &tail)) { + } else if (match_option(option, "-Xinternalversion")) { jio_fprintf(defaultStream::output_stream(), "%s\n", VM_Version::internal_vm_info_string()); vm_exit(0); #ifndef PRODUCT // -Xprintflags - } else if (match_option(option, "-Xprintflags", &tail)) { + } else if (match_option(option, "-Xprintflags")) { CommandLineFlags::printFlags(tty, false); vm_exit(0); #endif @@ -3014,29 +3025,29 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, #endif } // -Xint - } else if (match_option(option, "-Xint", &tail)) { + } else if (match_option(option, "-Xint")) { set_mode_flags(_int); // -Xmixed - } else if (match_option(option, "-Xmixed", &tail)) { + } else if (match_option(option, "-Xmixed")) { set_mode_flags(_mixed); // -Xcomp - } else if (match_option(option, "-Xcomp", &tail)) { + } else if (match_option(option, "-Xcomp")) { // for testing the compiler; turn off all flags that inhibit compilation set_mode_flags(_comp); // -Xshare:dump - } else if (match_option(option, "-Xshare:dump", &tail)) { + } else if (match_option(option, "-Xshare:dump")) { FLAG_SET_CMDLINE(bool, DumpSharedSpaces, true); set_mode_flags(_int); // Prevent compilation, which creates objects // -Xshare:on - } else if (match_option(option, "-Xshare:on", &tail)) { + } else if (match_option(option, "-Xshare:on")) { FLAG_SET_CMDLINE(bool, UseSharedSpaces, true); FLAG_SET_CMDLINE(bool, RequireSharedSpaces, true); // -Xshare:auto - } else if (match_option(option, "-Xshare:auto", &tail)) { + } else if (match_option(option, "-Xshare:auto")) { FLAG_SET_CMDLINE(bool, UseSharedSpaces, true); FLAG_SET_CMDLINE(bool, RequireSharedSpaces, false); // -Xshare:off - } else if (match_option(option, "-Xshare:off", &tail)) { + } else if (match_option(option, "-Xshare:off")) { FLAG_SET_CMDLINE(bool, UseSharedSpaces, false); FLAG_SET_CMDLINE(bool, RequireSharedSpaces, false); // -Xverify @@ -3054,13 +3065,13 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, return JNI_EINVAL; } // -Xdebug - } else if (match_option(option, "-Xdebug", &tail)) { + } else if (match_option(option, "-Xdebug")) { // note this flag has been used, then ignore set_xdebug_mode(true); // -Xnoagent - } else if (match_option(option, "-Xnoagent", &tail)) { + } else if (match_option(option, "-Xnoagent")) { // For compatibility with classic. HotSpot refuses to load the old style agent.dll. - } else if (match_option(option, "-Xboundthreads", &tail)) { + } else if (match_option(option, "-Xboundthreads")) { // Bind user level threads to kernel threads (Solaris only) FLAG_SET_CMDLINE(bool, UseBoundThreads, true); } else if (match_option(option, "-Xloggc:", &tail)) { @@ -3090,14 +3101,14 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, "check")) { return JNI_EINVAL; } - } else if (match_option(option, "vfprintf", &tail)) { + } else if (match_option(option, "vfprintf")) { _vfprintf_hook = CAST_TO_FN_PTR(vfprintf_hook_t, option->extraInfo); - } else if (match_option(option, "exit", &tail)) { + } else if (match_option(option, "exit")) { _exit_hook = CAST_TO_FN_PTR(exit_hook_t, option->extraInfo); - } else if (match_option(option, "abort", &tail)) { + } else if (match_option(option, "abort")) { _abort_hook = CAST_TO_FN_PTR(abort_hook_t, option->extraInfo); // -XX:+AggressiveHeap - } else if (match_option(option, "-XX:+AggressiveHeap", &tail)) { + } else if (match_option(option, "-XX:+AggressiveHeap")) { // This option inspects the machine and attempts to set various // parameters to be optimal for long-running, memory allocation @@ -3188,11 +3199,11 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, // Need to keep consistency of MaxTenuringThreshold and AlwaysTenure/NeverTenure; // and the last option wins. - } else if (match_option(option, "-XX:+NeverTenure", &tail)) { + } else if (match_option(option, "-XX:+NeverTenure")) { FLAG_SET_CMDLINE(bool, NeverTenure, true); FLAG_SET_CMDLINE(bool, AlwaysTenure, false); FLAG_SET_CMDLINE(uintx, MaxTenuringThreshold, markOopDesc::max_age + 1); - } else if (match_option(option, "-XX:+AlwaysTenure", &tail)) { + } else if (match_option(option, "-XX:+AlwaysTenure")) { FLAG_SET_CMDLINE(bool, NeverTenure, false); FLAG_SET_CMDLINE(bool, AlwaysTenure, true); FLAG_SET_CMDLINE(uintx, MaxTenuringThreshold, 0); @@ -3211,17 +3222,17 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, FLAG_SET_CMDLINE(bool, NeverTenure, false); FLAG_SET_CMDLINE(bool, AlwaysTenure, false); } - } else if (match_option(option, "-XX:+CMSPermGenSweepingEnabled", &tail) || - match_option(option, "-XX:-CMSPermGenSweepingEnabled", &tail)) { + } else if (match_option(option, "-XX:+CMSPermGenSweepingEnabled") || + match_option(option, "-XX:-CMSPermGenSweepingEnabled")) { jio_fprintf(defaultStream::error_stream(), "Please use CMSClassUnloadingEnabled in place of " "CMSPermGenSweepingEnabled in the future\n"); - } else if (match_option(option, "-XX:+UseGCTimeLimit", &tail)) { + } else if (match_option(option, "-XX:+UseGCTimeLimit")) { FLAG_SET_CMDLINE(bool, UseGCOverheadLimit, true); jio_fprintf(defaultStream::error_stream(), "Please use -XX:+UseGCOverheadLimit in place of " "-XX:+UseGCTimeLimit in the future\n"); - } else if (match_option(option, "-XX:-UseGCTimeLimit", &tail)) { + } else if (match_option(option, "-XX:-UseGCTimeLimit")) { FLAG_SET_CMDLINE(bool, UseGCOverheadLimit, false); jio_fprintf(defaultStream::error_stream(), "Please use -XX:-UseGCOverheadLimit in place of " @@ -3231,13 +3242,13 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, // are not to be documented. } else if (match_option(option, "-XX:MaxTLERatio=", &tail)) { // No longer used. - } else if (match_option(option, "-XX:+ResizeTLE", &tail)) { + } else if (match_option(option, "-XX:+ResizeTLE")) { FLAG_SET_CMDLINE(bool, ResizeTLAB, true); - } else if (match_option(option, "-XX:-ResizeTLE", &tail)) { + } else if (match_option(option, "-XX:-ResizeTLE")) { FLAG_SET_CMDLINE(bool, ResizeTLAB, false); - } else if (match_option(option, "-XX:+PrintTLE", &tail)) { + } else if (match_option(option, "-XX:+PrintTLE")) { FLAG_SET_CMDLINE(bool, PrintTLAB, true); - } else if (match_option(option, "-XX:-PrintTLE", &tail)) { + } else if (match_option(option, "-XX:-PrintTLE")) { FLAG_SET_CMDLINE(bool, PrintTLAB, false); } else if (match_option(option, "-XX:TLEFragmentationRatio=", &tail)) { // No longer used. @@ -3253,17 +3264,17 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, FLAG_SET_CMDLINE(uintx, TLABSize, long_tlab_size); } else if (match_option(option, "-XX:TLEThreadRatio=", &tail)) { // No longer used. - } else if (match_option(option, "-XX:+UseTLE", &tail)) { + } else if (match_option(option, "-XX:+UseTLE")) { FLAG_SET_CMDLINE(bool, UseTLAB, true); - } else if (match_option(option, "-XX:-UseTLE", &tail)) { + } else if (match_option(option, "-XX:-UseTLE")) { FLAG_SET_CMDLINE(bool, UseTLAB, false); - } else if (match_option(option, "-XX:+DisplayVMOutputToStderr", &tail)) { + } else if (match_option(option, "-XX:+DisplayVMOutputToStderr")) { FLAG_SET_CMDLINE(bool, DisplayVMOutputToStdout, false); FLAG_SET_CMDLINE(bool, DisplayVMOutputToStderr, true); - } else if (match_option(option, "-XX:+DisplayVMOutputToStdout", &tail)) { + } else if (match_option(option, "-XX:+DisplayVMOutputToStdout")) { FLAG_SET_CMDLINE(bool, DisplayVMOutputToStderr, false); FLAG_SET_CMDLINE(bool, DisplayVMOutputToStdout, true); - } else if (match_option(option, "-XX:+ExtendedDTraceProbes", &tail)) { + } else if (match_option(option, "-XX:+ExtendedDTraceProbes")) { #if defined(DTRACE_ENABLED) FLAG_SET_CMDLINE(bool, ExtendedDTraceProbes, true); FLAG_SET_CMDLINE(bool, DTraceMethodProbes, true); @@ -3275,7 +3286,7 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, return JNI_EINVAL; #endif // defined(DTRACE_ENABLED) #ifdef ASSERT - } else if (match_option(option, "-XX:+FullGCALot", &tail)) { + } else if (match_option(option, "-XX:+FullGCALot")) { FLAG_SET_CMDLINE(bool, FullGCALot, true); // disable scavenge before parallel mark-compact FLAG_SET_CMDLINE(bool, ScavengeBeforeFullGC, false); @@ -3361,7 +3372,7 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, } FLAG_SET_CMDLINE(uintx, MaxDirectMemorySize, max_direct_memory_size); #if !INCLUDE_MANAGEMENT - } else if (match_option(option, "-XX:+ManagementServer", &tail)) { + } else if (match_option(option, "-XX:+ManagementServer")) { jio_fprintf(defaultStream::error_stream(), "ManagementServer is not supported in this VM.\n"); return JNI_ERR; @@ -3796,23 +3807,23 @@ jint Arguments::parse(const JavaVMInitArgs* args) { settings_file_specified = true; continue; } - if (match_option(option, "-XX:+PrintVMOptions", &tail)) { + if (match_option(option, "-XX:+PrintVMOptions")) { PrintVMOptions = true; continue; } - if (match_option(option, "-XX:-PrintVMOptions", &tail)) { + if (match_option(option, "-XX:-PrintVMOptions")) { PrintVMOptions = false; continue; } - if (match_option(option, "-XX:+IgnoreUnrecognizedVMOptions", &tail)) { + if (match_option(option, "-XX:+IgnoreUnrecognizedVMOptions")) { IgnoreUnrecognizedVMOptions = true; continue; } - if (match_option(option, "-XX:-IgnoreUnrecognizedVMOptions", &tail)) { + if (match_option(option, "-XX:-IgnoreUnrecognizedVMOptions")) { IgnoreUnrecognizedVMOptions = false; continue; } - if (match_option(option, "-XX:+PrintFlagsInitial", &tail)) { + if (match_option(option, "-XX:+PrintFlagsInitial")) { CommandLineFlags::printFlags(tty, false); vm_exit(0); } @@ -3838,7 +3849,7 @@ jint Arguments::parse(const JavaVMInitArgs* args) { #ifndef PRODUCT - if (match_option(option, "-XX:+PrintFlagsWithComments", &tail)) { + if (match_option(option, "-XX:+PrintFlagsWithComments")) { CommandLineFlags::printFlags(tty, true); vm_exit(0); } From 827f52c966f0ad413720eab8af2bacdad75edc17 Mon Sep 17 00:00:00 2001 From: Jonathan Gibbons Date: Thu, 11 Dec 2014 18:20:00 -0800 Subject: [PATCH 23/58] 8066807: langtools/test/Makefile should use -agentvm not -samevm Reviewed-by: mcimadamore --- langtools/test/Makefile | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/langtools/test/Makefile b/langtools/test/Makefile index 08d4fc48882..722c740bbc3 100644 --- a/langtools/test/Makefile +++ b/langtools/test/Makefile @@ -105,21 +105,19 @@ endif # Default JDK for JTREG and JCK # -# JT_JAVA is the version of java used to run jtreg/JCK. Since it is now -# standard to execute tests in sameVM mode, it should normally be set the -# same as TESTJAVA (although not necessarily so.) +# JT_JAVA is the version of java used to run jtreg/JCK. # ifdef JPRT_JAVA_HOME JT_JAVA = $(JPRT_JAVA_HOME) else - JT_JAVA = $(SLASH_JAVA)/re/jdk/1.7.0/archive/fcs/binaries/$(PLATFORM)-$(ARCH) + JT_JAVA = $(SLASH_JAVA)/re/jdk/1.9.0/archive/fcs/binaries/$(PLATFORM)-$(ARCH) endif # Default JDK to test ifdef JPRT_IMPORT_PRODUCT_HOME TESTJAVA = $(JPRT_IMPORT_PRODUCT_HOME) else - TESTJAVA = $(SLASH_JAVA)/re/jdk/1.7.0/promoted/latest/binaries/$(PLATFORM)-$(ARCH) + TESTJAVA = $(SLASH_JAVA)/re/jdk/1.9.0/promoted/latest/binaries/$(PLATFORM)-$(ARCH) endif # PRODUCT_HOME is a JPRT variable pointing to a directory containing the output from @@ -152,7 +150,7 @@ endif ifdef CONCURRENCY JTREG_OPTIONS += -agentvm -concurrency:$(CONCURRENCY) else - JTREG_OPTIONS += -samevm + JTREG_OPTIONS += -agentvm endif ifdef JCK_CONCURRENCY From 0900c1f2e0dfd9ec8d4cfca32cf8fe6668f11af7 Mon Sep 17 00:00:00 2001 From: Jonathan Gibbons Date: Thu, 11 Dec 2014 18:23:17 -0800 Subject: [PATCH 24/58] 8066808: langtools/test/Makefile should not use OS-specific jtreg binary Reviewed-by: mcimadamore --- langtools/test/Makefile | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/langtools/test/Makefile b/langtools/test/Makefile index 722c740bbc3..c2151108101 100644 --- a/langtools/test/Makefile +++ b/langtools/test/Makefile @@ -21,7 +21,6 @@ OSNAME = $(shell uname -s) ifeq ($(OSNAME), SunOS) SLASH_JAVA = /java PLATFORM = solaris - JT_PLATFORM = solaris ARCH = $(shell uname -p) ifeq ($(ARCH), i386) ARCH=i586 @@ -30,7 +29,6 @@ endif ifeq ($(OSNAME), Linux) SLASH_JAVA = /java PLATFORM = linux - JT_PLATFORM = linux ARCH = $(shell uname -m) ifeq ($(ARCH), i386) ARCH=i586 @@ -38,7 +36,6 @@ ifeq ($(OSNAME), Linux) endif ifeq ($(OSNAME), Darwin) PLATFORM = bsd - JT_PLATFORM = linux ARCH = $(shell uname -m) ifeq ($(ARCH), i386) ARCH=i586 @@ -55,7 +52,6 @@ endif ifeq ($(PLATFORM), windows) SLASH_JAVA = J: - JT_PLATFORM = win32 ifeq ($(word 1, $(PROCESSOR_IDENTIFIER)),ia64) ARCH=ia64 else @@ -93,8 +89,8 @@ ifdef JPRT_JTREG_HOME else JTREG_HOME = $(SLASH_JAVA)/re/jtreg/4.1/promoted/latest/binaries/jtreg endif -JTREG = $(JTREG_HOME)/$(JT_PLATFORM)/bin/jtreg -JTDIFF = $(JTREG_HOME)/$(JT_PLATFORM)/bin/jtdiff +JTREG = $(JTREG_HOME)/bin/jtreg +JTDIFF = $(JTREG_HOME)/bin/jtdiff # Default JCK to run ifdef JPRT_JCK_HOME From a420a8b984bd7f6a1d751e596976f8c1e8481cd8 Mon Sep 17 00:00:00 2001 From: Erik Joelsson Date: Fri, 12 Dec 2014 15:33:10 +0100 Subject: [PATCH 25/58] 8067254: No debug symbols in JPRT Windows builds Reviewed-by: tbell --- make/StripBinaries.gmk | 5 ----- 1 file changed, 5 deletions(-) diff --git a/make/StripBinaries.gmk b/make/StripBinaries.gmk index 92bb17159a2..c05e2a1eef1 100644 --- a/make/StripBinaries.gmk +++ b/make/StripBinaries.gmk @@ -66,11 +66,6 @@ STRIP_LIBS_SRC := \ $(shell $(FIND) $(SUPPORT_OUTPUTDIR)/modules_libs \ -name '*$(SHARED_LIBRARY_SUFFIX)' -type f) -# On Windows, don't include debug info for libs either. -ifeq ($(OPENJDK_TARGET_OS), windows) - COPY_LIBS_SRC := $(filter-out %.diz %.map %.pdb, $(COPY_LIBS_SRC)) -endif - $(eval $(call SetupCopyFiles,STRIP_MODULES_CMDS, \ SRC := $(SUPPORT_OUTPUTDIR)/modules_cmds, \ DEST := $(MODULES_CMDS_STRIPPED), \ From f4e7330e548979193750cb9105661090a154d9b1 Mon Sep 17 00:00:00 2001 From: Severin Gehwolf Date: Fri, 12 Dec 2014 15:38:57 +0100 Subject: [PATCH 26/58] 8067330: ZERO_ARCHDEF incorrectly defined for PPC/PPC64 architectures Reviewed-by: simonis, tbell, erikj --- common/autoconf/generated-configure.sh | 5 +++-- common/autoconf/platform.m4 | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/common/autoconf/generated-configure.sh b/common/autoconf/generated-configure.sh index 0b1567ce0ce..59c1a8ae9e8 100644 --- a/common/autoconf/generated-configure.sh +++ b/common/autoconf/generated-configure.sh @@ -4329,7 +4329,7 @@ TOOLCHAIN_DESCRIPTION_xlc="IBM XL C/C++" #CUSTOM_AUTOCONF_INCLUDE # Do not change or remove the following line, it is needed for consistency checks: -DATE_WHEN_GENERATED=1418036274 +DATE_WHEN_GENERATED=1418395009 ############################################################################### # @@ -13965,7 +13965,8 @@ $as_echo "$COMPILE_TYPE" >&6; } # ZERO_ARCHDEF is used to enable architecture-specific code case "${OPENJDK_TARGET_CPU}" in - ppc*) ZERO_ARCHDEF=PPC ;; + ppc) ZERO_ARCHDEF=PPC32 ;; + ppc64) ZERO_ARCHDEF=PPC64 ;; s390*) ZERO_ARCHDEF=S390 ;; sparc*) ZERO_ARCHDEF=SPARC ;; x86_64*) ZERO_ARCHDEF=AMD64 ;; diff --git a/common/autoconf/platform.m4 b/common/autoconf/platform.m4 index 27db8d047eb..6f26b855e95 100644 --- a/common/autoconf/platform.m4 +++ b/common/autoconf/platform.m4 @@ -367,7 +367,8 @@ AC_DEFUN([PLATFORM_SETUP_LEGACY_VARS], # ZERO_ARCHDEF is used to enable architecture-specific code case "${OPENJDK_TARGET_CPU}" in - ppc*) ZERO_ARCHDEF=PPC ;; + ppc) ZERO_ARCHDEF=PPC32 ;; + ppc64) ZERO_ARCHDEF=PPC64 ;; s390*) ZERO_ARCHDEF=S390 ;; sparc*) ZERO_ARCHDEF=SPARC ;; x86_64*) ZERO_ARCHDEF=AMD64 ;; From 3076062240a5e3b2c304b029a75b01f9752082e5 Mon Sep 17 00:00:00 2001 From: Maurizio Cimadamore Date: Fri, 12 Dec 2014 18:07:24 +0000 Subject: [PATCH 27/58] 8064365: Better support for finder capabilities in target-typing context Add new framework to allow for easy creation of finder-like capabilities. Reviewed-by: jjg, jlahoda --- .../com/sun/tools/javac/comp/Analyzer.java | 493 ++++++++++++++++++ .../com/sun/tools/javac/comp/Attr.java | 95 +--- .../com/sun/tools/javac/comp/AttrContext.java | 5 + .../sun/tools/javac/comp/DeferredAttr.java | 65 ++- .../tools/javac/resources/compiler.properties | 15 +- .../com/sun/tools/javac/tree/TreeInfo.java | 8 + .../diags/examples/DiamondRedundantArgs.java | 6 +- .../diags/examples/DiamondRedundantArgs1.java | 6 +- .../examples/MethodRedundantTypeargs.java | 35 ++ .../diags/examples/PotentialLambdaFound.java | 4 +- .../generics/diamond/6939780/T6939780.java | 4 +- .../generics/diamond/6939780/T6939780_7.out | 7 +- .../generics/diamond/6939780/T6939780_8.out | 8 +- .../generics/diamond/7002837/T7002837.java | 2 +- .../test/tools/javac/lambda/LambdaConv18.java | 2 +- .../test/tools/javac/lambda/LambdaConv18.out | 3 +- .../lambda/speculative/DiamondFinder.java | 2 +- 17 files changed, 628 insertions(+), 132 deletions(-) create mode 100644 langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java create mode 100644 langtools/test/tools/javac/diags/examples/MethodRedundantTypeargs.java diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java new file mode 100644 index 00000000000..13fe2e26f57 --- /dev/null +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java @@ -0,0 +1,493 @@ +/* + * 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. + */ + +package com.sun.tools.javac.comp; + +import com.sun.tools.javac.code.Source; +import com.sun.tools.javac.code.Type; +import com.sun.tools.javac.code.Types; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCBlock; +import com.sun.tools.javac.tree.JCTree.JCClassDecl; +import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop; +import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop; +import com.sun.tools.javac.tree.JCTree.JCForLoop; +import com.sun.tools.javac.tree.JCTree.JCIf; +import com.sun.tools.javac.tree.JCTree.JCLambda; +import com.sun.tools.javac.tree.JCTree.JCMethodDecl; +import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; +import com.sun.tools.javac.tree.JCTree.JCNewClass; +import com.sun.tools.javac.tree.JCTree.JCStatement; +import com.sun.tools.javac.tree.JCTree.JCSwitch; +import com.sun.tools.javac.tree.JCTree.JCTypeApply; +import com.sun.tools.javac.tree.JCTree.JCVariableDecl; +import com.sun.tools.javac.tree.JCTree.JCWhileLoop; +import com.sun.tools.javac.tree.JCTree.Tag; +import com.sun.tools.javac.tree.TreeCopier; +import com.sun.tools.javac.tree.TreeInfo; +import com.sun.tools.javac.tree.TreeMaker; +import com.sun.tools.javac.tree.TreeScanner; +import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.Filter; +import com.sun.tools.javac.util.JCDiagnostic; +import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType; +import com.sun.tools.javac.util.List; +import com.sun.tools.javac.util.ListBuffer; +import com.sun.tools.javac.util.Log; +import com.sun.tools.javac.util.Names; +import com.sun.tools.javac.util.Options; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Predicate; + +import static com.sun.tools.javac.code.Flags.GENERATEDCONSTR; +import static com.sun.tools.javac.code.Flags.SYNTHETIC; +import static com.sun.tools.javac.code.TypeTag.CLASS; +import static com.sun.tools.javac.tree.JCTree.Tag.APPLY; +import static com.sun.tools.javac.tree.JCTree.Tag.CLASSDEF; +import static com.sun.tools.javac.tree.JCTree.Tag.METHODDEF; +import static com.sun.tools.javac.tree.JCTree.Tag.NEWCLASS; +import static com.sun.tools.javac.tree.JCTree.Tag.TYPEAPPLY; + +/** + * Helper class for defining custom code analysis, such as finding instance creation expression + * that can benefit from diamond syntax. + */ +public class Analyzer { + protected static final Context.Key analyzerKey = new Context.Key<>(); + + final Types types; + final Log log; + final Attr attr; + final DeferredAttr deferredAttr; + final TreeMaker make; + final Names names; + + final EnumSet analyzerModes; + + public static Analyzer instance(Context context) { + Analyzer instance = context.get(analyzerKey); + if (instance == null) + instance = new Analyzer(context); + return instance; + } + + protected Analyzer(Context context) { + context.put(analyzerKey, this); + types = Types.instance(context); + log = Log.instance(context); + attr = Attr.instance(context); + deferredAttr = DeferredAttr.instance(context); + make = TreeMaker.instance(context); + names = Names.instance(context); + Options options = Options.instance(context); + String findOpt = options.get("find"); + //parse modes + Source source = Source.instance(context); + analyzerModes = AnalyzerMode.getAnalyzerModes(findOpt, source); + } + + /** + * This enum defines supported analyzer modes, as well as defining the logic for decoding + * the {@code -XDfind} option. + */ + enum AnalyzerMode { + DIAMOND("diamond", Source::allowDiamond), + LAMBDA("lambda", Source::allowLambda), + METHOD("method", Source::allowGraphInference); + + final String opt; + final Predicate sourceFilter; + + AnalyzerMode(String opt, Predicate sourceFilter) { + this.opt = opt; + this.sourceFilter = sourceFilter; + } + + /** + * This method is used to parse the {@code find} option. + * Possible modes are separated by colon; a mode can be excluded by + * prepending '-' to its name. Finally, the special mode 'all' can be used to + * add all modes to the resulting enum. + */ + static EnumSet getAnalyzerModes(String opt, Source source) { + if (opt == null) { + return EnumSet.noneOf(AnalyzerMode.class); + } + List modes = List.from(opt.split(",")); + EnumSet res = EnumSet.noneOf(AnalyzerMode.class); + if (modes.contains("all")) { + res = EnumSet.allOf(AnalyzerMode.class); + } + for (AnalyzerMode mode : values()) { + if (modes.contains(mode.opt)) { + res.add(mode); + } else if (modes.contains("-" + mode.opt) || !mode.sourceFilter.test(source)) { + res.remove(mode); + } + } + return res; + } + } + + /** + * A statement analyzer is a work-unit that matches certain AST nodes (of given type {@code S}), + * rewrites them to different AST nodes (of type {@code T}) and then generates some meaningful + * messages in case the analysis has been successful. + */ + abstract class StatementAnalyzer { + + AnalyzerMode mode; + JCTree.Tag tag; + + StatementAnalyzer(AnalyzerMode mode, Tag tag) { + this.mode = mode; + this.tag = tag; + } + + /** + * Is this analyzer allowed to run? + */ + boolean isEnabled() { + return analyzerModes.contains(mode); + } + + /** + * Should this analyzer be rewriting the given tree? + */ + abstract boolean match(S tree); + + /** + * Rewrite a given AST node into a new one + */ + abstract T map(S oldTree, S newTree); + + /** + * Entry-point for comparing results and generating diagnostics. + */ + abstract void process(S oldTree, T newTree, boolean hasErrors); + + } + + /** + * This analyzer checks if generic instance creation expression can use diamond syntax. + */ + class DiamondInitializer extends StatementAnalyzer { + + DiamondInitializer() { + super(AnalyzerMode.DIAMOND, NEWCLASS); + } + + @Override + boolean match(JCNewClass tree) { + return tree.clazz.hasTag(TYPEAPPLY) && + !TreeInfo.isDiamond(tree) && + tree.def == null; + } + + @Override + JCNewClass map(JCNewClass oldTree, JCNewClass newTree) { + if (newTree.clazz.hasTag(TYPEAPPLY)) { + ((JCTypeApply)newTree.clazz).arguments = List.nil(); + } + return newTree; + } + + @Override + void process(JCNewClass oldTree, JCNewClass newTree, boolean hasErrors) { + if (!hasErrors) { + List inferredArgs = newTree.type.getTypeArguments(); + List explicitArgs = oldTree.type.getTypeArguments(); + for (Type t : inferredArgs) { + if (!types.isSameType(t, explicitArgs.head)) { + log.warning(oldTree.clazz, "diamond.redundant.args.1", + oldTree.clazz.type, newTree.clazz.type); + return; + } + explicitArgs = explicitArgs.tail; + } + //exact match + log.warning(oldTree.clazz, "diamond.redundant.args"); + } + } + } + + /** + * This analyzer checks if anonymous instance creation expression can replaced by lambda. + */ + class LambdaAnalyzer extends StatementAnalyzer { + + LambdaAnalyzer() { + super(AnalyzerMode.LAMBDA, NEWCLASS); + } + + @Override + boolean match (JCNewClass tree){ + Type clazztype = tree.clazz.type; + return tree.def != null && + clazztype.hasTag(CLASS) && + types.isFunctionalInterface(clazztype.tsym) && + decls(tree.def).length() == 1; + } + //where + private List decls(JCClassDecl decl) { + ListBuffer decls = new ListBuffer<>(); + for (JCTree t : decl.defs) { + if (t.hasTag(METHODDEF)) { + JCMethodDecl md = (JCMethodDecl)t; + if ((md.getModifiers().flags & GENERATEDCONSTR) == 0) { + decls.add(md); + } + } else { + decls.add(t); + } + } + return decls.toList(); + } + + @Override + JCLambda map (JCNewClass oldTree, JCNewClass newTree){ + JCMethodDecl md = (JCMethodDecl)decls(newTree.def).head; + List params = md.params; + JCBlock body = md.body; + return make.Lambda(params, body); + } + @Override + void process (JCNewClass oldTree, JCLambda newTree, boolean hasErrors){ + if (!hasErrors) { + log.warning(oldTree.def, "potential.lambda.found"); + } + } + } + + /** + * This analyzer checks if generic method call has redundant type arguments. + */ + class RedundantTypeArgAnalyzer extends StatementAnalyzer { + + RedundantTypeArgAnalyzer() { + super(AnalyzerMode.METHOD, APPLY); + } + + @Override + boolean match (JCMethodInvocation tree){ + return tree.typeargs != null && + tree.typeargs.nonEmpty(); + } + @Override + JCMethodInvocation map (JCMethodInvocation oldTree, JCMethodInvocation newTree){ + newTree.typeargs = List.nil(); + return newTree; + } + @Override + void process (JCMethodInvocation oldTree, JCMethodInvocation newTree, boolean hasErrors){ + if (!hasErrors) { + //exact match + log.warning(oldTree, "method.redundant.typeargs"); + } + } + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + StatementAnalyzer[] analyzers = new StatementAnalyzer[] { + new DiamondInitializer(), + new LambdaAnalyzer(), + new RedundantTypeArgAnalyzer() + }; + + /** + * Analyze an AST node if needed. + */ + void analyzeIfNeeded(JCTree tree, Env env) { + if (!analyzerModes.isEmpty() && + !env.info.isSpeculative && + TreeInfo.isStatement(tree)) { + JCStatement stmt = (JCStatement)tree; + analyze(stmt, env); + } + } + + /** + * Analyze an AST node; this involves collecting a list of all the nodes that needs rewriting, + * and speculatively type-check the rewritten code to compare results against previously attributed code. + */ + void analyze(JCStatement statement, Env env) { + AnalysisContext context = new AnalysisContext(); + StatementScanner statementScanner = new StatementScanner(context); + statementScanner.scan(statement); + + if (!context.treesToAnalyzer.isEmpty()) { + + //add a block to hoist potential dangling variable declarations + JCBlock fakeBlock = make.Block(SYNTHETIC, List.of(statement)); + + TreeMapper treeMapper = new TreeMapper(context); + //TODO: to further refine the analysis, try all rewriting combinations + deferredAttr.attribSpeculative(fakeBlock, env, attr.statInfo, treeMapper, + t -> new AnalyzeDeferredDiagHandler(context)); + + context.treeMap.entrySet().forEach(e -> { + context.treesToAnalyzer.get(e.getKey()) + .process(e.getKey(), e.getValue(), context.errors.nonEmpty()); + }); + } + } + + /** + * Simple deferred diagnostic handler which filters out all messages and keep track of errors. + */ + class AnalyzeDeferredDiagHandler extends Log.DeferredDiagnosticHandler { + AnalysisContext context; + + public AnalyzeDeferredDiagHandler(AnalysisContext context) { + super(log, d -> { + if (d.getType() == DiagnosticType.ERROR) { + context.errors.add(d); + } + return true; + }); + this.context = context; + } + } + + /** + * This class is used to pass around contextual information bewteen analyzer classes, such as + * trees to be rewritten, errors occurred during the speculative attribution step, etc. + */ + class AnalysisContext { + /** Map from trees to analyzers. */ + Map> treesToAnalyzer = new HashMap<>(); + + /** Map from original AST nodes to rewritten AST nodes */ + Map treeMap = new HashMap<>(); + + /** Errors in rewritten tree */ + ListBuffer errors = new ListBuffer<>(); + } + + /** + * Subclass of {@link com.sun.tools.javac.tree.TreeScanner} which visit AST-nodes w/o crossing + * statement boundaries. + */ + class StatementScanner extends TreeScanner { + + /** context */ + AnalysisContext context; + + StatementScanner(AnalysisContext context) { + this.context = context; + } + + @Override + @SuppressWarnings("unchecked") + public void scan(JCTree tree) { + if (tree != null) { + for (StatementAnalyzer analyzer : analyzers) { + if (analyzer.isEnabled() && + tree.hasTag(analyzer.tag) && + analyzer.match(tree)) { + context.treesToAnalyzer.put(tree, analyzer); + break; //TODO: cover cases where multiple matching analyzers are found + } + } + } + super.scan(tree); + } + + @Override + public void visitClassDef(JCClassDecl tree) { + //do nothing (prevents seeing same stuff twice + } + + @Override + public void visitMethodDef(JCMethodDecl tree) { + //do nothing (prevents seeing same stuff twice + } + + @Override + public void visitBlock(JCBlock tree) { + //do nothing (prevents seeing same stuff twice + } + + @Override + public void visitSwitch(JCSwitch tree) { + scan(tree.getExpression()); + } + + @Override + public void visitForLoop(JCForLoop tree) { + scan(tree.getInitializer()); + scan(tree.getCondition()); + scan(tree.getUpdate()); + } + + @Override + public void visitForeachLoop(JCEnhancedForLoop tree) { + scan(tree.getExpression()); + } + + @Override + public void visitWhileLoop(JCWhileLoop tree) { + scan(tree.getCondition()); + } + + @Override + public void visitDoLoop(JCDoWhileLoop tree) { + scan(tree.getCondition()); + } + + @Override + public void visitIf(JCIf tree) { + scan(tree.getCondition()); + } + } + + /** + * Subclass of TreeCopier that maps nodes matched by analyzers onto new AST nodes. + */ + class TreeMapper extends TreeCopier { + + AnalysisContext context; + + TreeMapper(AnalysisContext context) { + super(make); + this.context = context; + } + + @Override + @SuppressWarnings("unchecked") + public Z copy(Z tree, Void _unused) { + Z newTree = super.copy(tree, _unused); + StatementAnalyzer analyzer = context.treesToAnalyzer.get(tree); + if (analyzer != null) { + newTree = (Z)analyzer.map(tree, newTree); + context.treeMap.put(tree, newTree); + } + return newTree; + } + } +} diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index d6aa70a00d1..6da79c6e4db 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -83,6 +83,7 @@ public class Attr extends JCTree.Visitor { final Symtab syms; final Resolve rs; final Infer infer; + final Analyzer analyzer; final DeferredAttr deferredAttr; final Check chk; final Flow flow; @@ -121,6 +122,7 @@ public class Attr extends JCTree.Visitor { make = TreeMaker.instance(context); enter = Enter.instance(context); infer = Infer.instance(context); + analyzer = Analyzer.instance(context); deferredAttr = DeferredAttr.instance(context); cfolder = ConstFold.instance(context); target = Target.instance(context); @@ -143,11 +145,8 @@ public class Attr extends JCTree.Visitor { allowStaticInterfaceMethods = source.allowStaticInterfaceMethods(); sourceName = source.name; relax = (options.isSet("-retrofit") || - options.isSet("-relax")); - findDiamonds = options.get("findDiamond") != null && - source.allowDiamond(); + options.isSet("-relax")); useBeforeDeclarationWarning = options.isSet("useBeforeDeclarationWarning"); - identifyLambdaCandidate = options.getBoolean("identifyLambdaCandidate", false); statInfo = new ResultInfo(KindSelector.NIL, Type.noType); varAssignmentInfo = new ResultInfo(KindSelector.ASG, Type.noType); @@ -182,28 +181,12 @@ public class Attr extends JCTree.Visitor { */ boolean allowStaticInterfaceMethods; - /** Switch: generates a warning if diamond can be safely applied - * to a given new expression - */ - boolean findDiamonds; - - /** - * Internally enables/disables diamond finder feature - */ - static final boolean allowDiamondFinder = true; - /** * Switch: warn about use of variable before declaration? * RFE: 6425594 */ boolean useBeforeDeclarationWarning; - /** - * Switch: generate warnings whenever an anonymous inner class that is convertible - * to a lambda expression is found - */ - boolean identifyLambdaCandidate; - /** * Switch: allow strings in switch? */ @@ -610,7 +593,13 @@ public class Attr extends JCTree.Visitor { /** Derived visitor method: attribute a statement or definition tree. */ public Type attribStat(JCTree tree, Env env) { - return attribTree(tree, env, statInfo); + Env analyzeEnv = + env.dup(tree, env.info.dup(env.info.scope.dupUnshared(env.info.scope.owner))); + try { + return attribTree(tree, env, statInfo); + } finally { + analyzer.analyzeIfNeeded(tree, analyzeEnv); + } } /** Attribute a list of expressions, returning a list of types. @@ -792,8 +781,8 @@ public class Attr extends JCTree.Visitor { Type attribIdentAsEnumType(Env env, JCIdent id) { Assert.check((env.enclClass.sym.flags() & ENUM) != 0); - id.type = env.info.scope.owner.type; - id.sym = env.info.scope.owner; + id.type = env.info.scope.owner.enclClass().type; + id.sym = env.info.scope.owner.enclClass(); return id.type; } @@ -2052,12 +2041,6 @@ public class Attr extends JCTree.Visitor { if (rsEnv.info.lastResolveVarargs()) Assert.check(tree.constructorType.isErroneous() || tree.varargsElement != null); } - if (cdef == null && - !clazztype.isErroneous() && - clazztype.getTypeArguments().nonEmpty() && - findDiamonds) { - findDiamond(localEnv, tree, clazztype); - } } if (cdef != null) { @@ -2105,8 +2088,6 @@ public class Attr extends JCTree.Visitor { attribStat(cdef, localEnv); - checkLambdaCandidate(tree, cdef.sym, clazztype); - // If an outer instance is given, // prefix it to the constructor arguments // and delete it from the new expression @@ -2135,58 +2116,6 @@ public class Attr extends JCTree.Visitor { result = check(tree, owntype, KindSelector.VAL, resultInfo); chk.validate(tree.typeargs, localEnv); } - //where - void findDiamond(Env env, JCNewClass tree, Type clazztype) { - JCTypeApply ta = (JCTypeApply)tree.clazz; - List prevTypeargs = ta.arguments; - try { - //create a 'fake' diamond AST node by removing type-argument trees - ta.arguments = List.nil(); - ResultInfo findDiamondResult = new ResultInfo(KindSelector.VAL, - resultInfo.checkContext.inferenceContext().free(resultInfo.pt) ? Type.noType : pt()); - Type inferred = deferredAttr.attribSpeculative(tree, env, findDiamondResult).type; - Type polyPt = allowPoly ? - syms.objectType : - clazztype; - if (!inferred.isErroneous() && - (allowPoly && pt() == Infer.anyPoly ? - types.isSameType(inferred, clazztype) : - types.isAssignable(inferred, pt().hasTag(NONE) ? polyPt : pt(), types.noWarnings))) { - String key = types.isSameType(clazztype, inferred) ? - "diamond.redundant.args" : - "diamond.redundant.args.1"; - log.warning(tree.clazz.pos(), key, clazztype, inferred); - } - } finally { - ta.arguments = prevTypeargs; - } - } - - private void checkLambdaCandidate(JCNewClass tree, ClassSymbol csym, Type clazztype) { - if (allowLambda && - identifyLambdaCandidate && - clazztype.hasTag(CLASS) && - !pt().hasTag(NONE) && - types.isFunctionalInterface(clazztype.tsym)) { - Symbol descriptor = types.findDescriptorSymbol(clazztype.tsym); - int count = 0; - boolean found = false; - for (Symbol sym : csym.members().getSymbols()) { - if ((sym.flags() & SYNTHETIC) != 0 || - sym.isConstructor()) continue; - count++; - if (sym.kind != MTH || - !sym.name.equals(descriptor.name)) continue; - Type mtype = types.memberType(clazztype, sym); - if (types.overrideEquivalent(mtype, types.memberType(clazztype, descriptor))) { - found = true; - } - } - if (found && count == 1) { - log.note(tree.def, "potential.lambda.found"); - } - } - } /** Make an attributed null check tree. */ diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java index b79807f10e7..db812ff0687 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/AttrContext.java @@ -59,6 +59,10 @@ public class AttrContext { */ boolean isSerializable = false; + /** Is this a speculative attribution environment? + */ + boolean isSpeculative = false; + /** Are arguments to current function applications boxed into an array for varargs? */ Resolve.MethodResolutionPhase pendingResolutionPhase = null; @@ -95,6 +99,7 @@ public class AttrContext { info.returnResult = returnResult; info.defaultSuperCallSite = defaultSuperCallSite; info.isSerializable = isSerializable; + info.isSpeculative = isSpeculative; return info; } diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java index 64bd9ce7696..41c95fcfc3e 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java @@ -37,6 +37,7 @@ import com.sun.tools.javac.comp.Attr.ResultInfo; import com.sun.tools.javac.comp.Infer.InferenceContext; import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase; import com.sun.tools.javac.tree.JCTree.*; +import com.sun.tools.javac.util.Log.DeferredDiagnosticHandler; import java.util.ArrayList; import java.util.Collections; @@ -46,6 +47,7 @@ import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; +import java.util.function.Function; import static com.sun.tools.javac.code.TypeTag.*; import static com.sun.tools.javac.tree.JCTree.Tag.*; @@ -364,28 +366,16 @@ public class DeferredAttr extends JCTree.Visitor { * disabled during speculative type-checking. */ JCTree attribSpeculative(JCTree tree, Env env, ResultInfo resultInfo) { - final JCTree newTree = new TreeCopier<>(make).copy(tree); - Env speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared(env.info.scope.owner))); - Log.DeferredDiagnosticHandler deferredDiagnosticHandler = - new Log.DeferredDiagnosticHandler(log, new Filter() { - public boolean accepts(final JCDiagnostic d) { - class PosScanner extends TreeScanner { - boolean found = false; + return attribSpeculative(tree, env, resultInfo, new TreeCopier<>(make), + (newTree)->new DeferredAttrDiagHandler(log, newTree)); + } - @Override - public void scan(JCTree tree) { - if (tree != null && - tree.pos() == d.getDiagnosticPosition()) { - found = true; - } - super.scan(tree); - } - } - PosScanner posScanner = new PosScanner(); - posScanner.scan(newTree); - return posScanner.found; - } - }); + JCTree attribSpeculative(JCTree tree, Env env, ResultInfo resultInfo, TreeCopier deferredCopier, + Function diagHandlerCreator) { + final JCTree newTree = deferredCopier.copy(tree); + Env speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared(env.info.scope.owner))); + speculativeEnv.info.isSpeculative = true; + Log.DeferredDiagnosticHandler deferredDiagnosticHandler = diagHandlerCreator.apply(newTree); try { attr.attribTree(newTree, speculativeEnv, resultInfo); unenterScanner.scan(newTree); @@ -413,6 +403,37 @@ public class DeferredAttr extends JCTree.Visitor { } } + static class DeferredAttrDiagHandler extends Log.DeferredDiagnosticHandler { + + static class PosScanner extends TreeScanner { + DiagnosticPosition pos; + boolean found = false; + + PosScanner(DiagnosticPosition pos) { + this.pos = pos; + } + + @Override + public void scan(JCTree tree) { + if (tree != null && + tree.pos() == pos) { + found = true; + } + super.scan(tree); + } + } + + DeferredAttrDiagHandler(Log log, JCTree newTree) { + super(log, new Filter() { + public boolean accepts(JCDiagnostic d) { + PosScanner posScanner = new PosScanner(d.getDiagnosticPosition()); + posScanner.scan(newTree); + return posScanner.found; + } + }); + } + } + /** * A deferred context is created on each method check. A deferred context is * used to keep track of information associated with the method check, such as @@ -1221,7 +1242,7 @@ public class DeferredAttr extends JCTree.Visitor { @Override public void visitNewClass(JCNewClass tree) { - result = (TreeInfo.isDiamond(tree) || attr.findDiamonds) ? + result = TreeInfo.isDiamond(tree) ? ArgumentExpressionKind.POLY : ArgumentExpressionKind.NO_POLY; } diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index b22e6970567..89eeadaeaad 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -1196,9 +1196,6 @@ compiler.warn.file.from.future=\ compiler.note.compressed.diags=\ Some messages have been simplified; recompile with -Xdiags:verbose to get full output -compiler.note.potential.lambda.found=\ - This anonymous inner class creation can be turned into a lambda expression. - # 0: boolean, 1: symbol compiler.note.lambda.stat=\ Translating lambda expression\n\ @@ -1640,14 +1637,20 @@ compiler.warn.raw.class.use=\ # 0: unused, 1: unused compiler.warn.diamond.redundant.args=\ - redundant type arguments in new expression (use diamond operator instead). + Redundant type arguments in new expression (use diamond operator instead). -# 0: type, 1: type +# 0: type, 1: list of type compiler.warn.diamond.redundant.args.1=\ - redundant type arguments in new expression (use diamond operator instead).\n\ + Redundant type arguments in new expression (use diamond operator instead).\n\ explicit: {0}\n\ inferred: {1} +compiler.warn.potential.lambda.found=\ + This anonymous inner class creation can be turned into a lambda expression. + +compiler.warn.method.redundant.typeargs=\ + Redundant type arguments in method call. + # 0: symbol, 1: message segment compiler.warn.varargs.redundant.trustme.anno=\ Redundant {0} annotation. {1} diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java index 184c673eb0d..64dc16ada0c 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeInfo.java @@ -313,6 +313,14 @@ public class TreeInfo { } } + /** Return true if the tree corresponds to a statement */ + public static boolean isStatement(JCTree tree) { + return (tree instanceof JCStatement) && + !tree.hasTag(CLASSDEF) && + !tree.hasTag(Tag.BLOCK) && + !tree.hasTag(METHODDEF); + } + /** * Return true if the AST corresponds to a static select of the kind A.B */ diff --git a/langtools/test/tools/javac/diags/examples/DiamondRedundantArgs.java b/langtools/test/tools/javac/diags/examples/DiamondRedundantArgs.java index d8502c319ce..662a082575c 100644 --- a/langtools/test/tools/javac/diags/examples/DiamondRedundantArgs.java +++ b/langtools/test/tools/javac/diags/examples/DiamondRedundantArgs.java @@ -22,8 +22,8 @@ */ // key: compiler.warn.diamond.redundant.args -// options: -XDfindDiamond +// options: -XDfind=diamond -class Foo { - Foo fs = new Foo(); +class DiamondRedundantArgs { + DiamondRedundantArgs fs = new DiamondRedundantArgs(); } diff --git a/langtools/test/tools/javac/diags/examples/DiamondRedundantArgs1.java b/langtools/test/tools/javac/diags/examples/DiamondRedundantArgs1.java index ddc8502f92f..8fd781daaef 100644 --- a/langtools/test/tools/javac/diags/examples/DiamondRedundantArgs1.java +++ b/langtools/test/tools/javac/diags/examples/DiamondRedundantArgs1.java @@ -22,8 +22,8 @@ */ // key: compiler.warn.diamond.redundant.args.1 -// options: -XDfindDiamond +// options: -XDfind=diamond -class Foo { - Foo fs = new Foo(); +class DiamondRedundantArgs1 { + DiamondRedundantArgs1 fs = new DiamondRedundantArgs1(); } diff --git a/langtools/test/tools/javac/diags/examples/MethodRedundantTypeargs.java b/langtools/test/tools/javac/diags/examples/MethodRedundantTypeargs.java new file mode 100644 index 00000000000..373be444214 --- /dev/null +++ b/langtools/test/tools/javac/diags/examples/MethodRedundantTypeargs.java @@ -0,0 +1,35 @@ +/* + * 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. + */ + +// key: compiler.warn.method.redundant.typeargs +// options: -XDfind=method + +class MethodRedundantTypeargs { + Z id(Z z) { return z; } + + void test() { + String s = this.id(""); + } +} diff --git a/langtools/test/tools/javac/diags/examples/PotentialLambdaFound.java b/langtools/test/tools/javac/diags/examples/PotentialLambdaFound.java index 050f26c4eae..99cacac8795 100644 --- a/langtools/test/tools/javac/diags/examples/PotentialLambdaFound.java +++ b/langtools/test/tools/javac/diags/examples/PotentialLambdaFound.java @@ -21,8 +21,8 @@ * questions. */ -// key: compiler.note.potential.lambda.found -// options: -XDidentifyLambdaCandidate=true +// key: compiler.warn.potential.lambda.found +// options: -XDfind=lambda class PotentialLambdaFound { diff --git a/langtools/test/tools/javac/generics/diamond/6939780/T6939780.java b/langtools/test/tools/javac/generics/diamond/6939780/T6939780.java index d3f41e44fb7..d6d9bf3b776 100644 --- a/langtools/test/tools/javac/generics/diamond/6939780/T6939780.java +++ b/langtools/test/tools/javac/generics/diamond/6939780/T6939780.java @@ -4,8 +4,8 @@ * * @summary add a warning to detect diamond sites * @author mcimadamore - * @compile/ref=T6939780_7.out -Xlint:-options -source 7 T6939780.java -XDrawDiagnostics -XDfindDiamond - * @compile/ref=T6939780_8.out T6939780.java -XDrawDiagnostics -XDfindDiamond + * @compile/ref=T6939780_7.out -Xlint:-options -source 7 T6939780.java -XDrawDiagnostics -XDfind=diamond + * @compile/ref=T6939780_8.out T6939780.java -XDrawDiagnostics -XDfind=diamond * */ diff --git a/langtools/test/tools/javac/generics/diamond/6939780/T6939780_7.out b/langtools/test/tools/javac/generics/diamond/6939780/T6939780_7.out index f44ff80ba2e..52d621d78df 100644 --- a/langtools/test/tools/javac/generics/diamond/6939780/T6939780_7.out +++ b/langtools/test/tools/javac/generics/diamond/6939780/T6939780_7.out @@ -1,4 +1,5 @@ -T6939780.java:21:28: compiler.warn.diamond.redundant.args: T6939780.Foo, T6939780.Foo +T6939780.java:21:28: compiler.warn.diamond.redundant.args T6939780.java:22:28: compiler.warn.diamond.redundant.args.1: T6939780.Foo, T6939780.Foo -T6939780.java:30:19: compiler.warn.diamond.redundant.args: T6939780.Foo, T6939780.Foo -3 warnings +T6939780.java:30:19: compiler.warn.diamond.redundant.args +T6939780.java:31:19: compiler.warn.diamond.redundant.args.1: T6939780.Foo, T6939780.Foo +4 warnings diff --git a/langtools/test/tools/javac/generics/diamond/6939780/T6939780_8.out b/langtools/test/tools/javac/generics/diamond/6939780/T6939780_8.out index dfbda806230..3d979d15147 100644 --- a/langtools/test/tools/javac/generics/diamond/6939780/T6939780_8.out +++ b/langtools/test/tools/javac/generics/diamond/6939780/T6939780_8.out @@ -1,7 +1,7 @@ -T6939780.java:20:33: compiler.warn.diamond.redundant.args: T6939780.Foo, T6939780.Foo -T6939780.java:21:28: compiler.warn.diamond.redundant.args: T6939780.Foo, T6939780.Foo +T6939780.java:20:33: compiler.warn.diamond.redundant.args +T6939780.java:21:28: compiler.warn.diamond.redundant.args T6939780.java:22:28: compiler.warn.diamond.redundant.args.1: T6939780.Foo, T6939780.Foo -T6939780.java:29:19: compiler.warn.diamond.redundant.args: T6939780.Foo, T6939780.Foo -T6939780.java:30:19: compiler.warn.diamond.redundant.args: T6939780.Foo, T6939780.Foo +T6939780.java:29:19: compiler.warn.diamond.redundant.args +T6939780.java:30:19: compiler.warn.diamond.redundant.args T6939780.java:31:19: compiler.warn.diamond.redundant.args.1: T6939780.Foo, T6939780.Foo 6 warnings diff --git a/langtools/test/tools/javac/generics/diamond/7002837/T7002837.java b/langtools/test/tools/javac/generics/diamond/7002837/T7002837.java index acdb72bb02e..a75c357b91c 100644 --- a/langtools/test/tools/javac/generics/diamond/7002837/T7002837.java +++ b/langtools/test/tools/javac/generics/diamond/7002837/T7002837.java @@ -4,7 +4,7 @@ * * @summary Diamond: javac generates diamond inference errors when in 'finder' mode * @author mcimadamore - * @compile/fail/ref=T7002837.out -Werror -XDrawDiagnostics -XDfindDiamond T7002837.java + * @compile/fail/ref=T7002837.out -Werror -XDrawDiagnostics -XDfind=diamond T7002837.java * */ diff --git a/langtools/test/tools/javac/lambda/LambdaConv18.java b/langtools/test/tools/javac/lambda/LambdaConv18.java index 279d99ff17c..540e9569974 100644 --- a/langtools/test/tools/javac/lambda/LambdaConv18.java +++ b/langtools/test/tools/javac/lambda/LambdaConv18.java @@ -3,7 +3,7 @@ * @bug 8003280 * @summary Add lambda tests * simple test for lambda candidate check - * @compile/fail/ref=LambdaConv18.out -XDrawDiagnostics -XDidentifyLambdaCandidate=true LambdaConv18.java + * @compile/fail/ref=LambdaConv18.out -XDrawDiagnostics -XDfind=lambda LambdaConv18.java */ class LambdaConv18 { diff --git a/langtools/test/tools/javac/lambda/LambdaConv18.out b/langtools/test/tools/javac/lambda/LambdaConv18.out index fa84e6b43f3..2213718ab3c 100644 --- a/langtools/test/tools/javac/lambda/LambdaConv18.out +++ b/langtools/test/tools/javac/lambda/LambdaConv18.out @@ -1,4 +1,5 @@ LambdaConv18.java:23:5: compiler.err.cant.resolve.location: kindname.class, NonExistent, , , (compiler.misc.location: kindname.class, LambdaConv18, null) -LambdaConv18.java:20:24: compiler.note.potential.lambda.found +LambdaConv18.java:20:24: compiler.warn.potential.lambda.found LambdaConv18.java:23:26: compiler.err.cant.resolve.location: kindname.class, NonExistent, , , (compiler.misc.location: kindname.class, LambdaConv18, null) 2 errors +1 warning diff --git a/langtools/test/tools/javac/lambda/speculative/DiamondFinder.java b/langtools/test/tools/javac/lambda/speculative/DiamondFinder.java index 35f56afaacb..e8e2185b62b 100644 --- a/langtools/test/tools/javac/lambda/speculative/DiamondFinder.java +++ b/langtools/test/tools/javac/lambda/speculative/DiamondFinder.java @@ -26,7 +26,7 @@ * @bug 8003280 * @summary Add lambda tests * spurious crashes when running in 'diamond finder' mode - * @compile -XDfindDiamond DiamondFinder.java + * @compile -XDfind=diamond DiamondFinder.java */ import java.util.*; From 05bf0c5a4572622670341473e431dcd9512ecf40 Mon Sep 17 00:00:00 2001 From: Athijegannathan Sundararajan Date: Mon, 15 Dec 2014 16:30:45 +0530 Subject: [PATCH 28/58] 8067420: BrowserJSObjectLinker should give priority to beans linker for property get/set Reviewed-by: lagergren, attila, hannesw --- nashorn/samples/browser_dom.js | 8 +++--- .../runtime/linker/BrowserJSObjectLinker.java | 25 ++++++++++++------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/nashorn/samples/browser_dom.js b/nashorn/samples/browser_dom.js index 94324c7ad75..37ce98d1e65 100644 --- a/nashorn/samples/browser_dom.js +++ b/nashorn/samples/browser_dom.js @@ -1,4 +1,4 @@ -#// Usage: jjs -fx browser.js +#// Usage: jjs -fx browser_dom.js /* * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. @@ -32,7 +32,7 @@ */ if (!$OPTIONS._fx) { - print("Usage: jjs -fx browser.js"); + print("Usage: jjs -fx browser_dom.js"); exit(1); } @@ -74,10 +74,10 @@ EOF, "text/html"); var btn = document.createElement("button"); var n = 0; // attach a button handler - nashorn function! - btn.onclick = new EventListener(function() { + btn.onclick = function() { n++; print("You clicked " + n + " time(s)"); print("you clicked OK " + wv.engine.executeScript("okCount")); - }); + }; // attach text to button var t = document.createTextNode("Click Me!"); btn.appendChild(t); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java index 0056477c325..a49b79959f9 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java @@ -118,20 +118,21 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker { private GuardedInvocation lookup(final CallSiteDescriptor desc, final LinkRequest request, final LinkerServices linkerServices) throws Exception { final String operator = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0); final int c = desc.getNameTokenCount(); + GuardedInvocation inv; + try { + inv = nashornBeansLinker.getGuardedInvocation(request, linkerServices); + } catch (Throwable th) { + inv = null; + } switch (operator) { case "getProp": case "getElem": case "getMethod": - if (c > 2) { - return findGetMethod(desc); - } - // For indexed get, we want GuardedInvocation from beans linker and pass it. - // BrowserJSObjectLinker.get uses this fallback getter for explicit signature method access. - return findGetIndexMethod(nashornBeansLinker.getGuardedInvocation(request, linkerServices)); + return c > 2? findGetMethod(desc, inv) : findGetIndexMethod(inv); case "setProp": case "setElem": - return c > 2 ? findSetMethod(desc) : findSetIndexMethod(); + return c > 2? findSetMethod(desc, inv) : findSetIndexMethod(); case "call": return findCallMethod(desc); default: @@ -139,7 +140,10 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker { } } - private static GuardedInvocation findGetMethod(final CallSiteDescriptor desc) { + private static GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final GuardedInvocation inv) { + if (inv != null) { + return inv; + } final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); final MethodHandle getter = MH.insertArguments(JSOBJECT_GETMEMBER, 1, name); return new GuardedInvocation(getter, IS_JSOBJECT_GUARD); @@ -150,7 +154,10 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker { return inv.replaceMethods(getter, inv.getGuard()); } - private static GuardedInvocation findSetMethod(final CallSiteDescriptor desc) { + private static GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final GuardedInvocation inv) { + if (inv != null) { + return inv; + } final MethodHandle getter = MH.insertArguments(JSOBJECT_SETMEMBER, 1, desc.getNameToken(2)); return new GuardedInvocation(getter, IS_JSOBJECT_GUARD); } From b7637531a9f611698293f181f798658ca108e620 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?= Date: Mon, 15 Dec 2014 12:08:36 +0100 Subject: [PATCH 29/58] 8066215: Fuzzing bug: length valueOf bug Reviewed-by: attila, lagergren --- .../nashorn/internal/objects/NativeArray.java | 21 ++++----- .../objects/NativeRegExpExecResult.java | 2 +- nashorn/test/script/basic/JDK-8066215.js | 46 +++++++++++++++++++ .../test/script/basic/JDK-8066215.js.EXPECTED | 9 ++++ 4 files changed, 64 insertions(+), 14 deletions(-) create mode 100644 nashorn/test/script/basic/JDK-8066215.js create mode 100644 nashorn/test/script/basic/JDK-8066215.js.EXPECTED diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArray.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArray.java index 047d612ac54..0033ed71915 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArray.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArray.java @@ -275,7 +275,7 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin final PropertyDescriptor newLenDesc = desc; // Step 3c and 3d - get new length and convert to long - final long newLen = NativeArray.validLength(newLenDesc.getValue(), true); + final long newLen = NativeArray.validLength(newLenDesc.getValue()); // Step 3e newLenDesc.setValue(newLen); @@ -348,8 +348,8 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin final PropertyDescriptor oldLenDesc = (PropertyDescriptor) super.getOwnPropertyDescriptor("length"); // Step 2 - // get old length and convert to long - final long oldLen = NativeArray.validLength(oldLenDesc.getValue(), true); + // get old length and convert to long. Always a Long/Uint32 but we take the safe road. + final long oldLen = JSType.toUint32(oldLenDesc.getValue()); // Step 3 if ("length".equals(key)) { @@ -471,7 +471,7 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin @Setter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE) public static void length(final Object self, final Object length) { if (isArray(self)) { - ((ScriptObject)self).setLength(validLength(length, true)); + ((ScriptObject)self).setLength(validLength(length)); } } @@ -495,18 +495,13 @@ public final class NativeArray extends ScriptObject implements OptimisticBuiltin length(self, length); // Same as instance setter but we can't make nasgen use the same method for prototype } - static long validLength(final Object length, final boolean reject) { + static long validLength(final Object length) { + // ES5 15.4.5.1, steps 3.c and 3.d require two ToNumber conversions here final double doubleLength = JSType.toNumber(length); - if (!Double.isNaN(doubleLength) && JSType.isRepresentableAsLong(doubleLength)) { - final long len = (long) doubleLength; - if (len >= 0 && len <= JSType.MAX_UINT) { - return len; - } - } - if (reject) { + if (doubleLength != JSType.toUint32(length)) { throw rangeError("inappropriate.array.length", ScriptRuntime.safeToString(length)); } - return -1; + return (long) doubleLength; } /** diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeRegExpExecResult.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeRegExpExecResult.java index 467399aeae2..0df0eca4a3c 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeRegExpExecResult.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeRegExpExecResult.java @@ -88,7 +88,7 @@ public final class NativeRegExpExecResult extends ScriptObject { @Setter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE) public static void length(final Object self, final Object length) { if (self instanceof ScriptObject) { - ((ScriptObject)self).setLength(NativeArray.validLength(length, true)); + ((ScriptObject)self).setLength(NativeArray.validLength(length)); } } } diff --git a/nashorn/test/script/basic/JDK-8066215.js b/nashorn/test/script/basic/JDK-8066215.js new file mode 100644 index 00000000000..3987fd89ae8 --- /dev/null +++ b/nashorn/test/script/basic/JDK-8066215.js @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010, 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. + */ + +/** + * JDK-8066215: Fuzzing bug: length valueOf bug + * + * @test + * @run + */ + +function defineLength(arr, length) { + Object.defineProperty(arr, "length", { + value: { + valueOf: function() { + print("value retrieved: " + length); + return length; + } + } + }); + print("done: " + arr.length + ", " + typeof arr.length); +} + +var a = []; +defineLength(a, 3); +defineLength(a, 6); +defineLength(a, 3); diff --git a/nashorn/test/script/basic/JDK-8066215.js.EXPECTED b/nashorn/test/script/basic/JDK-8066215.js.EXPECTED new file mode 100644 index 00000000000..f10edda4287 --- /dev/null +++ b/nashorn/test/script/basic/JDK-8066215.js.EXPECTED @@ -0,0 +1,9 @@ +value retrieved: 3 +value retrieved: 3 +done: 3, number +value retrieved: 6 +value retrieved: 6 +done: 6, number +value retrieved: 3 +value retrieved: 3 +done: 3, number From 49252804f20a6f3d22e81f5dbda4cfbedb7ec569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?= Date: Mon, 15 Dec 2014 12:32:34 +0100 Subject: [PATCH 30/58] 8062030: Nashorn bug retrieving array property after key string concatenation Reviewed-by: sundar, lagergren, attila --- .../runtime/linker/BrowserJSObjectLinker.java | 13 +++++---- .../runtime/linker/JSObjectLinker.java | 11 +++---- nashorn/test/script/basic/JDK-8055762.js | 3 ++ .../test/script/basic/JDK-8055762.js.EXPECTED | 2 ++ .../api/scripting/PluggableJSObjectTest.java | 29 +++++++++++++++++++ 5 files changed, 47 insertions(+), 11 deletions(-) diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java index a49b79959f9..f61802bd1c0 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BrowserJSObjectLinker.java @@ -40,6 +40,7 @@ import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker; import jdk.internal.dynalink.support.CallSiteDescriptorFactory; import jdk.nashorn.internal.lookup.MethodHandleFactory; import jdk.nashorn.internal.lookup.MethodHandleFunctionality; +import jdk.nashorn.internal.runtime.ConsString; import jdk.nashorn.internal.runtime.JSType; /** @@ -185,12 +186,12 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker { if (index > -1) { return JSOBJECT_GETSLOT.invokeExact(jsobj, index); } - } else if (key instanceof String) { - final String name = (String)key; + } else if (key instanceof String || key instanceof ConsString) { + final String name = key.toString(); if (name.indexOf('(') != -1) { - return fallback.invokeExact(jsobj, key); + return fallback.invokeExact(jsobj, (Object) name); } - return JSOBJECT_GETMEMBER.invokeExact(jsobj, (String)key); + return JSOBJECT_GETMEMBER.invokeExact(jsobj, name); } return null; } @@ -201,8 +202,8 @@ final class BrowserJSObjectLinker implements TypeBasedGuardingDynamicLinker { JSOBJECT_SETSLOT.invokeExact(jsobj, (int)key, value); } else if (key instanceof Number) { JSOBJECT_SETSLOT.invokeExact(jsobj, getIndex((Number)key), value); - } else if (key instanceof String) { - JSOBJECT_SETMEMBER.invokeExact(jsobj, (String)key, value); + } else if (key instanceof String || key instanceof ConsString) { + JSOBJECT_SETMEMBER.invokeExact(jsobj, key.toString(), value); } } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java index aaf5a2c314b..48772ae6baa 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JSObjectLinker.java @@ -42,6 +42,7 @@ import jdk.internal.dynalink.support.CallSiteDescriptorFactory; import jdk.nashorn.api.scripting.JSObject; import jdk.nashorn.internal.lookup.MethodHandleFactory; import jdk.nashorn.internal.lookup.MethodHandleFunctionality; +import jdk.nashorn.internal.runtime.ConsString; import jdk.nashorn.internal.runtime.JSType; /** @@ -185,11 +186,11 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker, GuardingTy if (index > -1) { return ((JSObject)jsobj).getSlot(index); } - } else if (key instanceof String) { - final String name = (String)key; + } else if (key instanceof String || key instanceof ConsString) { + final String name = key.toString(); // get with method name and signature. delegate it to beans linker! if (name.indexOf('(') != -1) { - return fallback.invokeExact(jsobj, key); + return fallback.invokeExact(jsobj, (Object) name); } return ((JSObject)jsobj).getMember(name); } @@ -202,8 +203,8 @@ final class JSObjectLinker implements TypeBasedGuardingDynamicLinker, GuardingTy ((JSObject)jsobj).setSlot((Integer)key, value); } else if (key instanceof Number) { ((JSObject)jsobj).setSlot(getIndex((Number)key), value); - } else if (key instanceof String) { - ((JSObject)jsobj).setMember((String)key, value); + } else if (key instanceof String || key instanceof ConsString) { + ((JSObject)jsobj).setMember(key.toString(), value); } } diff --git a/nashorn/test/script/basic/JDK-8055762.js b/nashorn/test/script/basic/JDK-8055762.js index e772a579f4e..a0aac526937 100644 --- a/nashorn/test/script/basic/JDK-8055762.js +++ b/nashorn/test/script/basic/JDK-8055762.js @@ -74,9 +74,12 @@ function test(JSObject) { } }; + var a = "a"; print(obj["foo"]); + print(obj[a + "bc"]); print(obj[2]); obj.bar = 23; + obj[a + "bc"] = 23; obj[3] = 23; obj.func("hello"); } diff --git a/nashorn/test/script/basic/JDK-8055762.js.EXPECTED b/nashorn/test/script/basic/JDK-8055762.js.EXPECTED index 51a02015dd4..1cba6465a93 100644 --- a/nashorn/test/script/basic/JDK-8055762.js.EXPECTED +++ b/nashorn/test/script/basic/JDK-8055762.js.EXPECTED @@ -1,5 +1,7 @@ FOO +ABC 0 bar set to 23 +abc set to 23 [3] set to 23 func called with hello diff --git a/nashorn/test/src/jdk/nashorn/api/scripting/PluggableJSObjectTest.java b/nashorn/test/src/jdk/nashorn/api/scripting/PluggableJSObjectTest.java index b277bdefddc..d6b9dcde1e5 100644 --- a/nashorn/test/src/jdk/nashorn/api/scripting/PluggableJSObjectTest.java +++ b/nashorn/test/src/jdk/nashorn/api/scripting/PluggableJSObjectTest.java @@ -109,6 +109,35 @@ public class PluggableJSObjectTest { } } + // @bug 8062030: Nashorn bug retrieving array property after key string concatenation + @Test + // ConsString attribute access on a JSObject + public void consStringTest() { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + try { + final MapWrapperObject obj = new MapWrapperObject(); + e.put("obj", obj); + e.put("f", "f"); + e.eval("obj[f + 'oo'] = 'bar';"); + + assertEquals(obj.getMap().get("foo"), "bar"); + assertEquals(e.eval("obj[f + 'oo']"), "bar"); + assertEquals(e.eval("obj['foo']"), "bar"); + assertEquals(e.eval("f + 'oo' in obj"), Boolean.TRUE); + assertEquals(e.eval("'foo' in obj"), Boolean.TRUE); + e.eval("delete obj[f + 'oo']"); + assertFalse(obj.getMap().containsKey("foo")); + assertEquals(e.eval("obj[f + 'oo']"), null); + assertEquals(e.eval("obj['foo']"), null); + assertEquals(e.eval("f + 'oo' in obj"), Boolean.FALSE); + assertEquals(e.eval("'foo' in obj"), Boolean.FALSE); + } catch (final Exception exp) { + exp.printStackTrace(); + fail(exp.getMessage()); + } + } + public static class BufferObject extends AbstractJSObject { private final IntBuffer buf; From 12c05a89f5fb808df9418b71bbfb17ae3becb2d7 Mon Sep 17 00:00:00 2001 From: Mandy Chung Date: Mon, 15 Dec 2014 10:29:41 -0800 Subject: [PATCH 31/58] 8067360: verify-modules target was dropped in jdk9 b41 Reviewed-by: alanb, erikj --- make/Main.gmk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/Main.gmk b/make/Main.gmk index ba4ba3c2219..c3b40e55b15 100644 --- a/make/Main.gmk +++ b/make/Main.gmk @@ -442,7 +442,7 @@ exploded-image: $(ALL_MODULE_TARGETS) # alias for ease of use. jdk: exploded-image -images: jimages demos samples zip-security +images: jimages demos samples zip-security verify-modules ifeq ($(OPENJDK_TARGET_OS), macosx) images: mac-bundles From 7167a27a1f7275cf0f384a4383f1baffb1dfd1b9 Mon Sep 17 00:00:00 2001 From: Mandy Chung Date: Mon, 15 Dec 2014 10:29:54 -0800 Subject: [PATCH 32/58] 8067360: verify-modules target was dropped in jdk9 b41 Reviewed-by: alanb, erikj --- .../jdk.dev/share/classes/com/sun/tools/jdeps/JdepsTask.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/langtools/src/jdk.dev/share/classes/com/sun/tools/jdeps/JdepsTask.java b/langtools/src/jdk.dev/share/classes/com/sun/tools/jdeps/JdepsTask.java index 3a93327d5e4..a430df9b2ae 100644 --- a/langtools/src/jdk.dev/share/classes/com/sun/tools/jdeps/JdepsTask.java +++ b/langtools/src/jdk.dev/share/classes/com/sun/tools/jdeps/JdepsTask.java @@ -554,8 +554,9 @@ class JdepsTask { classpaths.addAll(PlatformClassPath.getModules(options.mpath)); if (options.mpath != null) { initialArchives.addAll(PlatformClassPath.getModules(options.mpath)); + } else { + classpaths.addAll(PlatformClassPath.getJarFiles()); } - classpaths.addAll(PlatformClassPath.getJarFiles()); // add all classpath archives to the source locations for reporting sourceLocations.addAll(classpaths); } From 629b26bb7204c61463a48fb8a8e869edd43570aa Mon Sep 17 00:00:00 2001 From: Tristan Yan Date: Mon, 15 Dec 2014 11:32:05 -0800 Subject: [PATCH 33/58] 8065673: XML Test Colo: Add test build system for JAXP tests Reviewed-by: alanb, joehw --- jaxp/test/Makefile | 325 ++++++++++++++++++++++++++++++++++++++++++ jaxp/test/TEST.ROOT | 4 +- jaxp/test/TEST.groups | 24 ++++ 3 files changed, 352 insertions(+), 1 deletion(-) create mode 100644 jaxp/test/Makefile create mode 100644 jaxp/test/TEST.groups diff --git a/jaxp/test/Makefile b/jaxp/test/Makefile new file mode 100644 index 00000000000..0a3acdafb7c --- /dev/null +++ b/jaxp/test/Makefile @@ -0,0 +1,325 @@ +# +# Copyright (c) 1995, 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. +# + +# +# Makefile to run various JAXP tests +# + +.DEFAULT : all + +# Empty these to get rid of some default rules +.SUFFIXES: +.SUFFIXES: .java +CO= +GET= + +# Utilities used +AWK = awk +CAT = cat +CD = cd +CHMOD = chmod +CP = cp +CUT = cut +DIRNAME = dirname +ECHO = echo +EGREP = egrep +EXPAND = expand +FIND = find +MKDIR = mkdir +PWD = pwd +SED = sed +SORT = sort +TEE = tee +UNAME = uname +UNIQ = uniq +WC = wc +ZIP = zip + +# Get OS name from uname (Cygwin inexplicably adds _NT-5.1) +UNAME_S := $(shell $(UNAME) -s | $(CUT) -f1 -d_) + +# Commands to run on paths to make mixed paths for java on windows +ifeq ($(UNAME_S), CYGWIN) + # Location of developer shared files + SLASH_JAVA = J: + GETMIXEDPATH = cygpath -m +else + # Location of developer shared files + SLASH_JAVA = /java + + GETMIXEDPATH=$(ECHO) +endif + +# Root of this test area (important to use full paths in some places) +TEST_ROOT := $(shell $(PWD)) + +# Root of all test results +ifdef ALT_OUTPUTDIR + ABS_OUTPUTDIR = $(shell $(CD) $(ALT_OUTPUTDIR) && $(PWD)) +else + ABS_OUTPUTDIR = $(shell $(CD) $(TEST_ROOT)/.. && $(PWD)) +endif + +ABS_PLATFORM_BUILD_ROOT = $(ABS_OUTPUTDIR) +ABS_TEST_OUTPUT_DIR := $(ABS_PLATFORM_BUILD_ROOT)/testoutput/$(UNIQUE_DIR) + +# Expect JPRT to set PRODUCT_HOME (the product or jdk in this case to test) +ifndef PRODUCT_HOME + # Try to use j2sdk-image if it exists + ABS_JDK_IMAGE = $(ABS_PLATFORM_BUILD_ROOT)/images/j2sdk-image + PRODUCT_HOME := \ + $(shell \ + if [ -d $(ABS_JDK_IMAGE) ] ; then \ + $(ECHO) "$(ABS_JDK_IMAGE)"; \ + else \ + $(ECHO) "$(ABS_PLATFORM_BUILD_ROOT)"; \ + fi) + PRODUCT_HOME := $(PRODUCT_HOME) +endif + +# Expect JPRT to set JPRT_PRODUCT_ARGS (e.g. -server etc.) +# Should be passed into 'java' only. +# Could include: -d64 -server -client OR any java option +ifdef JPRT_PRODUCT_ARGS + JAVA_ARGS = $(JPRT_PRODUCT_ARGS) +endif + +# Expect JPRT to set JPRT_PRODUCT_VM_ARGS (e.g. -Xcomp etc.) +# Should be passed into anything running the vm (java, javac, javadoc, ...). +ifdef JPRT_PRODUCT_VM_ARGS + JAVA_VM_ARGS = $(JPRT_PRODUCT_VM_ARGS) +endif + +# Expect JPRT to set JPRT_ARCHIVE_BUNDLE (path to zip bundle for results) +ifdef JPRT_ARCHIVE_BUNDLE + ARCHIVE_BUNDLE = $(JPRT_ARCHIVE_BUNDLE) +else + ARCHIVE_BUNDLE = $(ABS_TEST_OUTPUT_DIR)/ARCHIVE_BUNDLE.zip +endif + +# How to create the test bundle (pass or fail, we want to create this) +# Follow command with ";$(BUNDLE_UP_AND_EXIT)", so it always gets executed. +ZIP_UP_RESULTS = ( $(MKDIR) -p `$(DIRNAME) $(ARCHIVE_BUNDLE)` \ + && $(CD) $(ABS_TEST_OUTPUT_DIR) \ + && $(CHMOD) -R a+r . \ + && $(ZIP) -q -r $(ARCHIVE_BUNDLE) . ) + +# important results files +SUMMARY_TXT = $(shell $(GETMIXEDPATH) "$(ABS_TEST_OUTPUT_DIR)/JTreport/text/summary.txt") +STATS_TXT_NAME = Stats.txt +STATS_TXT = $(shell $(GETMIXEDPATH) "$(ABS_TEST_OUTPUT_DIR)/$(STATS_TXT_NAME)") +RUNLIST = $(shell $(GETMIXEDPATH) "$(ABS_TEST_OUTPUT_DIR)/runlist.txt") +PASSLIST = $(shell $(GETMIXEDPATH) "$(ABS_TEST_OUTPUT_DIR)/passlist.txt") +FAILLIST = $(shell $(GETMIXEDPATH) "$(ABS_TEST_OUTPUT_DIR)/faillist.txt") +EXITCODE = $(shell $(GETMIXEDPATH) "$(ABS_TEST_OUTPUT_DIR)/exitcode.txt") + +TESTEXIT = \ + if [ ! -s $(EXITCODE) ] ; then \ + $(ECHO) "ERROR: EXITCODE file not filled in."; \ + $(ECHO) "1" > $(EXITCODE); \ + fi ; \ + testExitCode=`$(CAT) $(EXITCODE)`; \ + $(ECHO) "EXIT CODE: $${testExitCode}"; \ + exit $${testExitCode} + +BUNDLE_UP_AND_EXIT = \ +( \ + jtregExitCode=$$? && \ + _summary="$(SUMMARY_TXT)"; \ + $(RM) -f $(STATS_TXT) $(RUNLIST) $(PASSLIST) $(FAILLIST) $(EXITCODE); \ + $(ECHO) "$${jtregExitCode}" > $(EXITCODE); \ + if [ -r "$${_summary}" ] ; then \ + $(ECHO) "Summary: $(UNIQUE_DIR)" > $(STATS_TXT); \ + $(EXPAND) $${_summary} | $(EGREP) -v ' Not run\.' > $(RUNLIST); \ + $(EGREP) ' Passed\.' $(RUNLIST) \ + | $(EGREP) -v ' Error\.' \ + | $(EGREP) -v ' Failed\.' > $(PASSLIST); \ + ( $(EGREP) ' Failed\.' $(RUNLIST); \ + $(EGREP) ' Error\.' $(RUNLIST); \ + $(EGREP) -v ' Passed\.' $(RUNLIST) ) \ + | $(SORT) | $(UNIQ) > $(FAILLIST); \ + if [ $${jtregExitCode} != 0 -o -s $(FAILLIST) ] ; then \ + $(EXPAND) $(FAILLIST) \ + | $(CUT) -d' ' -f1 \ + | $(SED) -e 's@^@FAILED: @' >> $(STATS_TXT); \ + if [ $${jtregExitCode} = 0 ] ; then \ + jtregExitCode=1; \ + fi; \ + fi; \ + runc="`$(CAT) $(RUNLIST) | $(WC) -l | $(AWK) '{print $$1;}'`"; \ + passc="`$(CAT) $(PASSLIST) | $(WC) -l | $(AWK) '{print $$1;}'`"; \ + failc="`$(CAT) $(FAILLIST) | $(WC) -l | $(AWK) '{print $$1;}'`"; \ + exclc="FIXME CODETOOLS-7900176"; \ + $(ECHO) "TEST STATS: name=$(UNIQUE_DIR) run=$${runc} pass=$${passc} fail=$${failc}" \ + >> $(STATS_TXT); \ + else \ + $(ECHO) "Missing file: $${_summary}" >> $(STATS_TXT); \ + fi; \ + if [ -f $(STATS_TXT) ] ; then \ + $(CAT) $(STATS_TXT); \ + fi; \ + $(ZIP_UP_RESULTS) ; \ + $(TESTEXIT) \ +) + +################################################################ + +# Default make rule (runs default JAXP tests) +all: jaxp_all + @$(ECHO) "Testing completed successfully" + +# Prep for output +# Change execute permissions on shared library files. +# Files in repositories should never have execute permissions, but +# there are some tests that have pre-built shared libraries, and these +# windows dll files must have execute permission. Adding execute +# permission may happen automatically on windows when using certain +# versions of mercurial but it cannot be guaranteed. And blindly +# adding execute permission might be seen as a mercurial 'change', so +# we avoid adding execute permission to repository files. But testing +# from a plain source tree needs the chmod a+rx. Applying the chmod to +# all shared libraries not just dll files. And with CYGWIN and sshd +# service, you may need CYGWIN=ntsec for this to work. +prep: + @$(MKDIR) -p $(ABS_TEST_OUTPUT_DIR) + @$(MKDIR) -p `$(DIRNAME) $(ARCHIVE_BUNDLE)` + @if [ ! -d $(TEST_ROOT)/../.hg ] ; then \ + $(FIND) $(TEST_ROOT) \( -name \*.dll -o -name \*.DLL -o -name \*.so \) \ + -exec $(CHMOD) a+rx {} \; ; \ + fi + +# Cleanup +clean: + @$(RM) -r $(ABS_TEST_OUTPUT_DIR) + @$(RM) $(ARCHIVE_BUNDLE) + +################################################################ + +# jtreg tests + +# Expect JT_HOME to be set for jtreg tests. (home for jtreg) +ifndef JT_HOME + JT_HOME = $(SLASH_JAVA)/re/jtreg/4.1/promoted/latest/binaries/jtreg + ifdef JPRT_JTREG_HOME + JT_HOME = $(JPRT_JTREG_HOME) + endif +endif + +# Problematic tests to be excluded +PROBLEM_LISTS=$(call MixedDirs,$(wildcard ProblemList.txt closed/ProblemList.txt)) + +# Create exclude list for this platform and arch +ifdef NO_EXCLUDES + JTREG_EXCLUSIONS = +else + JTREG_EXCLUSIONS = $(PROBLEM_LISTS:%=-exclude:%) +endif + +# convert list of directories to dos paths +define MixedDirs +$(foreach i,$1,$(shell $(GETMIXEDPATH) "${i}")) +endef + +define SummaryInfo +$(ECHO) "########################################################" +$(CAT) $(?:%=$(ABS_TEST_OUTPUT_DIR)/%/$(STATS_TXT_NAME)) +$(ECHO) "########################################################" +endef + +# ------------------------------------------------------------------ + +jaxp_%: + $(ECHO) "Running tests: $@" + for each in $@; do \ + $(MAKE) -j 1 TEST_SELECTION=":$$each" UNIQUE_DIR=$$each jtreg_tests; \ + done + +# ------------------------------------------------------------------ + +ifdef CONCURRENCY + EXTRA_JTREG_OPTIONS += -concurrency:$(CONCURRENCY) +endif + +# Default JTREG to run (win32 script works for everybody) +JTREG = $(JT_HOME)/win32/bin/jtreg +# run in agentvm mode +JTREG_BASIC_OPTIONS += -agentvm +# Only run automatic tests +JTREG_BASIC_OPTIONS += -a +# Always turn on assertions +JTREG_ASSERT_OPTION = -ea -esa +JTREG_BASIC_OPTIONS += $(JTREG_ASSERT_OPTION) +# Report details on all failed or error tests, times too +JTREG_BASIC_OPTIONS += -v:fail,error,time +# Retain all files for failing tests +JTREG_BASIC_OPTIONS += -retain:fail,error +# Ignore tests are not run and completely silent about it +JTREG_IGNORE_OPTION = -ignore:quiet +JTREG_BASIC_OPTIONS += $(JTREG_IGNORE_OPTION) +# Multiple by 4 the timeout numbers +JTREG_TIMEOUT_OPTION = -timeoutFactor:4 +JTREG_BASIC_OPTIONS += $(JTREG_TIMEOUT_OPTION) +# Set the max memory for jtreg control vm +JTREG_MEMORY_OPTION = -J-Xmx512m +JTREG_BASIC_OPTIONS += $(JTREG_MEMORY_OPTION) +# Add any extra options +JTREG_BASIC_OPTIONS += $(EXTRA_JTREG_OPTIONS) +# Set other vm and test options +JTREG_TEST_OPTIONS = $(JAVA_ARGS:%=-javaoptions:%) $(JAVA_VM_ARGS:%=-vmoption:%) +# Set the GC options for test vms +#JTREG_GC_OPTION = -vmoption:-XX:+UseSerialGC +#JTREG_TEST_OPTIONS += $(JTREG_GC_OPTION) +# Set the max memory for jtreg target test vms +JTREG_TESTVM_MEMORY_OPTION = -vmoption:-Xmx512m +JTREG_TEST_OPTIONS += $(JTREG_TESTVM_MEMORY_OPTION) + +# Make sure jtreg exists +$(JTREG): $(JT_HOME) + +# Run jtreg +jtreg_tests: prep $(PRODUCT_HOME) $(JTREG) + ( \ + ( JT_HOME=$(shell $(GETMIXEDPATH) "$(JT_HOME)"); \ + export JT_HOME; \ + $(shell $(GETMIXEDPATH) "$(JTREG)") \ + $(JTREG_BASIC_OPTIONS) \ + -r:$(shell $(GETMIXEDPATH) "$(ABS_TEST_OUTPUT_DIR)/JTreport") \ + -w:$(shell $(GETMIXEDPATH) "$(ABS_TEST_OUTPUT_DIR)/JTwork") \ + -jdk:$(shell $(GETMIXEDPATH) "$(PRODUCT_HOME)") \ + $(JTREG_EXCLUSIONS) \ + $(JTREG_TEST_OPTIONS) \ + $(TEST_SELECTION) \ + ) ; \ + $(BUNDLE_UP_AND_EXIT) \ + ) 2>&1 | $(TEE) $(ABS_TEST_OUTPUT_DIR)/output.txt ; $(TESTEXIT) + +PHONY_LIST += jtreg_tests + +################################################################ + +# Phony targets (e.g. these are not filenames) +.PHONY: all clean prep $(PHONY_LIST) + +################################################################ diff --git a/jaxp/test/TEST.ROOT b/jaxp/test/TEST.ROOT index cc03bd9063d..a098ba22044 100644 --- a/jaxp/test/TEST.ROOT +++ b/jaxp/test/TEST.ROOT @@ -2,5 +2,7 @@ # It also contains test-suite configuration information. # Tests that must run in othervm mode -othervm.dirs=javax/xml/jaxp/unittest +othervm.dirs=javax/xml/jaxp +# Group definitions +groups=TEST.groups diff --git a/jaxp/test/TEST.groups b/jaxp/test/TEST.groups new file mode 100644 index 00000000000..50751d4d7eb --- /dev/null +++ b/jaxp/test/TEST.groups @@ -0,0 +1,24 @@ +# 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 +# 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. +# + +jaxp_all = \ + javax/xml/jaxp From a78b9855a813b285559537f8908f299bc28fcac2 Mon Sep 17 00:00:00 2001 From: Tristan Yan Date: Mon, 15 Dec 2014 13:09:39 -0800 Subject: [PATCH 34/58] 8065673: XML Test Colo: Add test build system for JAXP tests Reviewed-by: alanb, joehw --- make/jprt.properties | 10 +++++++++- test/Makefile | 8 ++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/make/jprt.properties b/make/jprt.properties index fdb18513f52..d9cd1498649 100644 --- a/make/jprt.properties +++ b/make/jprt.properties @@ -188,6 +188,13 @@ my.make.rule.test.targets.svc= \ ${my.test.target.set:TESTNAME=svc_tools}, \ ${my.make.rule.test.targets.svc.extra} +# JAXP vm test targets (testset=jaxp) +my.test.targets.jaxp= + +# JAXP test targets (testset=jaxp) +my.make.rule.test.targets.jaxp= \ + ${my.test.target.set:TESTNAME=jaxp_all} + # All vm test targets (testset=all) my.test.targets.all= \ ${my.test.targets.default}, \ @@ -211,7 +218,8 @@ my.test.targets.pit= \ my.make.rule.test.targets.pit= \ ${my.test.target.set:TESTNAME=langtools_jtreg}, \ ${my.make.rule.test.targets.core}, \ - ${my.make.rule.test.targets.svc} + ${my.make.rule.test.targets.svc} \ + ${my.make.rule.test.targets.jaxp} # JCK test targets in test/Makefile (no windows) my.test.target.set.jck= \ diff --git a/test/Makefile b/test/Makefile index 89141cce04b..64d93fc929a 100644 --- a/test/Makefile +++ b/test/Makefile @@ -33,6 +33,7 @@ TOPDIR=.. # This makefile depends on the availability of sibling directories. LANGTOOLS_DIR=$(TOPDIR)/langtools JDK_DIR=$(TOPDIR)/jdk +JAXP_DIR=$(TOPDIR)/jaxp HOTSPOT_DIR=$(TOPDIR)/hotspot # Macro to run a test target in a subdir @@ -51,10 +52,10 @@ fi endef # Default test target (core) -default: jdk_core langtools_jtreg +default: jdk_core langtools_jtreg jaxp_all # All testing -all: jdk_all langtools_all +all: jdk_all langtools_all jaxp_all # Test targets langtools_% : @@ -63,6 +64,9 @@ langtools_% : jdk_% core_%s svc_%: @$(NO_STOPPING)$(call SUBDIR_TEST, $(JDK_DIR), TEST="$@" $@) +jaxp_%: + @$(NO_STOPPING)$(call SUBDIR_TEST, $(JAXP_DIR), TEST="$@" $@) + hotspot_%: @$(NO_STOPPING)$(call SUBDIR_TEST, $(HOTSPOT_DIR), TEST="$@" $@) From f158033fdade3537df3f01e69bc41ba155ee431c Mon Sep 17 00:00:00 2001 From: Andreas Lundblad Date: Tue, 16 Dec 2014 11:28:34 +0100 Subject: [PATCH 35/58] 8066138: Trailing whitespace in title of javadoc: Overview (Java Platform SE 7 ) Trims whitespace of arguments in OptionOnly, OptionPair and OptionTrip. Reviewed-by: erikj --- make/Javadoc.gmk | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/make/Javadoc.gmk b/make/Javadoc.gmk index d68c6f333ce..94078328315 100644 --- a/make/Javadoc.gmk +++ b/make/Javadoc.gmk @@ -231,17 +231,17 @@ endef # Common echo of option define OptionOnly # opt - if [ "$1" != "" ] ; then \ - $(PRINTF) "%s\n" "$1"; \ + if [ "$(strip $1)" != "" ] ; then \ + $(PRINTF) "%s\n" "$(strip $1)"; \ fi endef define OptionPair # opt arg - $(PRINTF) "%s '%s'\n" "$1" '$2' + $(PRINTF) "%s '%s'\n" "$(strip $1)" '$(strip $2)' endef define OptionTrip # opt arg arg - $(PRINTF) "%s '%s' '%s'\n" "$1" '$2' '$3' + $(PRINTF) "%s '%s' '%s'\n" "$(strip $1)" '$(strip $2)' '$(strip $3)' endef # Core api bottom argument (with special sauce) From 8ecec7ff2c6902577914adb0775c9b9ccfdc5b87 Mon Sep 17 00:00:00 2001 From: Erik Helin Date: Tue, 16 Dec 2014 12:53:25 +0100 Subject: [PATCH 36/58] 8067442: Tests using -Xshare:dump does not work with 'make test' Reviewed-by: erikj, dholmes --- make/Main.gmk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/make/Main.gmk b/make/Main.gmk index c3b40e55b15..383b0590bcb 100644 --- a/make/Main.gmk +++ b/make/Main.gmk @@ -257,7 +257,7 @@ ALL_TARGETS += docs-javadoc docs-jvmtidoc test: ($(CD) $(SRC_ROOT)/test && $(MAKE) $(MAKE_ARGS) -j1 -k MAKEFLAGS= \ - JT_HOME=$(JT_HOME) PRODUCT_HOME=$(JDK_OUTPUTDIR) \ + JT_HOME=$(JT_HOME) PRODUCT_HOME=$(JDK_IMAGE_DIR) \ ALT_OUTPUTDIR=$(OUTPUT_ROOT) CONCURRENCY=$(JOBS) $(TEST)) || true test-make: @@ -394,7 +394,7 @@ else docs-jvmtidoc: hotspot - test: exploded-image + test: jimages verify-modules: exploded-image From 7d8f013196c22e20d5908eded9db3dcd6a2edf72 Mon Sep 17 00:00:00 2001 From: Stuart Marks Date: Mon, 15 Dec 2014 17:49:56 -0800 Subject: [PATCH 37/58] 8067631: hgforest.sh mishandles arguments with spaces Reviewed-by: chegar --- common/bin/hgforest.sh | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/common/bin/hgforest.sh b/common/bin/hgforest.sh index 5bf586bc13c..244881ae77c 100644 --- a/common/bin/hgforest.sh +++ b/common/bin/hgforest.sh @@ -106,12 +106,15 @@ if [ ${vflag} = "true" ] ; then echo "# Mercurial command: ${command}" > ${status_output} fi - -# capture command options and arguments (if any) -command_args="${@:-}" +# At this point all command options and args are in "$@". +# Always use "$@" (within double quotes) to avoid breaking +# args with spaces into separate args. if [ ${vflag} = "true" ] ; then - echo "# Mercurial command arguments: ${command_args}" > ${status_output} + echo "# Mercurial command argument count: $#" > ${status_output} + for cmdarg in "$@" ; do + echo "# Mercurial command argument: ${cmdarg}" > ${status_output} + done fi # Clean out the temporary directory that stores the pid files. @@ -205,13 +208,14 @@ if [ "${command}" = "clone" -o "${command}" = "fclone" -o "${command}" = "tclone pull_default_tail=`echo ${pull_default} | sed -e 's@^.*://[^/]*/\(.*\)@\1@'` - if [ -n "${command_args}" ] ; then + if [ $# -gt 0 ] ; then # if there is an "extra sources" path then reparent "extra" repos to that path if [ "x${pull_default}" = "x${pull_default_tail}" ] ; then echo "ERROR: Need initial clone from non-local source" > ${status_output} exit 1 fi - pull_extra="${command_args}/${pull_default_tail}" + # assume that "extra sources" path is the first arg + pull_extra="${1}/${pull_default_tail}" # determine which extra subrepos need to be cloned. for i in ${subrepos_extra} ; do @@ -356,8 +360,8 @@ else (PYTHONUNBUFFERED=true hg${global_opts} clone ${clone_newrepo} ${i}; echo "$?" > ${tmp}/${repopidfile}.pid.rc ) 2>&1 & else # run the command. - echo "cd ${i} && hg${global_opts} ${command} ${command_args}" > ${status_output} - cd ${i} && (PYTHONUNBUFFERED=true hg${global_opts} ${command} ${command_args}; echo "$?" > ${tmp}/${repopidfile}.pid.rc ) 2>&1 & + echo "cd ${i} && hg${global_opts} ${command} ${@}" > ${status_output} + cd ${i} && (PYTHONUNBUFFERED=true hg${global_opts} ${command} "${@}"; echo "$?" > ${tmp}/${repopidfile}.pid.rc ) 2>&1 & fi echo $! > ${tmp}/${repopidfile}.pid From f73717b021d2fa14c4de1a4f5e1f69baa45bb15a Mon Sep 17 00:00:00 2001 From: Athijegannathan Sundararajan Date: Tue, 16 Dec 2014 14:06:32 +0530 Subject: [PATCH 38/58] 8067636: ant javadoc target is broken Reviewed-by: hannesw, lagergren --- nashorn/make/build.xml | 2 +- nashorn/make/project.properties | 2 +- nashorn/samples/browser_dom.js | 1 - nashorn/samples/time_color.fx | 89 +++++++++++++++++++ .../codegen/OptimisticTypesPersistence.java | 3 +- .../internal/runtime/CodeInstaller.java | 2 +- .../jdk/nashorn/internal/runtime/JSType.java | 4 +- .../internal/runtime/StoredScript.java | 2 +- .../internal/runtime/arrays/ArrayData.java | 2 +- 9 files changed, 98 insertions(+), 9 deletions(-) create mode 100644 nashorn/samples/time_color.fx diff --git a/nashorn/make/build.xml b/nashorn/make/build.xml index 732ac5fa0c2..8bdaf6071f3 100644 --- a/nashorn/make/build.xml +++ b/nashorn/make/build.xml @@ -209,7 +209,7 @@ - diff --git a/nashorn/make/project.properties b/nashorn/make/project.properties index 7180163a67e..a6a0b54a44b 100644 --- a/nashorn/make/project.properties +++ b/nashorn/make/project.properties @@ -24,7 +24,7 @@ application.title=nashorn # location of JDK embedded ASM sources -jdk.asm.src.dir=../jdk/src/share/classes/jdk/internal/org/objectweb/asm +jdk.asm.src.dir=../jdk/src/java.base/share/classes/jdk/internal/org/objectweb/asm # source and target levels build.compiler=modern diff --git a/nashorn/samples/browser_dom.js b/nashorn/samples/browser_dom.js index 37ce98d1e65..789801fc3f9 100644 --- a/nashorn/samples/browser_dom.js +++ b/nashorn/samples/browser_dom.js @@ -40,7 +40,6 @@ if (!$OPTIONS._fx) { var ChangeListener = Java.type("javafx.beans.value.ChangeListener"); var Scene = Java.type("javafx.scene.Scene"); var WebView = Java.type("javafx.scene.web.WebView"); -var EventListener = Java.type("org.w3c.dom.events.EventListener"); // JavaFX start method function start(stage) { diff --git a/nashorn/samples/time_color.fx b/nashorn/samples/time_color.fx new file mode 100644 index 00000000000..f4b1155d6e6 --- /dev/null +++ b/nashorn/samples/time_color.fx @@ -0,0 +1,89 @@ +#// Usage: jjs -fx time_color.js [-- true/false] + +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// A simple javafx program that changes background color +// of scene based on current time value (once per sec). +// inspired by http://whatcolourisit.scn9a.org/ + +if (!$OPTIONS._fx) { + print("Usage: jjs -fx time_color.js"); + print(" jjs -fx time_color.js -- true"); + exit(1); +} + +// JavaFX classes used +var Color = Java.type("javafx.scene.paint.Color"); +var Group = Java.type("javafx.scene.Group"); +var Label = Java.type("javafx.scene.control.Label"); +var Platform = Java.type("javafx.application.Platform"); +var Scene = Java.type("javafx.scene.Scene"); +var Timer = Java.type("java.util.Timer"); + +// execute function periodically once per given time in millisec +function setInterval(func, ms) { + // New timer, run as daemon so the application can quit + var timer = new Timer("setInterval", true); + timer.schedule(function() Platform.runLater(func), ms, ms); + return timer; +} + +// do you want to flip hour/min/sec for RGB? +var flip = arguments.length > 0? "true".equals(arguments[0]) : false; + +// JavaFX start method +function start(stage) { + start.title = "Time Color"; + var root = new Group(); + var label = new Label("time"); + label.textFill = Color.WHITE; + root.children.add(label); + stage.scene = new Scene(root, 700, 500); + + setInterval(function() { + var d = new Date(); + var hours = d.getHours(); + var mins = d.getMinutes(); + var secs = d.getSeconds(); + + if (hours < 10) hours = "0" + hours; + if (mins < 10) mins = "0" + mins; + if (secs < 10) secs = "0" + secs; + + var hex = flip? + "#" + secs + mins + hours : "#" + hours + mins + secs; + label.text = "Color: " + hex; + stage.scene.fill = Color.web(hex); + }, 1000); + + stage.show(); +} diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java index 5cbed1bece2..98a0c15be72 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java @@ -67,7 +67,7 @@ import jdk.nashorn.internal.runtime.logging.DebugLogger; import jdk.nashorn.internal.runtime.options.Options; /** - * Static utility that encapsulates persistence of type information for functions compiled with optimistic + *

Static utility that encapsulates persistence of type information for functions compiled with optimistic * typing. With this feature enabled, when a JavaScript function is recompiled because it gets deoptimized, * the type information for deoptimization is stored in a cache file. If the same function is compiled in a * subsequent JVM invocation, the type information is used for initial compilation, thus allowing the system @@ -83,6 +83,7 @@ import jdk.nashorn.internal.runtime.options.Options; * {@code nashorn.typeInfo.cleanupDelaySeconds} system property. You can also specify the word * {@code unlimited} as the value for {@code nashorn.typeInfo.maxFiles} in which case the type info cache is * allowed to grow without limits. + *

*/ public final class OptimisticTypesPersistence { // Default is 0, for disabling the feature when not specified. A reasonable default when enabled is diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CodeInstaller.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CodeInstaller.java index e2b5afe8b3e..49b5b0e6b6f 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CodeInstaller.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/CodeInstaller.java @@ -86,7 +86,7 @@ public interface CodeInstaller { * @param source the script source * @param mainClassName the main class name * @param classBytes map of class names to class bytes - * @param initializers compilation id -> FunctionInitializer map + * @param initializers compilation id -> FunctionInitializer map * @param constants constants array * @param compilationId compilation id */ diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java index 7e54b1e2257..e2c93db4b47 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java @@ -180,10 +180,10 @@ public enum JSType { /** Div exact wrapper for potentially integer division that turns into float point */ public static final Call DIV_EXACT_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "divExact", long.class, long.class, long.class, int.class); - /** Div zero wrapper for long division that handles (0/0) >>> 0 == 0 */ + /** Div zero wrapper for long division that handles (0/0) >>> 0 == 0 */ public static final Call DIV_ZERO_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "divZero", long.class, long.class, long.class); - /** Mod zero wrapper for long division that handles (0%0) >>> 0 == 0 */ + /** Mod zero wrapper for long division that handles (0%0) >>> 0 == 0 */ public static final Call REM_ZERO_LONG = staticCall(JSTYPE_LOOKUP, JSType.class, "remZero", long.class, long.class, long.class); /** Mod exact wrapper for potentially integer remainders that turns into float point */ diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/StoredScript.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/StoredScript.java index 14a0ced0c05..b36cec8b9d1 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/StoredScript.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/StoredScript.java @@ -58,7 +58,7 @@ public final class StoredScript implements Serializable { * @param compilationId compilation id * @param mainClassName main class name * @param classBytes map of class names to class bytes - * @param initializers initializer map, id -> FunctionInitializer + * @param initializers initializer map, id -> FunctionInitializer * @param constants constants array */ public StoredScript(final int compilationId, final String mainClassName, final Map classBytes, final Map initializers, final Object[] constants) { diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ArrayData.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ArrayData.java index f0a8c7a246b..ade9dce91b4 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ArrayData.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ArrayData.java @@ -275,7 +275,7 @@ public abstract class ArrayData { /** * Align an array size up to the nearest array chunk size * @param size size required - * @return size given, always >= size + * @return size given, always >= size */ protected final static int alignUp(final int size) { return size + CHUNK_SIZE - 1 & ~(CHUNK_SIZE - 1); From eb3798a14a3f8cf3ace37d598da011c85115297d Mon Sep 17 00:00:00 2001 From: Maurizio Cimadamore Date: Tue, 16 Dec 2014 13:44:22 +0000 Subject: [PATCH 39/58] 8067663: Add bugId to tests that have been modified as part of JDK-8064365 Add missing bug id to modified tests Reviewed-by: jlahoda --- .../test/tools/javac/generics/diamond/6939780/T6939780.java | 2 +- .../test/tools/javac/generics/diamond/7002837/T7002837.java | 2 +- langtools/test/tools/javac/lambda/LambdaConv18.java | 2 +- .../test/tools/javac/lambda/speculative/DiamondFinder.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/langtools/test/tools/javac/generics/diamond/6939780/T6939780.java b/langtools/test/tools/javac/generics/diamond/6939780/T6939780.java index d6d9bf3b776..6d7ef0da676 100644 --- a/langtools/test/tools/javac/generics/diamond/6939780/T6939780.java +++ b/langtools/test/tools/javac/generics/diamond/6939780/T6939780.java @@ -1,6 +1,6 @@ /* * @test /nodynamiccopyright/ - * @bug 6939780 7020044 8009459 8021338 + * @bug 6939780 7020044 8009459 8021338 8064365 * * @summary add a warning to detect diamond sites * @author mcimadamore diff --git a/langtools/test/tools/javac/generics/diamond/7002837/T7002837.java b/langtools/test/tools/javac/generics/diamond/7002837/T7002837.java index a75c357b91c..d5ff88dd858 100644 --- a/langtools/test/tools/javac/generics/diamond/7002837/T7002837.java +++ b/langtools/test/tools/javac/generics/diamond/7002837/T7002837.java @@ -1,6 +1,6 @@ /* * @test /nodynamiccopyright/ - * @bug 7002837 + * @bug 7002837 8064365 * * @summary Diamond: javac generates diamond inference errors when in 'finder' mode * @author mcimadamore diff --git a/langtools/test/tools/javac/lambda/LambdaConv18.java b/langtools/test/tools/javac/lambda/LambdaConv18.java index 540e9569974..3a8efb206fc 100644 --- a/langtools/test/tools/javac/lambda/LambdaConv18.java +++ b/langtools/test/tools/javac/lambda/LambdaConv18.java @@ -1,6 +1,6 @@ /* * @test /nodynamiccopyright/ - * @bug 8003280 + * @bug 8003280 8064365 * @summary Add lambda tests * simple test for lambda candidate check * @compile/fail/ref=LambdaConv18.out -XDrawDiagnostics -XDfind=lambda LambdaConv18.java diff --git a/langtools/test/tools/javac/lambda/speculative/DiamondFinder.java b/langtools/test/tools/javac/lambda/speculative/DiamondFinder.java index e8e2185b62b..38cd0266ef3 100644 --- a/langtools/test/tools/javac/lambda/speculative/DiamondFinder.java +++ b/langtools/test/tools/javac/lambda/speculative/DiamondFinder.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8003280 + * @bug 8003280 8064365 * @summary Add lambda tests * spurious crashes when running in 'diamond finder' mode * @compile -XDfind=diamond DiamondFinder.java From 36816b6c0a51a32a0a5bccffe5e51133dfec122e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?= Date: Tue, 16 Dec 2014 17:02:54 +0100 Subject: [PATCH 40/58] 8066226: Fuzzing bug: parameter counts differ in TypeConverterFactory Reviewed-by: attila, sundar --- .../runtime/linker/PrimitiveLookup.java | 116 +++++++++++---- nashorn/test/script/basic/JDK-8066226.js | 132 ++++++++++++++++++ .../test/script/basic/JDK-8066226.js.EXPECTED | 104 ++++++++++++++ 3 files changed, 327 insertions(+), 25 deletions(-) create mode 100644 nashorn/test/script/basic/JDK-8066226.js create mode 100644 nashorn/test/script/basic/JDK-8066226.js.EXPECTED diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java index 2a536bbf0a3..6c2f8854608 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java @@ -26,17 +26,23 @@ package jdk.nashorn.internal.runtime.linker; import static jdk.nashorn.internal.lookup.Lookup.MH; +import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; + import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.invoke.SwitchPoint; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; +import jdk.internal.dynalink.support.CallSiteDescriptorFactory; import jdk.internal.dynalink.support.Guards; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.FindProperty; import jdk.nashorn.internal.runtime.GlobalConstants; +import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptObject; +import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.UserAccessorProperty; /** @@ -46,6 +52,11 @@ import jdk.nashorn.internal.runtime.UserAccessorProperty; */ public final class PrimitiveLookup { + /** Method handle to link setters on primitive base. See ES5 8.7.2. */ + private static final MethodHandle PRIMITIVE_SETTER = findOwnMH("primitiveSetter", + MH.type(void.class, ScriptObject.class, Object.class, Object.class, boolean.class, Object.class)); + + private PrimitiveLookup() { } @@ -87,40 +98,58 @@ public final class PrimitiveLookup { final ScriptObject wrappedReceiver, final MethodHandle wrapFilter, final MethodHandle protoFilter) { final CallSiteDescriptor desc = request.getCallSiteDescriptor(); + final String name; + final FindProperty find; - //checks whether the property name is hard-coded in the call-site (i.e. a getProp vs a getElem, or setProp vs setElem) - //if it is we can make assumptions on the property: that if it is not defined on primitive wrapper itself it never will be. - //so in that case we can skip creation of primitive wrapper and start our search with the prototype. if (desc.getNameTokenCount() > 2) { - final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); - final FindProperty find = wrappedReceiver.findProperty(name, true); + name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND); + find = wrappedReceiver.findProperty(name, true); + } else { + name = null; + find = null; + } - if (find == null) { - // Give up early, give chance to BeanLinker and NashornBottomLinker to deal with it. - return null; - } + final String firstOp = CallSiteDescriptorFactory.tokenizeOperators(desc).get(0); - final SwitchPoint sp = find.getProperty().getBuiltinSwitchPoint(); //can use this instead of proto filter - if (sp instanceof Context.BuiltinSwitchPoint && !sp.hasBeenInvalidated()) { - return new GuardedInvocation(GlobalConstants.staticConstantGetter(find.getObjectValue()), guard, sp, null); - } + switch (firstOp) { + case "getProp": + case "getElem": + case "getMethod": + //checks whether the property name is hard-coded in the call-site (i.e. a getProp vs a getElem, or setProp vs setElem) + //if it is we can make assumptions on the property: that if it is not defined on primitive wrapper itself it never will be. + //so in that case we can skip creation of primitive wrapper and start our search with the prototype. + if (name != null) { + if (find == null) { + // Give up early, give chance to BeanLinker and NashornBottomLinker to deal with it. + return null; + } - if (find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) { - // If property is found in the prototype object bind the method handle directly to - // the proto filter instead of going through wrapper instantiation below. - final ScriptObject proto = wrappedReceiver.getProto(); - final GuardedInvocation link = proto.lookup(desc, request); + final SwitchPoint sp = find.getProperty().getBuiltinSwitchPoint(); //can use this instead of proto filter + if (sp instanceof Context.BuiltinSwitchPoint && !sp.hasBeenInvalidated()) { + return new GuardedInvocation(GlobalConstants.staticConstantGetter(find.getObjectValue()), guard, sp, null); + } - if (link != null) { - final MethodHandle invocation = link.getInvocation(); //this contains the builtin switchpoint + if (find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) { + // If property is found in the prototype object bind the method handle directly to + // the proto filter instead of going through wrapper instantiation below. + final ScriptObject proto = wrappedReceiver.getProto(); + final GuardedInvocation link = proto.lookup(desc, request); - final MethodHandle adaptedInvocation = MH.asType(invocation, invocation.type().changeParameterType(0, Object.class)); - final MethodHandle method = MH.filterArguments(adaptedInvocation, 0, protoFilter); - final MethodHandle protoGuard = MH.filterArguments(link.getGuard(), 0, protoFilter); - - return new GuardedInvocation(method, NashornGuards.combineGuards(guard, protoGuard)); + if (link != null) { + final MethodHandle invocation = link.getInvocation(); //this contains the builtin switchpoint + final MethodHandle adaptedInvocation = MH.asType(invocation, invocation.type().changeParameterType(0, Object.class)); + final MethodHandle method = MH.filterArguments(adaptedInvocation, 0, protoFilter); + final MethodHandle protoGuard = MH.filterArguments(link.getGuard(), 0, protoFilter); + return new GuardedInvocation(method, NashornGuards.combineGuards(guard, protoGuard)); + } } } + break; + case "setProp": + case "setElem": + return getPrimitiveSetter(name, guard, wrapFilter, NashornCallSiteDescriptor.isStrict(desc)); + default: + break; } final GuardedInvocation link = wrappedReceiver.lookup(desc, request); @@ -138,4 +167,41 @@ public final class PrimitiveLookup { return null; } + + private static GuardedInvocation getPrimitiveSetter(final String name, final MethodHandle guard, + final MethodHandle wrapFilter, final boolean isStrict) { + MethodHandle filter = MH.asType(wrapFilter, wrapFilter.type().changeReturnType(ScriptObject.class)); + final MethodHandle target; + + if (name == null) { + filter = MH.dropArguments(filter, 1, Object.class, Object.class); + target = MH.insertArguments(PRIMITIVE_SETTER, 3, isStrict); + } else { + filter = MH.dropArguments(filter, 1, Object.class); + target = MH.insertArguments(PRIMITIVE_SETTER, 2, name, isStrict); + } + + return new GuardedInvocation(MH.foldArguments(target, filter), guard); + } + + + @SuppressWarnings("unused") + private static void primitiveSetter(final ScriptObject wrappedSelf, final Object self, final Object key, + final boolean strict, final Object value) { + // See ES5.1 8.7.2 PutValue (V, W) + final String name = JSType.toString(key); + final FindProperty find = wrappedSelf.findProperty(name, true); + if (find == null || !(find.getProperty() instanceof UserAccessorProperty) || !find.getProperty().isWritable()) { + if (strict) { + throw typeError("property.not.writable", name, ScriptRuntime.safeToString(self)); + } + return; + } + // property found and is a UserAccessorProperty + find.setValue(value, strict); + } + + private static MethodHandle findOwnMH(final String name, final MethodType type) { + return MH.findStatic(MethodHandles.lookup(), PrimitiveLookup.class, name, type); + } } diff --git a/nashorn/test/script/basic/JDK-8066226.js b/nashorn/test/script/basic/JDK-8066226.js new file mode 100644 index 00000000000..7c43da9c351 --- /dev/null +++ b/nashorn/test/script/basic/JDK-8066226.js @@ -0,0 +1,132 @@ +/* + * 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. + */ + +/** + * + JDK-8066226: Fuzzing bug: parameter counts differ in TypeConverterFactory + * + * @test + * @run + */ + +Object.defineProperty(Object.prototype, "accessor", { + set: function(value) { + print("Setting accessor on " + this + " to " + value); + } +}); + +Object.defineProperty(Object.prototype, "getterOnly", { + get: function() { + return 1; + } +}); + +function set(o) { + print("set(" + o + ")"); + o.foo = 1; + o.constructor = 1; + o.accessor = 1; + o.getterOnly = 1; + print(); +} + +function setStrict(o) { + "use strict"; + print("setStrict(" + o + ")") + try { + o.foo = 1; + } catch (e) { + print(e); + } + try { + o.constructor = 1; + } catch (e) { + print(e); + } + try { + o.accessor = 1; + } catch (e) { + print(e); + } + try { + o.getterOnly = 1; + } catch (e) { + print(e); + } + print(); +} + +function setAttr(o, id) { + print("setAttr(" + o + ", " + id + ")") + o[id] = 1; + print(); +} + +function setAttrStrict(o, id) { + "use strict"; + print("setAttrStrict(" + o + ", " + id + ")") + try { + o[id] = 1; + } catch (e) { + print(e); + } + print(); +} + +set(1); +set("str"); +set(true); +set({}); +set([]); + +setStrict(1); +setStrict("str"); +setStrict(true); +setStrict({}); +setStrict([]); + +setAttr(1, "foo"); +setAttr(1, "constructor"); +setAttr(1, "accessor"); +setAttr(1, "getterOnly"); +setAttr("str", "foo"); +setAttr("str", "constructor"); +setAttr("str", "accessor"); +setAttr("str", "getterOnly"); +setAttr(true, "foo"); +setAttr(true, "constructor"); +setAttr(true, "accessor"); +setAttr(true, "getterOnly"); + +setAttrStrict(1, "foo"); +setAttrStrict(1, "constructor"); +setAttrStrict(1, "accessor"); +setAttrStrict(1, "getterOnly"); +setAttrStrict("str", "foo"); +setAttrStrict("str", "constructor"); +setAttrStrict("str", "accessor"); +setAttrStrict("str", "getterOnly"); +setAttrStrict(true, "foo"); +setAttrStrict(true, "constructor"); +setAttrStrict(true, "accessor"); +setAttrStrict(true, "getterOnly"); diff --git a/nashorn/test/script/basic/JDK-8066226.js.EXPECTED b/nashorn/test/script/basic/JDK-8066226.js.EXPECTED new file mode 100644 index 00000000000..7c6c3e1d62e --- /dev/null +++ b/nashorn/test/script/basic/JDK-8066226.js.EXPECTED @@ -0,0 +1,104 @@ +set(1) +Setting accessor on 1 to 1 + +set(str) +Setting accessor on str to 1 + +set(true) +Setting accessor on true to 1 + +set([object Object]) +Setting accessor on [object Object] to 1 + +set() +Setting accessor on to 1 + +setStrict(1) +TypeError: "foo" is not a writable property of 1 +TypeError: "constructor" is not a writable property of 1 +Setting accessor on 1 to 1 +TypeError: Cannot set property "getterOnly" of [object Object] that has only a getter + +setStrict(str) +TypeError: "foo" is not a writable property of str +TypeError: "constructor" is not a writable property of str +Setting accessor on str to 1 +TypeError: Cannot set property "getterOnly" of [object Object] that has only a getter + +setStrict(true) +TypeError: "foo" is not a writable property of true +TypeError: "constructor" is not a writable property of true +Setting accessor on true to 1 +TypeError: Cannot set property "getterOnly" of [object Object] that has only a getter + +setStrict([object Object]) +Setting accessor on [object Object] to 1 +TypeError: Cannot set property "getterOnly" of [object Object] that has only a getter + +setStrict() +Setting accessor on to 1 +TypeError: Cannot set property "getterOnly" of [object Array] that has only a getter + +setAttr(1, foo) + +setAttr(1, constructor) + +setAttr(1, accessor) +Setting accessor on 1 to 1 + +setAttr(1, getterOnly) + +setAttr(str, foo) + +setAttr(str, constructor) + +setAttr(str, accessor) +Setting accessor on str to 1 + +setAttr(str, getterOnly) + +setAttr(true, foo) + +setAttr(true, constructor) + +setAttr(true, accessor) +Setting accessor on true to 1 + +setAttr(true, getterOnly) + +setAttrStrict(1, foo) +TypeError: "foo" is not a writable property of 1 + +setAttrStrict(1, constructor) +TypeError: "constructor" is not a writable property of 1 + +setAttrStrict(1, accessor) +Setting accessor on 1 to 1 + +setAttrStrict(1, getterOnly) +TypeError: Cannot set property "getterOnly" of [object Object] that has only a getter + +setAttrStrict(str, foo) +TypeError: "foo" is not a writable property of str + +setAttrStrict(str, constructor) +TypeError: "constructor" is not a writable property of str + +setAttrStrict(str, accessor) +Setting accessor on str to 1 + +setAttrStrict(str, getterOnly) +TypeError: Cannot set property "getterOnly" of [object Object] that has only a getter + +setAttrStrict(true, foo) +TypeError: "foo" is not a writable property of true + +setAttrStrict(true, constructor) +TypeError: "constructor" is not a writable property of true + +setAttrStrict(true, accessor) +Setting accessor on true to 1 + +setAttrStrict(true, getterOnly) +TypeError: Cannot set property "getterOnly" of [object Object] that has only a getter + From d6ea52ffc7e1df7a7190527131cafe3931634515 Mon Sep 17 00:00:00 2001 From: Athijegannathan Sundararajan Date: Wed, 17 Dec 2014 17:15:14 +0530 Subject: [PATCH 41/58] 8067777: NetBeans nashorn debug target is broken. Nashorn source directory config. is wrong Reviewed-by: lagergren, attila --- nashorn/make/nbproject/ide-targets.xml | 3 +- nashorn/make/nbproject/project.xml | 38 +++++++++++++------------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/nashorn/make/nbproject/ide-targets.xml b/nashorn/make/nbproject/ide-targets.xml index d1e8135f101..7237f6d4120 100644 --- a/nashorn/make/nbproject/ide-targets.xml +++ b/nashorn/make/nbproject/ide-targets.xml @@ -32,9 +32,8 @@ - + - diff --git a/nashorn/make/nbproject/project.xml b/nashorn/make/nbproject/project.xml index 2fcc644292c..31e29ccf26d 100644 --- a/nashorn/make/nbproject/project.xml +++ b/nashorn/make/nbproject/project.xml @@ -37,10 +37,6 @@ . UTF-8 - - - ../src - ../test/src @@ -49,24 +45,28 @@ ../buildtools/nasgen/src + + + ../src/jdk.scripting.nashorn/share/classes + java ../test/src UTF-8 - - - java - ../src - UTF-8 - java ../buildtools/nasgen/src UTF-8 + + + java + ../src/jdk.scripting.nashorn/share/classes + UTF-8 + @@ -131,14 +131,14 @@ ../test/src - - - ../src - ../buildtools/nasgen/src + + + ../src/jdk.scripting.nashorn/share/classes + build.xml @@ -159,11 +159,7 @@ ../test/src - ../test/lib/testng.jar:../build/classes:../src - 1.7 - - - ../src + ../test/lib/testng.jar:../build/classes:../src/jdk.scripting.nashorn/share/classes 1.7 @@ -171,6 +167,10 @@ ../build/classes:../src 1.7 + + ../src/jdk.scripting.nashorn/share/classes + 1.7 + From 627ce960f5416c0c6b73de68ea1f9fd1e9f604a6 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 17 Dec 2014 12:49:57 +0100 Subject: [PATCH 42/58] 8067422: Lambda method names are unnecessarily unstable Lambda method numbers are now assigned per class for non-serializable lambdas. Reviewed-by: mcimadamore, rfield --- .../sun/tools/javac/comp/LambdaToMethod.java | 3 + ...estNonSerializableLambdaNameStability.java | 100 ++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 langtools/test/tools/javac/lambda/lambdaNaming/TestNonSerializableLambdaNameStability.java diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java index df06e137a9e..840f48e6335 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/LambdaToMethod.java @@ -1176,12 +1176,14 @@ public class LambdaToMethod extends TreeTranslator { @Override public void visitClassDef(JCClassDecl tree) { List prevStack = frameStack; + int prevLambdaCount = lambdaCount; SyntheticMethodNameCounter prevSyntheticMethodNameCounts = syntheticMethodNameCounts; Map prevClinits = clinits; DiagnosticSource prevSource = log.currentSource(); try { log.useSource(tree.sym.sourcefile); + lambdaCount = 0; syntheticMethodNameCounts = new SyntheticMethodNameCounter(); prevClinits = new HashMap<>(); if (tree.sym.owner.kind == MTH) { @@ -1208,6 +1210,7 @@ public class LambdaToMethod extends TreeTranslator { finally { log.useSource(prevSource.getFile()); frameStack = prevStack; + lambdaCount = prevLambdaCount; syntheticMethodNameCounts = prevSyntheticMethodNameCounts; clinits = prevClinits; } diff --git a/langtools/test/tools/javac/lambda/lambdaNaming/TestNonSerializableLambdaNameStability.java b/langtools/test/tools/javac/lambda/lambdaNaming/TestNonSerializableLambdaNameStability.java new file mode 100644 index 00000000000..40380bd55e8 --- /dev/null +++ b/langtools/test/tools/javac/lambda/lambdaNaming/TestNonSerializableLambdaNameStability.java @@ -0,0 +1,100 @@ +/* + * 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 8067422 + * @summary Check that the lambda names are not unnecessarily unstable + * @library /tools/lib + * @build ToolBox + * @run main TestNonSerializableLambdaNameStability + */ + +import com.sun.tools.classfile.ClassFile; +import com.sun.tools.classfile.Method; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import javax.tools.StandardLocation; + +public class TestNonSerializableLambdaNameStability { + + public static void main(String... args) throws Exception { + new TestNonSerializableLambdaNameStability().run(); + } + + String lambdaSource = "public class L%d {\n" + + " public static class A {\n" + + " private Runnable r = () -> { };\n" + + " }\n" + + " public static class B {\n" + + " private Runnable r = () -> { };\n" + + " }\n" + + " private Runnable r = () -> { };\n" + + "}\n"; + + String expectedLambdaMethodName = "lambda$new$0"; + + void run() throws Exception { + List sources = new ArrayList<>(); + + for (int i = 0; i < 3; i++) { + sources.add(String.format(lambdaSource, i)); + } + + ToolBox tb = new ToolBox(); + + try (ToolBox.MemoryFileManager fm = new ToolBox.MemoryFileManager()) { + tb.new JavacTask() + .sources(sources.toArray(new String[sources.size()])) + .fileManager(fm) + .run(); + + for (String file : fm.files.get(StandardLocation.CLASS_OUTPUT).keySet()) { + byte[] fileBytes = fm.getFileBytes(StandardLocation.CLASS_OUTPUT, file); + try (InputStream in = new ByteArrayInputStream(fileBytes)) { + boolean foundLambdaMethod = false; + ClassFile cf = ClassFile.read(in); + StringBuilder seenMethods = new StringBuilder(); + String sep = ""; + for (Method m : cf.methods) { + String methodName = m.getName(cf.constant_pool); + if (expectedLambdaMethodName.equals(methodName)) { + foundLambdaMethod = true; + break; + } + seenMethods.append(sep); + seenMethods.append(methodName); + sep = ", "; + } + + if (!foundLambdaMethod) { + throw new AbstractMethodError("Did not find the lambda method, " + + "found methods: " + seenMethods.toString()); + } + } + } + } + } +} From 41070244c96174fbf3e08dc4306c031bb6debbbb Mon Sep 17 00:00:00 2001 From: Maurizio Cimadamore Date: Wed, 17 Dec 2014 16:47:56 +0000 Subject: [PATCH 43/58] 8067792: Javac crashes in finder mode with nested implicit lambdas Revert tree changes occurred in Attr before running the analyzer Reviewed-by: jlahoda --- .../com/sun/tools/javac/comp/Analyzer.java | 16 ++++++++++++++++ .../tools/javac/lambda/8067792/T8067792.java | 19 +++++++++++++++++++ .../tools/javac/lambda/8067792/T8067792.out | 4 ++++ 3 files changed, 39 insertions(+) create mode 100644 langtools/test/tools/javac/lambda/8067792/T8067792.java create mode 100644 langtools/test/tools/javac/lambda/8067792/T8067792.out diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java index 13fe2e26f57..1eb1ab48808 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java @@ -25,6 +25,7 @@ package com.sun.tools.javac.comp; +import com.sun.source.tree.LambdaExpressionTree; import com.sun.tools.javac.code.Source; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.code.Types; @@ -36,6 +37,7 @@ import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop; import com.sun.tools.javac.tree.JCTree.JCForLoop; import com.sun.tools.javac.tree.JCTree.JCIf; import com.sun.tools.javac.tree.JCTree.JCLambda; +import com.sun.tools.javac.tree.JCTree.JCLambda.ParameterKind; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCNewClass; @@ -50,6 +52,8 @@ import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.tree.TreeMaker; import com.sun.tools.javac.tree.TreeScanner; import com.sun.tools.javac.util.Context; +import com.sun.tools.javac.util.DefinedBy; +import com.sun.tools.javac.util.DefinedBy.Api; import com.sun.tools.javac.util.Filter; import com.sun.tools.javac.util.JCDiagnostic; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType; @@ -489,5 +493,17 @@ public class Analyzer { } return newTree; } + + @Override @DefinedBy(Api.COMPILER_TREE) + public JCTree visitLambdaExpression(LambdaExpressionTree node, Void _unused) { + JCLambda oldLambda = (JCLambda)node; + JCLambda newLambda = (JCLambda)super.visitLambdaExpression(node, _unused); + if (oldLambda.paramKind == ParameterKind.IMPLICIT) { + //reset implicit lambda parameters (whose type might have been set during attr) + newLambda.paramKind = ParameterKind.IMPLICIT; + newLambda.params.forEach(p -> p.vartype = null); + } + return newLambda; + } } } diff --git a/langtools/test/tools/javac/lambda/8067792/T8067792.java b/langtools/test/tools/javac/lambda/8067792/T8067792.java new file mode 100644 index 00000000000..a050171991e --- /dev/null +++ b/langtools/test/tools/javac/lambda/8067792/T8067792.java @@ -0,0 +1,19 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8067792 + * @summary Javac crashes in finder mode with nested implicit lambdas + * @compile/fail/ref=T8067792.out -XDrawDiagnostics -Werror -XDfind=lambda T8067792.java + */ + +import java.util.stream.*; +import java.util.*; + +class T8067792 { + void test(Stream> sl) { + Runnable r = new Runnable() { + public void run() { + Stream> constructor = sl.filter(c -> true); + } + }; + } +} diff --git a/langtools/test/tools/javac/lambda/8067792/T8067792.out b/langtools/test/tools/javac/lambda/8067792/T8067792.out new file mode 100644 index 00000000000..718d1539d74 --- /dev/null +++ b/langtools/test/tools/javac/lambda/8067792/T8067792.out @@ -0,0 +1,4 @@ +T8067792.java:13:37: compiler.warn.potential.lambda.found +- compiler.err.warnings.and.werror +1 error +1 warning From f4ab205b3f943780c4cc83259ba8eae5170120fb Mon Sep 17 00:00:00 2001 From: Robert Field Date: Wed, 17 Dec 2014 12:48:04 -0800 Subject: [PATCH 44/58] 8067384: Facilitate extension of the javac parser Reviewed-by: jjg --- .../sun/tools/javac/parser/JavacParser.java | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java index be15a750dd9..c9f2e2d9c01 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java @@ -251,19 +251,19 @@ public class JavacParser implements Parser { * mode = NOPARAMS : no parameters allowed for type * mode = TYPEARG : type argument */ - static final int EXPR = 0x1; - static final int TYPE = 0x2; - static final int NOPARAMS = 0x4; - static final int TYPEARG = 0x8; - static final int DIAMOND = 0x10; + protected static final int EXPR = 0x1; + protected static final int TYPE = 0x2; + protected static final int NOPARAMS = 0x4; + protected static final int TYPEARG = 0x8; + protected static final int DIAMOND = 0x10; /** The current mode. */ - private int mode = 0; + protected int mode = 0; /** The mode of the term that was parsed last. */ - private int lastmode = 0; + protected int lastmode = 0; /* ---------- token management -------------- */ @@ -326,7 +326,7 @@ public class JavacParser implements Parser { /** Skip forward until a suitable stop token is found. */ - private void skip(boolean stopAtImport, boolean stopAtMemberDecl, boolean stopAtIdentifier, boolean stopAtStatement) { + protected void skip(boolean stopAtImport, boolean stopAtMemberDecl, boolean stopAtIdentifier, boolean stopAtStatement) { while (true) { switch (token.kind) { case SEMI: @@ -403,11 +403,11 @@ public class JavacParser implements Parser { } } - private JCErroneous syntaxError(int pos, String key, TokenKind... args) { + protected JCErroneous syntaxError(int pos, String key, TokenKind... args) { return syntaxError(pos, List.nil(), key, args); } - private JCErroneous syntaxError(int pos, List errs, String key, TokenKind... args) { + protected JCErroneous syntaxError(int pos, List errs, String key, TokenKind... args) { setErrorEndPos(pos); JCErroneous err = F.at(pos).Erroneous(errs); reportSyntaxError(err, key, (Object[])args); @@ -427,7 +427,7 @@ public class JavacParser implements Parser { * Report a syntax using the given the position parameter and arguments, * unless one was already reported at the same position. */ - private void reportSyntaxError(int pos, String key, Object... args) { + protected void reportSyntaxError(int pos, String key, Object... args) { JCDiagnostic.DiagnosticPosition diag = new JCDiagnostic.SimpleDiagnosticPosition(pos); reportSyntaxError(diag, key, args); } @@ -436,7 +436,7 @@ public class JavacParser implements Parser { * Report a syntax error using the given DiagnosticPosition object and * arguments, unless one was already reported at the same position. */ - private void reportSyntaxError(JCDiagnostic.DiagnosticPosition diagPos, String key, Object... args) { + protected void reportSyntaxError(JCDiagnostic.DiagnosticPosition diagPos, String key, Object... args) { int pos = diagPos.getPreferredPosition(); if (pos > S.errPos() || pos == Position.NOPOS) { if (token.kind == EOF) { @@ -459,14 +459,14 @@ public class JavacParser implements Parser { /** Generate a syntax error at current position unless one was already * reported at the same position. */ - private JCErroneous syntaxError(String key) { + protected JCErroneous syntaxError(String key) { return syntaxError(token.pos, key); } /** Generate a syntax error at current position unless one was * already reported at the same position. */ - private JCErroneous syntaxError(String key, TokenKind arg) { + protected JCErroneous syntaxError(String key, TokenKind arg) { return syntaxError(token.pos, key, arg); } @@ -500,7 +500,7 @@ public class JavacParser implements Parser { } /** Diagnose a modifier flag from the set, if any. */ - void checkNoMods(long mods) { + protected void checkNoMods(long mods) { if (mods != 0) { long lowestMod = mods & -mods; error(token.pos, "mod.not.allowed.here", @@ -521,7 +521,7 @@ public class JavacParser implements Parser { * @param tree The tree to be used as index in the hashtable * @param dc The doc comment to associate with the tree, or null. */ - void attach(JCTree tree, Comment dc) { + protected void attach(JCTree tree, Comment dc) { if (keepDocComments && dc != null) { // System.out.println("doc comment = ");System.out.println(dc);//DEBUG docComments.putComment(tree, dc); @@ -530,19 +530,19 @@ public class JavacParser implements Parser { /* -------- source positions ------- */ - private void setErrorEndPos(int errPos) { + protected void setErrorEndPos(int errPos) { endPosTable.setErrorEndPos(errPos); } - private void storeEnd(JCTree tree, int endpos) { + protected void storeEnd(JCTree tree, int endpos) { endPosTable.storeEnd(tree, endpos); } - private T to(T t) { + protected T to(T t) { return endPosTable.to(t); } - private T toP(T t) { + protected T toP(T t) { return endPosTable.toP(t); } @@ -574,7 +574,7 @@ public class JavacParser implements Parser { /** * Ident = IDENTIFIER */ - Name ident() { + protected Name ident() { if (token.kind == IDENTIFIER) { Name name = token.name(); nextToken(); @@ -789,7 +789,7 @@ public class JavacParser implements Parser { return term(TYPE); } - JCExpression term(int newmode) { + protected JCExpression term(int newmode) { int prevmode = mode; mode = newmode; JCExpression t = term(); @@ -1669,7 +1669,7 @@ public class JavacParser implements Parser { } /** Accepts all identifier-like tokens */ - Filter LAX_IDENTIFIER = new Filter() { + protected Filter LAX_IDENTIFIER = new Filter() { public boolean accepts(TokenKind t) { return t == IDENTIFIER || t == UNDERSCORE || t == ASSERT || t == ENUM; } @@ -2408,7 +2408,7 @@ public class JavacParser implements Parser { * | ASSERT Expression [ ":" Expression ] ";" * | ";" */ - JCStatement parseSimpleStatement() { + public JCStatement parseSimpleStatement() { int pos = token.pos; switch (token.kind) { case LBRACE: @@ -2706,7 +2706,7 @@ public class JavacParser implements Parser { * * @param kind Whether to parse an ANNOTATION or TYPE_ANNOTATION */ - List annotationsOpt(Tag kind) { + protected List annotationsOpt(Tag kind) { if (token.kind != MONKEYS_AT) return List.nil(); // optimization ListBuffer buf = new ListBuffer<>(); int prevmode = mode; @@ -2732,7 +2732,7 @@ public class JavacParser implements Parser { * | NATIVE | SYNCHRONIZED | TRANSIENT | VOLATILE | "@" * | "@" Annotation */ - JCModifiers modifiersOpt() { + protected JCModifiers modifiersOpt() { return modifiersOpt(null); } protected JCModifiers modifiersOpt(JCModifiers partial) { @@ -2914,7 +2914,7 @@ public class JavacParser implements Parser { * @param reqInit Is an initializer always required? * @param dc The documentation comment for the variable declarations, or null. */ - > T variableDeclaratorsRest(int pos, + protected > T variableDeclaratorsRest(int pos, JCModifiers mods, JCExpression type, Name name, @@ -3117,7 +3117,7 @@ public class JavacParser implements Parser { /** ImportDeclaration = IMPORT [ STATIC ] Ident { "." Ident } [ "." "*" ] ";" */ - JCTree importDeclaration() { + protected JCTree importDeclaration() { int pos = token.pos; nextToken(); boolean importStatic = false; @@ -3159,7 +3159,7 @@ public class JavacParser implements Parser { * @param mods Any modifiers starting the class or interface declaration * @param dc The documentation comment for the class, or null. */ - JCStatement classOrInterfaceOrEnumDeclaration(JCModifiers mods, Comment dc) { + protected JCStatement classOrInterfaceOrEnumDeclaration(JCModifiers mods, Comment dc) { if (token.kind == CLASS) { return classDeclaration(mods, dc); } else if (token.kind == INTERFACE) { @@ -3569,7 +3569,7 @@ public class JavacParser implements Parser { * TypeParametersOpt = ["<" TypeParameter {"," TypeParameter} ">"] * } */ - List typeParametersOpt() { + protected List typeParametersOpt() { if (token.kind == LT) { ListBuffer typarams = new ListBuffer<>(); nextToken(); @@ -4004,7 +4004,7 @@ public class JavacParser implements Parser { allowTypeAnnotations = true; } } - void checkAnnotationsAfterTypeParams(int pos) { + protected void checkAnnotationsAfterTypeParams(int pos) { if (!allowAnnotationsAfterTypeParams) { log.error(pos, "annotations.after.type.params.not.supported.in.source", source.name); allowAnnotationsAfterTypeParams = true; @@ -4092,7 +4092,7 @@ public class JavacParser implements Parser { /** * Store the last error position. */ - protected int errorEndPos = Position.NOPOS; + public int errorEndPos = Position.NOPOS; public AbstractEndPosTable(JavacParser parser) { this.parser = parser; @@ -4119,13 +4119,13 @@ public class JavacParser implements Parser { * will be set only if it is greater than the last stored error position. * @param errPos The error position */ - protected void setErrorEndPos(int errPos) { + public void setErrorEndPos(int errPos) { if (errPos > errorEndPos) { errorEndPos = errPos; } } - protected void setParser(JavacParser parser) { + public void setParser(JavacParser parser) { this.parser = parser; } } From f8731d01ac80b9f0da9c5ab86a24d8ae77364bf5 Mon Sep 17 00:00:00 2001 From: Mandy Chung Date: Wed, 17 Dec 2014 16:36:21 -0800 Subject: [PATCH 45/58] 8067829: Remove setting -bootclasspath $(JDK_OUTPUTDIR)/classes from Javadoc.gmk Reviewed-by: erikj --- make/Javadoc.gmk | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/make/Javadoc.gmk b/make/Javadoc.gmk index 94078328315..a12d0df7cee 100644 --- a/make/Javadoc.gmk +++ b/make/Javadoc.gmk @@ -53,8 +53,7 @@ BUILD_NUMBER=$(JDK_BUILD_NUMBER) JAVADOC_CMD = $(JAVA) \ -Djava.awt.headless=true \ - $(NEW_JAVADOC) \ - -bootclasspath $(JDK_OUTPUTDIR)/classes + $(NEW_JAVADOC) # Copyright year for beginning of Java and some of the apis # (Needed when creating the javadocs) From ae38f756256620e1eac69b1e472ca28ad8b4c402 Mon Sep 17 00:00:00 2001 From: Athijegannathan Sundararajan Date: Thu, 18 Dec 2014 16:33:33 +0530 Subject: [PATCH 46/58] 8067854: bound java static method throws NPE when 'null' is used for this argument Reviewed-by: attila, hannesw --- .../runtime/linker/BoundCallableLinker.java | 2 +- nashorn/test/script/trusted/JDK-8067854.js | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 nashorn/test/script/trusted/JDK-8067854.js diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java index d52063bf3f5..b8710569e84 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/BoundCallableLinker.java @@ -99,7 +99,7 @@ final class BoundCallableLinker implements TypeBasedGuardingDynamicLinker { MethodType newMethodType = descriptor.getMethodType().changeParameterType(0, callable.getClass()); if (isCall) { // R(callable.class, T1, ...) => R(callable.class, boundThis.class, ...) - newMethodType = newMethodType.changeParameterType(1, boundThis.getClass()); + newMethodType = newMethodType.changeParameterType(1, boundThis == null? Object.class : boundThis.getClass()); } // R(callable.class[, boundThis.class], T2, ...) => R(callable.class[, boundThis.class], boundArg0.class, ..., boundArgn.class, T2, ...) for(int i = boundArgs.length; i-- > 0;) { diff --git a/nashorn/test/script/trusted/JDK-8067854.js b/nashorn/test/script/trusted/JDK-8067854.js new file mode 100644 index 00000000000..487bd77bbfc --- /dev/null +++ b/nashorn/test/script/trusted/JDK-8067854.js @@ -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. + * + * 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. + */ + +/** + * JDK-8067854: bound java static method throws NPE when 'null' is used for this argument + * + * @test + * @run + */ + +getProp = java.lang.System.getProperty; + +// bind this and an argument. "null" for this as getProperty is a +// static method of java.lang.System +getHome = Function.prototype.bind.call(getProp, null, "java.home"); + +if (getHome() != getProp("java.home")) { + fail("getHome() failed to get java.home"); +} From 88c5d41ddc3e17783335b66d18d49ecf69776496 Mon Sep 17 00:00:00 2001 From: Attila Szegedi Date: Thu, 18 Dec 2014 12:10:10 +0100 Subject: [PATCH 47/58] 8067774: Use a stack of types when calculating local variable types Reviewed-by: lagergren, sundar --- .../codegen/LocalVariableTypesCalculator.java | 385 +++++++++++------- .../jdk/nashorn/internal/ir/BaseNode.java | 3 +- .../jdk/nashorn/internal/ir/BinaryNode.java | 71 ++-- .../jdk/nashorn/internal/ir/CallNode.java | 3 +- .../jdk/nashorn/internal/ir/Expression.java | 23 +- .../jdk/nashorn/internal/ir/FunctionNode.java | 3 +- .../nashorn/internal/ir/GetSplitState.java | 3 +- .../jdk/nashorn/internal/ir/IdentNode.java | 6 +- .../ir/JoinPredecessorExpression.java | 5 +- .../jdk/nashorn/internal/ir/LiteralNode.java | 33 +- .../jdk/nashorn/internal/ir/ObjectNode.java | 3 +- .../jdk/nashorn/internal/ir/RuntimeNode.java | 3 +- .../jdk/nashorn/internal/ir/TernaryNode.java | 5 +- .../jdk/nashorn/internal/ir/UnaryNode.java | 21 +- .../nashorn/internal/ir/debug/JSONWriter.java | 4 +- nashorn/test/script/basic/JDK-8067774.js | 37 ++ .../test/script/basic/JDK-8067774.js.EXPECTED | 1 + 17 files changed, 329 insertions(+), 280 deletions(-) create mode 100644 nashorn/test/script/basic/JDK-8067774.js create mode 100644 nashorn/test/script/basic/JDK-8067774.js.EXPECTED diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java index ac3c29349b0..28cc55104b1 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java @@ -40,21 +40,22 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.AccessNode; -import jdk.nashorn.internal.ir.BaseNode; import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; import jdk.nashorn.internal.ir.BreakNode; import jdk.nashorn.internal.ir.BreakableNode; +import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CatchNode; import jdk.nashorn.internal.ir.ContinueNode; import jdk.nashorn.internal.ir.Expression; +import jdk.nashorn.internal.ir.ExpressionStatement; import jdk.nashorn.internal.ir.ForNode; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.FunctionNode.CompilationState; +import jdk.nashorn.internal.ir.GetSplitState; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; @@ -65,9 +66,11 @@ import jdk.nashorn.internal.ir.LabelNode; import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LexicalContextNode; import jdk.nashorn.internal.ir.LiteralNode; +import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode; import jdk.nashorn.internal.ir.LocalVariableConversion; import jdk.nashorn.internal.ir.LoopNode; import jdk.nashorn.internal.ir.Node; +import jdk.nashorn.internal.ir.ObjectNode; import jdk.nashorn.internal.ir.PropertyNode; import jdk.nashorn.internal.ir.ReturnNode; import jdk.nashorn.internal.ir.RuntimeNode; @@ -82,6 +85,7 @@ import jdk.nashorn.internal.ir.TryNode; import jdk.nashorn.internal.ir.UnaryNode; import jdk.nashorn.internal.ir.VarNode; import jdk.nashorn.internal.ir.WhileNode; +import jdk.nashorn.internal.ir.WithNode; import jdk.nashorn.internal.ir.visitor.NodeVisitor; import jdk.nashorn.internal.parser.TokenType; @@ -131,8 +135,44 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ OBJECT(Type.OBJECT); private final Type type; + private final TypeHolderExpression typeExpression; + private LvarType(final Type type) { this.type = type; + this.typeExpression = new TypeHolderExpression(type); + } + } + + /** + * A bogus Expression subclass that only reports its type. Used to interrogate BinaryNode and UnaryNode about their + * types by creating temporary copies of them and replacing their operands with instances of these. An alternative + * solution would be to add BinaryNode.getType(Type lhsType, Type rhsType) and UnaryNode.getType(Type exprType) + * methods. For the time being though, this is easier to implement and is in fact fairly clean. It does result in + * generation of higher number of temporary short lived nodes, though. + */ + private static class TypeHolderExpression extends Expression { + private static final long serialVersionUID = 1L; + + private final Type type; + + TypeHolderExpression(final Type type) { + super(0L, 0, 0); + this.type = type; + } + + @Override + public Node accept(final NodeVisitor visitor) { + throw new AssertionError(); + } + + @Override + public Type getType() { + return type; + } + + @Override + public void toString(final StringBuilder sb, final boolean printType) { + throw new AssertionError(); } } @@ -359,6 +399,8 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ // allocates a new map. Immutability of maps allows for cheap snapshots by just keeping the reference to the current // value. private Map localVariableTypes = new IdentityHashMap<>(); + // Stack for evaluated expression types. + private final Deque typeStack = new ArrayDeque<>(); // Whether the current point in the AST is reachable code private boolean reachable = true; @@ -375,8 +417,6 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ private final Map identifierLvarTypes = new IdentityHashMap<>(); private final Map symbolConversions = new IdentityHashMap<>(); - private SymbolToType symbolToType = new SymbolToType(); - // Stack of open labels for starts of catch blocks, one for every currently traversed try block; for inserting // control flow edges to them. Note that we currently don't insert actual control flow edges, but instead edges that // help us with type calculations. This means that some operations that can result in an exception being thrown @@ -400,62 +440,56 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ private void doesNotContinueSequentially() { reachable = false; localVariableTypes = Collections.emptyMap(); + assertTypeStackIsEmpty(); } + private boolean pushExpressionType(final Expression expr) { + typeStack.push(toLvarType(expr.getType())); + return false; + } + + @Override + public boolean enterAccessNode(final AccessNode accessNode) { + visitExpression(accessNode.getBase()); + return pushExpressionType(accessNode); + } @Override public boolean enterBinaryNode(final BinaryNode binaryNode) { // NOTE: regardless of operator's lexical associativity, lhs is always evaluated first. final Expression lhs = binaryNode.lhs(); - final boolean isAssignment = binaryNode.isAssignment(); - LvarType lhsTypeOnLoad = null; - if(isAssignment) { - if(lhs instanceof BaseNode) { - ((BaseNode)lhs).getBase().accept(this); - if(lhs instanceof IndexNode) { - ((IndexNode)lhs).getIndex().accept(this); - } else { - assert lhs instanceof AccessNode; - } - } else { - assert lhs instanceof IdentNode; - if(binaryNode.isSelfModifying()) { - final IdentNode ident = ((IdentNode)lhs); - ident.accept(this); - // Self-assignment can cause a change in the type of the variable. For purposes of evaluating - // the type of the operation, we must use its type as it was when it was loaded. If we didn't - // do this, some awkward expressions would end up being calculated incorrectly, e.g. - // "var x; x += x = 0;". In this case we have undefined+int so the result type is double (NaN). - // However, if we used the type of "x" on LHS after we evaluated RHS, we'd see int+int, so the - // result type would be either optimistic int or pessimistic long, which would be wrong. - lhsTypeOnLoad = getLocalVariableTypeIfBytecode(ident.getSymbol()); - } - } + final LvarType lhsType; + if (!(lhs instanceof IdentNode && binaryNode.tokenType() == TokenType.ASSIGN)) { + lhsType = visitExpression(lhs); } else { - lhs.accept(this); + // Can't visit IdentNode on LHS of a simple assignment, as visits imply use, and this is def. + // The type is irrelevant, as only RHS is used to determine the type anyway. + lhsType = LvarType.UNDEFINED; } final boolean isLogical = binaryNode.isLogical(); - assert !(isAssignment && isLogical); // there are no logical assignment operators in JS final Label joinLabel = isLogical ? new Label("") : null; if(isLogical) { jumpToLabel((JoinPredecessor)lhs, joinLabel); } final Expression rhs = binaryNode.rhs(); - rhs.accept(this); + final LvarType rhsType = visitExpression(rhs); if(isLogical) { jumpToLabel((JoinPredecessor)rhs, joinLabel); } joinOnLabel(joinLabel); - if(isAssignment && lhs instanceof IdentNode) { + final LvarType type = toLvarType(binaryNode.setOperands(lhsType.typeExpression, rhsType.typeExpression).getType()); + + if(binaryNode.isAssignment() && lhs instanceof IdentNode) { if(binaryNode.isSelfModifying()) { - onSelfAssignment((IdentNode)lhs, binaryNode, lhsTypeOnLoad); + onSelfAssignment((IdentNode)lhs, type); } else { - onAssignment((IdentNode)lhs, rhs); + onAssignment((IdentNode)lhs, type); } } + typeStack.push(type); return false; } @@ -474,6 +508,17 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ return enterJumpStatement(breakNode); } + @Override + public boolean enterCallNode(final CallNode callNode) { + visitExpression(callNode.getFunction()); + visitExpressions(callNode.getArgs()); + final CallNode.EvalArgs evalArgs = callNode.getEvalArgs(); + if (evalArgs != null) { + visitExpressions(evalArgs.getArgs()); + } + return pushExpressionType(callNode); + } + @Override public boolean enterContinueNode(final ContinueNode continueNode) { return enterJumpStatement(continueNode); @@ -483,6 +528,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ if(!reachable) { return false; } + assertTypeStackIsEmpty(); final BreakableNode target = jump.getTarget(lc); jumpToLabel(jump, jump.getTargetLabel(target), getBreakTargetTypes(target)); doesNotContinueSequentially(); @@ -495,6 +541,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ } private void enterDoWhileLoop(final WhileNode loopNode) { + assertTypeStackIsEmpty(); final JoinPredecessorExpression test = loopNode.getTest(); final Block body = loopNode.getBody(); final Label continueLabel = loopNode.getContinueLabel(); @@ -512,7 +559,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ if(!reachable) { break; } - test.accept(this); + visitExpressionOnEmptyStack(test); jumpToLabel(test, breakLabel); if(isAlwaysFalse(test)) { break; @@ -534,6 +581,45 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ leaveBreakable(loopNode); } + @Override + public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) { + if (reachable) { + visitExpressionOnEmptyStack(expressionStatement.getExpression()); + } + return false; + } + + private void assertTypeStackIsEmpty() { + assert typeStack.isEmpty(); + } + + @Override + protected Node leaveDefault(final Node node) { + assert !(node instanceof Expression); // All expressions were handled + assert !(node instanceof Statement) || typeStack.isEmpty(); // No statements leave with a non-empty stack + return node; + } + + private LvarType visitExpressionOnEmptyStack(final Expression expr) { + assertTypeStackIsEmpty(); + return visitExpression(expr); + } + + private LvarType visitExpression(final Expression expr) { + final int stackSize = typeStack.size(); + expr.accept(this); + assert typeStack.size() == stackSize + 1; + return typeStack.pop(); + } + + private void visitExpressions(final List exprs) { + for(final Expression expr: exprs) { + if (expr != null) { + visitExpression(expr); + } + } + } + @Override public boolean enterForNode(final ForNode forNode) { if(!reachable) { @@ -543,7 +629,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ final Expression init = forNode.getInit(); if(forNode.isForIn()) { final JoinPredecessorExpression iterable = forNode.getModify(); - iterable.accept(this); + visitExpression(iterable); enterTestFirstLoop(forNode, null, init, // If we're iterating over property names, and we can discern from the runtime environment // of the compilation that the object being iterated over must use strings for property @@ -552,16 +638,18 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ !compiler.useOptimisticTypes() || (!forNode.isForEach() && compiler.hasStringPropertyIterator(iterable.getExpression()))); } else { if(init != null) { - init.accept(this); + visitExpressionOnEmptyStack(init); } enterTestFirstLoop(forNode, forNode.getModify(), null, false); } + assertTypeStackIsEmpty(); return false; } @Override public boolean enterFunctionNode(final FunctionNode functionNode) { if(alreadyEnteredTopLevelFunction) { + typeStack.push(LvarType.OBJECT); return false; } int pos = 0; @@ -602,12 +690,21 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ return true; } + @Override + public boolean enterGetSplitState(final GetSplitState getSplitState) { + return pushExpressionType(getSplitState); + } + @Override public boolean enterIdentNode(final IdentNode identNode) { final Symbol symbol = identNode.getSymbol(); if(symbol.isBytecodeLocal()) { symbolIsUsed(symbol); - setIdentifierLvarType(identNode, getLocalVariableType(symbol)); + final LvarType type = getLocalVariableType(symbol); + setIdentifierLvarType(identNode, type); + typeStack.push(type); + } else { + pushExpressionType(identNode); } return false; } @@ -622,11 +719,12 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ final Block pass = ifNode.getPass(); final Block fail = ifNode.getFail(); - test.accept(this); + visitExpressionOnEmptyStack(test); final Map afterTestLvarTypes = localVariableTypes; if(!isAlwaysFalse(test)) { pass.accept(this); + assertTypeStackIsEmpty(); } final Map passLvarTypes = localVariableTypes; final boolean reachableFromPass = reachable; @@ -635,6 +733,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ localVariableTypes = afterTestLvarTypes; if(!isAlwaysTrue(test) && fail != null) { fail.accept(this); + assertTypeStackIsEmpty(); final boolean reachableFromFail = reachable; reachable |= reachableFromPass; if(!reachable) { @@ -667,14 +766,53 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ } @Override - public boolean enterPropertyNode(final PropertyNode propertyNode) { - // Avoid falsely adding property keys to the control flow graph - if(propertyNode.getValue() != null) { - propertyNode.getValue().accept(this); + public boolean enterIndexNode(final IndexNode indexNode) { + visitExpression(indexNode.getBase()); + visitExpression(indexNode.getIndex()); + return pushExpressionType(indexNode); + } + + @Override + public boolean enterJoinPredecessorExpression(final JoinPredecessorExpression joinExpr) { + final Expression expr = joinExpr.getExpression(); + if (expr != null) { + expr.accept(this); + } else { + typeStack.push(LvarType.UNDEFINED); } return false; } + @Override + public boolean enterLiteralNode(final LiteralNode literalNode) { + if (literalNode instanceof ArrayLiteralNode) { + final List expressions = ((ArrayLiteralNode)literalNode).getElementExpressions(); + if (expressions != null) { + visitExpressions(expressions); + } + } + pushExpressionType(literalNode); + return false; + } + + @Override + public boolean enterObjectNode(final ObjectNode objectNode) { + for(final PropertyNode propertyNode: objectNode.getElements()) { + // Avoid falsely adding property keys to the control flow graph + final Expression value = propertyNode.getValue(); + if (value != null) { + visitExpression(value); + } + } + return pushExpressionType(objectNode); + } + + @Override + public boolean enterPropertyNode(final PropertyNode propertyNode) { + // Property nodes are only accessible through object literals, and we handled that case above + throw new AssertionError(); + } + @Override public boolean enterReturnNode(final ReturnNode returnNode) { if(!reachable) { @@ -684,9 +822,9 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ final Expression returnExpr = returnNode.getExpression(); final Type returnExprType; if(returnExpr != null) { - returnExpr.accept(this); - returnExprType = getType(returnExpr); + returnExprType = visitExpressionOnEmptyStack(returnExpr).type; } else { + assertTypeStackIsEmpty(); returnExprType = Type.UNDEFINED; } returnType = Type.widestReturnType(returnType, returnExprType); @@ -694,6 +832,12 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ return false; } + @Override + public boolean enterRuntimeNode(final RuntimeNode runtimeNode) { + visitExpressions(runtimeNode.getArgs()); + return pushExpressionType(runtimeNode); + } + @Override public boolean enterSplitReturn(final SplitReturn splitReturn) { doesNotContinueSequentially(); @@ -706,8 +850,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ return false; } - final Expression expr = switchNode.getExpression(); - expr.accept(this); + visitExpressionOnEmptyStack(switchNode.getExpression()); final List cases = switchNode.getCases(); if(cases.isEmpty()) { @@ -724,7 +867,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ for(final CaseNode caseNode: cases) { final Expression test = caseNode.getTest(); if(!isInteger && test != null) { - test.accept(this); + visitExpressionOnEmptyStack(test); if(!tagUsed) { symbolIsUsed(switchNode.getTag(), LvarType.OBJECT); tagUsed = true; @@ -769,29 +912,42 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ final Expression trueExpr = ternaryNode.getTrueExpression(); final Expression falseExpr = ternaryNode.getFalseExpression(); - test.accept(this); + visitExpression(test); final Map testExitLvarTypes = localVariableTypes; + final LvarType trueType; if(!isAlwaysFalse(test)) { - trueExpr.accept(this); + trueType = visitExpression(trueExpr); + } else { + trueType = null; } final Map trueExitLvarTypes = localVariableTypes; localVariableTypes = testExitLvarTypes; + final LvarType falseType; if(!isAlwaysTrue(test)) { - falseExpr.accept(this); + falseType = visitExpression(falseExpr); + } else { + falseType = null; } final Map falseExitLvarTypes = localVariableTypes; localVariableTypes = getUnionTypes(trueExitLvarTypes, falseExitLvarTypes); setConversion((JoinPredecessor)trueExpr, trueExitLvarTypes, localVariableTypes); setConversion((JoinPredecessor)falseExpr, falseExitLvarTypes, localVariableTypes); + + typeStack.push(trueType != null ? falseType != null ? widestLvarType(trueType, falseType) : trueType : assertNotNull(falseType)); return false; } + private static T assertNotNull(final T t) { + assert t != null; + return t; + } + private void enterTestFirstLoop(final LoopNode loopNode, final JoinPredecessorExpression modify, final Expression iteratorValues, final boolean iteratorValuesAreObject) { final JoinPredecessorExpression test = loopNode.getTest(); if(isAlwaysFalse(test)) { - test.accept(this); + visitExpressionOnEmptyStack(test); return; } @@ -804,7 +960,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ jumpToLabel(loopNode, repeatLabel, beforeLoopTypes); final Map beforeRepeatTypes = localVariableTypes; if(test != null) { - test.accept(this); + visitExpressionOnEmptyStack(test); } if(!isAlwaysTrue(test)) { jumpToLabel(test, breakLabel); @@ -827,7 +983,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ break; } if(modify != null) { - modify.accept(this); + visitExpressionOnEmptyStack(modify); jumpToLabel(modify, repeatLabel); joinOnLabel(repeatLabel); } @@ -853,7 +1009,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ return false; } - throwNode.getExpression().accept(this); + visitExpressionOnEmptyStack(throwNode.getExpression()); jumpToCatchBlock(throwNode); doesNotContinueSequentially(); return false; @@ -892,7 +1048,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ onAssignment(exception, LvarType.OBJECT); final Expression condition = catchNode.getExceptionCondition(); if(condition != null) { - condition.accept(this); + visitExpression(condition); } final Map afterConditionTypes = localVariableTypes; final Block catchBody = catchNode.getBody(); @@ -927,14 +1083,11 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ @Override public boolean enterUnaryNode(final UnaryNode unaryNode) { final Expression expr = unaryNode.getExpression(); - expr.accept(this); - - if(unaryNode.isSelfModifying()) { - if(expr instanceof IdentNode) { - final IdentNode ident = (IdentNode)expr; - onSelfAssignment(ident, unaryNode, getLocalVariableTypeIfBytecode(ident.getSymbol())); - } + final LvarType unaryType = toLvarType(unaryNode.setExpression(visitExpression(expr).typeExpression).getType()); + if(unaryNode.isSelfModifying() && expr instanceof IdentNode) { + onSelfAssignment((IdentNode)expr, unaryType); } + typeStack.push(unaryType); return false; } @@ -945,8 +1098,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ } final Expression init = varNode.getInit(); if(init != null) { - init.accept(this); - onAssignment(varNode.getName(), init); + onAssignment(varNode.getName(), visitExpression(init)); } return false; } @@ -964,6 +1116,15 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ return false; } + @Override + public boolean enterWithNode(final WithNode withNode) { + if (reachable) { + visitExpression(withNode.getExpression()); + withNode.getBody().accept(this); + } + return false; + }; + private Map getBreakTargetTypes(final BreakableNode target) { // Remove symbols defined in the the blocks that are being broken out of. Map types = localVariableTypes; @@ -1001,18 +1162,6 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ return type; } - /** - * Gets the type for a local variable if it is a bytecode local, otherwise null. Can be used in circumstances where - * the type is irrelevant if the symbol is not a bytecode local. Note that for bytecode locals, it delegates to - * {@link #getLocalVariableType(Symbol)}, so it will still assert that the type for such variable is already - * defined (that is, not null). - * @param symbol the symbol representing the variable. - * @return the current variable type, if it is a bytecode local, otherwise null. - */ - private LvarType getLocalVariableTypeIfBytecode(final Symbol symbol) { - return symbol.isBytecodeLocal() ? getLocalVariableType(symbol) : null; - } - /** * Gets the type for a variable represented by a symbol, or null if the type is not know. This is the least strict * of all local variable type getters, and as such its use is discouraged except in initialization scenarios (where @@ -1154,6 +1303,7 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ */ private void leaveBreakable(final BreakableNode breakable) { joinOnLabel(breakable.getBreakLabel()); + assertTypeStackIsEmpty(); } @Override @@ -1329,10 +1479,6 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ return conv == null || !conv.isLive(); } - private void onAssignment(final IdentNode identNode, final Expression rhs) { - onAssignment(identNode, toLvarType(getType(rhs))); - } - private void onAssignment(final IdentNode identNode, final LvarType type) { final Symbol symbol = identNode.getSymbol(); assert symbol != null : identNode.getName(); @@ -1400,13 +1546,12 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ jumpToCatchBlock(identNode); } - private void onSelfAssignment(final IdentNode identNode, final Expression assignment, final LvarType typeOnLoad) { + private void onSelfAssignment(final IdentNode identNode, final LvarType type) { final Symbol symbol = identNode.getSymbol(); assert symbol != null : identNode.getName(); if(!symbol.isBytecodeLocal()) { return; } - final LvarType type = toLvarType(getType(assignment, symbol, typeOnLoad.type)); // Self-assignment never produce either a boolean or undefined assert type != null && type != LvarType.UNDEFINED && type != LvarType.BOOLEAN; setType(symbol, type); @@ -1466,7 +1611,6 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ * @param symbol the symbol representing the variable * @param type the type */ - @SuppressWarnings("unused") private void setType(final Symbol symbol, final LvarType type) { if(getLocalVariableTypeOrNull(symbol) == type) { return; @@ -1486,77 +1630,4 @@ final class LocalVariableTypesCalculator extends NodeVisitor{ private void symbolIsUsed(final Symbol symbol) { symbolIsUsed(symbol, getLocalVariableType(symbol)); } - - /** - * Gets the type of the expression, dependent on the current types of the local variables. - * - * @param expr the expression - * @return the current type of the expression dependent on the current types of the local variables. - */ - private Type getType(final Expression expr) { - return expr.getType(getSymbolToType()); - } - - /** - * Returns a function object from symbols to their types, used by the expressions to evaluate their type. - * {@link BinaryNode} specifically uses identity of the function to cache type calculations. This method makes - * sure to return the same function object while the local variable types don't change, and create a new function - * object if the local variable types have been changed. - * @return a function object representing a mapping from symbols to their types. - */ - private Function getSymbolToType() { - if(symbolToType.isStale()) { - symbolToType = new SymbolToType(); - } - return symbolToType; - } - - private class SymbolToType implements Function { - private final Object boundTypes = localVariableTypes; - @Override - public Type apply(final Symbol t) { - return getLocalVariableType(t).type; - } - - boolean isStale() { - return boundTypes != localVariableTypes; - } - } - - /** - * Gets the type of the expression, dependent on the current types of the local variables and a single overridden - * symbol type. Used by type calculation on compound operators to ensure the type of the LHS at the time it was - * loaded (which can potentially be different after RHS evaluation, e.g. "var x; x += x = 0;") is preserved for - * the calculation. - * - * @param expr the expression - * @param overriddenSymbol the overridden symbol - * @param overriddenType the overridden type - * @return the current type of the expression dependent on the current types of the local variables and the single - * potentially overridden type. - */ - private Type getType(final Expression expr, final Symbol overriddenSymbol, final Type overriddenType) { - return expr.getType(getSymbolToType(overriddenSymbol, overriddenType)); - } - - private Function getSymbolToType(final Symbol overriddenSymbol, final Type overriddenType) { - return getLocalVariableType(overriddenSymbol).type == overriddenType ? getSymbolToType() : - new SymbolToTypeOverride(overriddenSymbol, overriddenType); - } - - private class SymbolToTypeOverride implements Function { - private final Function originalSymbolToType = getSymbolToType(); - private final Symbol overriddenSymbol; - private final Type overriddenType; - - SymbolToTypeOverride(final Symbol overriddenSymbol, final Type overriddenType) { - this.overriddenSymbol = overriddenSymbol; - this.overriddenType = overriddenType; - } - - @Override - public Type apply(final Symbol symbol) { - return symbol == overriddenSymbol ? overriddenType : originalSymbolToType.apply(symbol); - } - } } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BaseNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BaseNode.java index 35479a7a58b..9c4156e8993 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BaseNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BaseNode.java @@ -27,7 +27,6 @@ package jdk.nashorn.internal.ir; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; @@ -98,7 +97,7 @@ public abstract class BaseNode extends Expression implements FunctionCall, Optim } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return type == null ? getMostPessimisticType() : type; } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BinaryNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BinaryNode.java index f625fe1a691..24be9c2d814 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BinaryNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/BinaryNode.java @@ -31,7 +31,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Ignore; import jdk.nashorn.internal.ir.annotations.Immutable; @@ -57,9 +56,7 @@ public final class BinaryNode extends Expression implements Assignment CAN_OVERFLOW = @@ -143,24 +140,6 @@ public final class BinaryNode extends Expression implements Assignment UNKNOWN_LOCALS = new Function() { - @Override - public Type apply(final Symbol t) { - return null; - } - }; - - /** - * Return the widest possible type for this operation. This is used for compile time - * static type inference - * - * @return Type - */ - @Override - public Type getWidestOperationType() { - return getWidestOperationType(UNKNOWN_LOCALS); - } - /** * Return the widest possible operand type for this operation. * @@ -181,14 +160,15 @@ public final class BinaryNode extends Expression implements Assignment localVariableTypes) { + @Override + public Type getWidestOperationType() { switch (tokenType()) { case ADD: case ASSIGN_ADD: { // Compare this logic to decideType(Type, Type); it's similar, but it handles the optimistic type // calculation case while this handles the conservative case. - final Type lhsType = lhs.getType(localVariableTypes); - final Type rhsType = rhs.getType(localVariableTypes); + final Type lhsType = lhs.getType(); + final Type rhsType = rhs.getType(); if(lhsType == Type.BOOLEAN && rhsType == Type.BOOLEAN) { // Will always fit in an int, as the value range is [0, 1, 2]. If we didn't treat them specially here, // they'd end up being treated as generic INT operands and their sum would be conservatively considered @@ -238,8 +218,8 @@ public final class BinaryNode extends Expression implements Assignment localVariableTypes) { - if(localVariableTypes == cachedTypeFunction) { - return cachedType; + public Type getType() { + if (cachedType == null) { + cachedType = getTypeUncached(); } - cachedType = getTypeUncached(localVariableTypes); - cachedTypeFunction = localVariableTypes; return cachedType; } - private Type getTypeUncached(final Function localVariableTypes) { + private Type getTypeUncached() { if(type == OPTIMISTIC_UNDECIDED_TYPE) { - return decideType(lhs.getType(localVariableTypes), rhs.getType(localVariableTypes)); + return decideType(lhs.getType(), rhs.getType()); } - final Type widest = getWidestOperationType(localVariableTypes); + final Type widest = getWidestOperationType(); if(type == null) { return widest; } - return Type.narrowest(widest, Type.widest(type, Type.widest(lhs.getType(localVariableTypes), rhs.getType(localVariableTypes)))); + return Type.narrowest(widest, Type.widest(type, Type.widest(lhs.getType(), rhs.getType()))); } private static Type decideType(final Type lhsType, final Type rhsType) { diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CallNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CallNode.java index 72eab68b850..95a116652d3 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CallNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/CallNode.java @@ -30,7 +30,6 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_ import java.io.Serializable; import java.util.Collections; import java.util.List; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Ignore; import jdk.nashorn.internal.ir.annotations.Immutable; @@ -154,7 +153,7 @@ public final class CallNode extends LexicalContextExpression implements Optimist } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return optimisticType == null ? Type.OBJECT : optimisticType; } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Expression.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Expression.java index 78523412e46..e62eb732d52 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Expression.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Expression.java @@ -25,7 +25,6 @@ package jdk.nashorn.internal.ir; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.runtime.UnwarrantedOptimismException; @@ -39,14 +38,7 @@ public abstract class Expression extends Node { static final String OPT_IDENTIFIER = "%"; - private static final Function UNKNOWN_LOCALS = new Function() { - @Override - public Type apply(final Symbol t) { - return null; - } - }; - - Expression(final long token, final int start, final int finish) { + protected Expression(final long token, final int start, final int finish) { super(token, start, finish); } @@ -63,18 +55,7 @@ public abstract class Expression extends Node { * * @return the type of the expression. */ - public final Type getType() { - return getType(UNKNOWN_LOCALS); - } - - /** - * Returns the type of the expression under the specified symbol-to-type mapping. By default delegates to - * {@link #getType()} but expressions whose type depends on their subexpressions' types and expressions whose type - * depends on symbol type ({@link IdentNode}) will have a special implementation. - * @param localVariableTypes a mapping from symbols to their types, used for type calculation. - * @return the type of the expression under the specified symbol-to-type mapping. - */ - public abstract Type getType(final Function localVariableTypes); + public abstract Type getType(); /** * Returns {@code true} if this expression depends exclusively on state that is constant diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java index 383b9bb6427..783043f520f 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java @@ -36,7 +36,6 @@ import java.util.Collections; import java.util.EnumSet; import java.util.Iterator; import java.util.List; -import java.util.function.Function; import jdk.nashorn.internal.AssertsEnabled; import jdk.nashorn.internal.codegen.CompileUnit; import jdk.nashorn.internal.codegen.Compiler; @@ -1101,7 +1100,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return FUNCTION_TYPE; } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/GetSplitState.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/GetSplitState.java index bf4a4c01b8a..29c11b48eab 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/GetSplitState.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/GetSplitState.java @@ -25,7 +25,6 @@ package jdk.nashorn.internal.ir; -import java.util.function.Function; import jdk.nashorn.internal.codegen.CompilerConstants; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -47,7 +46,7 @@ public final class GetSplitState extends Expression { } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return Type.INT; } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IdentNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IdentNode.java index 7059734da1b..4570181af07 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IdentNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IdentNode.java @@ -30,7 +30,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.__FILE__; import static jdk.nashorn.internal.codegen.CompilerConstants.__LINE__; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -118,14 +117,13 @@ public final class IdentNode extends Expression implements PropertyKey, Function } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { if(type != null) { return type; } else if(symbol != null && symbol.isScope()) { return Type.OBJECT; } - final Type symbolType = localVariableTypes.apply(symbol); - return symbolType == null ? Type.UNDEFINED : symbolType; + return Type.UNDEFINED; } /** diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/JoinPredecessorExpression.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/JoinPredecessorExpression.java index cfb0086b270..7f0d990dea8 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/JoinPredecessorExpression.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/JoinPredecessorExpression.java @@ -25,7 +25,6 @@ package jdk.nashorn.internal.ir; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -71,8 +70,8 @@ public class JoinPredecessorExpression extends Expression implements JoinPredece } @Override - public Type getType(final Function localVariableTypes) { - return expression.getType(localVariableTypes); + public Type getType() { + return expression.getType(); } @Override diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java index 6aee20cd7d7..f2a9003c39b 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LiteralNode.java @@ -29,7 +29,6 @@ import java.io.Serializable; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.function.Function; import jdk.nashorn.internal.codegen.CompileUnit; import jdk.nashorn.internal.codegen.types.ArrayType; import jdk.nashorn.internal.codegen.types.Type; @@ -109,7 +108,7 @@ public abstract class LiteralNode extends Expression implements PropertyKey { } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return Type.typeFor(value.getClass()); } @@ -163,16 +162,6 @@ public abstract class LiteralNode extends Expression implements PropertyKey { return JSType.toNumber(value); } - /** - * Get the array value of the node - * - * @return the array value - */ - public Node[] getArray() { - assert false : "not an array node"; - return null; - } - /** * Fetch String value of node. * @@ -325,7 +314,7 @@ public abstract class LiteralNode extends Expression implements PropertyKey { } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return Type.BOOLEAN; } @@ -389,7 +378,7 @@ public abstract class LiteralNode extends Expression implements PropertyKey { } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return type; } @@ -519,7 +508,7 @@ public abstract class LiteralNode extends Expression implements PropertyKey { } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return Type.OBJECT; } @@ -589,7 +578,7 @@ public abstract class LiteralNode extends Expression implements PropertyKey { } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return Type.OBJECT; } @@ -840,9 +829,13 @@ public abstract class LiteralNode extends Expression implements PropertyKey { this.units = units; } - @Override - public Node[] getArray() { - return value; + /** + * Returns a list of array element expressions. Note that empty array elements manifest themselves as + * null. + * @return a list of array element expressions. + */ + public List getElementExpressions() { + return Collections.unmodifiableList(Arrays.asList(value)); } /** @@ -879,7 +872,7 @@ public abstract class LiteralNode extends Expression implements PropertyKey { } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return Type.typeFor(NativeArray.class); } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ObjectNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ObjectNode.java index d5bd78b57d2..31ddc1cd608 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ObjectNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ObjectNode.java @@ -27,7 +27,6 @@ package jdk.nashorn.internal.ir; import java.util.Collections; import java.util.List; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -69,7 +68,7 @@ public final class ObjectNode extends Expression { } @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return Type.OBJECT; } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/RuntimeNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/RuntimeNode.java index 4eca8ff084b..0f0ea78b189 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/RuntimeNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/RuntimeNode.java @@ -30,7 +30,6 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_ import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -460,7 +459,7 @@ public class RuntimeNode extends Expression implements Optimistic { * Return type for the ReferenceNode */ @Override - public Type getType(final Function localVariableTypes) { + public Type getType() { return request.getReturnType(); } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TernaryNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TernaryNode.java index 913262d9a8c..e3bca0b980f 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TernaryNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TernaryNode.java @@ -25,7 +25,6 @@ package jdk.nashorn.internal.ir; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; @@ -122,8 +121,8 @@ public final class TernaryNode extends Expression { } @Override - public Type getType(final Function localVariableTypes) { - return Type.widestReturnType(getTrueExpression().getType(localVariableTypes), getFalseExpression().getType(localVariableTypes)); + public Type getType() { + return Type.widestReturnType(getTrueExpression().getType(), getFalseExpression().getType()); } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/UnaryNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/UnaryNode.java index 2cee33b6c1c..2f7b268a4c5 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/UnaryNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/UnaryNode.java @@ -33,7 +33,6 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_ import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.function.Function; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Ignore; import jdk.nashorn.internal.ir.annotations.Immutable; @@ -123,23 +122,11 @@ public final class UnaryNode extends Expression implements Assignment UNKNOWN_LOCALS = new Function() { - @Override - public Type apply(final Symbol t) { - return null; - } - }; - - @Override public Type getWidestOperationType() { - return getWidestOperationType(UNKNOWN_LOCALS); - } - - private Type getWidestOperationType(final Function localVariableTypes) { switch (tokenType()) { case ADD: - final Type operandType = getExpression().getType(localVariableTypes); + final Type operandType = getExpression().getType(); if(operandType == Type.BOOLEAN) { return Type.INT; } else if(operandType.isObject()) { @@ -326,12 +313,12 @@ public final class UnaryNode extends Expression implements Assignment localVariableTypes) { - final Type widest = getWidestOperationType(localVariableTypes); + public Type getType() { + final Type widest = getWidestOperationType(); if(type == null) { return widest; } - return Type.narrowest(widest, Type.widest(type, expression.getType(localVariableTypes))); + return Type.narrowest(widest, Type.widest(type, expression.getType())); } @Override diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/JSONWriter.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/JSONWriter.java index e3916d694e6..9cee46ff8d2 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/JSONWriter.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/debug/JSONWriter.java @@ -28,7 +28,6 @@ package jdk.nashorn.internal.ir.debug; import static jdk.nashorn.internal.runtime.Source.sourceFor; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import jdk.nashorn.internal.ir.AccessNode; import jdk.nashorn.internal.ir.BinaryNode; @@ -553,8 +552,7 @@ public final class JSONWriter extends NodeVisitor { type("ArrayExpression"); comma(); - final Node[] value = literalNode.getArray(); - array("elements", Arrays.asList(value)); + array("elements", ((LiteralNode.ArrayLiteralNode)literalNode).getElementExpressions()); } else { type("Literal"); comma(); diff --git a/nashorn/test/script/basic/JDK-8067774.js b/nashorn/test/script/basic/JDK-8067774.js new file mode 100644 index 00000000000..4eeeb8d829d --- /dev/null +++ b/nashorn/test/script/basic/JDK-8067774.js @@ -0,0 +1,37 @@ +/* + * 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. + */ + +/** + * JDK-8067774: Use a stack of types when calculating local variable types + * + * @test + * @run + */ + +print((function (p) { + var a, b; + + a = p ? ((b = 1), b) : 0; + + return a; +})(true)); diff --git a/nashorn/test/script/basic/JDK-8067774.js.EXPECTED b/nashorn/test/script/basic/JDK-8067774.js.EXPECTED new file mode 100644 index 00000000000..d00491fd7e5 --- /dev/null +++ b/nashorn/test/script/basic/JDK-8067774.js.EXPECTED @@ -0,0 +1 @@ +1 From c269c9451bc4cbca414220382caf4786bd812244 Mon Sep 17 00:00:00 2001 From: Maurizio Cimadamore Date: Thu, 18 Dec 2014 13:21:44 +0000 Subject: [PATCH 48/58] 8066974: Compiler doesn't infer method's generic type information in lambda body Add loghic to avoid post-inference triggers on temporarty AST types Reviewed-by: jlahoda, vromero --- .../com/sun/tools/javac/comp/Attr.java | 68 ++++++++++++------- .../tools/javac/lambda/8066974/T8066974.java | 44 ++++++++++++ .../tools/javac/lambda/8066974/T8066974.out | 4 ++ 3 files changed, 91 insertions(+), 25 deletions(-) create mode 100644 langtools/test/tools/javac/lambda/8066974/T8066974.java create mode 100644 langtools/test/tools/javac/lambda/8066974/T8066974.out diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 6da79c6e4db..7ea795423d0 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -155,6 +155,8 @@ public class Attr extends JCTree.Visitor { unknownTypeInfo = new ResultInfo(KindSelector.TYP, Type.noType); unknownTypeExprInfo = new ResultInfo(KindSelector.VAL_TYP, Type.noType); recoveryInfo = new RecoveryInfo(deferredAttr.emptyDeferredAttrContext); + + noCheckTree = make.at(-1).Skip(); } /** Switch: relax some constraints for retrofit mode. @@ -214,31 +216,32 @@ public class Attr extends JCTree.Visitor { final ResultInfo resultInfo) { InferenceContext inferenceContext = resultInfo.checkContext.inferenceContext(); Type owntype; - if (!found.hasTag(ERROR) && !resultInfo.pt.hasTag(METHOD) && !resultInfo.pt.hasTag(FORALL)) { - if (!ownkind.subset(resultInfo.pkind)) { - log.error(tree.pos(), "unexpected.type", - resultInfo.pkind.kindNames(), - ownkind.kindNames()); - owntype = types.createErrorType(found); - } else if (allowPoly && inferenceContext.free(found)) { - //delay the check if there are inference variables in the found type - //this means we are dealing with a partially inferred poly expression - owntype = resultInfo.pt; - inferenceContext.addFreeTypeListener(List.of(found, resultInfo.pt), new FreeTypeListener() { - @Override - public void typesInferred(InferenceContext inferenceContext) { + boolean shouldCheck = !found.hasTag(ERROR) && + !resultInfo.pt.hasTag(METHOD) && + !resultInfo.pt.hasTag(FORALL); + if (shouldCheck && !ownkind.subset(resultInfo.pkind)) { + log.error(tree.pos(), "unexpected.type", + resultInfo.pkind.kindNames(), + ownkind.kindNames()); + owntype = types.createErrorType(found); + } else if (allowPoly && inferenceContext.free(found)) { + //delay the check if there are inference variables in the found type + //this means we are dealing with a partially inferred poly expression + owntype = shouldCheck ? resultInfo.pt : found; + inferenceContext.addFreeTypeListener(List.of(found, resultInfo.pt), + instantiatedContext -> { ResultInfo pendingResult = resultInfo.dup(inferenceContext.asInstType(resultInfo.pt)); check(tree, inferenceContext.asInstType(found), ownkind, pendingResult); - } - }); - } else { - owntype = resultInfo.check(tree, found); - } + }); } else { - owntype = found; + owntype = shouldCheck ? + resultInfo.check(tree, found) : + found; + } + if (tree != noCheckTree) { + tree.type = owntype; } - tree.type = owntype; return owntype; } @@ -514,6 +517,10 @@ public class Attr extends JCTree.Visitor { */ Type result; + /** Synthetic tree to be used during 'fake' checks. + */ + JCTree noCheckTree; + /** Visitor method: attribute a tree, catching any completion failure * exceptions. Return the tree's type. * @@ -2007,7 +2014,7 @@ public class Attr extends JCTree.Visitor { } }); Type constructorType = tree.constructorType = types.createErrorType(clazztype); - constructorType = checkId(tree, site, + constructorType = checkId(noCheckTree, site, constructor, diamondEnv, diamondResult); @@ -2033,7 +2040,7 @@ public class Attr extends JCTree.Visitor { tree.constructor = rs.resolveConstructor( tree.pos(), rsEnv, clazztype, argtypes, typeargtypes); if (cdef == null) { //do not check twice! - tree.constructorType = checkId(tree, + tree.constructorType = checkId(noCheckTree, clazztype, tree.constructor, rsEnv, @@ -2103,7 +2110,7 @@ public class Attr extends JCTree.Visitor { tree.pos(), localEnv, clazztype, argtypes, typeargtypes); Assert.check(!sym.kind.isOverloadError()); tree.constructor = sym; - tree.constructorType = checkId(tree, + tree.constructorType = checkId(noCheckTree, clazztype, tree.constructor, localEnv, @@ -2114,6 +2121,14 @@ public class Attr extends JCTree.Visitor { owntype = clazztype; } result = check(tree, owntype, KindSelector.VAL, resultInfo); + InferenceContext inferenceContext = resultInfo.checkContext.inferenceContext(); + if (tree.constructorType != null && inferenceContext.free(tree.constructorType)) { + //we need to wait for inference to finish and then replace inference vars in the constructor type + inferenceContext.addFreeTypeListener(List.of(tree.constructorType), + instantiatedContext -> { + tree.constructorType = instantiatedContext.asInstType(tree.constructorType); + }); + } chk.validate(tree.typeargs, localEnv); } @@ -2290,6 +2305,7 @@ public class Attr extends JCTree.Visitor { preFlow(that); flow.analyzeLambda(env, that, make, isSpeculativeRound); + that.type = currentTarget; //avoids recovery at this stage checkLambdaCompatible(that, lambdaType, resultInfo.checkContext); if (!isSpeculativeRound) { @@ -2730,7 +2746,7 @@ public class Attr extends JCTree.Visitor { that.kind.isUnbound() ? argtypes.tail : argtypes, typeargtypes), new FunctionalReturnContext(resultInfo.checkContext)); - Type refType = checkId(that, lookupHelper.site, refSym, localEnv, checkInfo); + Type refType = checkId(noCheckTree, lookupHelper.site, refSym, localEnv, checkInfo); if (that.kind.isUnbound() && resultInfo.checkContext.inferenceContext().free(argtypes.head)) { @@ -2752,6 +2768,8 @@ public class Attr extends JCTree.Visitor { //is a no-op (as this has been taken care during method applicability) boolean isSpeculativeRound = resultInfo.checkContext.deferredAttrContext().mode == DeferredAttr.AttrMode.SPECULATIVE; + + that.type = currentTarget; //avoids recovery at this stage checkReferenceCompatible(that, desc, refType, resultInfo.checkContext, isSpeculativeRound); if (!isSpeculativeRound) { checkAccessibleTypes(that, localEnv, resultInfo.checkContext.inferenceContext(), desc, currentTarget); @@ -3882,7 +3900,7 @@ public class Attr extends JCTree.Visitor { all_multicatchTypes.append(ctype); } } - Type t = check(tree, types.lub(multicatchTypes.toList()), + Type t = check(noCheckTree, types.lub(multicatchTypes.toList()), KindSelector.TYP, resultInfo); if (t.hasTag(CLASS)) { List alternatives = diff --git a/langtools/test/tools/javac/lambda/8066974/T8066974.java b/langtools/test/tools/javac/lambda/8066974/T8066974.java new file mode 100644 index 00000000000..8f47277f734 --- /dev/null +++ b/langtools/test/tools/javac/lambda/8066974/T8066974.java @@ -0,0 +1,44 @@ +/* + * @test /nodynamiccopyright/ + * @bug 8066974 + * @summary Compiler doesn't infer method's generic type information in lambda body + * @compile/fail/ref=T8066974.out -XDrawDiagnostics T8066974.java + */ +class T8066974 { + static class Throwing { } + static class RuntimeThrowing extends Throwing { } + static class CheckedThrowing extends Throwing { } + + interface Parameter { + Object m(Throwing tw) throws E; + } + + interface Mapper { + R m(Parameter p); + } + + Z map(Mapper mz) { return null; } + + Mapper> mapper(Throwing tz) throws Z { return null; } + + static class ThrowingMapper implements Mapper> { + ThrowingMapper(Throwing arg) throws X { } + + @Override + public Throwing m(Parameter p) { + return null; + } + } + + void testRuntime(RuntimeThrowing rt) { + map(p->p.m(rt)); + map(mapper(rt)); + map(new ThrowingMapper<>(rt)); + } + + void testChecked(CheckedThrowing ct) { + map(p->p.m(ct)); + map(mapper(ct)); + map(new ThrowingMapper<>(ct)); + } +} diff --git a/langtools/test/tools/javac/lambda/8066974/T8066974.out b/langtools/test/tools/javac/lambda/8066974/T8066974.out new file mode 100644 index 00000000000..59772fc59a4 --- /dev/null +++ b/langtools/test/tools/javac/lambda/8066974/T8066974.out @@ -0,0 +1,4 @@ +T8066974.java:40:19: compiler.err.unreported.exception.need.to.catch.or.throw: java.lang.Exception +T8066974.java:41:19: compiler.err.unreported.exception.need.to.catch.or.throw: java.lang.Exception +T8066974.java:42:13: compiler.err.unreported.exception.need.to.catch.or.throw: java.lang.Exception +3 errors From ec11dd0153100325a6a0e12f199dc18817c3e056 Mon Sep 17 00:00:00 2001 From: Mandy Chung Date: Thu, 18 Dec 2014 13:56:46 -0800 Subject: [PATCH 49/58] 8067898: Disable verify-modules until JDK-8067479 is resolved Reviewed-by: ksrini --- make/Main.gmk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/Main.gmk b/make/Main.gmk index 383b0590bcb..0f705491b0a 100644 --- a/make/Main.gmk +++ b/make/Main.gmk @@ -442,7 +442,7 @@ exploded-image: $(ALL_MODULE_TARGETS) # alias for ease of use. jdk: exploded-image -images: jimages demos samples zip-security verify-modules +images: jimages demos samples zip-security ifeq ($(OPENJDK_TARGET_OS), macosx) images: mac-bundles From 35fec5bd098a91a911adfdbcba08fb605a6d1656 Mon Sep 17 00:00:00 2001 From: Lana Steuck Date: Thu, 18 Dec 2014 19:57:47 -0800 Subject: [PATCH 50/58] Added tag jdk9-b43 for changeset 2d5bf96c8f17 --- .hgtags-top-repo | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags-top-repo b/.hgtags-top-repo index 9cbf0f90d4e..6bcda895e37 100644 --- a/.hgtags-top-repo +++ b/.hgtags-top-repo @@ -285,3 +285,4 @@ d42c0a90afc3c66ca87543076ec9aafd4b4680de jdk9-b38 cf136458ee747e151a27aa9ea0c1492ea55ef3e7 jdk9-b40 67395f7ca2db3b52e3a62a84888487de5cb9210a jdk9-b41 f7c11da0b0481d49cc7a65a453336c108191e821 jdk9-b42 +02ee8c65622e8bd97496d584e22fc7dcf0edc4ae jdk9-b43 From 9605b3ca96569e52656d08ef20ec67aaf80aa566 Mon Sep 17 00:00:00 2001 From: Lana Steuck Date: Thu, 18 Dec 2014 19:57:48 -0800 Subject: [PATCH 51/58] Added tag jdk9-b43 for changeset f323111baaa9 --- corba/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/corba/.hgtags b/corba/.hgtags index 26c65ed70d5..74eb22ce97b 100644 --- a/corba/.hgtags +++ b/corba/.hgtags @@ -285,3 +285,4 @@ ffd90c81d4ef9d94d880fc852e2fc482ecd9b374 jdk9-b36 e27c725d6c9d155667b35255f442d4ceb8c3c084 jdk9-b40 1908b886ba1eda46fa725cf1160fe5d30fd1a7e5 jdk9-b41 078bb11af876fe528d4b516f33ad4dd9bb60549e jdk9-b42 +9645e35616b60c5c07b4fdf11a132afc8081dfa8 jdk9-b43 From cce553e64c4a7eac25183f6f90b308eeb1beac5e Mon Sep 17 00:00:00 2001 From: Lana Steuck Date: Thu, 18 Dec 2014 19:57:49 -0800 Subject: [PATCH 52/58] Added tag jdk9-b43 for changeset c12e49897c5a --- hotspot/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/hotspot/.hgtags b/hotspot/.hgtags index ae7d006d45b..987d1e0cfb4 100644 --- a/hotspot/.hgtags +++ b/hotspot/.hgtags @@ -445,3 +445,4 @@ c363a8b87e477ee45d6d3cb2a36cb365141bc596 jdk9-b38 6b09b3193d731e3288e2a240c504a20d0a06c766 jdk9-b40 1d29b13e8a515a7ea3b882f140576d5d675bc11f jdk9-b41 38cb4fbd47e3472bd1b5ebac83bda96fe4869c4f jdk9-b42 +65a9747147b8090037541040ba67156ec914db6a jdk9-b43 From 16817fe94b1c82f6fc78c311d88d1c73d39977f0 Mon Sep 17 00:00:00 2001 From: Lana Steuck Date: Thu, 18 Dec 2014 19:57:51 -0800 Subject: [PATCH 53/58] Added tag jdk9-b43 for changeset f5f19fe0e83b --- jaxp/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/jaxp/.hgtags b/jaxp/.hgtags index 87019760616..cf67c438915 100644 --- a/jaxp/.hgtags +++ b/jaxp/.hgtags @@ -285,3 +285,4 @@ a12d347f84176200593999f4da91ae2bb86865b2 jdk9-b39 3f46e2196498de33e7c65efa7b372e46f1faba01 jdk9-b40 71dd8f7649428efd3a56ca5fefc80e59d37b8434 jdk9-b41 47b0d3fa4118b9d56870cf4004987438c501f5c0 jdk9-b42 +40b242363040229a05224fbc5dc203a3f46a8f8f jdk9-b43 From d62d10cb781d263c83b354b2fa5dfa5fb54896f4 Mon Sep 17 00:00:00 2001 From: Lana Steuck Date: Thu, 18 Dec 2014 19:57:51 -0800 Subject: [PATCH 54/58] Added tag jdk9-b43 for changeset 1eddc8b954d5 --- jaxws/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/jaxws/.hgtags b/jaxws/.hgtags index defa79ac988..9c619cb0340 100644 --- a/jaxws/.hgtags +++ b/jaxws/.hgtags @@ -288,3 +288,4 @@ dd4ba422dba858b1c3c4b38f49a3e514be4e2790 jdk9-b38 5455969de31f3083bcfd779b7acc3ab758ecb308 jdk9-b40 4f785187377fe4c7ff388a7026dd72fcccdcfe7a jdk9-b41 301ddb4478fb36d1f025d14e7e48c2a434e9e6ff jdk9-b42 +edc13d27dc871be57d7ca77eef77e6d04972fee2 jdk9-b43 From d90d57cd11974d43377450f95e6e52aa8ee1540d Mon Sep 17 00:00:00 2001 From: Lana Steuck Date: Thu, 18 Dec 2014 19:57:52 -0800 Subject: [PATCH 55/58] Added tag jdk9-b43 for changeset 52310706ed47 --- jdk/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/jdk/.hgtags b/jdk/.hgtags index fa59d457917..cd095f30f14 100644 --- a/jdk/.hgtags +++ b/jdk/.hgtags @@ -285,3 +285,4 @@ ca6edf957fe1c6ea818530b503578e872cea7239 jdk9-b39 f1ed1540da70a066527fd043413107e47721edbf jdk9-b40 e336cbd8b15e959e70ed02f0f5e93fa76ebd4c07 jdk9-b41 6b2314173433467245261364a52fb8e347fe6342 jdk9-b42 +8c6ad41974f9ab6c33d544b088648314963f2a50 jdk9-b43 From 8edd68a854dc45ccb3fc95802c022c71d207496d Mon Sep 17 00:00:00 2001 From: Lana Steuck Date: Thu, 18 Dec 2014 19:57:56 -0800 Subject: [PATCH 56/58] Added tag jdk9-b43 for changeset 592c5d3be509 --- langtools/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/langtools/.hgtags b/langtools/.hgtags index 7bd86ea5a39..613b3ea0391 100644 --- a/langtools/.hgtags +++ b/langtools/.hgtags @@ -285,3 +285,4 @@ c536541235e566701ff772700c15de14b75e2979 jdk9-b36 c286272a81dd8f6005e22fed0238c4a3f75188c7 jdk9-b40 f7ce2cfa4cdbec0ae0f46080484eace66be7987a jdk9-b41 23a3a063a906a91ba696d792f0eeabf157cd2f86 jdk9-b42 +6a06008aec10d32898ca665685f531c681b28f5f jdk9-b43 From 6253fc41272187be93fc474bc6e9b058d1f1f6d8 Mon Sep 17 00:00:00 2001 From: Lana Steuck Date: Thu, 18 Dec 2014 19:57:57 -0800 Subject: [PATCH 57/58] Added tag jdk9-b43 for changeset b74f34ddea43 --- nashorn/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/nashorn/.hgtags b/nashorn/.hgtags index e5dd9ffa61b..4fd346958fd 100644 --- a/nashorn/.hgtags +++ b/nashorn/.hgtags @@ -276,3 +276,4 @@ dd7bbdf81a537106cfa9227d1a9a57849cb26b4d jdk9-b37 74dcd8dbef252938d6deb032aefb46b8f452dd9e jdk9-b40 52340a35aec9955d4aeaaf01d6337284f179b31c jdk9-b41 498d1d6c4219086143b764b3bf61afe65dcece47 jdk9-b42 +8ae8dff2a28f3b8831cce97ae0c7a957c5dc650a jdk9-b43 From 16fd24d195c04afb669046030d0a8477f126e751 Mon Sep 17 00:00:00 2001 From: "J. Duke" Date: Wed, 5 Jul 2017 20:11:08 +0200 Subject: [PATCH 58/58] Added tag jdk9-b43 for changeset 6494b13f88a8 --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index aaf78d35e9c..367f0b9dc82 100644 --- a/.hgtags +++ b/.hgtags @@ -285,3 +285,4 @@ b409bc51bc23cfd51f2bd04ea919ec83535af9d0 jdk9-b37 82f4cb44b2d7af2352f48568a64b7b6a5ae960cd jdk9-b40 9fffb959eb4197ff806e4ac12244761815b4deee jdk9-b41 3107be2ba9c6e208a0b86bc7100a141abbc5b5fb jdk9-b42 +6494b13f88a867026ee316b444d9a4fa589dd6bd jdk9-b43