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 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 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 ;; 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 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 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 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/src/os/windows/vm/os_windows.cpp b/hotspot/src/os/windows/vm/os_windows.cpp index 924dd4966e6..85a392783f8 100644 --- a/hotspot/src/os/windows/vm/os_windows.cpp +++ b/hotspot/src/os/windows/vm/os_windows.cpp @@ -3074,7 +3074,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. 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; } 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 826cfd5def2..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 @@ -30,9 +30,10 @@ #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 "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/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 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/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/cardGeneration.cpp b/hotspot/src/share/vm/memory/cardGeneration.cpp new file mode 100644 index 00000000000..21e8d9e51a7 --- /dev/null +++ b/hotspot/src/share/vm/memory/cardGeneration.cpp @@ -0,0 +1,360 @@ +/* + * 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/cardGeneration.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::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) { + 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; +} + +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()); +} + +// 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() {} + +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 new file mode 100644 index 00000000000..24d0fa0f2c2 --- /dev/null +++ b/hotspot/src/share/vm/memory/cardGeneration.hpp @@ -0,0 +1,99 @@ +/* + * 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 CompactibleSpace; + +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); + + 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 + // 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); + + 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) + bool grow_by(size_t bytes); + // Grow generation to reserved size. + 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/cardTableModRefBS.cpp b/hotspot/src/share/vm/memory/cardTableModRefBS.cpp index d9460308ac7..d3586332b2f 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/src/share/vm/memory/filemap.cpp b/hotspot/src/share/vm/memory/filemap.cpp index c9c3e6233d6..cbe369f327e 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/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/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp index 0b4d69efb83..0a922ca67e5 100644 --- a/hotspot/src/share/vm/memory/metaspace.cpp +++ b/hotspot/src/share/vm/memory/metaspace.cpp @@ -3158,7 +3158,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.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/src/share/vm/memory/metaspaceShared.hpp b/hotspot/src/share/vm/memory/metaspaceShared.hpp index cb34465ec69..e6e2c9a353e 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/src/share/vm/memory/tenuredGeneration.cpp b/hotspot/src/share/vm/memory/tenuredGeneration.cpp index 37cd32c8585..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,28 +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) { - _last_gc = WaterMark(the_space(), the_space()->top()); - // 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 0ecd54dd55f..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 @@ -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 @@ -42,27 +42,18 @@ class TenuredGeneration: public CardGeneration { friend class VM_PopulateDumpSharedSpace; protected: - ContiguousSpace* _the_space; // actual space holding objects - WaterMark _last_gc; // watermark between objects allocated before - // and after last GC. + 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); @@ -81,33 +72,15 @@ 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); - // 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..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 @@ -22,53 +22,35 @@ * */ -#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" #include "memory/tenuredGeneration.hpp" -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(); -} - 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); -} - -WaterMark TenuredGeneration::bottom_mark() { - return the_space()->bottom_mark(); + 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_GENERATION_INLINE_HPP +#endif // SHARE_VM_MEMORY_TENUREDGENERATION_INLINE_HPP 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/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; 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); 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/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/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/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/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); } diff --git a/hotspot/src/share/vm/runtime/deoptimization.cpp b/hotspot/src/share/vm/runtime/deoptimization.cpp index 75da1fef019..90156bfd4ca 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,19 +208,16 @@ 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 - } - if (reallocated) { - reassign_fields(&deoptee, &map, objects); + 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); + print_objects(objects, realloc_failures); } #endif } @@ -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/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*) \ 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. + + + + + + + + + + + + + + + + + + diff --git a/hotspot/test/Makefile b/hotspot/test/Makefile index e0526fcf35c..657501a7622 100644 --- a/hotspot/test/Makefile +++ b/hotspot/test/Makefile @@ -275,6 +275,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 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", 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); + } + } + } + +} + 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; + } +} 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) {} + } +} 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(); + } +} 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); + } +} 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); + } +} 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); 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); + } + } +} 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()) { 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 { 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 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 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 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 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 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..1eb1ab48808 --- /dev/null +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Analyzer.java @@ -0,0 +1,509 @@ +/* + * 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.source.tree.LambdaExpressionTree; +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.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; +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.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; +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; + } + + @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/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..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 @@ -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); @@ -156,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. @@ -182,28 +183,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? */ @@ -231,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; } @@ -531,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. * @@ -610,7 +600,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 +788,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; } @@ -2018,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); @@ -2044,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, @@ -2052,12 +2048,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 +2095,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 @@ -2122,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, @@ -2133,60 +2121,16 @@ 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); } - //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. */ @@ -2361,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) { @@ -2801,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)) { @@ -2823,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); @@ -3953,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/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/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/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; } } 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/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); } diff --git a/langtools/test/Makefile b/langtools/test/Makefile index 08d4fc48882..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 @@ -105,21 +101,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 +146,7 @@ endif ifdef CONCURRENCY JTREG_OPTIONS += -agentvm -concurrency:$(CONCURRENCY) else - JTREG_OPTIONS += -samevm + JTREG_OPTIONS += -agentvm endif ifdef JCK_CONCURRENCY 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..6d7ef0da676 100644 --- a/langtools/test/tools/javac/generics/diamond/6939780/T6939780.java +++ b/langtools/test/tools/javac/generics/diamond/6939780/T6939780.java @@ -1,11 +1,11 @@ /* * @test /nodynamiccopyright/ - * @bug 6939780 7020044 8009459 8021338 + * @bug 6939780 7020044 8009459 8021338 8064365 * * @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..d5ff88dd858 100644 --- a/langtools/test/tools/javac/generics/diamond/7002837/T7002837.java +++ b/langtools/test/tools/javac/generics/diamond/7002837/T7002837.java @@ -1,10 +1,10 @@ /* * @test /nodynamiccopyright/ - * @bug 7002837 + * @bug 7002837 8064365 * * @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/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 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 diff --git a/langtools/test/tools/javac/lambda/LambdaConv18.java b/langtools/test/tools/javac/lambda/LambdaConv18.java index 279d99ff17c..3a8efb206fc 100644 --- a/langtools/test/tools/javac/lambda/LambdaConv18.java +++ b/langtools/test/tools/javac/lambda/LambdaConv18.java @@ -1,9 +1,9 @@ /* * @test /nodynamiccopyright/ - * @bug 8003280 + * @bug 8003280 8064365 * @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/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()); + } + } + } + } + } +} diff --git a/langtools/test/tools/javac/lambda/speculative/DiamondFinder.java b/langtools/test/tools/javac/lambda/speculative/DiamondFinder.java index 35f56afaacb..38cd0266ef3 100644 --- a/langtools/test/tools/javac/lambda/speculative/DiamondFinder.java +++ b/langtools/test/tools/javac/lambda/speculative/DiamondFinder.java @@ -23,10 +23,10 @@ /* * @test - * @bug 8003280 + * @bug 8003280 8064365 * @summary Add lambda tests * spurious crashes when running in 'diamond finder' mode - * @compile -XDfindDiamond DiamondFinder.java + * @compile -XDfind=diamond DiamondFinder.java */ import java.util.*; diff --git a/make/Javadoc.gmk b/make/Javadoc.gmk index d68c6f333ce..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) @@ -231,17 +230,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) diff --git a/make/Main.gmk b/make/Main.gmk index ba4ba3c2219..0f705491b0a 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 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), \ 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/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 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/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 + 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 94324c7ad75..789801fc3f9 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); } @@ -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) { @@ -74,10 +73,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/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/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/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/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/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/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); 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/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..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; /** @@ -118,20 +119,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 +141,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 +155,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); } @@ -178,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; } @@ -194,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/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-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/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 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 + 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 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"); +} 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; 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="$@" $@)