From 5fb46fd5a88e446fe62c4119c02a2dc6477318a4 Mon Sep 17 00:00:00 2001 From: Kevin Walls Date: Fri, 30 Jul 2010 22:43:50 +0100 Subject: [PATCH 001/100] 6581734: CMS Old Gen's collection usage is zero after GC which is incorrect Management code enabled for use by a concurrent collector. Reviewed-by: mchung, ysr --- .../concurrentMarkSweepGeneration.cpp | 61 +++++++ .../concurrentMarkSweepGeneration.hpp | 9 ++ .../includeDB_gc_concurrentMarkSweep | 2 + hotspot/src/share/vm/services/management.cpp | 9 +- .../src/share/vm/services/memoryManager.cpp | 153 ++++++++++++------ .../src/share/vm/services/memoryManager.hpp | 18 ++- .../src/share/vm/services/memoryService.cpp | 76 +++++++-- .../src/share/vm/services/memoryService.hpp | 37 ++++- hotspot/test/gc/6581734/Test6581734.java | 149 +++++++++++++++++ 9 files changed, 435 insertions(+), 79 deletions(-) create mode 100644 hotspot/test/gc/6581734/Test6581734.java diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp index 0aa08ec3018..a0bace5bf0d 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp @@ -1970,6 +1970,9 @@ void CMSCollector::do_compaction_work(bool clear_all_soft_refs) { _intra_sweep_estimate.padded_average()); } + { + TraceCMSMemoryManagerStats(); + } GenMarkSweep::invoke_at_safepoint(_cmsGen->level(), ref_processor(), clear_all_soft_refs); #ifdef ASSERT @@ -3420,6 +3423,7 @@ CMSPhaseAccounting::~CMSPhaseAccounting() { void CMSCollector::checkpointRootsInitial(bool asynch) { assert(_collectorState == InitialMarking, "Wrong collector state"); check_correct_thread_executing(); + TraceCMSMemoryManagerStats tms(_collectorState); ReferenceProcessor* rp = ref_processor(); SpecializationStats::clear(); assert(_restart_addr == NULL, "Control point invariant"); @@ -4753,6 +4757,7 @@ void CMSCollector::checkpointRootsFinal(bool asynch, // world is stopped at this checkpoint assert(SafepointSynchronize::is_at_safepoint(), "world should be stopped"); + TraceCMSMemoryManagerStats tms(_collectorState); verify_work_stacks_empty(); verify_overflow_empty(); @@ -5854,6 +5859,8 @@ void CMSCollector::sweep(bool asynch) { verify_work_stacks_empty(); verify_overflow_empty(); increment_sweep_count(); + TraceCMSMemoryManagerStats tms(_collectorState); + _inter_sweep_timer.stop(); _inter_sweep_estimate.sample(_inter_sweep_timer.seconds()); size_policy()->avg_cms_free_at_sweep()->sample(_cmsGen->free()); @@ -9126,3 +9133,57 @@ size_t MarkDeadObjectsClosure::do_blk(HeapWord* addr) { } return res; } + +TraceCMSMemoryManagerStats::TraceCMSMemoryManagerStats(CMSCollector::CollectorState phase): TraceMemoryManagerStats() { + + switch (phase) { + case CMSCollector::InitialMarking: + initialize(true /* fullGC */ , + true /* recordGCBeginTime */, + true /* recordPreGCUsage */, + false /* recordPeakUsage */, + false /* recordPostGCusage */, + true /* recordAccumulatedGCTime */, + false /* recordGCEndTime */, + false /* countCollection */ ); + break; + + case CMSCollector::FinalMarking: + initialize(true /* fullGC */ , + false /* recordGCBeginTime */, + false /* recordPreGCUsage */, + false /* recordPeakUsage */, + false /* recordPostGCusage */, + true /* recordAccumulatedGCTime */, + false /* recordGCEndTime */, + false /* countCollection */ ); + break; + + case CMSCollector::Sweeping: + initialize(true /* fullGC */ , + false /* recordGCBeginTime */, + false /* recordPreGCUsage */, + true /* recordPeakUsage */, + true /* recordPostGCusage */, + false /* recordAccumulatedGCTime */, + true /* recordGCEndTime */, + true /* countCollection */ ); + break; + + default: + ShouldNotReachHere(); + } +} + +// when bailing out of cms in concurrent mode failure +TraceCMSMemoryManagerStats::TraceCMSMemoryManagerStats(): TraceMemoryManagerStats() { + initialize(true /* fullGC */ , + true /* recordGCBeginTime */, + true /* recordPreGCUsage */, + true /* recordPeakUsage */, + true /* recordPostGCusage */, + true /* recordAccumulatedGCTime */, + true /* recordGCEndTime */, + true /* countCollection */ ); +} + diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp index 451538472fd..16ed981bbe8 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp @@ -507,6 +507,7 @@ class CMSCollector: public CHeapObj { friend class VM_CMS_Operation; friend class VM_CMS_Initial_Mark; friend class VM_CMS_Final_Remark; + friend class TraceCMSMemoryManagerStats; private: jlong _time_of_last_gc; @@ -1858,3 +1859,11 @@ public: _dead_bit_map(dead_bit_map) {} size_t do_blk(HeapWord* addr); }; + +class TraceCMSMemoryManagerStats : public TraceMemoryManagerStats { + + public: + TraceCMSMemoryManagerStats(CMSCollector::CollectorState phase); + TraceCMSMemoryManagerStats(); +}; + diff --git a/hotspot/src/share/vm/gc_implementation/includeDB_gc_concurrentMarkSweep b/hotspot/src/share/vm/gc_implementation/includeDB_gc_concurrentMarkSweep index 7cda699d3ce..99c5b3ae271 100644 --- a/hotspot/src/share/vm/gc_implementation/includeDB_gc_concurrentMarkSweep +++ b/hotspot/src/share/vm/gc_implementation/includeDB_gc_concurrentMarkSweep @@ -149,6 +149,7 @@ concurrentMarkSweepGeneration.cpp isGCActiveMark.hpp concurrentMarkSweepGeneration.cpp iterator.hpp concurrentMarkSweepGeneration.cpp java.hpp concurrentMarkSweepGeneration.cpp jvmtiExport.hpp +concurrentMarkSweepGeneration.cpp memoryService.hpp concurrentMarkSweepGeneration.cpp oop.inline.hpp concurrentMarkSweepGeneration.cpp parNewGeneration.hpp concurrentMarkSweepGeneration.cpp referencePolicy.hpp @@ -165,6 +166,7 @@ concurrentMarkSweepGeneration.hpp gSpaceCounters.hpp concurrentMarkSweepGeneration.hpp gcStats.hpp concurrentMarkSweepGeneration.hpp generation.hpp concurrentMarkSweepGeneration.hpp generationCounters.hpp +concurrentMarkSweepGeneration.hpp memoryService.hpp concurrentMarkSweepGeneration.hpp mutexLocker.hpp concurrentMarkSweepGeneration.hpp taskqueue.hpp concurrentMarkSweepGeneration.hpp virtualspace.hpp diff --git a/hotspot/src/share/vm/services/management.cpp b/hotspot/src/share/vm/services/management.cpp index dd6c3bc9b0e..c91a1acfc96 100644 --- a/hotspot/src/share/vm/services/management.cpp +++ b/hotspot/src/share/vm/services/management.cpp @@ -1900,16 +1900,15 @@ JVM_ENTRY(void, jmm_GetLastGCStat(JNIEnv *env, jobject obj, jmmGCStat *gc_stat)) // Get the GCMemoryManager GCMemoryManager* mgr = get_gc_memory_manager_from_jobject(obj, CHECK); - if (mgr->last_gc_stat() == NULL) { - gc_stat->gc_index = 0; - return; - } // Make a copy of the last GC statistics // GC may occur while constructing the last GC information int num_pools = MemoryService::num_memory_pools(); GCStatInfo* stat = new GCStatInfo(num_pools); - stat->copy_stat(mgr->last_gc_stat()); + if (mgr->get_last_gc_stat(stat) == 0) { + gc_stat->gc_index = 0; + return; + } gc_stat->gc_index = stat->gc_index(); gc_stat->start_time = Management::ticks_to_ms(stat->start_time()); diff --git a/hotspot/src/share/vm/services/memoryManager.cpp b/hotspot/src/share/vm/services/memoryManager.cpp index 0266b331dcd..4921ae1f569 100644 --- a/hotspot/src/share/vm/services/memoryManager.cpp +++ b/hotspot/src/share/vm/services/memoryManager.cpp @@ -166,17 +166,6 @@ GCStatInfo::~GCStatInfo() { FREE_C_HEAP_ARRAY(MemoryUsage*, _after_gc_usage_array); } -void GCStatInfo::copy_stat(GCStatInfo* stat) { - set_index(stat->gc_index()); - set_start_time(stat->start_time()); - set_end_time(stat->end_time()); - assert(_usage_array_size == stat->usage_array_size(), "Must have same array size"); - for (int i = 0; i < _usage_array_size; i++) { - set_before_gc_usage(i, stat->before_gc_usage_for_pool(i)); - set_after_gc_usage(i, stat->after_gc_usage_for_pool(i)); - } -} - void GCStatInfo::set_gc_usage(int pool_index, MemoryUsage usage, bool before_gc) { MemoryUsage* gc_usage_array; if (before_gc) { @@ -187,67 +176,129 @@ void GCStatInfo::set_gc_usage(int pool_index, MemoryUsage usage, bool before_gc) gc_usage_array[pool_index] = usage; } +void GCStatInfo::clear() { + _index = 0; + _start_time = 0L; + _end_time = 0L; + size_t len = _usage_array_size * sizeof(MemoryUsage); + memset(_before_gc_usage_array, 0, len); + memset(_after_gc_usage_array, 0, len); +} + + GCMemoryManager::GCMemoryManager() : MemoryManager() { _num_collections = 0; _last_gc_stat = NULL; + _last_gc_lock = new Mutex(Mutex::leaf, "_last_gc_lock", true); + _current_gc_stat = NULL; _num_gc_threads = 1; } GCMemoryManager::~GCMemoryManager() { delete _last_gc_stat; + delete _last_gc_lock; + delete _current_gc_stat; } void GCMemoryManager::initialize_gc_stat_info() { assert(MemoryService::num_memory_pools() > 0, "should have one or more memory pools"); _last_gc_stat = new GCStatInfo(MemoryService::num_memory_pools()); + _current_gc_stat = new GCStatInfo(MemoryService::num_memory_pools()); + // tracking concurrent collections we need two objects: one to update, and one to + // hold the publicly available "last (completed) gc" information. } -void GCMemoryManager::gc_begin() { - assert(_last_gc_stat != NULL, "Just checking"); - _accumulated_timer.start(); - _num_collections++; - _last_gc_stat->set_index(_num_collections); - _last_gc_stat->set_start_time(Management::timestamp()); +void GCMemoryManager::gc_begin(bool recordGCBeginTime, bool recordPreGCUsage, + bool recordAccumulatedGCTime) { + assert(_last_gc_stat != NULL && _current_gc_stat != NULL, "Just checking"); + if (recordAccumulatedGCTime) { + _accumulated_timer.start(); + } + // _num_collections now increases in gc_end, to count completed collections + if (recordGCBeginTime) { + _current_gc_stat->set_index(_num_collections+1); + _current_gc_stat->set_start_time(Management::timestamp()); + } - // Keep memory usage of all memory pools - for (int i = 0; i < MemoryService::num_memory_pools(); i++) { - MemoryPool* pool = MemoryService::get_memory_pool(i); - MemoryUsage usage = pool->get_memory_usage(); - _last_gc_stat->set_before_gc_usage(i, usage); - HS_DTRACE_PROBE8(hotspot, mem__pool__gc__begin, - name(), strlen(name()), - pool->name(), strlen(pool->name()), - usage.init_size(), usage.used(), - usage.committed(), usage.max_size()); + if (recordPreGCUsage) { + // Keep memory usage of all memory pools + for (int i = 0; i < MemoryService::num_memory_pools(); i++) { + MemoryPool* pool = MemoryService::get_memory_pool(i); + MemoryUsage usage = pool->get_memory_usage(); + _current_gc_stat->set_before_gc_usage(i, usage); + HS_DTRACE_PROBE8(hotspot, mem__pool__gc__begin, + name(), strlen(name()), + pool->name(), strlen(pool->name()), + usage.init_size(), usage.used(), + usage.committed(), usage.max_size()); + } } } -void GCMemoryManager::gc_end() { - _accumulated_timer.stop(); - _last_gc_stat->set_end_time(Management::timestamp()); - - int i; - // keep the last gc statistics for all memory pools - for (i = 0; i < MemoryService::num_memory_pools(); i++) { - MemoryPool* pool = MemoryService::get_memory_pool(i); - MemoryUsage usage = pool->get_memory_usage(); - - HS_DTRACE_PROBE8(hotspot, mem__pool__gc__end, - name(), strlen(name()), - pool->name(), strlen(pool->name()), - usage.init_size(), usage.used(), - usage.committed(), usage.max_size()); - - _last_gc_stat->set_after_gc_usage(i, usage); +// A collector MUST, even if it does not complete for some reason, +// make a TraceMemoryManagerStats object where countCollection is true, +// to ensure the current gc stat is placed in _last_gc_stat. +void GCMemoryManager::gc_end(bool recordPostGCUsage, + bool recordAccumulatedGCTime, + bool recordGCEndTime, bool countCollection) { + if (recordAccumulatedGCTime) { + _accumulated_timer.stop(); + } + if (recordGCEndTime) { + _current_gc_stat->set_end_time(Management::timestamp()); } - // Set last collection usage of the memory pools managed by this collector - for (i = 0; i < num_memory_pools(); i++) { - MemoryPool* pool = get_memory_pool(i); - MemoryUsage usage = pool->get_memory_usage(); + if (recordPostGCUsage) { + int i; + // keep the last gc statistics for all memory pools + for (i = 0; i < MemoryService::num_memory_pools(); i++) { + MemoryPool* pool = MemoryService::get_memory_pool(i); + MemoryUsage usage = pool->get_memory_usage(); - // Compare with GC usage threshold - pool->set_last_collection_usage(usage); - LowMemoryDetector::detect_after_gc_memory(pool); + HS_DTRACE_PROBE8(hotspot, mem__pool__gc__end, + name(), strlen(name()), + pool->name(), strlen(pool->name()), + usage.init_size(), usage.used(), + usage.committed(), usage.max_size()); + + _current_gc_stat->set_after_gc_usage(i, usage); + } + + // Set last collection usage of the memory pools managed by this collector + for (i = 0; i < num_memory_pools(); i++) { + MemoryPool* pool = get_memory_pool(i); + MemoryUsage usage = pool->get_memory_usage(); + + // Compare with GC usage threshold + pool->set_last_collection_usage(usage); + LowMemoryDetector::detect_after_gc_memory(pool); + } + } + if (countCollection) { + _num_collections++; + // alternately update two objects making one public when complete + { + MutexLockerEx ml(_last_gc_lock, Mutex::_no_safepoint_check_flag); + GCStatInfo *tmp = _last_gc_stat; + _last_gc_stat = _current_gc_stat; + _current_gc_stat = tmp; + // reset the current stat for diagnosability purposes + _current_gc_stat->clear(); + } } } + +size_t GCMemoryManager::get_last_gc_stat(GCStatInfo* dest) { + MutexLockerEx ml(_last_gc_lock, Mutex::_no_safepoint_check_flag); + if (_last_gc_stat->gc_index() != 0) { + dest->set_index(_last_gc_stat->gc_index()); + dest->set_start_time(_last_gc_stat->start_time()); + dest->set_end_time(_last_gc_stat->end_time()); + assert(dest->usage_array_size() == _last_gc_stat->usage_array_size(), + "Must have same array size"); + size_t len = dest->usage_array_size() * sizeof(MemoryUsage); + memcpy(dest->before_gc_usage_array(), _last_gc_stat->before_gc_usage_array(), len); + memcpy(dest->after_gc_usage_array(), _last_gc_stat->after_gc_usage_array(), len); + } + return _last_gc_stat->gc_index(); +} diff --git a/hotspot/src/share/vm/services/memoryManager.hpp b/hotspot/src/share/vm/services/memoryManager.hpp index 70b7eef717b..7399141a83a 100644 --- a/hotspot/src/share/vm/services/memoryManager.hpp +++ b/hotspot/src/share/vm/services/memoryManager.hpp @@ -131,6 +131,9 @@ public: return _after_gc_usage_array[pool_index]; } + MemoryUsage* before_gc_usage_array() { return _before_gc_usage_array; } + MemoryUsage* after_gc_usage_array() { return _after_gc_usage_array; } + void set_index(size_t index) { _index = index; } void set_start_time(jlong time) { _start_time = time; } void set_end_time(jlong time) { _end_time = time; } @@ -143,7 +146,7 @@ public: set_gc_usage(pool_index, usage, false /* after gc */); } - void copy_stat(GCStatInfo* stat); + void clear(); }; class GCMemoryManager : public MemoryManager { @@ -153,6 +156,8 @@ private: elapsedTimer _accumulated_timer; elapsedTimer _gc_timer; // for measuring every GC duration GCStatInfo* _last_gc_stat; + Mutex* _last_gc_lock; + GCStatInfo* _current_gc_stat; int _num_gc_threads; public: GCMemoryManager(); @@ -166,11 +171,16 @@ public: int num_gc_threads() { return _num_gc_threads; } void set_num_gc_threads(int count) { _num_gc_threads = count; } - void gc_begin(); - void gc_end(); + void gc_begin(bool recordGCBeginTime, bool recordPreGCUsage, + bool recordAccumulatedGCTime); + void gc_end(bool recordPostGCUsage, bool recordAccumulatedGCTime, + bool recordGCEndTime, bool countCollection); void reset_gc_stat() { _num_collections = 0; _accumulated_timer.reset(); } - GCStatInfo* last_gc_stat() { return _last_gc_stat; } + + // Copy out _last_gc_stat to the given destination, returning + // the collection count. Zero signifies no gc has taken place. + size_t get_last_gc_stat(GCStatInfo* dest); virtual MemoryManager::Name kind() = 0; }; diff --git a/hotspot/src/share/vm/services/memoryService.cpp b/hotspot/src/share/vm/services/memoryService.cpp index 3c55fffadae..c50a4fefe68 100644 --- a/hotspot/src/share/vm/services/memoryService.cpp +++ b/hotspot/src/share/vm/services/memoryService.cpp @@ -509,7 +509,10 @@ void MemoryService::track_memory_pool_usage(MemoryPool* pool) { } } -void MemoryService::gc_begin(bool fullGC) { +void MemoryService::gc_begin(bool fullGC, bool recordGCBeginTime, + bool recordAccumulatedGCTime, + bool recordPreGCUsage, bool recordPeakUsage) { + GCMemoryManager* mgr; if (fullGC) { mgr = _major_gc_manager; @@ -517,16 +520,21 @@ void MemoryService::gc_begin(bool fullGC) { mgr = _minor_gc_manager; } assert(mgr->is_gc_memory_manager(), "Sanity check"); - mgr->gc_begin(); + mgr->gc_begin(recordGCBeginTime, recordPreGCUsage, recordAccumulatedGCTime); // Track the peak memory usage when GC begins - for (int i = 0; i < _pools_list->length(); i++) { - MemoryPool* pool = _pools_list->at(i); - pool->record_peak_memory_usage(); + if (recordPeakUsage) { + for (int i = 0; i < _pools_list->length(); i++) { + MemoryPool* pool = _pools_list->at(i); + pool->record_peak_memory_usage(); + } } } -void MemoryService::gc_end(bool fullGC) { +void MemoryService::gc_end(bool fullGC, bool recordPostGCUsage, + bool recordAccumulatedGCTime, + bool recordGCEndTime, bool countCollection) { + GCMemoryManager* mgr; if (fullGC) { mgr = (GCMemoryManager*) _major_gc_manager; @@ -536,7 +544,8 @@ void MemoryService::gc_end(bool fullGC) { assert(mgr->is_gc_memory_manager(), "Sanity check"); // register the GC end statistics and memory usage - mgr->gc_end(); + mgr->gc_end(recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime, + countCollection); } void MemoryService::oops_do(OopClosure* f) { @@ -585,12 +594,12 @@ Handle MemoryService::create_MemoryUsage_obj(MemoryUsage usage, TRAPS) { return obj; } // -// GC manager type depends on the type of Generation. Depending the space -// availablity and vm option the gc uses major gc manager or minor gc +// GC manager type depends on the type of Generation. Depending on the space +// availablity and vm options the gc uses major gc manager or minor gc // manager or both. The type of gc manager depends on the generation kind. -// For DefNew, ParNew and ASParNew generation doing scavange gc uses minor -// gc manager (so _fullGC is set to false ) and for other generation kind -// DOing mark-sweep-compact uses major gc manager (so _fullGC is set +// For DefNew, ParNew and ASParNew generation doing scavenge gc uses minor +// gc manager (so _fullGC is set to false ) and for other generation kinds +// doing mark-sweep-compact uses major gc manager (so _fullGC is set // to true). TraceMemoryManagerStats::TraceMemoryManagerStats(Generation::Name kind) { switch (kind) { @@ -611,13 +620,48 @@ TraceMemoryManagerStats::TraceMemoryManagerStats(Generation::Name kind) { default: assert(false, "Unrecognized gc generation kind."); } - MemoryService::gc_begin(_fullGC); + // this has to be called in a stop the world pause and represent + // an entire gc pause, start to finish: + initialize(_fullGC, true, true, true, true, true, true, true); } -TraceMemoryManagerStats::TraceMemoryManagerStats(bool fullGC) { +TraceMemoryManagerStats::TraceMemoryManagerStats(bool fullGC, + bool recordGCBeginTime, + bool recordPreGCUsage, + bool recordPeakUsage, + bool recordPostGCUsage, + bool recordAccumulatedGCTime, + bool recordGCEndTime, + bool countCollection) { + initialize(fullGC, recordGCBeginTime, recordPreGCUsage, recordPeakUsage, + recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime, + countCollection); +} + +// for a subclass to create then initialize an instance before invoking +// the MemoryService +void TraceMemoryManagerStats::initialize(bool fullGC, + bool recordGCBeginTime, + bool recordPreGCUsage, + bool recordPeakUsage, + bool recordPostGCUsage, + bool recordAccumulatedGCTime, + bool recordGCEndTime, + bool countCollection) { _fullGC = fullGC; - MemoryService::gc_begin(_fullGC); + _recordGCBeginTime = recordGCBeginTime; + _recordPreGCUsage = recordPreGCUsage; + _recordPeakUsage = recordPeakUsage; + _recordPostGCUsage = recordPostGCUsage; + _recordAccumulatedGCTime = recordAccumulatedGCTime; + _recordGCEndTime = recordGCEndTime; + _countCollection = countCollection; + + MemoryService::gc_begin(_fullGC, _recordGCBeginTime, _recordAccumulatedGCTime, + _recordPreGCUsage, _recordPeakUsage); } TraceMemoryManagerStats::~TraceMemoryManagerStats() { - MemoryService::gc_end(_fullGC); + MemoryService::gc_end(_fullGC, _recordPostGCUsage, _recordAccumulatedGCTime, + _recordGCEndTime, _countCollection); } + diff --git a/hotspot/src/share/vm/services/memoryService.hpp b/hotspot/src/share/vm/services/memoryService.hpp index a21f3d1dc7f..cf26f1987bb 100644 --- a/hotspot/src/share/vm/services/memoryService.hpp +++ b/hotspot/src/share/vm/services/memoryService.hpp @@ -149,8 +149,13 @@ public: } static void track_memory_pool_usage(MemoryPool* pool); - static void gc_begin(bool fullGC); - static void gc_end(bool fullGC); + static void gc_begin(bool fullGC, bool recordGCBeginTime, + bool recordAccumulatedGCTime, + bool recordPreGCUsage, bool recordPeakUsage); + static void gc_end(bool fullGC, bool recordPostGCUsage, + bool recordAccumulatedGCTime, + bool recordGCEndTime, bool countCollection); + static void oops_do(OopClosure* f); @@ -164,8 +169,34 @@ public: class TraceMemoryManagerStats : public StackObj { private: bool _fullGC; + bool _recordGCBeginTime; + bool _recordPreGCUsage; + bool _recordPeakUsage; + bool _recordPostGCUsage; + bool _recordAccumulatedGCTime; + bool _recordGCEndTime; + bool _countCollection; + public: - TraceMemoryManagerStats(bool fullGC); + TraceMemoryManagerStats() {} + TraceMemoryManagerStats(bool fullGC, + bool recordGCBeginTime = true, + bool recordPreGCUsage = true, + bool recordPeakUsage = true, + bool recordPostGCUsage = true, + bool recordAccumulatedGCTime = true, + bool recordGCEndTime = true, + bool countCollection = true); + + void initialize(bool fullGC, + bool recordGCBeginTime, + bool recordPreGCUsage, + bool recordPeakUsage, + bool recordPostGCUsage, + bool recordAccumulatedGCTime, + bool recordGCEndTime, + bool countCollection); + TraceMemoryManagerStats(Generation::Name kind); ~TraceMemoryManagerStats(); }; diff --git a/hotspot/test/gc/6581734/Test6581734.java b/hotspot/test/gc/6581734/Test6581734.java new file mode 100644 index 00000000000..1967f00880c --- /dev/null +++ b/hotspot/test/gc/6581734/Test6581734.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2010, 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 Test6581734.java + * @bug 6581734 + * @summary CMS Old Gen's collection usage is zero after GC which is incorrect + * @run main/othervm -Xmx512m -verbose:gc -XX:+UseConcMarkSweepGC Test6581734 + * + */ +import java.util.*; +import java.lang.management.*; + +// 6581734 states that memory pool usage via the mbean is wrong +// for CMS (zero, even after a collection). +// +// 6580448 states that the collection count similarly is wrong +// (stays at zero for CMS collections) +// -- closed as dup of 6581734 as the same fix resolves both. + + +public class Test6581734 { + + private String poolName = "CMS"; + private String collectorName = "ConcurrentMarkSweep"; + + public static void main(String [] args) { + + Test6581734 t = null; + if (args.length==2) { + t = new Test6581734(args[0], args[1]); + } else { + System.out.println("Defaulting to monitor CMS pool and collector."); + t = new Test6581734(); + } + t.run(); + } + + public Test6581734(String pool, String collector) { + poolName = pool; + collectorName = collector; + } + + public Test6581734() { + } + + public void run() { + // Use some memory, enough that we expect collections should + // have happened. + // Must run with options to ensure no stop the world full GC, + // but e.g. at least one CMS cycle. + allocationWork(300*1024*1024); + System.out.println("Done allocationWork"); + + // Verify some non-zero results are stored. + List pools = ManagementFactory.getMemoryPoolMXBeans(); + int poolsFound = 0; + int poolsWithStats = 0; + for (int i=0; i collectors = ManagementFactory.getGarbageCollectorMXBeans(); + int collectorsFound = 0; + int collectorsWithTime= 0; + for (int i=0; i 0) { + collectorsWithTime++; + } + } + } + // verify: + if (poolsWithStats < poolsFound) { + throw new RuntimeException("pools found with zero stats"); + } + + if (collectorsWithTime Date: Mon, 2 Aug 2010 12:51:43 -0700 Subject: [PATCH 002/100] 6814437: G1: remove the _new_refs array The per-worker _new_refs array is used to hold references that point into the collection set. It is populated during RSet updating and subsequently processed. In the event of an evacuation failure it processed again to recreate the RSets of regions in the collection set. Remove the per-worker _new_refs array by processing the references directly. Use a DirtyCardQueue to hold the cards containing the references so that the RSets of regions in the collection set can be recreated when handling an evacuation failure. Reviewed-by: iveresov, jmasa, tonyp --- .../g1/concurrentG1Refine.cpp | 18 +- .../g1/concurrentG1Refine.hpp | 4 +- .../gc_implementation/g1/dirtyCardQueue.cpp | 32 +- .../gc_implementation/g1/dirtyCardQueue.hpp | 21 +- .../gc_implementation/g1/g1CollectedHeap.cpp | 49 ++- .../gc_implementation/g1/g1CollectedHeap.hpp | 17 +- .../g1/g1CollectorPolicy.cpp | 2 - .../g1/g1CollectorPolicy.hpp | 11 - .../g1/g1OopClosures.inline.hpp | 8 +- .../vm/gc_implementation/g1/g1RemSet.cpp | 336 +++++++++++++----- .../vm/gc_implementation/g1/g1RemSet.hpp | 53 ++- .../gc_implementation/g1/g1RemSet.inline.hpp | 31 +- .../vm/gc_implementation/g1/heapRegion.cpp | 2 + .../vm/gc_implementation/includeDB_gc_g1 | 3 +- 14 files changed, 407 insertions(+), 180 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp b/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp index 78d3d5cb2cd..e144aa7cc86 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp @@ -339,7 +339,9 @@ jbyte* ConcurrentG1Refine::cache_insert(jbyte* card_ptr, bool* defer) { return res; } -void ConcurrentG1Refine::clean_up_cache(int worker_i, G1RemSet* g1rs) { +void ConcurrentG1Refine::clean_up_cache(int worker_i, + G1RemSet* g1rs, + DirtyCardQueue* into_cset_dcq) { assert(!use_cache(), "cache should be disabled"); int start_idx; @@ -353,7 +355,19 @@ void ConcurrentG1Refine::clean_up_cache(int worker_i, G1RemSet* g1rs) { for (int i = start_idx; i < end_idx; i++) { jbyte* entry = _hot_cache[i]; if (entry != NULL) { - g1rs->concurrentRefineOneCard(entry, worker_i); + if (g1rs->concurrentRefineOneCard(entry, worker_i, true)) { + // 'entry' contains references that point into the current + // collection set. We need to record 'entry' in the DCQS + // that's used for that purpose. + // + // The only time we care about recording cards that contain + // references that point into the collection set is during + // RSet updating while within an evacuation pause. + // In this case worker_i should be the id of a GC worker thread + assert(SafepointSynchronize::is_at_safepoint(), "not during an evacuation pause"); + assert(worker_i < (int) DirtyCardQueueSet::num_par_ids(), "incorrect worker id"); + into_cset_dcq->enqueue(entry); + } } } } diff --git a/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp b/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp index 85a50f7c7d5..f5c3653f888 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2010, 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 @@ -184,7 +184,7 @@ class ConcurrentG1Refine: public CHeapObj { jbyte* cache_insert(jbyte* card_ptr, bool* defer); // Process the cached entries. - void clean_up_cache(int worker_i, G1RemSet* g1rs); + void clean_up_cache(int worker_i, G1RemSet* g1rs, DirtyCardQueue* into_cset_dcq); // Set up for parallel processing of the cards in the hot cache void clear_hot_cache_claimed_index() { diff --git a/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp b/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp index 760f3630129..c8100c4770e 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2010, 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 @@ -178,13 +178,14 @@ DirtyCardQueueSet::get_completed_buffer(int stop_at) { } bool DirtyCardQueueSet:: -apply_closure_to_completed_buffer_helper(int worker_i, +apply_closure_to_completed_buffer_helper(CardTableEntryClosure* cl, + int worker_i, BufferNode* nd) { if (nd != NULL) { void **buf = BufferNode::make_buffer_from_node(nd); size_t index = nd->index(); bool b = - DirtyCardQueue::apply_closure_to_buffer(_closure, buf, + DirtyCardQueue::apply_closure_to_buffer(cl, buf, index, _sz, true, worker_i); if (b) { @@ -199,17 +200,24 @@ apply_closure_to_completed_buffer_helper(int worker_i, } } -bool DirtyCardQueueSet::apply_closure_to_completed_buffer(int worker_i, +bool DirtyCardQueueSet::apply_closure_to_completed_buffer(CardTableEntryClosure* cl, + int worker_i, int stop_at, - bool during_pause) -{ + bool during_pause) { assert(!during_pause || stop_at == 0, "Should not leave any completed buffers during a pause"); BufferNode* nd = get_completed_buffer(stop_at); - bool res = apply_closure_to_completed_buffer_helper(worker_i, nd); + bool res = apply_closure_to_completed_buffer_helper(cl, worker_i, nd); if (res) Atomic::inc(&_processed_buffers_rs_thread); return res; } +bool DirtyCardQueueSet::apply_closure_to_completed_buffer(int worker_i, + int stop_at, + bool during_pause) { + return apply_closure_to_completed_buffer(_closure, worker_i, + stop_at, during_pause); +} + void DirtyCardQueueSet::apply_closure_to_all_completed_buffers() { BufferNode* nd = _completed_buffers_head; while (nd != NULL) { @@ -222,8 +230,8 @@ void DirtyCardQueueSet::apply_closure_to_all_completed_buffers() { } } -void DirtyCardQueueSet::abandon_logs() { - assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint."); +// Deallocates any completed log buffers +void DirtyCardQueueSet::clear() { BufferNode* buffers_to_delete = NULL; { MutexLockerEx x(_cbl_mon, Mutex::_no_safepoint_check_flag); @@ -242,6 +250,12 @@ void DirtyCardQueueSet::abandon_logs() { buffers_to_delete = nd->next(); deallocate_buffer(BufferNode::make_buffer_from_node(nd)); } + +} + +void DirtyCardQueueSet::abandon_logs() { + assert(SafepointSynchronize::is_at_safepoint(), "Must be at safepoint."); + clear(); // Since abandon is done only at safepoints, we can safely manipulate // these queues. for (JavaThread* t = Threads::first(); t; t = t->next()) { diff --git a/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp b/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp index e1b68981648..524c0c25681 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2010, 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 @@ -123,7 +123,21 @@ public: int stop_at = 0, bool during_pause = false); - bool apply_closure_to_completed_buffer_helper(int worker_i, + // If there exists some completed buffer, pop it, then apply the + // specified closure to all its elements, nulling out those elements + // processed. If all elements are processed, returns "true". If no + // completed buffers exist, returns false. If a completed buffer exists, + // but is only partially completed before a "yield" happens, the + // partially completed buffer (with its processed elements set to NULL) + // is returned to the completed buffer set, and this call returns false. + bool apply_closure_to_completed_buffer(CardTableEntryClosure* cl, + int worker_i = 0, + int stop_at = 0, + bool during_pause = false); + + // Helper routine for the above. + bool apply_closure_to_completed_buffer_helper(CardTableEntryClosure* cl, + int worker_i, BufferNode* nd); BufferNode* get_completed_buffer(int stop_at); @@ -136,6 +150,9 @@ public: return &_shared_dirty_card_queue; } + // Deallocate any completed log buffers + void clear(); + // If a full collection is happening, reset partial logs, and ignore // completed ones: the full collection will make them all irrelevant. void abandon_logs(); diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp index 658ac777f49..6d798a54889 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp @@ -56,7 +56,12 @@ public: _sts(sts), _g1rs(g1rs), _cg1r(cg1r), _concurrent(true) {} bool do_card_ptr(jbyte* card_ptr, int worker_i) { - _g1rs->concurrentRefineOneCard(card_ptr, worker_i); + bool oops_into_cset = _g1rs->concurrentRefineOneCard(card_ptr, worker_i, false); + // This path is executed by the concurrent refine or mutator threads, + // concurrently, and so we do not care if card_ptr contains references + // that point into the collection set. + assert(!oops_into_cset, "should be"); + if (_concurrent && _sts->should_yield()) { // Caller will actually yield. return false; @@ -1322,6 +1327,7 @@ G1CollectedHeap::G1CollectedHeap(G1CollectorPolicy* policy_) : SharedHeap(policy_), _g1_policy(policy_), _dirty_card_queue_set(false), + _into_cset_dirty_card_queue_set(false), _ref_processor(NULL), _process_strong_tasks(new SubTasksDone(G1H_PS_NumElements)), _bot_shared(NULL), @@ -1572,6 +1578,16 @@ jint G1CollectedHeap::initialize() { Shared_DirtyCardQ_lock, &JavaThread::dirty_card_queue_set()); } + + // Initialize the card queue set used to hold cards containing + // references into the collection set. + _into_cset_dirty_card_queue_set.initialize(DirtyCardQ_CBL_mon, + DirtyCardQ_FL_lock, + -1, // never trigger processing + -1, // no limit on length + Shared_DirtyCardQ_lock, + &JavaThread::dirty_card_queue_set()); + // In case we're keeping closure specialization stats, initialize those // counts and that mechanism. SpecializationStats::clear(); @@ -1603,14 +1619,16 @@ size_t G1CollectedHeap::capacity() const { return _g1_committed.byte_size(); } -void G1CollectedHeap::iterate_dirty_card_closure(bool concurrent, +void G1CollectedHeap::iterate_dirty_card_closure(CardTableEntryClosure* cl, + DirtyCardQueue* into_cset_dcq, + bool concurrent, int worker_i) { // Clean cards in the hot card cache - concurrent_g1_refine()->clean_up_cache(worker_i, g1_rem_set()); + concurrent_g1_refine()->clean_up_cache(worker_i, g1_rem_set(), into_cset_dcq); DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set(); int n_completed_buffers = 0; - while (dcqs.apply_closure_to_completed_buffer(worker_i, 0, true)) { + while (dcqs.apply_closure_to_completed_buffer(cl, worker_i, 0, true)) { n_completed_buffers++; } g1_policy()->record_update_rs_processed_buffers(worker_i, @@ -3346,25 +3364,6 @@ public: } }; -class UpdateRSetImmediate : public OopsInHeapRegionClosure { -private: - G1CollectedHeap* _g1; - G1RemSet* _g1_rem_set; -public: - UpdateRSetImmediate(G1CollectedHeap* g1) : - _g1(g1), _g1_rem_set(g1->g1_rem_set()) {} - - virtual void do_oop(narrowOop* p) { do_oop_work(p); } - virtual void do_oop( oop* p) { do_oop_work(p); } - template void do_oop_work(T* p) { - assert(_from->is_in_reserved(p), "paranoia"); - T heap_oop = oopDesc::load_heap_oop(p); - if (!oopDesc::is_null(heap_oop) && !_from->is_survivor()) { - _g1_rem_set->par_write_ref(_from, p, 0); - } - } -}; - class UpdateRSetDeferred : public OopsInHeapRegionClosure { private: G1CollectedHeap* _g1; @@ -3389,8 +3388,6 @@ public: } }; - - class RemoveSelfPointerClosure: public ObjectClosure { private: G1CollectedHeap* _g1; @@ -3453,7 +3450,7 @@ public: }; void G1CollectedHeap::remove_self_forwarding_pointers() { - UpdateRSetImmediate immediate_update(_g1h); + UpdateRSetImmediate immediate_update(_g1h->g1_rem_set()); DirtyCardQueue dcq(&_g1h->dirty_card_queue_set()); UpdateRSetDeferred deferred_update(_g1h, &dcq); OopsInHeapRegionClosure *cl; diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp index 74606c18bdf..6daaf614f44 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp @@ -505,6 +505,12 @@ protected: // A function to check the consistency of dirty card logs. void check_ct_logs_at_safepoint(); + // A DirtyCardQueueSet that is used to hold cards that contain + // references into the current collection set. This is used to + // update the remembered sets of the regions in the collection + // set in the event of an evacuation failure. + DirtyCardQueueSet _into_cset_dirty_card_queue_set; + // After a collection pause, make the regions in the CS into free // regions. void free_collection_set(HeapRegion* cs_head); @@ -661,6 +667,13 @@ public: // A set of cards where updates happened during the GC DirtyCardQueueSet& dirty_card_queue_set() { return _dirty_card_queue_set; } + // A DirtyCardQueueSet that is used to hold cards that contain + // references into the current collection set. This is used to + // update the remembered sets of the regions in the collection + // set in the event of an evacuation failure. + DirtyCardQueueSet& into_cset_dirty_card_queue_set() + { return _into_cset_dirty_card_queue_set; } + // Create a G1CollectedHeap with the specified policy. // Must call the initialize method afterwards. // May not return if something goes wrong. @@ -715,7 +728,9 @@ public: OrderAccess::fence(); } - void iterate_dirty_card_closure(bool concurrent, int worker_i); + void iterate_dirty_card_closure(CardTableEntryClosure* cl, + DirtyCardQueue* into_cset_dcq, + bool concurrent, int worker_i); // The shared block offset table array. G1BlockOffsetSharedArray* bot_shared() const { return _bot_shared; } diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp index 0cb73eb0c6b..2feaae7df3a 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp @@ -238,7 +238,6 @@ G1CollectorPolicy::G1CollectorPolicy() : _par_last_update_rs_processed_buffers = new double[_parallel_gc_threads]; _par_last_scan_rs_times_ms = new double[_parallel_gc_threads]; - _par_last_scan_new_refs_times_ms = new double[_parallel_gc_threads]; _par_last_obj_copy_times_ms = new double[_parallel_gc_threads]; @@ -842,7 +841,6 @@ void G1CollectorPolicy::record_collection_pause_start(double start_time_sec, _par_last_update_rs_times_ms[i] = -1234.0; _par_last_update_rs_processed_buffers[i] = -1234.0; _par_last_scan_rs_times_ms[i] = -1234.0; - _par_last_scan_new_refs_times_ms[i] = -1234.0; _par_last_obj_copy_times_ms[i] = -1234.0; _par_last_termination_times_ms[i] = -1234.0; _par_last_termination_attempts[i] = -1234.0; diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp index 61dbee6c09e..84a8491142c 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp @@ -63,8 +63,6 @@ class MainBodySummary: public CHeapObj { define_num_seq(mark_stack_scan) define_num_seq(update_rs) define_num_seq(scan_rs) - define_num_seq(scan_new_refs) // Only for temp use; added to - // in parallel case. define_num_seq(obj_copy) define_num_seq(termination) // parallel only define_num_seq(parallel_other) // parallel only @@ -177,7 +175,6 @@ protected: double* _par_last_update_rs_times_ms; double* _par_last_update_rs_processed_buffers; double* _par_last_scan_rs_times_ms; - double* _par_last_scan_new_refs_times_ms; double* _par_last_obj_copy_times_ms; double* _par_last_termination_times_ms; double* _par_last_termination_attempts; @@ -933,14 +930,6 @@ public: _par_last_scan_rs_times_ms[thread] = ms; } - void record_scan_new_refs_time(int thread, double ms) { - _par_last_scan_new_refs_times_ms[thread] = ms; - } - - double get_scan_new_refs_time(int thread) { - return _par_last_scan_new_refs_times_ms[thread]; - } - void reset_obj_copy_time(int thread) { _par_last_obj_copy_times_ms[thread] = 0.0; } diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp index 1037b83bd48..28ec22b04f3 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2010, 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 @@ -37,7 +37,8 @@ template inline void FilterIntoCSClosure::do_oop_nv(T* p) { _g1->obj_in_cs(oopDesc::decode_heap_oop_not_null(heap_oop))) { _oc->do_oop(p); #if FILTERINTOCSCLOSURE_DOHISTOGRAMCOUNT - _dcto_cl->incr_count(); + if (_dcto_cl != NULL) + _dcto_cl->incr_count(); #endif } } @@ -113,7 +114,10 @@ template inline void G1ParPushHeapRSClosure::do_oop_nv(T* p) { if (_g1->in_cset_fast_test(obj)) { Prefetch::write(obj->mark_addr(), 0); Prefetch::read(obj->mark_addr(), (HeapWordSize*2)); + + // Place on the references queue _par_scan_state->push_on_queue(p); } } } + diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp index a3a77ed186b..9c081fce5af 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp @@ -122,23 +122,24 @@ public: HRInto_G1RemSet::HRInto_G1RemSet(G1CollectedHeap* g1, CardTableModRefBS* ct_bs) : G1RemSet(g1), _ct_bs(ct_bs), _g1p(_g1->g1_policy()), _cg1r(g1->concurrent_g1_refine()), - _par_traversal_in_progress(false), _new_refs(NULL), + _par_traversal_in_progress(false), + _cset_rs_update_cl(NULL), _cards_scanned(NULL), _total_cards_scanned(0) { _seq_task = new SubTasksDone(NumSeqTasks); guarantee(n_workers() > 0, "There should be some workers"); - _new_refs = NEW_C_HEAP_ARRAY(GrowableArray*, n_workers()); + _cset_rs_update_cl = NEW_C_HEAP_ARRAY(OopsInHeapRegionClosure*, n_workers()); for (uint i = 0; i < n_workers(); i++) { - _new_refs[i] = new (ResourceObj::C_HEAP) GrowableArray(8192,true); + _cset_rs_update_cl[i] = NULL; } } HRInto_G1RemSet::~HRInto_G1RemSet() { delete _seq_task; for (uint i = 0; i < n_workers(); i++) { - delete _new_refs[i]; + assert(_cset_rs_update_cl[i] == NULL, "it should be"); } - FREE_C_HEAP_ARRAY(GrowableArray*, _new_refs); + FREE_C_HEAP_ARRAY(OopsInHeapRegionClosure*, _cset_rs_update_cl); } void CountNonCleanMemRegionClosure::do_MemRegion(MemRegion mr) { @@ -306,12 +307,45 @@ void HRInto_G1RemSet::scanRS(OopsInHeapRegionClosure* oc, int worker_i) { _g1p->record_scan_rs_time(worker_i, scan_rs_time_sec * 1000.0); } -void HRInto_G1RemSet::updateRS(int worker_i) { - ConcurrentG1Refine* cg1r = _g1->concurrent_g1_refine(); +// Closure used for updating RSets and recording references that +// point into the collection set. Only called during an +// evacuation pause. +class RefineRecordRefsIntoCSCardTableEntryClosure: public CardTableEntryClosure { + G1RemSet* _g1rs; + DirtyCardQueue* _into_cset_dcq; +public: + RefineRecordRefsIntoCSCardTableEntryClosure(G1CollectedHeap* g1h, + DirtyCardQueue* into_cset_dcq) : + _g1rs(g1h->g1_rem_set()), _into_cset_dcq(into_cset_dcq) + {} + bool do_card_ptr(jbyte* card_ptr, int worker_i) { + // The only time we care about recording cards that + // contain references that point into the collection set + // is during RSet updating within an evacuation pause. + // In this case worker_i should be the id of a GC worker thread. + assert(SafepointSynchronize::is_at_safepoint(), "not during an evacuation pause"); + assert(worker_i < (int) DirtyCardQueueSet::num_par_ids(), "should be a GC worker"); + + if (_g1rs->concurrentRefineOneCard(card_ptr, worker_i, true)) { + // 'card_ptr' contains references that point into the collection + // set. We need to record the card in the DCQS + // (G1CollectedHeap::into_cset_dirty_card_queue_set()) + // that's used for that purpose. + // + // Enqueue the card + _into_cset_dcq->enqueue(card_ptr); + } + return true; + } +}; + +void HRInto_G1RemSet::updateRS(DirtyCardQueue* into_cset_dcq, int worker_i) { double start = os::elapsedTime(); - // Apply the appropriate closure to all remaining log entries. - _g1->iterate_dirty_card_closure(false, worker_i); + // Apply the given closure to all remaining log entries. + RefineRecordRefsIntoCSCardTableEntryClosure into_cset_update_rs_cl(_g1, into_cset_dcq); + _g1->iterate_dirty_card_closure(&into_cset_update_rs_cl, into_cset_dcq, false, worker_i); + // Now there should be no dirty cards. if (G1RSLogCheckCardTable) { CountNonCleanMemRegionClosure cl(_g1); @@ -405,33 +439,6 @@ public: } }; -template void -HRInto_G1RemSet::scanNewRefsRS_work(OopsInHeapRegionClosure* oc, - int worker_i) { - double scan_new_refs_start_sec = os::elapsedTime(); - G1CollectedHeap* g1h = G1CollectedHeap::heap(); - CardTableModRefBS* ct_bs = (CardTableModRefBS*) (g1h->barrier_set()); - for (int i = 0; i < _new_refs[worker_i]->length(); i++) { - T* p = (T*) _new_refs[worker_i]->at(i); - oop obj = oopDesc::load_decode_heap_oop(p); - // *p was in the collection set when p was pushed on "_new_refs", but - // another thread may have processed this location from an RS, so it - // might not point into the CS any longer. If so, it's obviously been - // processed, and we don't need to do anything further. - if (g1h->obj_in_cs(obj)) { - HeapRegion* r = g1h->heap_region_containing(p); - - DEBUG_ONLY(HeapRegion* to = g1h->heap_region_containing(obj)); - oc->set_region(r); - // If "p" has already been processed concurrently, this is - // idempotent. - oc->do_oop(p); - } - } - double scan_new_refs_time_ms = (os::elapsedTime() - scan_new_refs_start_sec) * 1000.0; - _g1p->record_scan_new_refs_time(worker_i, scan_new_refs_time_ms); -} - void HRInto_G1RemSet::cleanupHRRS() { HeapRegionRemSet::cleanup(); } @@ -457,6 +464,26 @@ HRInto_G1RemSet::oops_into_collection_set_do(OopsInHeapRegionClosure* oc, count_cl.print_histo(); } + // We cache the value of 'oc' closure into the appropriate slot in the + // _cset_rs_update_cl for this worker + assert(worker_i < (int)n_workers(), "sanity"); + _cset_rs_update_cl[worker_i] = oc; + + // A DirtyCardQueue that is used to hold cards containing references + // that point into the collection set. This DCQ is associated with a + // special DirtyCardQueueSet (see g1CollectedHeap.hpp). Under normal + // circumstances (i.e. the pause successfully completes), these cards + // are just discarded (there's no need to update the RSets of regions + // that were in the collection set - after the pause these regions + // are wholly 'free' of live objects. In the event of an evacuation + // failure the cards/buffers in this queue set are: + // * passed to the DirtyCardQueueSet that is used to manage deferred + // RSet updates, or + // * scanned for references that point into the collection set + // and the RSet of the corresponding region in the collection set + // is updated immediately. + DirtyCardQueue into_cset_dcq(&_g1->into_cset_dirty_card_queue_set()); + if (ParallelGCThreads > 0) { // The two flags below were introduced temporarily to serialize // the updating and scanning of remembered sets. There are some @@ -465,12 +492,10 @@ HRInto_G1RemSet::oops_into_collection_set_do(OopsInHeapRegionClosure* oc, // conditions, we'll revert back to parallel remembered set // updating and scanning. See CRs 6677707 and 6677708. if (G1UseParallelRSetUpdating || (worker_i == 0)) { - updateRS(worker_i); - scanNewRefsRS(oc, worker_i); + updateRS(&into_cset_dcq, worker_i); } else { _g1p->record_update_rs_processed_buffers(worker_i, 0.0); _g1p->record_update_rs_time(worker_i, 0.0); - _g1p->record_scan_new_refs_time(worker_i, 0.0); } if (G1UseParallelRSetScanning || (worker_i == 0)) { scanRS(oc, worker_i); @@ -479,10 +504,12 @@ HRInto_G1RemSet::oops_into_collection_set_do(OopsInHeapRegionClosure* oc, } } else { assert(worker_i == 0, "invariant"); - updateRS(0); - scanNewRefsRS(oc, 0); + updateRS(&into_cset_dcq, 0); scanRS(oc, 0); } + + // We now clear the cached values of _cset_rs_update_cl for this worker + _cset_rs_update_cl[worker_i] = NULL; } void HRInto_G1RemSet:: @@ -519,49 +546,65 @@ class cleanUpIteratorsClosure : public HeapRegionClosure { } }; -class UpdateRSetOopsIntoCSImmediate : public OopClosure { - G1CollectedHeap* _g1; -public: - UpdateRSetOopsIntoCSImmediate(G1CollectedHeap* g1) : _g1(g1) { } - virtual void do_oop(narrowOop* p) { do_oop_work(p); } - virtual void do_oop( oop* p) { do_oop_work(p); } - template void do_oop_work(T* p) { - HeapRegion* to = _g1->heap_region_containing(oopDesc::load_decode_heap_oop(p)); - if (to->in_collection_set()) { - to->rem_set()->add_reference(p, 0); - } - } -}; - -class UpdateRSetOopsIntoCSDeferred : public OopClosure { +// This closure, applied to a DirtyCardQueueSet, is used to immediately +// update the RSets for the regions in the CSet. For each card it iterates +// through the oops which coincide with that card. It scans the reference +// fields in each oop; when it finds an oop that points into the collection +// set, the RSet for the region containing the referenced object is updated. +// Note: _par_traversal_in_progress in the G1RemSet must be FALSE; otherwise +// the UpdateRSetImmediate closure will cause cards to be enqueued on to +// the DCQS that we're iterating over, causing an infinite loop. +class UpdateRSetCardTableEntryIntoCSetClosure: public CardTableEntryClosure { G1CollectedHeap* _g1; CardTableModRefBS* _ct_bs; - DirtyCardQueue* _dcq; public: - UpdateRSetOopsIntoCSDeferred(G1CollectedHeap* g1, DirtyCardQueue* dcq) : - _g1(g1), _ct_bs((CardTableModRefBS*)_g1->barrier_set()), _dcq(dcq) { } - virtual void do_oop(narrowOop* p) { do_oop_work(p); } - virtual void do_oop( oop* p) { do_oop_work(p); } - template void do_oop_work(T* p) { - oop obj = oopDesc::load_decode_heap_oop(p); - if (_g1->obj_in_cs(obj)) { - size_t card_index = _ct_bs->index_for(p); - if (_ct_bs->mark_card_deferred(card_index)) { - _dcq->enqueue((jbyte*)_ct_bs->byte_for_index(card_index)); - } - } + UpdateRSetCardTableEntryIntoCSetClosure(G1CollectedHeap* g1, + CardTableModRefBS* bs): + _g1(g1), _ct_bs(bs) + { } + + bool do_card_ptr(jbyte* card_ptr, int worker_i) { + // Construct the region representing the card. + HeapWord* start = _ct_bs->addr_for(card_ptr); + // And find the region containing it. + HeapRegion* r = _g1->heap_region_containing(start); + assert(r != NULL, "unexpected null"); + + // Scan oops in the card looking for references into the collection set + HeapWord* end = _ct_bs->addr_for(card_ptr + 1); + MemRegion scanRegion(start, end); + + UpdateRSetImmediate update_rs_cl(_g1->g1_rem_set()); + FilterIntoCSClosure update_rs_cset_oop_cl(NULL, _g1, &update_rs_cl); + FilterOutOfRegionClosure filter_then_update_rs_cset_oop_cl(r, &update_rs_cset_oop_cl); + + // We can pass false as the "filter_young" parameter here as: + // * we should be in a STW pause, + // * the DCQS to which this closure is applied is used to hold + // references that point into the collection set from the prior + // RSet updating, + // * the post-write barrier shouldn't be logging updates to young + // regions (but there is a situation where this can happen - see + // the comment in HRInto_G1RemSet::concurrentRefineOneCard below - + // that should not be applicable here), and + // * during actual RSet updating, the filtering of cards in young + // regions in HeapRegion::oops_on_card_seq_iterate_careful is + // employed. + // As a result, when this closure is applied to "refs into cset" + // DCQS, we shouldn't see any cards in young regions. + update_rs_cl.set_region(r); + HeapWord* stop_point = + r->oops_on_card_seq_iterate_careful(scanRegion, + &filter_then_update_rs_cset_oop_cl, + false /* filter_young */); + + // Since this is performed in the event of an evacuation failure, we + // we shouldn't see a non-null stop point + assert(stop_point == NULL, "saw an unallocated region"); + return true; } }; -template void HRInto_G1RemSet::new_refs_iterate_work(OopClosure* cl) { - for (size_t i = 0; i < n_workers(); i++) { - for (int j = 0; j < _new_refs[i]->length(); j++) { - T* p = (T*) _new_refs[i]->at(j); - cl->do_oop(p); - } - } -} - void HRInto_G1RemSet::cleanup_after_oops_into_collection_set_do() { guarantee( _cards_scanned != NULL, "invariant" ); _total_cards_scanned = 0; @@ -584,21 +627,38 @@ void HRInto_G1RemSet::cleanup_after_oops_into_collection_set_do() { set_par_traversal(false); } + DirtyCardQueueSet& into_cset_dcqs = _g1->into_cset_dirty_card_queue_set(); + int into_cset_n_buffers = into_cset_dcqs.completed_buffers_num(); + if (_g1->evacuation_failed()) { - // Restore remembered sets for the regions pointing into - // the collection set. + // Restore remembered sets for the regions pointing into the collection set. + if (G1DeferredRSUpdate) { - DirtyCardQueue dcq(&_g1->dirty_card_queue_set()); - UpdateRSetOopsIntoCSDeferred deferred_update(_g1, &dcq); - new_refs_iterate(&deferred_update); + // If deferred RS updates are enabled then we just need to transfer + // the completed buffers from (a) the DirtyCardQueueSet used to hold + // cards that contain references that point into the collection set + // to (b) the DCQS used to hold the deferred RS updates + _g1->dirty_card_queue_set().merge_bufferlists(&into_cset_dcqs); } else { - UpdateRSetOopsIntoCSImmediate immediate_update(_g1); - new_refs_iterate(&immediate_update); + + CardTableModRefBS* bs = (CardTableModRefBS*)_g1->barrier_set(); + UpdateRSetCardTableEntryIntoCSetClosure update_rs_cset_immediate(_g1, bs); + + int n_completed_buffers = 0; + while (into_cset_dcqs.apply_closure_to_completed_buffer(&update_rs_cset_immediate, + 0, 0, true)) { + n_completed_buffers++; + } + assert(n_completed_buffers == into_cset_n_buffers, "missed some buffers"); } } - for (uint i = 0; i < n_workers(); i++) { - _new_refs[i]->clear(); - } + + // Free any completed buffers in the DirtyCardQueueSet used to hold cards + // which contain references that point into the collection. + _g1->into_cset_dirty_card_queue_set().clear(); + assert(_g1->into_cset_dirty_card_queue_set().completed_buffers_num() == 0, + "all buffers should be freed"); + _g1->into_cset_dirty_card_queue_set().clear_n_completed_buffers(); assert(!_par_traversal_in_progress, "Invariant between iterations."); } @@ -652,7 +712,43 @@ void HRInto_G1RemSet::scrub_par(BitMap* region_bm, BitMap* card_bm, static IntHistogram out_of_histo(50, 50); -void HRInto_G1RemSet::concurrentRefineOneCard_impl(jbyte* card_ptr, int worker_i) { +class TriggerClosure : public OopClosure { + bool _trigger; +public: + TriggerClosure() : _trigger(false) { } + bool value() const { return _trigger; } + template void do_oop_nv(T* p) { _trigger = true; } + virtual void do_oop(oop* p) { do_oop_nv(p); } + virtual void do_oop(narrowOop* p) { do_oop_nv(p); } +}; + +class InvokeIfNotTriggeredClosure: public OopClosure { + TriggerClosure* _t; + OopClosure* _oc; +public: + InvokeIfNotTriggeredClosure(TriggerClosure* t, OopClosure* oc): + _t(t), _oc(oc) { } + template void do_oop_nv(T* p) { + if (!_t->value()) _oc->do_oop(p); + } + virtual void do_oop(oop* p) { do_oop_nv(p); } + virtual void do_oop(narrowOop* p) { do_oop_nv(p); } +}; + +class Mux2Closure : public OopClosure { + OopClosure* _c1; + OopClosure* _c2; +public: + Mux2Closure(OopClosure *c1, OopClosure *c2) : _c1(c1), _c2(c2) { } + template void do_oop_nv(T* p) { + _c1->do_oop(p); _c2->do_oop(p); + } + virtual void do_oop(oop* p) { do_oop_nv(p); } + virtual void do_oop(narrowOop* p) { do_oop_nv(p); } +}; + +bool HRInto_G1RemSet::concurrentRefineOneCard_impl(jbyte* card_ptr, int worker_i, + bool check_for_refs_into_cset) { // Construct the region representing the card. HeapWord* start = _ct_bs->addr_for(card_ptr); // And find the region containing it. @@ -669,7 +765,16 @@ void HRInto_G1RemSet::concurrentRefineOneCard_impl(jbyte* card_ptr, int worker_i UpdateRSOopClosure update_rs_oop_cl(this, worker_i); update_rs_oop_cl.set_from(r); - FilterOutOfRegionClosure filter_then_update_rs_oop_cl(r, &update_rs_oop_cl); + + TriggerClosure trigger_cl; + FilterIntoCSClosure into_cs_cl(NULL, _g1, &trigger_cl); + InvokeIfNotTriggeredClosure invoke_cl(&trigger_cl, &into_cs_cl); + Mux2Closure mux(&invoke_cl, &update_rs_oop_cl); + + FilterOutOfRegionClosure filter_then_update_rs_oop_cl(r, + (check_for_refs_into_cset ? + (OopClosure*)&mux : + (OopClosure*)&update_rs_oop_cl)); // Undirty the card. *card_ptr = CardTableModRefBS::clean_card_val(); @@ -717,11 +822,18 @@ void HRInto_G1RemSet::concurrentRefineOneCard_impl(jbyte* card_ptr, int worker_i out_of_histo.add_entry(filter_then_update_rs_oop_cl.out_of_region()); _conc_refine_cards++; } + + return trigger_cl.value(); } -void HRInto_G1RemSet::concurrentRefineOneCard(jbyte* card_ptr, int worker_i) { +bool HRInto_G1RemSet::concurrentRefineOneCard(jbyte* card_ptr, int worker_i, + bool check_for_refs_into_cset) { // If the card is no longer dirty, nothing to do. - if (*card_ptr != CardTableModRefBS::dirty_card_val()) return; + if (*card_ptr != CardTableModRefBS::dirty_card_val()) { + // No need to return that this card contains refs that point + // into the collection set. + return false; + } // Construct the region representing the card. HeapWord* start = _ct_bs->addr_for(card_ptr); @@ -729,7 +841,9 @@ void HRInto_G1RemSet::concurrentRefineOneCard(jbyte* card_ptr, int worker_i) { HeapRegion* r = _g1->heap_region_containing(start); if (r == NULL) { guarantee(_g1->is_in_permanent(start), "Or else where?"); - return; // Not in the G1 heap (might be in perm, for example.) + // Again no need to return that this card contains refs that + // point into the collection set. + return false; // Not in the G1 heap (might be in perm, for example.) } // Why do we have to check here whether a card is on a young region, // given that we dirty young regions and, as a result, the @@ -743,7 +857,7 @@ void HRInto_G1RemSet::concurrentRefineOneCard(jbyte* card_ptr, int worker_i) { // and it doesn't happen often, but it can happen. So, the extra // check below filters out those cards. if (r->is_young()) { - return; + return false; } // While we are processing RSet buffers during the collection, we // actually don't want to scan any cards on the collection set, @@ -756,7 +870,7 @@ void HRInto_G1RemSet::concurrentRefineOneCard(jbyte* card_ptr, int worker_i) { // however, that if evacuation fails, we have to scan any objects // that were not moved and create any missing entries. if (r->in_collection_set()) { - return; + return false; } // Should we defer processing the card? @@ -797,8 +911,14 @@ void HRInto_G1RemSet::concurrentRefineOneCard(jbyte* card_ptr, int worker_i) { // cache. // Immediately process res; no need to process card_ptr. + jbyte* res = card_ptr; bool defer = false; + + // This gets set to true if the card being refined has references + // that point into the collection set. + bool oops_into_cset = false; + if (_cg1r->use_cache()) { jbyte* res = _cg1r->cache_insert(card_ptr, &defer); if (res != NULL && (res != card_ptr || defer)) { @@ -815,14 +935,31 @@ void HRInto_G1RemSet::concurrentRefineOneCard(jbyte* card_ptr, int worker_i) { // Process card pointer we get back from the hot card cache. This // will check whether the region containing the card is young // _after_ checking that the region has been allocated from. - concurrentRefineOneCard_impl(res, worker_i); + oops_into_cset = concurrentRefineOneCard_impl(res, worker_i, + false /* check_for_refs_into_cset */); + // The above call to concurrentRefineOneCard_impl is only + // performed if the hot card cache is enabled. This cache is + // disabled during an evacuation pause - which is the only + // time when we need know if the card contains references + // that point into the collection set. Also when the hot card + // cache is enabled, this code is executed by the concurrent + // refine threads - rather than the GC worker threads - and + // concurrentRefineOneCard_impl will return false. + assert(!oops_into_cset, "should not see true here"); } } } if (!defer) { - concurrentRefineOneCard_impl(card_ptr, worker_i); + oops_into_cset = + concurrentRefineOneCard_impl(card_ptr, worker_i, check_for_refs_into_cset); + // We should only be detecting that the card contains references + // that point into the collection set if the current thread is + // a GC worker thread. + assert(!oops_into_cset || SafepointSynchronize::is_at_safepoint(), + "invalid result at non safepoint"); } + return oops_into_cset; } class HRRSStatsIter: public HeapRegionClosure { @@ -920,6 +1057,7 @@ void HRInto_G1RemSet::print_summary_info() { } } + void HRInto_G1RemSet::prepare_for_verify() { if (G1HRRSFlushLogBuffersOnVerify && (VerifyBeforeGC || VerifyAfterGC) @@ -932,7 +1070,9 @@ void HRInto_G1RemSet::prepare_for_verify() { } bool cg1r_use_cache = _cg1r->use_cache(); _cg1r->set_use_cache(false); - updateRS(0); + DirtyCardQueue into_cset_dcq(&_g1->into_cset_dirty_card_queue_set()); + updateRS(&into_cset_dcq, 0); + _g1->into_cset_dirty_card_queue_set().clear(); _cg1r->set_use_cache(cg1r_use_cache); assert(JavaThread::dirty_card_queue_set().completed_buffers_num() == 0, "All should be consumed"); diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp index bc30a9699c4..112f9aa646c 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2010, 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 @@ -83,7 +83,13 @@ public: // Refine the card corresponding to "card_ptr". If "sts" is non-NULL, // join and leave around parts that must be atomic wrt GC. (NULL means // being done at a safepoint.) - virtual void concurrentRefineOneCard(jbyte* card_ptr, int worker_i) {} + // With some implementations of this routine, when check_for_refs_into_cset + // is true, a true result may be returned if the given card contains oops + // that have references into the current collection set. + virtual bool concurrentRefineOneCard(jbyte* card_ptr, int worker_i, + bool check_for_refs_into_cset) { + return false; + } // Print any relevant summary info. virtual void print_summary_info() {} @@ -143,23 +149,21 @@ protected: size_t _total_cards_scanned; // _par_traversal_in_progress is "true" iff a parallel traversal is in - // progress. If so, then cards added to remembered sets should also have - // their references into the collection summarized in "_new_refs". + // progress. bool _par_traversal_in_progress; void set_par_traversal(bool b) { _par_traversal_in_progress = b; } - GrowableArray** _new_refs; - template void new_refs_iterate_work(OopClosure* cl); - void new_refs_iterate(OopClosure* cl) { - if (UseCompressedOops) { - new_refs_iterate_work(cl); - } else { - new_refs_iterate_work(cl); - } - } + + // Used for caching the closure that is responsible for scanning + // references into the collection set. + OopsInHeapRegionClosure** _cset_rs_update_cl; // The routine that performs the actual work of refining a dirty // card. - void concurrentRefineOneCard_impl(jbyte* card_ptr, int worker_i); + // If check_for_refs_into_refs is true then a true result is returned + // if the card contains oops that have references into the current + // collection set. + bool concurrentRefineOneCard_impl(jbyte* card_ptr, int worker_i, + bool check_for_refs_into_cset); protected: template void write_ref_nv(HeapRegion* from, T* p); @@ -188,7 +192,7 @@ public: scanNewRefsRS_work(oc, worker_i); } } - void updateRS(int worker_i); + void updateRS(DirtyCardQueue* into_cset_dcq, int worker_i); HeapRegion* calculateStartRegion(int i); HRInto_G1RemSet* as_HRInto_G1RemSet() { return this; } @@ -219,7 +223,11 @@ public: void scrub_par(BitMap* region_bm, BitMap* card_bm, int worker_num, int claim_val); - virtual void concurrentRefineOneCard(jbyte* card_ptr, int worker_i); + // If check_for_refs_into_cset is true then a true result is returned + // if the card contains oops that have references into the current + // collection set. + virtual bool concurrentRefineOneCard(jbyte* card_ptr, int worker_i, + bool check_for_refs_into_cset); virtual void print_summary_info(); virtual void prepare_for_verify(); @@ -265,3 +273,16 @@ public: // bool idempotent() { return true; } bool apply_to_weak_ref_discovered_field() { return true; } }; + +class UpdateRSetImmediate: public OopsInHeapRegionClosure { +private: + G1RemSet* _g1_rem_set; + + template void do_oop_work(T* p); +public: + UpdateRSetImmediate(G1RemSet* rs) : + _g1_rem_set(rs) {} + + virtual void do_oop(narrowOop* p) { do_oop_work(p); } + virtual void do_oop( oop* p) { do_oop_work(p); } +}; diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.inline.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.inline.hpp index 2b916f866d5..d3fa77ae92a 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.inline.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2010, 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 @@ -56,19 +56,25 @@ template inline void HRInto_G1RemSet::par_write_ref_nv(HeapRegion* fro assert(Universe::heap()->is_in_reserved(obj), "must be in heap"); } #endif // ASSERT - assert(from == NULL || from->is_in_reserved(p), - "p is not in from"); + + assert(from == NULL || from->is_in_reserved(p), "p is not in from"); + HeapRegion* to = _g1->heap_region_containing(obj); // The test below could be optimized by applying a bit op to to and from. if (to != NULL && from != NULL && from != to) { - // There is a tricky infinite loop if we keep pushing - // self forwarding pointers onto our _new_refs list. // The _par_traversal_in_progress flag is true during the collection pause, - // false during the evacuation failure handing. + // false during the evacuation failure handing. This should avoid a + // potential loop if we were to add the card containing 'p' to the DCQS + // that's used to regenerate the remembered sets for the collection set, + // in the event of an evacuation failure, here. The UpdateRSImmediate + // closure will eventally call this routine. if (_par_traversal_in_progress && to->in_collection_set() && !self_forwarded(obj)) { - _new_refs[tid]->push((void*)p); - // Deferred updates to the Cset are either discarded (in the normal case), + + assert(_cset_rs_update_cl[tid] != NULL, "should have been set already"); + _cset_rs_update_cl[tid]->do_oop(p); + + // Deferred updates to the CSet are either discarded (in the normal case), // or processed (if an evacuation failure occurs) at the end // of the collection. // See HRInto_G1RemSet::cleanup_after_oops_into_collection_set_do(). @@ -89,3 +95,12 @@ template inline void UpdateRSOopClosure::do_oop_work(T* p) { assert(_from != NULL, "from region must be non-NULL"); _rs->par_write_ref(_from, p, _worker_i); } + +template inline void UpdateRSetImmediate::do_oop_work(T* p) { + assert(_from->is_in_reserved(p), "paranoia"); + T heap_oop = oopDesc::load_heap_oop(p); + if (!oopDesc::is_null(heap_oop) && !_from->is_survivor()) { + _g1_rem_set->par_write_ref(_from, p, 0); + } +} + diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp index 4e5446e1f5e..08d8902ce88 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp @@ -683,6 +683,8 @@ oops_on_card_seq_iterate_careful(MemRegion mr, return NULL; } + assert(!is_young(), "check value of filter_young"); + // We used to use "block_start_careful" here. But we're actually happy // to update the BOT while we do this... HeapWord* cur = block_start(mr.start()); diff --git a/hotspot/src/share/vm/gc_implementation/includeDB_gc_g1 b/hotspot/src/share/vm/gc_implementation/includeDB_gc_g1 index 13e3464ed8f..d1476972275 100644 --- a/hotspot/src/share/vm/gc_implementation/includeDB_gc_g1 +++ b/hotspot/src/share/vm/gc_implementation/includeDB_gc_g1 @@ -1,5 +1,5 @@ // -// Copyright (c) 2004, 2009, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2004, 2010, 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 @@ -241,6 +241,7 @@ g1MMUTracker.cpp mutexLocker.hpp g1MMUTracker.hpp debug.hpp g1MMUTracker.hpp allocation.hpp + g1RemSet.cpp bufferingOopClosure.hpp g1RemSet.cpp concurrentG1Refine.hpp g1RemSet.cpp concurrentG1RefineThread.hpp From 38ab95c64b9f0bb29c22e811d7216f74ac6f5d10 Mon Sep 17 00:00:00 2001 From: Antonios Printezis Date: Thu, 22 Jul 2010 10:27:41 -0400 Subject: [PATCH 003/100] 6962589: remove breadth first scanning code from parallel gc Remove the breadth-first copying order from ParallelScavenge and use depth-first by default. Reviewed-by: jcoomes, ysr, johnc --- .../includeDB_gc_parallelScavenge | 1 - .../parallelScavenge/cardTableExtension.cpp | 70 +++------- .../parallelScavenge/prefetchQueue.hpp | 68 --------- .../parallelScavenge/psPromotionManager.cpp | 130 ++++-------------- .../parallelScavenge/psPromotionManager.hpp | 37 +---- .../psPromotionManager.inline.hpp | 55 -------- .../parallelScavenge/psScavenge.cpp | 12 +- .../parallelScavenge/psScavenge.inline.hpp | 2 +- .../parallelScavenge/psTasks.cpp | 32 ++--- hotspot/src/share/vm/oops/arrayKlassKlass.cpp | 4 - .../share/vm/oops/compiledICHolderKlass.cpp | 4 - .../src/share/vm/oops/constMethodKlass.cpp | 4 - .../src/share/vm/oops/constantPoolKlass.cpp | 15 -- hotspot/src/share/vm/oops/cpCacheKlass.cpp | 23 ---- hotspot/src/share/vm/oops/instanceKlass.cpp | 22 --- hotspot/src/share/vm/oops/instanceKlass.hpp | 1 - .../src/share/vm/oops/instanceKlassKlass.cpp | 36 +---- .../src/share/vm/oops/instanceRefKlass.cpp | 33 ----- hotspot/src/share/vm/oops/klassKlass.cpp | 3 - hotspot/src/share/vm/oops/klassPS.hpp | 2 - hotspot/src/share/vm/oops/methodDataKlass.cpp | 7 - hotspot/src/share/vm/oops/methodKlass.cpp | 4 - hotspot/src/share/vm/oops/objArrayKlass.cpp | 11 -- .../src/share/vm/oops/objArrayKlassKlass.cpp | 4 - hotspot/src/share/vm/oops/oop.hpp | 1 - hotspot/src/share/vm/oops/oop.psgc.inline.hpp | 9 -- hotspot/src/share/vm/oops/symbolKlass.cpp | 4 - hotspot/src/share/vm/oops/typeArrayKlass.cpp | 4 - hotspot/src/share/vm/runtime/arguments.cpp | 2 + hotspot/src/share/vm/runtime/globals.hpp | 4 - 30 files changed, 63 insertions(+), 541 deletions(-) delete mode 100644 hotspot/src/share/vm/gc_implementation/parallelScavenge/prefetchQueue.hpp diff --git a/hotspot/src/share/vm/gc_implementation/includeDB_gc_parallelScavenge b/hotspot/src/share/vm/gc_implementation/includeDB_gc_parallelScavenge index e06ed445a0d..83eae5eebe7 100644 --- a/hotspot/src/share/vm/gc_implementation/includeDB_gc_parallelScavenge +++ b/hotspot/src/share/vm/gc_implementation/includeDB_gc_parallelScavenge @@ -330,7 +330,6 @@ psPromotionManager.cpp psPromotionManager.inline.hpp psPromotionManager.cpp psScavenge.inline.hpp psPromotionManager.hpp allocation.hpp -psPromotionManager.hpp prefetchQueue.hpp psPromotionManager.hpp psPromotionLAB.hpp psPromotionManager.hpp taskqueue.hpp diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.cpp index c87190d0ccb..e9da8d5fa66 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.cpp @@ -123,7 +123,6 @@ void CardTableExtension::scavenge_contents(ObjectStartArray* start_array, assert(start_array != NULL && sp != NULL && pm != NULL, "Sanity"); assert(start_array->covered_region().contains(sp->used_region()), "ObjectStartArray does not cover space"); - bool depth_first = pm->depth_first(); if (sp->not_empty()) { oop* sp_top = (oop*)space_top; @@ -201,21 +200,12 @@ void CardTableExtension::scavenge_contents(ObjectStartArray* start_array, *first_nonclean_card++ = clean_card; } // scan oops in objects - // hoisted the if (depth_first) check out of the loop - if (depth_first){ - do { - oop(bottom_obj)->push_contents(pm); - bottom_obj += oop(bottom_obj)->size(); - assert(bottom_obj <= sp_top, "just checking"); - } while (bottom_obj < top); - pm->drain_stacks_cond_depth(); - } else { - do { - oop(bottom_obj)->copy_contents(pm); - bottom_obj += oop(bottom_obj)->size(); - assert(bottom_obj <= sp_top, "just checking"); - } while (bottom_obj < top); - } + do { + oop(bottom_obj)->push_contents(pm); + bottom_obj += oop(bottom_obj)->size(); + assert(bottom_obj <= sp_top, "just checking"); + } while (bottom_obj < top); + pm->drain_stacks_cond_depth(); // remember top oop* scanned prev_top = top; } @@ -230,7 +220,6 @@ void CardTableExtension::scavenge_contents_parallel(ObjectStartArray* start_arra uint stripe_number) { int ssize = 128; // Naked constant! Work unit = 64k. int dirty_card_count = 0; - bool depth_first = pm->depth_first(); oop* sp_top = (oop*)space_top; jbyte* start_card = byte_for(sp->bottom()); @@ -363,43 +352,22 @@ void CardTableExtension::scavenge_contents_parallel(ObjectStartArray* start_arra const int interval = PrefetchScanIntervalInBytes; // scan all objects in the range if (interval != 0) { - // hoisted the if (depth_first) check out of the loop - if (depth_first) { - while (p < to) { - Prefetch::write(p, interval); - oop m = oop(p); - assert(m->is_oop_or_null(), "check for header"); - m->push_contents(pm); - p += m->size(); - } - pm->drain_stacks_cond_depth(); - } else { - while (p < to) { - Prefetch::write(p, interval); - oop m = oop(p); - assert(m->is_oop_or_null(), "check for header"); - m->copy_contents(pm); - p += m->size(); - } + while (p < to) { + Prefetch::write(p, interval); + oop m = oop(p); + assert(m->is_oop_or_null(), "check for header"); + m->push_contents(pm); + p += m->size(); } + pm->drain_stacks_cond_depth(); } else { - // hoisted the if (depth_first) check out of the loop - if (depth_first) { - while (p < to) { - oop m = oop(p); - assert(m->is_oop_or_null(), "check for header"); - m->push_contents(pm); - p += m->size(); - } - pm->drain_stacks_cond_depth(); - } else { - while (p < to) { - oop m = oop(p); - assert(m->is_oop_or_null(), "check for header"); - m->copy_contents(pm); - p += m->size(); - } + while (p < to) { + oop m = oop(p); + assert(m->is_oop_or_null(), "check for header"); + m->push_contents(pm); + p += m->size(); } + pm->drain_stacks_cond_depth(); } last_scanned = p; } diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/prefetchQueue.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/prefetchQueue.hpp deleted file mode 100644 index cd57b5ff8b3..00000000000 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/prefetchQueue.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2002, 2008, 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. - * - */ - -// -// PrefetchQueue is a FIFO queue of variable length (currently 8). -// -// We need to examine the performance penalty of variable lengths. -// We may also want to split this into cpu dependent bits. -// - -const int PREFETCH_QUEUE_SIZE = 8; - -class PrefetchQueue : public CHeapObj { - private: - void* _prefetch_queue[PREFETCH_QUEUE_SIZE]; - uint _prefetch_index; - - public: - int length() { return PREFETCH_QUEUE_SIZE; } - - inline void clear() { - for(int i=0; i inline void* push_and_pop(T* p) { - oop o = oopDesc::load_decode_heap_oop_not_null(p); - Prefetch::write(o->mark_addr(), 0); - // This prefetch is intended to make sure the size field of array - // oops is in cache. It assumes the the object layout is - // mark -> klass -> size, and that mark and klass are heapword - // sized. If this should change, this prefetch will need updating! - Prefetch::write(o->mark_addr() + (HeapWordSize*2), 0); - _prefetch_queue[_prefetch_index++] = p; - _prefetch_index &= (PREFETCH_QUEUE_SIZE-1); - return _prefetch_queue[_prefetch_index]; - } - - // Stores a NULL pointer in the pop'd location. - inline void* pop() { - _prefetch_queue[_prefetch_index++] = NULL; - _prefetch_index &= (PREFETCH_QUEUE_SIZE-1); - return _prefetch_queue[_prefetch_index]; - } -}; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.cpp index 2da32555dee..1e73d11d1e3 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.cpp @@ -27,7 +27,6 @@ PSPromotionManager** PSPromotionManager::_manager_array = NULL; OopStarTaskQueueSet* PSPromotionManager::_stack_array_depth = NULL; -OopTaskQueueSet* PSPromotionManager::_stack_array_breadth = NULL; PSOldGen* PSPromotionManager::_old_gen = NULL; MutableSpace* PSPromotionManager::_young_space = NULL; @@ -42,23 +41,14 @@ void PSPromotionManager::initialize() { _manager_array = NEW_C_HEAP_ARRAY(PSPromotionManager*, ParallelGCThreads+1 ); guarantee(_manager_array != NULL, "Could not initialize promotion manager"); - if (UseDepthFirstScavengeOrder) { - _stack_array_depth = new OopStarTaskQueueSet(ParallelGCThreads); - guarantee(_stack_array_depth != NULL, "Count not initialize promotion manager"); - } else { - _stack_array_breadth = new OopTaskQueueSet(ParallelGCThreads); - guarantee(_stack_array_breadth != NULL, "Count not initialize promotion manager"); - } + _stack_array_depth = new OopStarTaskQueueSet(ParallelGCThreads); + guarantee(_stack_array_depth != NULL, "Cound not initialize promotion manager"); // Create and register the PSPromotionManager(s) for the worker threads. for(uint i=0; iregister_queue(i, _manager_array[i]->claimed_stack_depth()); - } else { - stack_array_breadth()->register_queue(i, _manager_array[i]->claimed_stack_breadth()); - } + stack_array_depth()->register_queue(i, _manager_array[i]->claimed_stack_depth()); } // The VMThread gets its own PSPromotionManager, which is not available @@ -93,11 +83,7 @@ void PSPromotionManager::post_scavenge() { TASKQUEUE_STATS_ONLY(if (PrintGCDetails && ParallelGCVerbose) print_stats()); for (uint i = 0; i < ParallelGCThreads + 1; i++) { PSPromotionManager* manager = manager_array(i); - if (UseDepthFirstScavengeOrder) { - assert(manager->claimed_stack_depth()->is_empty(), "should be empty"); - } else { - assert(manager->claimed_stack_breadth()->is_empty(), "should be empty"); - } + assert(manager->claimed_stack_depth()->is_empty(), "should be empty"); manager->flush_labs(); } } @@ -105,10 +91,8 @@ void PSPromotionManager::post_scavenge() { #if TASKQUEUE_STATS void PSPromotionManager::print_taskqueue_stats(uint i) const { - const TaskQueueStats& stats = depth_first() ? - _claimed_stack_depth.stats : _claimed_stack_breadth.stats; tty->print("%3u ", i); - stats.print(); + _claimed_stack_depth.stats.print(); tty->cr(); } @@ -128,8 +112,7 @@ static const char* const pm_stats_hdr[] = { void PSPromotionManager::print_stats() { - const bool df = UseDepthFirstScavengeOrder; - tty->print_cr("== GC Task Stats (%s-First), GC %3d", df ? "Depth" : "Breadth", + tty->print_cr("== GC Tasks Stats, GC %3d", Universe::heap()->total_collections()); tty->print("thr "); TaskQueueStats::print_header(1); tty->cr(); @@ -147,9 +130,7 @@ PSPromotionManager::print_stats() { void PSPromotionManager::reset_stats() { - TaskQueueStats& stats = depth_first() ? - claimed_stack_depth()->stats : claimed_stack_breadth()->stats; - stats.reset(); + claimed_stack_depth()->stats.reset(); _masked_pushes = _masked_steals = 0; _arrays_chunked = _array_chunks_processed = 0; } @@ -158,19 +139,13 @@ PSPromotionManager::reset_stats() { PSPromotionManager::PSPromotionManager() { ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); - _depth_first = UseDepthFirstScavengeOrder; // We set the old lab's start array. _old_lab.set_start_array(old_gen()->start_array()); uint queue_size; - if (depth_first()) { - claimed_stack_depth()->initialize(); - queue_size = claimed_stack_depth()->max_elems(); - } else { - claimed_stack_breadth()->initialize(); - queue_size = claimed_stack_breadth()->max_elems(); - } + claimed_stack_depth()->initialize(); + queue_size = claimed_stack_depth()->max_elems(); _totally_drain = (ParallelGCThreads == 1) || (GCDrainStackTargetSize == 0); if (_totally_drain) { @@ -205,14 +180,11 @@ void PSPromotionManager::reset() { _old_lab.initialize(MemRegion(lab_base, (size_t)0)); _old_gen_is_full = false; - _prefetch_queue.clear(); - TASKQUEUE_STATS_ONLY(reset_stats()); } void PSPromotionManager::drain_stacks_depth(bool totally_drain) { - assert(depth_first(), "invariant"); assert(claimed_stack_depth()->overflow_stack() != NULL, "invariant"); totally_drain = totally_drain || _totally_drain; @@ -250,50 +222,6 @@ void PSPromotionManager::drain_stacks_depth(bool totally_drain) { assert(tq->overflow_empty(), "Sanity"); } -void PSPromotionManager::drain_stacks_breadth(bool totally_drain) { - assert(!depth_first(), "invariant"); - assert(claimed_stack_breadth()->overflow_stack() != NULL, "invariant"); - totally_drain = totally_drain || _totally_drain; - -#ifdef ASSERT - ParallelScavengeHeap* heap = (ParallelScavengeHeap*)Universe::heap(); - assert(heap->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); - MutableSpace* to_space = heap->young_gen()->to_space(); - MutableSpace* old_space = heap->old_gen()->object_space(); - MutableSpace* perm_space = heap->perm_gen()->object_space(); -#endif /* ASSERT */ - - OverflowTaskQueue* const tq = claimed_stack_breadth(); - do { - oop obj; - - // Drain overflow stack first, so other threads can steal from - // claimed stack while we work. - while (tq->pop_overflow(obj)) { - obj->copy_contents(this); - } - - if (totally_drain) { - while (tq->pop_local(obj)) { - obj->copy_contents(this); - } - } else { - while (tq->size() > _target_stack_size && tq->pop_local(obj)) { - obj->copy_contents(this); - } - } - - // If we could not find any other work, flush the prefetch queue - if (tq->is_empty()) { - flush_prefetch_queue(); - } - } while (totally_drain && !tq->taskqueue_empty() || !tq->overflow_empty()); - - assert(!totally_drain || tq->taskqueue_empty(), "Sanity"); - assert(totally_drain || tq->size() <= _target_stack_size, "Sanity"); - assert(tq->overflow_empty(), "Sanity"); -} - void PSPromotionManager::flush_labs() { assert(stacks_empty(), "Attempt to flush lab with live stack"); @@ -319,7 +247,7 @@ void PSPromotionManager::flush_labs() { // performance. // -oop PSPromotionManager::copy_to_survivor_space(oop o, bool depth_first) { +oop PSPromotionManager::copy_to_survivor_space(oop o) { assert(PSScavenge::should_scavenge(&o), "Sanity"); oop new_obj = NULL; @@ -423,24 +351,20 @@ oop PSPromotionManager::copy_to_survivor_space(oop o, bool depth_first) { assert(young_space()->contains(new_obj), "Attempt to push non-promoted obj"); } - if (depth_first) { - // Do the size comparison first with new_obj_size, which we - // already have. Hopefully, only a few objects are larger than - // _min_array_size_for_chunking, and most of them will be arrays. - // So, the is->objArray() test would be very infrequent. - if (new_obj_size > _min_array_size_for_chunking && - new_obj->is_objArray() && - PSChunkLargeArrays) { - // we'll chunk it - oop* const masked_o = mask_chunked_array_oop(o); - push_depth(masked_o); - TASKQUEUE_STATS_ONLY(++_arrays_chunked; ++_masked_pushes); - } else { - // we'll just push its contents - new_obj->push_contents(this); - } + // Do the size comparison first with new_obj_size, which we + // already have. Hopefully, only a few objects are larger than + // _min_array_size_for_chunking, and most of them will be arrays. + // So, the is->objArray() test would be very infrequent. + if (new_obj_size > _min_array_size_for_chunking && + new_obj->is_objArray() && + PSChunkLargeArrays) { + // we'll chunk it + oop* const masked_o = mask_chunked_array_oop(o); + push_depth(masked_o); + TASKQUEUE_STATS_ONLY(++_arrays_chunked; ++_masked_pushes); } else { - push_breadth(new_obj); + // we'll just push its contents + new_obj->push_contents(this); } } else { // We lost, someone else "owns" this object @@ -537,13 +461,7 @@ oop PSPromotionManager::oop_promotion_failed(oop obj, markOop obj_mark) { // We won any races, we "own" this object. assert(obj == obj->forwardee(), "Sanity"); - if (depth_first()) { - obj->push_contents(this); - } else { - // Don't bother incrementing the age, just push - // onto the claimed_stack.. - push_breadth(obj); - } + obj->push_contents(this); // Save the mark if needed PSScavenge::oop_promotion_failed(obj, obj_mark); diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.hpp index ec89b9557bb..aa14478d480 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.hpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.hpp @@ -48,7 +48,6 @@ class PSPromotionManager : public CHeapObj { private: static PSPromotionManager** _manager_array; static OopStarTaskQueueSet* _stack_array_depth; - static OopTaskQueueSet* _stack_array_breadth; static PSOldGen* _old_gen; static MutableSpace* _young_space; @@ -69,12 +68,10 @@ class PSPromotionManager : public CHeapObj { PSOldPromotionLAB _old_lab; bool _young_gen_is_full; bool _old_gen_is_full; - PrefetchQueue _prefetch_queue; OopStarTaskQueue _claimed_stack_depth; OverflowTaskQueue _claimed_stack_breadth; - bool _depth_first; bool _totally_drain; uint _target_stack_size; @@ -87,7 +84,6 @@ class PSPromotionManager : public CHeapObj { inline static PSPromotionManager* manager_array(int index); template inline void claim_or_forward_internal_depth(T* p); - template inline void claim_or_forward_internal_breadth(T* p); // On the task queues we push reference locations as well as // partially-scanned arrays (in the latter case, we push an oop to @@ -136,19 +132,11 @@ class PSPromotionManager : public CHeapObj { void process_array_chunk(oop old); template void push_depth(T* p) { - assert(depth_first(), "pre-condition"); claimed_stack_depth()->push(p); } - void push_breadth(oop o) { - assert(!depth_first(), "pre-condition"); - claimed_stack_breadth()->push(o); - } - protected: static OopStarTaskQueueSet* stack_array_depth() { return _stack_array_depth; } - static OopTaskQueueSet* stack_array_breadth() { return _stack_array_breadth; } - public: // Static static void initialize(); @@ -163,19 +151,12 @@ class PSPromotionManager : public CHeapObj { return stack_array_depth()->steal(queue_num, seed, t); } - static bool steal_breadth(int queue_num, int* seed, oop& t) { - return stack_array_breadth()->steal(queue_num, seed, t); - } - PSPromotionManager(); // Accessors OopStarTaskQueue* claimed_stack_depth() { return &_claimed_stack_depth; } - OverflowTaskQueue* claimed_stack_breadth() { - return &_claimed_stack_breadth; - } bool young_gen_is_full() { return _young_gen_is_full; } @@ -183,18 +164,14 @@ class PSPromotionManager : public CHeapObj { void set_old_gen_is_full(bool state) { _old_gen_is_full = state; } // Promotion methods - oop copy_to_survivor_space(oop o, bool depth_first); + oop copy_to_survivor_space(oop o); oop oop_promotion_failed(oop obj, markOop obj_mark); void reset(); void flush_labs(); void drain_stacks(bool totally_drain) { - if (depth_first()) { - drain_stacks_depth(totally_drain); - } else { - drain_stacks_breadth(totally_drain); - } + drain_stacks_depth(totally_drain); } public: void drain_stacks_cond_depth() { @@ -203,22 +180,14 @@ class PSPromotionManager : public CHeapObj { } } void drain_stacks_depth(bool totally_drain); - void drain_stacks_breadth(bool totally_drain); - bool depth_first() const { - return _depth_first; - } bool stacks_empty() { - return depth_first() ? - claimed_stack_depth()->is_empty() : - claimed_stack_breadth()->is_empty(); + return claimed_stack_depth()->is_empty(); } inline void process_popped_location_depth(StarTask p); - inline void flush_prefetch_queue(); template inline void claim_or_forward_depth(T* p); - template inline void claim_or_forward_breadth(T* p); TASKQUEUE_STATS_ONLY(inline void record_steal(StarTask& p);) }; diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp index ea81c817b30..decc5e99a46 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp @@ -45,33 +45,8 @@ inline void PSPromotionManager::claim_or_forward_internal_depth(T* p) { } } -template -inline void PSPromotionManager::claim_or_forward_internal_breadth(T* p) { - if (p != NULL) { // XXX: error if p != NULL here - oop o = oopDesc::load_decode_heap_oop_not_null(p); - if (o->is_forwarded()) { - o = o->forwardee(); - } else { - o = copy_to_survivor_space(o, false); - } - // Card mark - if (PSScavenge::is_obj_in_young((HeapWord*) o)) { - PSScavenge::card_table()->inline_write_ref_field_gc(p, o); - } - oopDesc::encode_store_heap_oop_not_null(p, o); - } -} - -inline void PSPromotionManager::flush_prefetch_queue() { - assert(!depth_first(), "invariant"); - for (int i = 0; i < _prefetch_queue.length(); i++) { - claim_or_forward_internal_breadth((oop*)_prefetch_queue.pop()); - } -} - template inline void PSPromotionManager::claim_or_forward_depth(T* p) { - assert(depth_first(), "invariant"); assert(PSScavenge::should_scavenge(p, true), "revisiting object?"); assert(Universe::heap()->kind() == CollectedHeap::ParallelScavengeHeap, "Sanity"); @@ -80,36 +55,6 @@ inline void PSPromotionManager::claim_or_forward_depth(T* p) { claim_or_forward_internal_depth(p); } -template -inline void PSPromotionManager::claim_or_forward_breadth(T* p) { - assert(!depth_first(), "invariant"); - assert(PSScavenge::should_scavenge(p, true), "revisiting object?"); - assert(Universe::heap()->kind() == CollectedHeap::ParallelScavengeHeap, - "Sanity"); - assert(Universe::heap()->is_in(p), "pointer outside heap"); - - if (UsePrefetchQueue) { - claim_or_forward_internal_breadth((T*)_prefetch_queue.push_and_pop(p)); - } else { - // This option is used for testing. The use of the prefetch - // queue can delay the processing of the objects and thus - // change the order of object scans. For example, remembered - // set updates are typically the clearing of the remembered - // set (the cards) followed by updates of the remembered set - // for young-to-old pointers. In a situation where there - // is an error in the sequence of clearing and updating - // (e.g. clear card A, update card A, erroneously clear - // card A again) the error can be obscured by a delay - // in the update due to the use of the prefetch queue - // (e.g., clear card A, erroneously clear card A again, - // update card A that was pushed into the prefetch queue - // and thus delayed until after the erronous clear). The - // length of the delay is random depending on the objects - // in the queue and the delay can be zero. - claim_or_forward_internal_breadth(p); - } -} - inline void PSPromotionManager::process_popped_location_depth(StarTask p) { if (is_oop_masked(p)) { assert(PSChunkLargeArrays, "invariant"); diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp index 9cc02479d91..0ea076812b0 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp @@ -157,10 +157,8 @@ void PSRefProcTaskExecutor::execute(ProcessTask& task) q->enqueue(new PSRefProcTaskProxy(task, i)); } ParallelTaskTerminator terminator( - ParallelScavengeHeap::gc_task_manager()->workers(), - UseDepthFirstScavengeOrder ? - (TaskQueueSetSuper*) PSPromotionManager::stack_array_depth() - : (TaskQueueSetSuper*) PSPromotionManager::stack_array_breadth()); + ParallelScavengeHeap::gc_task_manager()->workers(), + (TaskQueueSetSuper*) PSPromotionManager::stack_array_depth()); if (task.marks_oops_alive() && ParallelGCThreads > 1) { for (uint j=0; jenqueue(new StealTask(&terminator)); @@ -375,10 +373,8 @@ bool PSScavenge::invoke_no_policy() { q->enqueue(new ScavengeRootsTask(ScavengeRootsTask::code_cache)); ParallelTaskTerminator terminator( - gc_task_manager()->workers(), - promotion_manager->depth_first() ? - (TaskQueueSetSuper*) promotion_manager->stack_array_depth() - : (TaskQueueSetSuper*) promotion_manager->stack_array_breadth()); + gc_task_manager()->workers(), + (TaskQueueSetSuper*) promotion_manager->stack_array_depth()); if (ParallelGCThreads>1) { for (uint j=0; jenqueue(new StealTask(&terminator)); diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.inline.hpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.inline.hpp index 7dcf93a9540..3de87882fa5 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.inline.hpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.inline.hpp @@ -65,7 +65,7 @@ inline void PSScavenge::copy_and_push_safe_barrier(PSPromotionManager* pm, oop o = oopDesc::load_decode_heap_oop_not_null(p); oop new_obj = o->is_forwarded() ? o->forwardee() - : pm->copy_to_survivor_space(o, pm->depth_first()); + : pm->copy_to_survivor_space(o); oopDesc::encode_store_heap_oop_not_null(p, new_obj); // We cannot mark without test, as some code passes us pointers diff --git a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp index 6f72724bfda..11774d941f1 100644 --- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp +++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp @@ -144,29 +144,15 @@ void StealTask::do_it(GCTaskManager* manager, uint which) { "stacks should be empty at this point"); int random_seed = 17; - if (pm->depth_first()) { - while(true) { - StarTask p; - if (PSPromotionManager::steal_depth(which, &random_seed, p)) { - TASKQUEUE_STATS_ONLY(pm->record_steal(p)); - pm->process_popped_location_depth(p); - pm->drain_stacks_depth(true); - } else { - if (terminator()->offer_termination()) { - break; - } - } - } - } else { - while(true) { - oop obj; - if (PSPromotionManager::steal_breadth(which, &random_seed, obj)) { - obj->copy_contents(pm); - pm->drain_stacks_breadth(true); - } else { - if (terminator()->offer_termination()) { - break; - } + while(true) { + StarTask p; + if (PSPromotionManager::steal_depth(which, &random_seed, p)) { + TASKQUEUE_STATS_ONLY(pm->record_steal(p)); + pm->process_popped_location_depth(p); + pm->drain_stacks_depth(true); + } else { + if (terminator()->offer_termination()) { + break; } } } diff --git a/hotspot/src/share/vm/oops/arrayKlassKlass.cpp b/hotspot/src/share/vm/oops/arrayKlassKlass.cpp index e2bb1432c58..36fefd98852 100644 --- a/hotspot/src/share/vm/oops/arrayKlassKlass.cpp +++ b/hotspot/src/share/vm/oops/arrayKlassKlass.cpp @@ -108,10 +108,6 @@ int arrayKlassKlass::oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr) { } #ifndef SERIALGC -void arrayKlassKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { - assert(obj->blueprint()->oop_is_arrayKlass(),"must be an array klass"); -} - void arrayKlassKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { assert(obj->blueprint()->oop_is_arrayKlass(),"must be an array klass"); } diff --git a/hotspot/src/share/vm/oops/compiledICHolderKlass.cpp b/hotspot/src/share/vm/oops/compiledICHolderKlass.cpp index fcda8a56adb..904fa1b2b93 100644 --- a/hotspot/src/share/vm/oops/compiledICHolderKlass.cpp +++ b/hotspot/src/share/vm/oops/compiledICHolderKlass.cpp @@ -120,10 +120,6 @@ int compiledICHolderKlass::oop_adjust_pointers(oop obj) { } #ifndef SERIALGC -void compiledICHolderKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { - assert(obj->is_compiledICHolder(), "must be compiledICHolder"); -} - void compiledICHolderKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { assert(obj->is_compiledICHolder(), "must be compiledICHolder"); } diff --git a/hotspot/src/share/vm/oops/constMethodKlass.cpp b/hotspot/src/share/vm/oops/constMethodKlass.cpp index 593e4c9838a..ec5162adf72 100644 --- a/hotspot/src/share/vm/oops/constMethodKlass.cpp +++ b/hotspot/src/share/vm/oops/constMethodKlass.cpp @@ -157,10 +157,6 @@ int constMethodKlass::oop_adjust_pointers(oop obj) { } #ifndef SERIALGC -void constMethodKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { - assert(obj->is_constMethod(), "should be constMethod"); -} - void constMethodKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { assert(obj->is_constMethod(), "should be constMethod"); } diff --git a/hotspot/src/share/vm/oops/constantPoolKlass.cpp b/hotspot/src/share/vm/oops/constantPoolKlass.cpp index dd4cb287c4a..8291ed6f38b 100644 --- a/hotspot/src/share/vm/oops/constantPoolKlass.cpp +++ b/hotspot/src/share/vm/oops/constantPoolKlass.cpp @@ -268,21 +268,6 @@ constantPoolKlass::oop_update_pointers(ParCompactionManager* cm, oop obj, return cp->object_size(); } -void constantPoolKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { - assert(obj->is_constantPool(), "should be constant pool"); - constantPoolOop cp = (constantPoolOop) obj; - if (AnonymousClasses && cp->has_pseudo_string() && cp->tags() != NULL) { - oop* base = (oop*)cp->base(); - for (int i = 0; i < cp->length(); ++i, ++base) { - if (cp->tag_at(i).is_string()) { - if (PSScavenge::should_scavenge(base)) { - pm->claim_or_forward_breadth(base); - } - } - } - } -} - void constantPoolKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { assert(obj->is_constantPool(), "should be constant pool"); constantPoolOop cp = (constantPoolOop) obj; diff --git a/hotspot/src/share/vm/oops/cpCacheKlass.cpp b/hotspot/src/share/vm/oops/cpCacheKlass.cpp index afff3352a6c..6a3a11e6a5b 100644 --- a/hotspot/src/share/vm/oops/cpCacheKlass.cpp +++ b/hotspot/src/share/vm/oops/cpCacheKlass.cpp @@ -166,29 +166,6 @@ bool constantPoolCacheKlass::oop_is_conc_safe(oop obj) const { } #ifndef SERIALGC -void constantPoolCacheKlass::oop_copy_contents(PSPromotionManager* pm, - oop obj) { - assert(obj->is_constantPoolCache(), "should be constant pool"); - if (EnableInvokeDynamic) { - constantPoolCacheOop cache = (constantPoolCacheOop)obj; - // during a scavenge, it is safe to inspect my pool, since it is perm - constantPoolOop pool = cache->constant_pool(); - assert(pool->is_constantPool(), "should be constant pool"); - if (pool->has_invokedynamic()) { - for (int i = 0; i < cache->length(); i++) { - ConstantPoolCacheEntry* e = cache->entry_at(i); - oop* p = (oop*)&e->_f1; - if (e->is_secondary_entry()) { - if (PSScavenge::should_scavenge(p)) - pm->claim_or_forward_breadth(p); - assert(!(e->is_vfinal() && PSScavenge::should_scavenge((oop*)&e->_f2)), - "no live oops here"); - } - } - } - } -} - void constantPoolCacheKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { assert(obj->is_constantPoolCache(), "should be constant pool"); diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp index 87bbddc5d4f..ff8077f1bcd 100644 --- a/hotspot/src/share/vm/oops/instanceKlass.cpp +++ b/hotspot/src/share/vm/oops/instanceKlass.cpp @@ -1809,18 +1809,7 @@ int instanceKlass::oop_adjust_pointers(oop obj) { } #ifndef SERIALGC -void instanceKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { - assert(!pm->depth_first(), "invariant"); - InstanceKlass_OOP_MAP_REVERSE_ITERATE( \ - obj, \ - if (PSScavenge::should_scavenge(p)) { \ - pm->claim_or_forward_breadth(p); \ - }, \ - assert_nothing ) -} - void instanceKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { - assert(pm->depth_first(), "invariant"); InstanceKlass_OOP_MAP_REVERSE_ITERATE( \ obj, \ if (PSScavenge::should_scavenge(p)) { \ @@ -1846,18 +1835,7 @@ int instanceKlass::oop_update_pointers(ParCompactionManager* cm, oop obj, return size_helper(); } -void instanceKlass::copy_static_fields(PSPromotionManager* pm) { - assert(!pm->depth_first(), "invariant"); - InstanceKlass_OOP_ITERATE( \ - start_of_static_fields(), static_oop_field_size(), \ - if (PSScavenge::should_scavenge(p)) { \ - pm->claim_or_forward_breadth(p); \ - }, \ - assert_nothing ) -} - void instanceKlass::push_static_fields(PSPromotionManager* pm) { - assert(pm->depth_first(), "invariant"); InstanceKlass_OOP_ITERATE( \ start_of_static_fields(), static_oop_field_size(), \ if (PSScavenge::should_scavenge(p)) { \ diff --git a/hotspot/src/share/vm/oops/instanceKlass.hpp b/hotspot/src/share/vm/oops/instanceKlass.hpp index c25b84a36eb..d895ce10895 100644 --- a/hotspot/src/share/vm/oops/instanceKlass.hpp +++ b/hotspot/src/share/vm/oops/instanceKlass.hpp @@ -711,7 +711,6 @@ class instanceKlass: public Klass { #ifndef SERIALGC // Parallel Scavenge - void copy_static_fields(PSPromotionManager* pm); void push_static_fields(PSPromotionManager* pm); // Parallel Old diff --git a/hotspot/src/share/vm/oops/instanceKlassKlass.cpp b/hotspot/src/share/vm/oops/instanceKlassKlass.cpp index 4184ce029bf..74268925ee5 100644 --- a/hotspot/src/share/vm/oops/instanceKlassKlass.cpp +++ b/hotspot/src/share/vm/oops/instanceKlassKlass.cpp @@ -292,41 +292,7 @@ int instanceKlassKlass::oop_adjust_pointers(oop obj) { } #ifndef SERIALGC -void instanceKlassKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { - assert(!pm->depth_first(), "invariant"); - instanceKlass* ik = instanceKlass::cast(klassOop(obj)); - ik->copy_static_fields(pm); - - oop* loader_addr = ik->adr_class_loader(); - if (PSScavenge::should_scavenge(loader_addr)) { - pm->claim_or_forward_breadth(loader_addr); - } - - oop* pd_addr = ik->adr_protection_domain(); - if (PSScavenge::should_scavenge(pd_addr)) { - pm->claim_or_forward_breadth(pd_addr); - } - - oop* hk_addr = ik->adr_host_klass(); - if (PSScavenge::should_scavenge(hk_addr)) { - pm->claim_or_forward_breadth(hk_addr); - } - - oop* sg_addr = ik->adr_signers(); - if (PSScavenge::should_scavenge(sg_addr)) { - pm->claim_or_forward_breadth(sg_addr); - } - - oop* bsm_addr = ik->adr_bootstrap_method(); - if (PSScavenge::should_scavenge(bsm_addr)) { - pm->claim_or_forward_breadth(bsm_addr); - } - - klassKlass::oop_copy_contents(pm, obj); -} - void instanceKlassKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { - assert(pm->depth_first(), "invariant"); instanceKlass* ik = instanceKlass::cast(klassOop(obj)); ik->push_static_fields(pm); @@ -355,7 +321,7 @@ void instanceKlassKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { pm->claim_or_forward_depth(bsm_addr); } - klassKlass::oop_copy_contents(pm, obj); + klassKlass::oop_push_contents(pm, obj); } int instanceKlassKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) { diff --git a/hotspot/src/share/vm/oops/instanceRefKlass.cpp b/hotspot/src/share/vm/oops/instanceRefKlass.cpp index f8670f99266..022827f309f 100644 --- a/hotspot/src/share/vm/oops/instanceRefKlass.cpp +++ b/hotspot/src/share/vm/oops/instanceRefKlass.cpp @@ -272,42 +272,9 @@ ALL_OOP_OOP_ITERATE_CLOSURES_1(InstanceRefKlass_OOP_OOP_ITERATE_DEFN_m) ALL_OOP_OOP_ITERATE_CLOSURES_2(InstanceRefKlass_OOP_OOP_ITERATE_DEFN_m) #ifndef SERIALGC -template -void specialized_oop_copy_contents(instanceRefKlass *ref, - PSPromotionManager* pm, oop obj) { - assert(!pm->depth_first(), "invariant"); - T* referent_addr = (T*)java_lang_ref_Reference::referent_addr(obj); - if (PSScavenge::should_scavenge(referent_addr)) { - ReferenceProcessor* rp = PSScavenge::reference_processor(); - if (rp->discover_reference(obj, ref->reference_type())) { - // reference already enqueued, referent and next will be traversed later - ref->instanceKlass::oop_copy_contents(pm, obj); - return; - } else { - // treat referent as normal oop - pm->claim_or_forward_breadth(referent_addr); - } - } - // treat next as normal oop - T* next_addr = (T*)java_lang_ref_Reference::next_addr(obj); - if (PSScavenge::should_scavenge(next_addr)) { - pm->claim_or_forward_breadth(next_addr); - } - ref->instanceKlass::oop_copy_contents(pm, obj); -} - -void instanceRefKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { - if (UseCompressedOops) { - specialized_oop_copy_contents(this, pm, obj); - } else { - specialized_oop_copy_contents(this, pm, obj); - } -} - template void specialized_oop_push_contents(instanceRefKlass *ref, PSPromotionManager* pm, oop obj) { - assert(pm->depth_first(), "invariant"); T* referent_addr = (T*)java_lang_ref_Reference::referent_addr(obj); if (PSScavenge::should_scavenge(referent_addr)) { ReferenceProcessor* rp = PSScavenge::reference_processor(); diff --git a/hotspot/src/share/vm/oops/klassKlass.cpp b/hotspot/src/share/vm/oops/klassKlass.cpp index d4647e1081f..48377c370c8 100644 --- a/hotspot/src/share/vm/oops/klassKlass.cpp +++ b/hotspot/src/share/vm/oops/klassKlass.cpp @@ -161,9 +161,6 @@ int klassKlass::oop_adjust_pointers(oop obj) { } #ifndef SERIALGC -void klassKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { -} - void klassKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { } diff --git a/hotspot/src/share/vm/oops/klassPS.hpp b/hotspot/src/share/vm/oops/klassPS.hpp index c53abf770c2..6c02905db9b 100644 --- a/hotspot/src/share/vm/oops/klassPS.hpp +++ b/hotspot/src/share/vm/oops/klassPS.hpp @@ -28,7 +28,6 @@ #ifndef SERIALGC #define PARALLEL_GC_DECLS \ - virtual void oop_copy_contents(PSPromotionManager* pm, oop obj); \ virtual void oop_push_contents(PSPromotionManager* pm, oop obj); \ /* Parallel Old GC support \ \ @@ -43,7 +42,6 @@ // Pure virtual version for klass.hpp #define PARALLEL_GC_DECLS_PV \ - virtual void oop_copy_contents(PSPromotionManager* pm, oop obj) = 0; \ virtual void oop_push_contents(PSPromotionManager* pm, oop obj) = 0; \ virtual void oop_follow_contents(ParCompactionManager* cm, oop obj) = 0; \ virtual int oop_update_pointers(ParCompactionManager* cm, oop obj) = 0; \ diff --git a/hotspot/src/share/vm/oops/methodDataKlass.cpp b/hotspot/src/share/vm/oops/methodDataKlass.cpp index 03b5bd8878b..e11f6afd6a0 100644 --- a/hotspot/src/share/vm/oops/methodDataKlass.cpp +++ b/hotspot/src/share/vm/oops/methodDataKlass.cpp @@ -154,13 +154,6 @@ int methodDataKlass::oop_adjust_pointers(oop obj) { #ifndef SERIALGC -void methodDataKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { - assert (obj->is_methodData(), "object must be method data"); - methodDataOop m = methodDataOop(obj); - // This should never point into the young gen. - assert(!PSScavenge::should_scavenge(m->adr_method()), "Sanity"); -} - void methodDataKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { assert (obj->is_methodData(), "object must be method data"); methodDataOop m = methodDataOop(obj); diff --git a/hotspot/src/share/vm/oops/methodKlass.cpp b/hotspot/src/share/vm/oops/methodKlass.cpp index f0ba4ad8413..40677d19290 100644 --- a/hotspot/src/share/vm/oops/methodKlass.cpp +++ b/hotspot/src/share/vm/oops/methodKlass.cpp @@ -184,10 +184,6 @@ int methodKlass::oop_adjust_pointers(oop obj) { } #ifndef SERIALGC -void methodKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { - assert(obj->is_method(), "should be method"); -} - void methodKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { assert(obj->is_method(), "should be method"); } diff --git a/hotspot/src/share/vm/oops/objArrayKlass.cpp b/hotspot/src/share/vm/oops/objArrayKlass.cpp index 96935b61448..a932a117d67 100644 --- a/hotspot/src/share/vm/oops/objArrayKlass.cpp +++ b/hotspot/src/share/vm/oops/objArrayKlass.cpp @@ -426,18 +426,7 @@ int objArrayKlass::oop_adjust_pointers(oop obj) { } #ifndef SERIALGC -void objArrayKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { - assert(!pm->depth_first(), "invariant"); - assert(obj->is_objArray(), "obj must be obj array"); - ObjArrayKlass_OOP_ITERATE( \ - objArrayOop(obj), p, \ - if (PSScavenge::should_scavenge(p)) { \ - pm->claim_or_forward_breadth(p); \ - }) -} - void objArrayKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { - assert(pm->depth_first(), "invariant"); assert(obj->is_objArray(), "obj must be obj array"); ObjArrayKlass_OOP_ITERATE( \ objArrayOop(obj), p, \ diff --git a/hotspot/src/share/vm/oops/objArrayKlassKlass.cpp b/hotspot/src/share/vm/oops/objArrayKlassKlass.cpp index f486dc7db40..7050d57b14b 100644 --- a/hotspot/src/share/vm/oops/objArrayKlassKlass.cpp +++ b/hotspot/src/share/vm/oops/objArrayKlassKlass.cpp @@ -229,10 +229,6 @@ objArrayKlassKlass::oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr) { } #ifndef SERIALGC -void objArrayKlassKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { - assert(obj->blueprint()->oop_is_objArrayKlass(),"must be an obj array klass"); -} - void objArrayKlassKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { assert(obj->blueprint()->oop_is_objArrayKlass(),"must be an obj array klass"); } diff --git a/hotspot/src/share/vm/oops/oop.hpp b/hotspot/src/share/vm/oops/oop.hpp index 952802c7801..04d31180401 100644 --- a/hotspot/src/share/vm/oops/oop.hpp +++ b/hotspot/src/share/vm/oops/oop.hpp @@ -306,7 +306,6 @@ class oopDesc { #ifndef SERIALGC // Parallel Scavenge - void copy_contents(PSPromotionManager* pm); void push_contents(PSPromotionManager* pm); // Parallel Old diff --git a/hotspot/src/share/vm/oops/oop.psgc.inline.hpp b/hotspot/src/share/vm/oops/oop.psgc.inline.hpp index 07ad5c5a653..40d40a3725b 100644 --- a/hotspot/src/share/vm/oops/oop.psgc.inline.hpp +++ b/hotspot/src/share/vm/oops/oop.psgc.inline.hpp @@ -24,15 +24,6 @@ // ParallelScavengeHeap methods -inline void oopDesc::copy_contents(PSPromotionManager* pm) { - Klass* klass = blueprint(); - if (!klass->oop_is_typeArray()) { - // It might contain oops beyond the header, so take the virtual call. - klass->oop_copy_contents(pm, this); - } - // Else skip it. The typeArrayKlass in the header never needs scavenging. -} - inline void oopDesc::push_contents(PSPromotionManager* pm) { Klass* klass = blueprint(); if (!klass->oop_is_typeArray()) { diff --git a/hotspot/src/share/vm/oops/symbolKlass.cpp b/hotspot/src/share/vm/oops/symbolKlass.cpp index 4fe4dc86090..e3806db1202 100644 --- a/hotspot/src/share/vm/oops/symbolKlass.cpp +++ b/hotspot/src/share/vm/oops/symbolKlass.cpp @@ -184,10 +184,6 @@ int symbolKlass::oop_adjust_pointers(oop obj) { #ifndef SERIALGC -void symbolKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { - assert(obj->is_symbol(), "should be symbol"); -} - void symbolKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { assert(obj->is_symbol(), "should be symbol"); } diff --git a/hotspot/src/share/vm/oops/typeArrayKlass.cpp b/hotspot/src/share/vm/oops/typeArrayKlass.cpp index edb216e68ae..088960ce0b6 100644 --- a/hotspot/src/share/vm/oops/typeArrayKlass.cpp +++ b/hotspot/src/share/vm/oops/typeArrayKlass.cpp @@ -228,10 +228,6 @@ int typeArrayKlass::oop_oop_iterate_m(oop obj, OopClosure* blk, MemRegion mr) { } #ifndef SERIALGC -void typeArrayKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) { - assert(obj->is_typeArray(),"must be a type array"); -} - void typeArrayKlass::oop_push_contents(PSPromotionManager* pm, oop obj) { assert(obj->is_typeArray(),"must be a type array"); } diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp index 5ba65fba422..b2182419673 100644 --- a/hotspot/src/share/vm/runtime/arguments.cpp +++ b/hotspot/src/share/vm/runtime/arguments.cpp @@ -184,6 +184,8 @@ static ObsoleteFlag obsolete_jvm_flags[] = { { "DefaultMaxRAM", JDK_Version::jdk_update(6,18), JDK_Version::jdk(7) }, { "DefaultInitialRAMFraction", JDK_Version::jdk_update(6,18), JDK_Version::jdk(7) }, + { "UseDepthFirstScavengeOrder", + JDK_Version::jdk_update(6,22), JDK_Version::jdk(7) }, { NULL, JDK_Version(0), JDK_Version(0) } }; diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp index daf5b6b6ba5..bf7264a578a 100644 --- a/hotspot/src/share/vm/runtime/globals.hpp +++ b/hotspot/src/share/vm/runtime/globals.hpp @@ -3088,10 +3088,6 @@ class CommandLineFlags { \ product(intx, SafepointSpinBeforeYield, 2000, "(Unstable)") \ \ - product(bool, UseDepthFirstScavengeOrder, true, \ - "true: the scavenge order will be depth-first, " \ - "false: the scavenge order will be breadth-first") \ - \ product(bool, PSChunkLargeArrays, true, \ "true: process large arrays in chunks") \ \ From 784f62993b0d54abbf2a228d71ad89840208f85e Mon Sep 17 00:00:00 2001 From: Chris Hegarty Date: Tue, 3 Aug 2010 12:03:03 +0100 Subject: [PATCH 004/100] 6973030: NTLM proxy authentication fails with https Reviewed-by: michaelm --- .../www/protocol/http/HttpURLConnection.java | 4 + .../https/HttpsURLConnection/B6226610.java | 79 ++++++------------- 2 files changed, 30 insertions(+), 53 deletions(-) diff --git a/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java index 713c7d85c21..e888abe26e8 100644 --- a/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java +++ b/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java @@ -1768,6 +1768,10 @@ public class HttpURLConnection extends java.net.HttpURLConnection { // Not really necessary for a tunnel, but can't hurt requests.setIfNotSet("Accept", acceptString); + if (http.getHttpKeepAliveSet()) { + requests.setIfNotSet("Proxy-Connection", "keep-alive"); + } + setPreemptiveProxyAuthentication(requests); /* Log the CONNECT request */ diff --git a/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/B6226610.java b/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/B6226610.java index 3856ef7ebe1..7481a6cbf04 100644 --- a/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/B6226610.java +++ b/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/B6226610.java @@ -23,7 +23,7 @@ /* * @test - * @bug 6226610 + * @bug 6226610 6973030 * @run main/othervm B6226610 * @summary HTTP tunnel connections send user headers to proxy */ @@ -36,45 +36,23 @@ import java.io.*; import java.net.*; -import javax.net.ssl.*; -import javax.net.ServerSocketFactory; -import sun.net.www.*; -import java.util.Enumeration; +import sun.net.www.MessageHeader; public class B6226610 { static HeaderCheckerProxyTunnelServer proxy; - // it seems there's no proxy ever if a url points to 'localhost', - // even if proxy related properties are set. so we need to bind - // our simple http proxy and http server to a non-loopback address - static InetAddress firstNonLoAddress = null; - - public static void main(String[] args) + public static void main(String[] args) throws Exception { - try { - proxy = new HeaderCheckerProxyTunnelServer(); - proxy.start(); - } catch (Exception e) { - System.out.println("Cannot create proxy: " + e); - } + proxy = new HeaderCheckerProxyTunnelServer(); + proxy.start(); - try { - firstNonLoAddress = getNonLoAddress(); - - if (firstNonLoAddress == null) { - System.out.println("The test needs at least one non-loopback address to run. Quit now."); - System.exit(0); - } - } catch (Exception e) { - e.printStackTrace(); - } - - System.setProperty( "https.proxyHost", firstNonLoAddress.getHostAddress()); - System.setProperty( "https.proxyPort", (new Integer(proxy.getLocalPort())).toString() ); + String hostname = InetAddress.getLocalHost().getHostName(); try { - URL u = new URL("https://" + firstNonLoAddress.getHostAddress()); - java.net.URLConnection c = u.openConnection(); + URL u = new URL("https://" + hostname + "/"); + System.out.println("Connecting to " + u); + InetSocketAddress proxyAddr = new InetSocketAddress(hostname, proxy.getLocalPort()); + java.net.URLConnection c = u.openConnection(new Proxy(Proxy.Type.HTTP, proxyAddr)); /* I want this header to go to the destination server only, protected * by SSL @@ -89,33 +67,15 @@ public class B6226610 { } else System.out.println(e); - + } finally { + if (proxy != null) proxy.shutdown(); } if (HeaderCheckerProxyTunnelServer.failed) - throw new RuntimeException("Test failed: Proxy should not receive user defined headers for tunneled requests"); + throw new RuntimeException("Test failed; see output"); } - - public static InetAddress getNonLoAddress() throws Exception { - NetworkInterface loNIC = NetworkInterface.getByInetAddress(InetAddress.getByName("localhost")); - Enumeration nics = NetworkInterface.getNetworkInterfaces(); - while (nics.hasMoreElements()) { - NetworkInterface nic = nics.nextElement(); - if (!nic.getName().equalsIgnoreCase(loNIC.getName())) { - Enumeration addrs = nic.getInetAddresses(); - while (addrs.hasMoreElements()) { - InetAddress addr = addrs.nextElement(); - if (!addr.isLoopbackAddress()) - return addr; - } - } - } - return null; - } - } - class HeaderCheckerProxyTunnelServer extends Thread { public static boolean failed = false; @@ -139,6 +99,10 @@ class HeaderCheckerProxyTunnelServer extends Thread } } + void shutdown() { + try { ss.close(); } catch (IOException e) {} + } + public void run() { try { @@ -178,6 +142,15 @@ class HeaderCheckerProxyTunnelServer extends Thread retrieveConnectInfo(statusLine); if (mheader.findValue("X-TestHeader") != null) { + System.out.println("Proxy should not receive user defined headers for tunneled requests"); + failed = true; + } + + // 6973030 + String value; + if ((value = mheader.findValue("Proxy-Connection")) == null || + !value.equals("keep-alive")) { + System.out.println("Proxy-Connection:keep-alive not being sent"); failed = true; } From 707346f0994cc94ec8c3b4f8008adee2fc84b613 Mon Sep 17 00:00:00 2001 From: Sean Mullan Date: Tue, 3 Aug 2010 09:39:52 -0400 Subject: [PATCH 005/100] 6653372: Error in java.security.KeyStore example code Reviewed-by: weijun --- jdk/src/share/classes/java/security/KeyStore.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/jdk/src/share/classes/java/security/KeyStore.java b/jdk/src/share/classes/java/security/KeyStore.java index 7ecac8025c1..c1e2e4649e7 100644 --- a/jdk/src/share/classes/java/security/KeyStore.java +++ b/jdk/src/share/classes/java/security/KeyStore.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, 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 @@ -131,17 +131,19 @@ import javax.security.auth.callback.*; * to read existing entries from the keystore, or to write new entries * into the keystore: *
+ *    KeyStore.ProtectionParameter protParam =
+ *        new KeyStore.PasswordProtection(password);
+ *
  *    // get my private key
  *    KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry)
- *        ks.getEntry("privateKeyAlias", password);
+ *        ks.getEntry("privateKeyAlias", protParam);
  *    PrivateKey myPrivateKey = pkEntry.getPrivateKey();
  *
  *    // save my secret key
  *    javax.crypto.SecretKey mySecretKey;
  *    KeyStore.SecretKeyEntry skEntry =
  *        new KeyStore.SecretKeyEntry(mySecretKey);
- *    ks.setEntry("secretKeyAlias", skEntry,
- *        new KeyStore.PasswordProtection(password));
+ *    ks.setEntry("secretKeyAlias", skEntry, protParam);
  *
  *    // store away the keystore
  *    java.io.FileOutputStream fos = null;

From 500a66b0d8a31f482597b26ca1e41aeb66738322 Mon Sep 17 00:00:00 2001
From: Martin Buchholz 
Date: Tue, 3 Aug 2010 12:22:49 -0700
Subject: [PATCH 006/100] 6955504: (str)
 String[Builder/Buffer].append(char[],int,int) throws OutOfMemoryError in b94

Let arraycopy throw AIOOBE for invalid negative length

Reviewed-by: chegar, forax
---
 jdk/src/share/classes/java/lang/AbstractStringBuilder.java | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/jdk/src/share/classes/java/lang/AbstractStringBuilder.java b/jdk/src/share/classes/java/lang/AbstractStringBuilder.java
index fad8ad6da66..46a14a0aa20 100644
--- a/jdk/src/share/classes/java/lang/AbstractStringBuilder.java
+++ b/jdk/src/share/classes/java/lang/AbstractStringBuilder.java
@@ -470,7 +470,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
     public AbstractStringBuilder append(CharSequence s, int start, int end) {
         if (s == null)
             s = "null";
-        if ((start < 0) || (end < 0) || (start > end) || (end > s.length()))
+        if ((start < 0) || (start > end) || (end > s.length()))
             throw new IndexOutOfBoundsException(
                 "start " + start + ", end " + end + ", s.length() "
                 + s.length());
@@ -529,7 +529,8 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
      *         or {@code offset+len > str.length}
      */
     public AbstractStringBuilder append(char str[], int offset, int len) {
-        ensureCapacityInternal(count + len);
+        if (len > 0)                // let arraycopy report AIOOBE for len < 0
+            ensureCapacityInternal(count + len);
         System.arraycopy(str, offset, value, count, len);
         count += len;
         return this;

From b4148f33790bb490fc8fd5c02a581cd018067374 Mon Sep 17 00:00:00 2001
From: Antonios Printezis 
Date: Wed, 4 Aug 2010 13:03:23 -0400
Subject: [PATCH 007/100] 6963209: G1: remove the concept of abandoned pauses

As part of 6944166 we disabled the concept of abandoned pauses (i.e., if the collection set is empty, we would still try to do a pause even if it is to update the RSets and scan the roots). This changeset removes the code and structures associated with abandoned pauses.

Reviewed-by: iveresov, johnc
---
 .../gc_implementation/g1/g1CollectedHeap.cpp  | 100 +++------
 .../g1/g1CollectorPolicy.cpp                  | 208 ++++++------------
 .../g1/g1CollectorPolicy.hpp                  |  13 +-
 3 files changed, 107 insertions(+), 214 deletions(-)

diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
index 6d798a54889..76baff21b62 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
@@ -2843,93 +2843,57 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
       g1_policy()->print_collection_set(g1_policy()->inc_cset_head(), gclog_or_tty);
 #endif // YOUNG_LIST_VERBOSE
 
-      // Now choose the CS. We may abandon a pause if we find no
-      // region that will fit in the MMU pause.
-      bool abandoned = g1_policy()->choose_collection_set(target_pause_time_ms);
+      g1_policy()->choose_collection_set(target_pause_time_ms);
 
       // Nothing to do if we were unable to choose a collection set.
-      if (!abandoned) {
 #if G1_REM_SET_LOGGING
-        gclog_or_tty->print_cr("\nAfter pause, heap:");
-        print();
+      gclog_or_tty->print_cr("\nAfter pause, heap:");
+      print();
 #endif
-        PrepareForRSScanningClosure prepare_for_rs_scan;
-        collection_set_iterate(&prepare_for_rs_scan);
+      PrepareForRSScanningClosure prepare_for_rs_scan;
+      collection_set_iterate(&prepare_for_rs_scan);
 
-        setup_surviving_young_words();
+      setup_surviving_young_words();
 
-        // Set up the gc allocation regions.
-        get_gc_alloc_regions();
+      // Set up the gc allocation regions.
+      get_gc_alloc_regions();
 
-        // Actually do the work...
-        evacuate_collection_set();
+      // Actually do the work...
+      evacuate_collection_set();
 
-        free_collection_set(g1_policy()->collection_set());
-        g1_policy()->clear_collection_set();
+      free_collection_set(g1_policy()->collection_set());
+      g1_policy()->clear_collection_set();
 
-        cleanup_surviving_young_words();
+      cleanup_surviving_young_words();
 
-        // Start a new incremental collection set for the next pause.
-        g1_policy()->start_incremental_cset_building();
+      // Start a new incremental collection set for the next pause.
+      g1_policy()->start_incremental_cset_building();
 
-        // Clear the _cset_fast_test bitmap in anticipation of adding
-        // regions to the incremental collection set for the next
-        // evacuation pause.
-        clear_cset_fast_test();
+      // Clear the _cset_fast_test bitmap in anticipation of adding
+      // regions to the incremental collection set for the next
+      // evacuation pause.
+      clear_cset_fast_test();
 
-        if (g1_policy()->in_young_gc_mode()) {
-          _young_list->reset_sampled_info();
+      if (g1_policy()->in_young_gc_mode()) {
+        _young_list->reset_sampled_info();
 
-          // Don't check the whole heap at this point as the
-          // GC alloc regions from this pause have been tagged
-          // as survivors and moved on to the survivor list.
-          // Survivor regions will fail the !is_young() check.
-          assert(check_young_list_empty(false /* check_heap */),
-              "young list should be empty");
+        // Don't check the whole heap at this point as the
+        // GC alloc regions from this pause have been tagged
+        // as survivors and moved on to the survivor list.
+        // Survivor regions will fail the !is_young() check.
+        assert(check_young_list_empty(false /* check_heap */),
+               "young list should be empty");
 
 #if YOUNG_LIST_VERBOSE
-          gclog_or_tty->print_cr("Before recording survivors.\nYoung List:");
-          _young_list->print();
+        gclog_or_tty->print_cr("Before recording survivors.\nYoung List:");
+        _young_list->print();
 #endif // YOUNG_LIST_VERBOSE
 
-          g1_policy()->record_survivor_regions(_young_list->survivor_length(),
+        g1_policy()->record_survivor_regions(_young_list->survivor_length(),
                                           _young_list->first_survivor_region(),
                                           _young_list->last_survivor_region());
 
-          _young_list->reset_auxilary_lists();
-        }
-      } else {
-        // We have abandoned the current collection. This can only happen
-        // if we're not doing young or partially young collections, and
-        // we didn't find an old region that we're able to collect within
-        // the allowed time.
-
-        assert(g1_policy()->collection_set() == NULL, "should be");
-        assert(_young_list->length() == 0, "because it should be");
-
-        // This should be a no-op.
-        abandon_collection_set(g1_policy()->inc_cset_head());
-
-        g1_policy()->clear_incremental_cset();
-        g1_policy()->stop_incremental_cset_building();
-
-        // Start a new incremental collection set for the next pause.
-        g1_policy()->start_incremental_cset_building();
-
-        // Clear the _cset_fast_test bitmap in anticipation of adding
-        // regions to the incremental collection set for the next
-        // evacuation pause.
-        clear_cset_fast_test();
-
-        // This looks confusing, because the DPT should really be empty
-        // at this point -- since we have not done any collection work,
-        // there should not be any derived pointers in the table to update;
-        // however, there is some additional state in the DPT which is
-        // reset at the end of the (null) "gc" here via the following call.
-        // A better approach might be to split off that state resetting work
-        // into a separate method that asserts that the DPT is empty and call
-        // that here. That is deferred for now.
-        COMPILER2_PRESENT(DerivedPointerTable::update_pointers());
+        _young_list->reset_auxilary_lists();
       }
 
       if (evacuation_failed()) {
@@ -2963,7 +2927,7 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
       double end_time_sec = os::elapsedTime();
       double pause_time_ms = (end_time_sec - start_time_sec) * MILLIUNITS;
       g1_policy()->record_pause_time_ms(pause_time_ms);
-      g1_policy()->record_collection_pause_end(abandoned);
+      g1_policy()->record_collection_pause_end();
 
       assert(regions_accounted_for(), "Region leakage.");
 
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp
index 2feaae7df3a..ef21287146c 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp
@@ -88,7 +88,6 @@ G1CollectorPolicy::G1CollectorPolicy() :
   _all_mod_union_times_ms(new NumberSeq()),
 
   _summary(new Summary()),
-  _abandoned_summary(new AbandonedSummary()),
 
 #ifndef PRODUCT
   _cur_clear_ct_time_ms(0.0),
@@ -1124,7 +1123,7 @@ double G1CollectorPolicy::max_sum (double* data1,
 // Anything below that is considered to be zero
 #define MIN_TIMER_GRANULARITY 0.0000001
 
-void G1CollectorPolicy::record_collection_pause_end(bool abandoned) {
+void G1CollectorPolicy::record_collection_pause_end() {
   double end_time_sec = os::elapsedTime();
   double elapsed_ms = _last_pause_time_ms;
   bool parallel = ParallelGCThreads > 0;
@@ -1134,7 +1133,7 @@ void G1CollectorPolicy::record_collection_pause_end(bool abandoned) {
   size_t cur_used_bytes = _g1->used();
   assert(cur_used_bytes == _g1->recalculate_used(), "It should!");
   bool last_pause_included_initial_mark = false;
-  bool update_stats = !abandoned && !_g1->evacuation_failed();
+  bool update_stats = !_g1->evacuation_failed();
 
 #ifndef PRODUCT
   if (G1YoungSurvRateVerbose) {
@@ -1273,12 +1272,7 @@ void G1CollectorPolicy::record_collection_pause_end(bool abandoned) {
     gclog_or_tty->print_cr("   Recording collection pause(%d)", _n_pauses);
   }
 
-  PauseSummary* summary;
-  if (abandoned) {
-    summary = _abandoned_summary;
-  } else {
-    summary = _summary;
-  }
+  PauseSummary* summary = _summary;
 
   double ext_root_scan_time = avg_value(_par_last_ext_root_scan_times_ms);
   double mark_stack_scan_time = avg_value(_par_last_mark_stack_scan_times_ms);
@@ -1346,61 +1340,58 @@ void G1CollectorPolicy::record_collection_pause_end(bool abandoned) {
 
   double other_time_ms = elapsed_ms;
 
-  if (!abandoned) {
-    if (_satb_drain_time_set)
-      other_time_ms -= _cur_satb_drain_time_ms;
+  if (_satb_drain_time_set) {
+    other_time_ms -= _cur_satb_drain_time_ms;
+  }
 
-    if (parallel)
-      other_time_ms -= _cur_collection_par_time_ms + _cur_clear_ct_time_ms;
-    else
-      other_time_ms -=
-        update_rs_time +
-        ext_root_scan_time + mark_stack_scan_time +
-        scan_rs_time + obj_copy_time;
+  if (parallel) {
+    other_time_ms -= _cur_collection_par_time_ms + _cur_clear_ct_time_ms;
+  } else {
+    other_time_ms -=
+      update_rs_time +
+      ext_root_scan_time + mark_stack_scan_time +
+      scan_rs_time + obj_copy_time;
   }
 
   if (PrintGCDetails) {
-    gclog_or_tty->print_cr("%s%s, %1.8lf secs]",
-                           abandoned ? " (abandoned)" : "",
+    gclog_or_tty->print_cr("%s, %1.8lf secs]",
                            (last_pause_included_initial_mark) ? " (initial-mark)" : "",
                            elapsed_ms / 1000.0);
 
-    if (!abandoned) {
-      if (_satb_drain_time_set) {
-        print_stats(1, "SATB Drain Time", _cur_satb_drain_time_ms);
-      }
-      if (_last_satb_drain_processed_buffers >= 0) {
-        print_stats(2, "Processed Buffers", _last_satb_drain_processed_buffers);
-      }
-      if (parallel) {
-        print_stats(1, "Parallel Time", _cur_collection_par_time_ms);
-        print_par_stats(2, "GC Worker Start Time",
-                        _par_last_gc_worker_start_times_ms, false);
-        print_par_stats(2, "Update RS", _par_last_update_rs_times_ms);
-        print_par_sizes(3, "Processed Buffers",
-                        _par_last_update_rs_processed_buffers, true);
-        print_par_stats(2, "Ext Root Scanning",
-                        _par_last_ext_root_scan_times_ms);
-        print_par_stats(2, "Mark Stack Scanning",
-                        _par_last_mark_stack_scan_times_ms);
-        print_par_stats(2, "Scan RS", _par_last_scan_rs_times_ms);
-        print_par_stats(2, "Object Copy", _par_last_obj_copy_times_ms);
-        print_par_stats(2, "Termination", _par_last_termination_times_ms);
-        print_par_sizes(3, "Termination Attempts",
-                        _par_last_termination_attempts, true);
-        print_par_stats(2, "GC Worker End Time",
-                        _par_last_gc_worker_end_times_ms, false);
-        print_stats(2, "Other", parallel_other_time);
-        print_stats(1, "Clear CT", _cur_clear_ct_time_ms);
-      } else {
-        print_stats(1, "Update RS", update_rs_time);
-        print_stats(2, "Processed Buffers",
-                    (int)update_rs_processed_buffers);
-        print_stats(1, "Ext Root Scanning", ext_root_scan_time);
-        print_stats(1, "Mark Stack Scanning", mark_stack_scan_time);
-        print_stats(1, "Scan RS", scan_rs_time);
-        print_stats(1, "Object Copying", obj_copy_time);
-      }
+    if (_satb_drain_time_set) {
+      print_stats(1, "SATB Drain Time", _cur_satb_drain_time_ms);
+    }
+    if (_last_satb_drain_processed_buffers >= 0) {
+      print_stats(2, "Processed Buffers", _last_satb_drain_processed_buffers);
+    }
+    if (parallel) {
+      print_stats(1, "Parallel Time", _cur_collection_par_time_ms);
+      print_par_stats(2, "GC Worker Start Time",
+                      _par_last_gc_worker_start_times_ms, false);
+      print_par_stats(2, "Update RS", _par_last_update_rs_times_ms);
+      print_par_sizes(3, "Processed Buffers",
+                      _par_last_update_rs_processed_buffers, true);
+      print_par_stats(2, "Ext Root Scanning",
+                      _par_last_ext_root_scan_times_ms);
+      print_par_stats(2, "Mark Stack Scanning",
+                      _par_last_mark_stack_scan_times_ms);
+      print_par_stats(2, "Scan RS", _par_last_scan_rs_times_ms);
+      print_par_stats(2, "Object Copy", _par_last_obj_copy_times_ms);
+      print_par_stats(2, "Termination", _par_last_termination_times_ms);
+      print_par_sizes(3, "Termination Attempts",
+                      _par_last_termination_attempts, true);
+      print_par_stats(2, "GC Worker End Time",
+                      _par_last_gc_worker_end_times_ms, false);
+      print_stats(2, "Other", parallel_other_time);
+      print_stats(1, "Clear CT", _cur_clear_ct_time_ms);
+    } else {
+      print_stats(1, "Update RS", update_rs_time);
+      print_stats(2, "Processed Buffers",
+                  (int)update_rs_processed_buffers);
+      print_stats(1, "Ext Root Scanning", ext_root_scan_time);
+      print_stats(1, "Mark Stack Scanning", mark_stack_scan_time);
+      print_stats(1, "Scan RS", scan_rs_time);
+      print_stats(1, "Object Copying", obj_copy_time);
     }
 #ifndef PRODUCT
     print_stats(1, "Cur Clear CC", _cur_clear_cc_time_ms);
@@ -2176,33 +2167,27 @@ void G1CollectorPolicy::print_summary(PauseSummary* summary) const {
     print_summary(1, "Other", summary->get_other_seq());
     {
       NumberSeq calc_other_times_ms;
-      if (body_summary != NULL) {
-        // not abandoned
-        if (parallel) {
-          // parallel
-          NumberSeq* other_parts[] = {
-            body_summary->get_satb_drain_seq(),
-            body_summary->get_parallel_seq(),
-            body_summary->get_clear_ct_seq()
-          };
-          calc_other_times_ms = NumberSeq(summary->get_total_seq(),
-                                          3, other_parts);
-        } else {
-          // serial
-          NumberSeq* other_parts[] = {
-            body_summary->get_satb_drain_seq(),
-            body_summary->get_update_rs_seq(),
-            body_summary->get_ext_root_scan_seq(),
-            body_summary->get_mark_stack_scan_seq(),
-            body_summary->get_scan_rs_seq(),
-            body_summary->get_obj_copy_seq()
-          };
-          calc_other_times_ms = NumberSeq(summary->get_total_seq(),
-                                          7, other_parts);
-        }
+      if (parallel) {
+        // parallel
+        NumberSeq* other_parts[] = {
+          body_summary->get_satb_drain_seq(),
+          body_summary->get_parallel_seq(),
+          body_summary->get_clear_ct_seq()
+        };
+        calc_other_times_ms = NumberSeq(summary->get_total_seq(),
+                                        3, other_parts);
       } else {
-        // abandoned
-        calc_other_times_ms = NumberSeq();
+        // serial
+        NumberSeq* other_parts[] = {
+          body_summary->get_satb_drain_seq(),
+          body_summary->get_update_rs_seq(),
+          body_summary->get_ext_root_scan_seq(),
+          body_summary->get_mark_stack_scan_seq(),
+          body_summary->get_scan_rs_seq(),
+          body_summary->get_obj_copy_seq()
+        };
+        calc_other_times_ms = NumberSeq(summary->get_total_seq(),
+                                        7, other_parts);
       }
       check_other_times(1,  summary->get_other_seq(), &calc_other_times_ms);
     }
@@ -2213,20 +2198,6 @@ void G1CollectorPolicy::print_summary(PauseSummary* summary) const {
   gclog_or_tty->print_cr("");
 }
 
-void
-G1CollectorPolicy::print_abandoned_summary(PauseSummary* summary) const {
-  bool printed = false;
-  if (summary->get_total_seq()->num() > 0) {
-    printed = true;
-    print_summary(summary);
-  }
-  if (!printed) {
-    print_indent(0);
-    gclog_or_tty->print_cr("none");
-    gclog_or_tty->print_cr("");
-  }
-}
-
 void G1CollectorPolicy::print_tracing_info() const {
   if (TraceGen0Time) {
     gclog_or_tty->print_cr("ALL PAUSES");
@@ -2240,9 +2211,6 @@ void G1CollectorPolicy::print_tracing_info() const {
     gclog_or_tty->print_cr("EVACUATION PAUSES");
     print_summary(_summary);
 
-    gclog_or_tty->print_cr("ABANDONED PAUSES");
-    print_abandoned_summary(_abandoned_summary);
-
     gclog_or_tty->print_cr("MISC");
     print_summary_sd(0, "Stop World", _all_stop_world_times_ms);
     print_summary_sd(0, "Yields", _all_yield_times_ms);
@@ -2868,19 +2836,12 @@ void G1CollectorPolicy::print_collection_set(HeapRegion* list_head, outputStream
 }
 #endif // !PRODUCT
 
-bool
+void
 G1CollectorPolicy_BestRegionsFirst::choose_collection_set(
                                                   double target_pause_time_ms) {
   // Set this here - in case we're not doing young collections.
   double non_young_start_time_sec = os::elapsedTime();
 
-  // The result that this routine will return. This will be set to
-  // false if:
-  // * we're doing a young or partially young collection and we
-  //   have added the youg regions to collection set, or
-  // * we add old regions to the collection set.
-  bool abandon_collection = true;
-
   start_recording_regions();
 
   guarantee(target_pause_time_ms > 0.0,
@@ -2984,10 +2945,6 @@ G1CollectorPolicy_BestRegionsFirst::choose_collection_set(
     }
 
     assert(_inc_cset_size == _g1->young_list()->length(), "Invariant");
-    if (_inc_cset_size > 0) {
-      assert(_collection_set != NULL, "Invariant");
-      abandon_collection = false;
-    }
 
     double young_end_time_sec = os::elapsedTime();
     _recorded_young_cset_choice_time_ms =
@@ -3009,10 +2966,6 @@ G1CollectorPolicy_BestRegionsFirst::choose_collection_set(
     NumberSeq seq;
     double avg_prediction = 100000000000000000.0; // something very large
 
-    // Save the current size of the collection set to detect
-    // if we actually added any old regions.
-    size_t n_young_regions = _collection_set_size;
-
     do {
       hr = _collectionSetChooser->getNextMarkedRegion(time_remaining_ms,
                                                       avg_prediction);
@@ -3039,12 +2992,6 @@ G1CollectorPolicy_BestRegionsFirst::choose_collection_set(
     if (!adaptive_young_list_length() &&
         _collection_set_size < _young_list_fixed_length)
       _should_revert_to_full_young_gcs  = true;
-
-    if (_collection_set_size > n_young_regions) {
-      // We actually added old regions to the collection set
-      // so we are not abandoning this collection.
-      abandon_collection = false;
-    }
   }
 
 choose_collection_set_end:
@@ -3057,19 +3004,6 @@ choose_collection_set_end:
   double non_young_end_time_sec = os::elapsedTime();
   _recorded_non_young_cset_choice_time_ms =
     (non_young_end_time_sec - non_young_start_time_sec) * 1000.0;
-
-  // Here we are supposed to return whether the pause should be
-  // abandoned or not (i.e., whether the collection set is empty or
-  // not). However, this introduces a subtle issue when a pause is
-  // initiated explicitly with System.gc() and
-  // +ExplicitGCInvokesConcurrent (see Comment #2 in CR 6944166), it's
-  // supposed to start a marking cycle, and it's abandoned. So, by
-  // returning false here we are telling the caller never to consider
-  // a pause to be abandoned. We'll actually remove all the code
-  // associated with abandoned pauses as part of CR 6963209, but we are
-  // just disabling them this way for the moment to avoid increasing
-  // further the amount of changes for CR 6944166.
-  return false;
 }
 
 void G1CollectorPolicy_BestRegionsFirst::record_full_collection_end() {
@@ -3084,7 +3018,7 @@ expand_if_possible(size_t numRegions) {
 }
 
 void G1CollectorPolicy_BestRegionsFirst::
-record_collection_pause_end(bool abandoned) {
-  G1CollectorPolicy::record_collection_pause_end(abandoned);
+record_collection_pause_end() {
+  G1CollectorPolicy::record_collection_pause_end();
   assert(assertMarkedBytesDataOK(), "Marked regions not OK at pause end.");
 }
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp
index 84a8491142c..33ee6ebc446 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp
@@ -76,9 +76,6 @@ public:
   virtual MainBodySummary*    main_body_summary()    { return this; }
 };
 
-class AbandonedSummary: public PauseSummary {
-};
-
 class G1CollectorPolicy: public CollectorPolicy {
 protected:
   // The number of pauses during the execution.
@@ -148,7 +145,6 @@ protected:
   TruncatedSeq* _concurrent_mark_cleanup_times_ms;
 
   Summary*           _summary;
-  AbandonedSummary*  _abandoned_summary;
 
   NumberSeq* _all_pause_times_ms;
   NumberSeq* _all_full_gc_times_ms;
@@ -573,7 +569,6 @@ protected:
                          NumberSeq* calc_other_times_ms) const;
 
   void print_summary (PauseSummary* stats) const;
-  void print_abandoned_summary(PauseSummary* summary) const;
 
   void print_summary (int level, const char* str, NumberSeq* seq) const;
   void print_summary_sd (int level, const char* str, NumberSeq* seq) const;
@@ -886,7 +881,7 @@ public:
   virtual void record_collection_pause_end_CH_strong_roots();
   virtual void record_collection_pause_end_G1_strong_roots();
 
-  virtual void record_collection_pause_end(bool abandoned);
+  virtual void record_collection_pause_end();
 
   // Record the fact that a full collection occurred.
   virtual void record_full_collection_start();
@@ -999,7 +994,7 @@ public:
   // Choose a new collection set.  Marks the chosen regions as being
   // "in_collection_set", and links them together.  The head and number of
   // the collection set are available via access methods.
-  virtual bool choose_collection_set(double target_pause_time_ms) = 0;
+  virtual void choose_collection_set(double target_pause_time_ms) = 0;
 
   // The head of the list (via "next_in_collection_set()") representing the
   // current collection set.
@@ -1256,7 +1251,7 @@ class G1CollectorPolicy_BestRegionsFirst: public G1CollectorPolicy {
   // If the estimated is less then desirable, resize if possible.
   void expand_if_possible(size_t numRegions);
 
-  virtual bool choose_collection_set(double target_pause_time_ms);
+  virtual void choose_collection_set(double target_pause_time_ms);
   virtual void record_collection_pause_start(double start_time_sec,
                                              size_t start_used);
   virtual void record_concurrent_mark_cleanup_end(size_t freed_bytes,
@@ -1267,7 +1262,7 @@ public:
   G1CollectorPolicy_BestRegionsFirst() {
     _collectionSetChooser = new CollectionSetChooser();
   }
-  void record_collection_pause_end(bool abandoned);
+  void record_collection_pause_end();
   bool should_do_collection_pause(size_t word_size);
   // This is not needed any more, after the CSet choosing code was
   // changed to use the pause prediction work. But let's leave the

From bc38b1c703b1d89b77a14020cdaf85908d94d1b7 Mon Sep 17 00:00:00 2001
From: Andrei Pangin 
Date: Wed, 4 Aug 2010 20:25:02 -0700
Subject: [PATCH 008/100] 6945961: SIGSEGV in memcpy() during class loading on
 linux-i586

Check the result of strchr() in Bytecode Verifier

Reviewed-by: kamg, acorn
---
 jdk/src/share/native/common/check_code.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/jdk/src/share/native/common/check_code.c b/jdk/src/share/native/common/check_code.c
index 337a11f4ca4..0a2cb1433b4 100644
--- a/jdk/src/share/native/common/check_code.c
+++ b/jdk/src/share/native/common/check_code.c
@@ -2730,7 +2730,10 @@ push_stack(context_type *context, unsigned int inumber, stack_info_type *new_sta
                                                                 operand);
             const char *result_signature;
             check_and_push(context, signature, VM_STRING_UTF);
-            result_signature = strchr(signature, JVM_SIGNATURE_ENDFUNC) + 1;
+            result_signature = strchr(signature, JVM_SIGNATURE_ENDFUNC);
+            if (result_signature++ == NULL) {
+                CCerror(context, "Illegal signature %s", signature);
+            }
             if (result_signature[0] == JVM_SIGNATURE_VOID) {
                 stack_results = "";
             } else {
@@ -3654,14 +3657,13 @@ signature_to_fieldtype(context_type *context,
                        const char **signature_p, fullinfo_type *full_info_p)
 {
     const char *p = *signature_p;
-    fullinfo_type full_info = MAKE_FULLINFO(0, 0, 0);
+    fullinfo_type full_info = MAKE_FULLINFO(ITEM_Bogus, 0, 0);
     char result;
     int array_depth = 0;
 
     for (;;) {
         switch(*p++) {
             default:
-                full_info = MAKE_FULLINFO(ITEM_Bogus, 0, 0);
                 result = 0;
                 break;
 
@@ -3714,7 +3716,14 @@ signature_to_fieldtype(context_type *context,
                 char buffer_space[256];
                 char *buffer = buffer_space;
                 char *finish = strchr(p, JVM_SIGNATURE_ENDCLASS);
-                int length = finish - p;
+                int length;
+                if (finish == NULL) {
+                    /* Signature must have ';' after the class name.
+                     * If it does not, return 0 and ITEM_Bogus in full_info. */
+                    result = 0;
+                    break;
+                }
+                length = finish - p;
                 if (length + 1 > (int)sizeof(buffer_space)) {
                     buffer = malloc(length + 1);
                     check_and_push(context, buffer, VM_MALLOC_BLK);

From 156e82c164f046bd0d006b62130d5546b97c212c Mon Sep 17 00:00:00 2001
From: Maurizio Cimadamore 
Date: Thu, 5 Aug 2010 09:44:54 +0100
Subject: [PATCH 009/100] 6881115: javac permits nested anno w/o mandatory
 attrs => IncompleteAnnotationException

Default annotation value is not attributed

Reviewed-by: jjg, darcy
---
 .../com/sun/tools/javac/comp/Attr.java        | 32 ++++++++++++-------
 .../com/sun/tools/javac/comp/Check.java       | 17 ++++++++++
 .../javac/annotations/6881115/T6881115.java   | 18 +++++++++++
 .../javac/annotations/6881115/T6881115.out    |  6 ++++
 4 files changed, 61 insertions(+), 12 deletions(-)
 create mode 100644 langtools/test/tools/javac/annotations/6881115/T6881115.java
 create mode 100644 langtools/test/tools/javac/annotations/6881115/T6881115.out

diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
index 76f6e9456fe..b923c669ded 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
@@ -675,24 +675,32 @@ public class Attr extends JCTree.Visitor {
 
             // Check that type parameters are well-formed.
             chk.validate(tree.typarams, localEnv);
-            if ((owner.flags() & ANNOTATION) != 0 &&
-                tree.typarams.nonEmpty())
-                log.error(tree.typarams.head.pos(),
-                          "intf.annotation.members.cant.have.type.params");
 
             // Check that result type is well-formed.
             chk.validate(tree.restype, localEnv);
-            if ((owner.flags() & ANNOTATION) != 0)
-                chk.validateAnnotationType(tree.restype);
 
-            if ((owner.flags() & ANNOTATION) != 0)
+            // annotation method checks
+            if ((owner.flags() & ANNOTATION) != 0) {
+                // annotation method cannot have throws clause
+                if (tree.thrown.nonEmpty()) {
+                    log.error(tree.thrown.head.pos(),
+                            "throws.not.allowed.in.intf.annotation");
+                }
+                // annotation method cannot declare type-parameters
+                if (tree.typarams.nonEmpty()) {
+                    log.error(tree.typarams.head.pos(),
+                            "intf.annotation.members.cant.have.type.params");
+                }
+                // validate annotation method's return type (could be an annotation type)
+                chk.validateAnnotationType(tree.restype);
+                // ensure that annotation method does not clash with members of Object/Annotation
                 chk.validateAnnotationMethod(tree.pos(), m);
 
-            // Check that all exceptions mentioned in the throws clause extend
-            // java.lang.Throwable.
-            if ((owner.flags() & ANNOTATION) != 0 && tree.thrown.nonEmpty())
-                log.error(tree.thrown.head.pos(),
-                          "throws.not.allowed.in.intf.annotation");
+                // if default value is an annotation, check it is a well-formed
+                // annotation value (e.g. no duplicate values, no missing values, etc.)
+                chk.validateAnnotationDefaultValue(tree.defaultValue);
+            }
+
             for (List l = tree.thrown; l.nonEmpty(); l = l.tail)
                 chk.checkType(l.head.pos(), l.head.type, syms.throwableType);
 
diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java
index 0fbe06eb15e..41f2f280da6 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java
@@ -1929,6 +1929,23 @@ public class Check {
  * Check annotations
  **************************************************************************/
 
+    /**
+     * Validate annotations in default values
+     */
+    void validateAnnotationDefaultValue(JCTree defaultValue) {
+        class DefaultValueValidator extends TreeScanner {
+            @Override
+            public void visitAnnotation(JCAnnotation tree) {
+                super.visitAnnotation(tree);
+                validateAnnotation(tree);
+            }
+        }
+        // defaultValue may be null if an error occurred, so don't bother validating it
+        if (defaultValue != null) {
+            defaultValue.accept(new DefaultValueValidator());
+        }
+    }
+
     /** Annotation types are restricted to primitives, String, an
      *  enum, an annotation, Class, Class, Class, arrays of the preceding.
diff --git a/langtools/test/tools/javac/annotations/6881115/T6881115.java b/langtools/test/tools/javac/annotations/6881115/T6881115.java
new file mode 100644
index 00000000000..43c896b62d6
--- /dev/null
+++ b/langtools/test/tools/javac/annotations/6881115/T6881115.java
@@ -0,0 +1,18 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug     6881115
+ * @summary javac permits nested anno w/o mandatory attrs => IncompleteAnnotationException
+ * @author  mcimadamore
+ * @compile/fail/ref=T6881115.out -XDrawDiagnostics T6881115.java
+ */
+
+@interface A {
+    B b() default @B(b2 = 1, b2 = 2);
+    B[] b_arr() default {@B(), @B(b2 = 1, b2 = 2)};
+}
+@interface B {
+    String b1();
+    int b2();
+}
+@A
+class T6881115 {}
diff --git a/langtools/test/tools/javac/annotations/6881115/T6881115.out b/langtools/test/tools/javac/annotations/6881115/T6881115.out
new file mode 100644
index 00000000000..75a54722c16
--- /dev/null
+++ b/langtools/test/tools/javac/annotations/6881115/T6881115.out
@@ -0,0 +1,6 @@
+T6881115.java:10:30: compiler.err.duplicate.annotation.member.value: b2, B
+T6881115.java:10:19: compiler.err.annotation.missing.default.value: B, b1
+T6881115.java:11:26: compiler.err.annotation.missing.default.value: B, b1
+T6881115.java:11:43: compiler.err.duplicate.annotation.member.value: b2, B
+T6881115.java:11:32: compiler.err.annotation.missing.default.value: B, b1
+5 errors

From fa2cb78a09ccc5497a0975362162ee4d02f58b25 Mon Sep 17 00:00:00 2001
From: Maurizio Cimadamore 
Date: Thu, 5 Aug 2010 09:45:25 +0100
Subject: [PATCH 010/100] 6857948: Calling a constructor with a doubly bogus
 argument causes an internal error

Problem when constructor resolution returns an erroneous symbol

Reviewed-by: jjg
---
 .../classes/com/sun/tools/javac/comp/Attr.java | 16 +++++++++-------
 .../test/tools/javac/6857948/T6857948.java     | 18 ++++++++++++++++++
 .../test/tools/javac/6857948/T6857948.out      |  3 +++
 3 files changed, 30 insertions(+), 7 deletions(-)
 create mode 100644 langtools/test/tools/javac/6857948/T6857948.java
 create mode 100644 langtools/test/tools/javac/6857948/T6857948.out

diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
index b923c669ded..70dcc5f6df0 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
@@ -1594,13 +1594,15 @@ public class Attr extends JCTree.Visitor {
                 localEnv.info.varArgs = false;
                 tree.constructor = rs.resolveConstructor(
                     tree.pos(), localEnv, clazztype, argtypes, typeargtypes);
-                tree.constructorType = checkMethod(clazztype,
-                                                tree.constructor,
-                                                localEnv,
-                                                tree.args,
-                                                argtypes,
-                                                typeargtypes,
-                                                localEnv.info.varArgs);
+                tree.constructorType = tree.constructor.type.isErroneous() ?
+                    syms.errType :
+                    checkMethod(clazztype,
+                        tree.constructor,
+                        localEnv,
+                        tree.args,
+                        argtypes,
+                        typeargtypes,
+                        localEnv.info.varArgs);
                 if (localEnv.info.varArgs)
                     assert tree.constructorType.isErroneous() || tree.varargsElement != null;
             }
diff --git a/langtools/test/tools/javac/6857948/T6857948.java b/langtools/test/tools/javac/6857948/T6857948.java
new file mode 100644
index 00000000000..e4f7ee527d9
--- /dev/null
+++ b/langtools/test/tools/javac/6857948/T6857948.java
@@ -0,0 +1,18 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 6857948
+ * @summary 6857948: Calling a constructor with a doubly bogus argument causes an internal error
+ * @author Maurizio Cimadamore
+ *
+ * @compile/fail/ref=T6857948.out -XDrawDiagnostics T6857948.java
+ */
+
+class Foo {
+   Foo(String v) {}
+};
+
+class Test {
+   public static void main() {
+      Foo f = new Foo("Hello!",nosuchfunction()) {};
+   }
+}
diff --git a/langtools/test/tools/javac/6857948/T6857948.out b/langtools/test/tools/javac/6857948/T6857948.out
new file mode 100644
index 00000000000..f6a79ed7745
--- /dev/null
+++ b/langtools/test/tools/javac/6857948/T6857948.out
@@ -0,0 +1,3 @@
+T6857948.java:16:32: compiler.err.cant.resolve.location.args: kindname.method, nosuchfunction, , , kindname.class, Test
+T6857948.java:16:50: compiler.err.cant.apply.symbol: kindname.constructor, Foo, java.lang.String, compiler.misc.no.args, kindname.class, Foo, null
+2 errors

From f8eed77f36cf4ba7879b9d261283d4065f283b15 Mon Sep 17 00:00:00 2001
From: John Cuthbertson 
Date: Fri, 6 Aug 2010 10:17:21 -0700
Subject: [PATCH 011/100] 6930581: G1: assert(ParallelGCThreads > 1 ||
 n_yielded() == _hrrs->occupied(),"Should have yielded all the .

During RSet updating, when ParallelGCThreads is zero, references that point into the collection set are added directly the referenced region's RSet. This can cause the sparse table in the RSet to expand. RSet scanning and the "occupied" routine will then operate on different instances of the sparse table causing the assert to trip. This may also cause some cards added post expansion to be missed during RSet scanning. When ParallelGCThreads is non-zero such references are recorded on the "references to be scanned" queue and the card containing the reference is recorded in a dirty card queue for use in the event of an evacuation failure. Employ the parallel code in the serial case to avoid expanding the RSets of regions in the collection set.

Reviewed-by: iveresov, ysr, tonyp
---
 .../vm/gc_implementation/g1/g1RemSet.cpp      | 50 ++++++++-----------
 .../vm/gc_implementation/g1/g1RemSet.hpp      |  8 +--
 .../gc_implementation/g1/g1RemSet.inline.hpp  |  6 +--
 .../vm/gc_implementation/g1/sparsePRT.cpp     |  2 +-
 4 files changed, 30 insertions(+), 36 deletions(-)

diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp
index 9c081fce5af..19ec341f980 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp
@@ -122,7 +122,7 @@ public:
 HRInto_G1RemSet::HRInto_G1RemSet(G1CollectedHeap* g1, CardTableModRefBS* ct_bs)
   : G1RemSet(g1), _ct_bs(ct_bs), _g1p(_g1->g1_policy()),
     _cg1r(g1->concurrent_g1_refine()),
-    _par_traversal_in_progress(false),
+    _traversal_in_progress(false),
     _cset_rs_update_cl(NULL),
     _cards_scanned(NULL), _total_cards_scanned(0)
 {
@@ -484,28 +484,24 @@ HRInto_G1RemSet::oops_into_collection_set_do(OopsInHeapRegionClosure* oc,
   //   is updated immediately.
   DirtyCardQueue into_cset_dcq(&_g1->into_cset_dirty_card_queue_set());
 
-  if (ParallelGCThreads > 0) {
-    // The two flags below were introduced temporarily to serialize
-    // the updating and scanning of remembered sets. There are some
-    // race conditions when these two operations are done in parallel
-    // and they are causing failures. When we resolve said race
-    // conditions, we'll revert back to parallel remembered set
-    // updating and scanning. See CRs 6677707 and 6677708.
-    if (G1UseParallelRSetUpdating || (worker_i == 0)) {
-      updateRS(&into_cset_dcq, worker_i);
-    } else {
-      _g1p->record_update_rs_processed_buffers(worker_i, 0.0);
-      _g1p->record_update_rs_time(worker_i, 0.0);
-    }
-    if (G1UseParallelRSetScanning || (worker_i == 0)) {
-      scanRS(oc, worker_i);
-    } else {
-      _g1p->record_scan_rs_time(worker_i, 0.0);
-    }
+  assert((ParallelGCThreads > 0) || worker_i == 0, "invariant");
+
+  // The two flags below were introduced temporarily to serialize
+  // the updating and scanning of remembered sets. There are some
+  // race conditions when these two operations are done in parallel
+  // and they are causing failures. When we resolve said race
+  // conditions, we'll revert back to parallel remembered set
+  // updating and scanning. See CRs 6677707 and 6677708.
+  if (G1UseParallelRSetUpdating || (worker_i == 0)) {
+    updateRS(&into_cset_dcq, worker_i);
   } else {
-    assert(worker_i == 0, "invariant");
-    updateRS(&into_cset_dcq, 0);
-    scanRS(oc, 0);
+    _g1p->record_update_rs_processed_buffers(worker_i, 0.0);
+    _g1p->record_update_rs_time(worker_i, 0.0);
+  }
+  if (G1UseParallelRSetScanning || (worker_i == 0)) {
+    scanRS(oc, worker_i);
+  } else {
+    _g1p->record_scan_rs_time(worker_i, 0.0);
   }
 
   // We now clear the cached values of _cset_rs_update_cl for this worker
@@ -524,9 +520,9 @@ prepare_for_oops_into_collection_set_do() {
   DirtyCardQueueSet& dcqs = JavaThread::dirty_card_queue_set();
   dcqs.concatenate_logs();
 
-  assert(!_par_traversal_in_progress, "Invariant between iterations.");
+  assert(!_traversal_in_progress, "Invariant between iterations.");
+  set_traversal(true);
   if (ParallelGCThreads > 0) {
-    set_par_traversal(true);
     _seq_task->set_par_threads((int)n_workers());
   }
   guarantee( _cards_scanned == NULL, "invariant" );
@@ -623,9 +619,7 @@ void HRInto_G1RemSet::cleanup_after_oops_into_collection_set_do() {
   // Set all cards back to clean.
   _g1->cleanUpCardTable();
 
-  if (ParallelGCThreads > 0) {
-    set_par_traversal(false);
-  }
+  set_traversal(false);
 
   DirtyCardQueueSet& into_cset_dcqs = _g1->into_cset_dirty_card_queue_set();
   int into_cset_n_buffers = into_cset_dcqs.completed_buffers_num();
@@ -660,7 +654,7 @@ void HRInto_G1RemSet::cleanup_after_oops_into_collection_set_do() {
          "all buffers should be freed");
   _g1->into_cset_dirty_card_queue_set().clear_n_completed_buffers();
 
-  assert(!_par_traversal_in_progress, "Invariant between iterations.");
+  assert(!_traversal_in_progress, "Invariant between iterations.");
 }
 
 class UpdateRSObjectClosure: public ObjectClosure {
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp
index 112f9aa646c..fcb5ecd76cf 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp
@@ -148,10 +148,10 @@ protected:
   size_t*             _cards_scanned;
   size_t              _total_cards_scanned;
 
-  // _par_traversal_in_progress is "true" iff a parallel traversal is in
-  // progress.
-  bool _par_traversal_in_progress;
-  void set_par_traversal(bool b) { _par_traversal_in_progress = b; }
+  // _traversal_in_progress is "true" iff a traversal is in progress.
+
+  bool _traversal_in_progress;
+  void set_traversal(bool b) { _traversal_in_progress = b; }
 
   // Used for caching the closure that is responsible for scanning
   // references into the collection set.
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.inline.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.inline.hpp
index d3fa77ae92a..ce64065527b 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.inline.hpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.inline.hpp
@@ -62,13 +62,13 @@ template  inline void HRInto_G1RemSet::par_write_ref_nv(HeapRegion* fro
   HeapRegion* to = _g1->heap_region_containing(obj);
   // The test below could be optimized by applying a bit op to to and from.
   if (to != NULL && from != NULL && from != to) {
-    // The _par_traversal_in_progress flag is true during the collection pause,
-    // false during the evacuation failure handing. This should avoid a
+    // The _traversal_in_progress flag is true during the collection pause,
+    // false during the evacuation failure handling. This should avoid a
     // potential loop if we were to add the card containing 'p' to the DCQS
     // that's used to regenerate the remembered sets for the collection set,
     // in the event of an evacuation failure, here. The UpdateRSImmediate
     // closure will eventally call this routine.
-    if (_par_traversal_in_progress &&
+    if (_traversal_in_progress &&
         to->in_collection_set() && !self_forwarded(obj)) {
 
       assert(_cset_rs_update_cl[tid] != NULL, "should have been set already");
diff --git a/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp b/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp
index 434836345c8..5bc3ab29c70 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp
@@ -424,7 +424,7 @@ void SparsePRT::cleanup_all() {
 
 
 SparsePRT::SparsePRT(HeapRegion* hr) :
-  _expanded(false), _next_expanded(NULL)
+  _hr(hr), _expanded(false), _next_expanded(NULL)
 {
   _cur = new RSHashTable(InitialCapacity);
   _next = _cur;

From 438ccadcce697c4189638a282a490b1290a59ac3 Mon Sep 17 00:00:00 2001
From: "Daniel D. Daugherty" 
Date: Fri, 6 Aug 2010 11:07:16 -0700
Subject: [PATCH 012/100] 6962604: 3/3 Testcase
 sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.sh failure

Disable MonitorVmStartTerminate.sh until 6543856 is fixed.

Reviewed-by: ohair
---
 .../sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.sh   | 1 +
 1 file changed, 1 insertion(+)

diff --git a/jdk/test/sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.sh b/jdk/test/sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.sh
index 917f1c22833..49d5f44be71 100644
--- a/jdk/test/sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.sh
+++ b/jdk/test/sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.sh
@@ -23,6 +23,7 @@
 
 #
 # @test
+# @ignore until 6543856 is fixed
 # @bug 4990825
 # @summary attach to external but local JVM processes
 # @library ../../testlibrary

From 7fe1c05c7c42c4394cd1f402d86a4185f552e11b Mon Sep 17 00:00:00 2001
From: John Coomes 
Date: Mon, 9 Aug 2010 05:41:05 -0700
Subject: [PATCH 013/100] 6966222: G1: simplify TaskQueue overflow handling

Reviewed-by: tonyp, ysr
---
 .../gc_implementation/g1/g1CollectedHeap.cpp  | 101 +++++++++++-------
 .../gc_implementation/g1/g1CollectedHeap.hpp  |  76 +++++--------
 .../g1/g1CollectedHeap.inline.hpp             |   3 +-
 hotspot/src/share/vm/utilities/taskqueue.cpp  |  31 ++++++
 hotspot/src/share/vm/utilities/taskqueue.hpp  |   6 ++
 5 files changed, 127 insertions(+), 90 deletions(-)

diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
index 76baff21b62..d1dd28ca2f3 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
@@ -2710,6 +2710,35 @@ struct PrepareForRSScanningClosure : public HeapRegionClosure {
   }
 };
 
+#if TASKQUEUE_STATS
+void G1CollectedHeap::print_taskqueue_stats_hdr(outputStream* const st) {
+  st->print_raw_cr("GC Task Stats");
+  st->print_raw("thr "); TaskQueueStats::print_header(1, st); st->cr();
+  st->print_raw("--- "); TaskQueueStats::print_header(2, st); st->cr();
+}
+
+void G1CollectedHeap::print_taskqueue_stats(outputStream* const st) const {
+  print_taskqueue_stats_hdr(st);
+
+  TaskQueueStats totals;
+  const int n = MAX2(workers()->total_workers(), 1);
+  for (int i = 0; i < n; ++i) {
+    st->print("%3d ", i); task_queue(i)->stats.print(st); st->cr();
+    totals += task_queue(i)->stats;
+  }
+  st->print_raw("tot "); totals.print(st); st->cr();
+
+  DEBUG_ONLY(totals.verify());
+}
+
+void G1CollectedHeap::reset_taskqueue_stats() {
+  const int n = MAX2(workers()->total_workers(), 1);
+  for (int i = 0; i < n; ++i) {
+    task_queue(i)->stats.reset();
+  }
+}
+#endif // TASKQUEUE_STATS
+
 void
 G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
   if (GC_locker::check_active_before_gc()) {
@@ -2970,6 +2999,9 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
     }
   }
 
+  TASKQUEUE_STATS_ONLY(if (ParallelGCVerbose) print_taskqueue_stats());
+  TASKQUEUE_STATS_ONLY(reset_taskqueue_stats());
+
   if (PrintHeapAtGC) {
     Universe::print_heap_after_gc();
   }
@@ -3715,10 +3747,6 @@ G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h, int queue_num)
     _surviving_alloc_buffer(g1h->desired_plab_sz(GCAllocForSurvived)),
     _tenured_alloc_buffer(g1h->desired_plab_sz(GCAllocForTenured)),
     _age_table(false),
-#if G1_DETAILED_STATS
-    _pushes(0), _pops(0), _steals(0),
-    _steal_attempts(0),  _overflow_pushes(0),
-#endif
     _strong_roots_time(0), _term_time(0),
     _alloc_buffer_waste(0), _undo_waste(0)
 {
@@ -3738,14 +3766,41 @@ G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h, int queue_num)
   _surviving_young_words = _surviving_young_words_base + PADDING_ELEM_NUM;
   memset(_surviving_young_words, 0, real_length * sizeof(size_t));
 
-  _overflowed_refs = new OverflowQueue(10);
-
   _alloc_buffers[GCAllocForSurvived] = &_surviving_alloc_buffer;
   _alloc_buffers[GCAllocForTenured]  = &_tenured_alloc_buffer;
 
   _start = os::elapsedTime();
 }
 
+void
+G1ParScanThreadState::print_termination_stats_hdr(outputStream* const st)
+{
+  st->print_raw_cr("GC Termination Stats");
+  st->print_raw_cr("     elapsed  --strong roots-- -------termination-------"
+                   " ------waste (KiB)------");
+  st->print_raw_cr("thr     ms        ms      %        ms      %    attempts"
+                   "  total   alloc    undo");
+  st->print_raw_cr("--- --------- --------- ------ --------- ------ --------"
+                   " ------- ------- -------");
+}
+
+void
+G1ParScanThreadState::print_termination_stats(int i,
+                                              outputStream* const st) const
+{
+  const double elapsed_ms = elapsed_time() * 1000.0;
+  const double s_roots_ms = strong_roots_time() * 1000.0;
+  const double term_ms    = term_time() * 1000.0;
+  st->print_cr("%3d %9.2f %9.2f %6.2f "
+               "%9.2f %6.2f " SIZE_FORMAT_W(8) " "
+               SIZE_FORMAT_W(7) " " SIZE_FORMAT_W(7) " " SIZE_FORMAT_W(7),
+               i, elapsed_ms, s_roots_ms, s_roots_ms * 100 / elapsed_ms,
+               term_ms, term_ms * 100 / elapsed_ms, term_attempts(),
+               (alloc_buffer_waste() + undo_waste()) * HeapWordSize / K,
+               alloc_buffer_waste() * HeapWordSize / K,
+               undo_waste() * HeapWordSize / K);
+}
+
 G1ParClosureSuper::G1ParClosureSuper(G1CollectedHeap* g1, G1ParScanThreadState* par_scan_state) :
   _g1(g1), _g1_rem(_g1->g1_rem_set()), _cm(_g1->concurrent_mark()),
   _par_scan_state(par_scan_state) { }
@@ -3952,12 +4007,9 @@ public:
     G1ParScanThreadState* pss = par_scan_state();
     while (true) {
       pss->trim_queue();
-      IF_G1_DETAILED_STATS(pss->note_steal_attempt());
 
       StarTask stolen_task;
       if (queues()->steal(pss->queue_num(), pss->hash_seed(), stolen_task)) {
-        IF_G1_DETAILED_STATS(pss->note_steal());
-
         // slightly paranoid tests; I'm trying to catch potential
         // problems before we go into push_on_queue to know where the
         // problem is coming from
@@ -4076,35 +4128,9 @@ public:
     // Clean up any par-expanded rem sets.
     HeapRegionRemSet::par_cleanup();
 
-    MutexLocker x(stats_lock());
     if (ParallelGCVerbose) {
-      gclog_or_tty->print("Thread %d complete:\n", i);
-#if G1_DETAILED_STATS
-      gclog_or_tty->print("  Pushes: %7d    Pops: %7d   Overflows: %7d   Steals %7d (in %d attempts)\n",
-                          pss.pushes(),
-                          pss.pops(),
-                          pss.overflow_pushes(),
-                          pss.steals(),
-                          pss.steal_attempts());
-#endif
-      double elapsed      = pss.elapsed();
-      double strong_roots = pss.strong_roots_time();
-      double term         = pss.term_time();
-      gclog_or_tty->print("  Elapsed: %7.2f ms.\n"
-                          "    Strong roots: %7.2f ms (%6.2f%%)\n"
-                          "    Termination:  %7.2f ms (%6.2f%%) "
-                                                 "(in "SIZE_FORMAT" entries)\n",
-                          elapsed * 1000.0,
-                          strong_roots * 1000.0, (strong_roots*100.0/elapsed),
-                          term * 1000.0, (term*100.0/elapsed),
-                          pss.term_attempts());
-      size_t total_waste = pss.alloc_buffer_waste() + pss.undo_waste();
-      gclog_or_tty->print("  Waste: %8dK\n"
-                 "    Alloc Buffer: %8dK\n"
-                 "    Undo: %8dK\n",
-                 (total_waste * HeapWordSize) / K,
-                 (pss.alloc_buffer_waste() * HeapWordSize) / K,
-                 (pss.undo_waste() * HeapWordSize) / K);
+      MutexLocker x(stats_lock());
+      pss.print_termination_stats(i);
     }
 
     assert(pss.refs_to_scan() == 0, "Task queue should be empty");
@@ -4221,6 +4247,7 @@ void G1CollectedHeap::evacuate_collection_set() {
   if (ParallelGCThreads > 0) {
     // The individual threads will set their evac-failure closures.
     StrongRootsScope srs(this);
+    if (ParallelGCVerbose) G1ParScanThreadState::print_termination_stats_hdr();
     workers()->run_task(&g1_par_task);
   } else {
     StrongRootsScope srs(this);
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp
index 6daaf614f44..d831e8d7d7e 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp
@@ -46,17 +46,7 @@ class ConcurrentMarkThread;
 class ConcurrentG1Refine;
 class ConcurrentZFThread;
 
-// If want to accumulate detailed statistics on work queues
-// turn this on.
-#define G1_DETAILED_STATS 0
-
-#if G1_DETAILED_STATS
-#  define IF_G1_DETAILED_STATS(code) code
-#else
-#  define IF_G1_DETAILED_STATS(code)
-#endif
-
-typedef GenericTaskQueue          RefToScanQueue;
+typedef OverflowTaskQueue         RefToScanQueue;
 typedef GenericTaskQueueSet RefToScanQueueSet;
 
 typedef int RegionIdx_t;   // needs to hold [ 0..max_regions() )
@@ -471,6 +461,12 @@ protected:
   virtual void shrink(size_t expand_bytes);
   void shrink_helper(size_t expand_bytes);
 
+  #if TASKQUEUE_STATS
+  static void print_taskqueue_stats_hdr(outputStream* const st = gclog_or_tty);
+  void print_taskqueue_stats(outputStream* const st = gclog_or_tty) const;
+  void reset_taskqueue_stats();
+  #endif // TASKQUEUE_STATS
+
   // Do an incremental collection: identify a collection set, and evacuate
   // its live objects elsewhere.
   virtual void do_collection_pause();
@@ -662,7 +658,7 @@ protected:
 public:
   void set_refine_cte_cl_concurrency(bool concurrent);
 
-  RefToScanQueue *task_queue(int i);
+  RefToScanQueue *task_queue(int i) const;
 
   // A set of cards where updates happened during the GC
   DirtyCardQueueSet& dirty_card_queue_set() { return _dirty_card_queue_set; }
@@ -1579,9 +1575,6 @@ protected:
   CardTableModRefBS* _ct_bs;
   G1RemSet* _g1_rem;
 
-  typedef GrowableArray OverflowQueue;
-  OverflowQueue* _overflowed_refs;
-
   G1ParGCAllocBuffer  _surviving_alloc_buffer;
   G1ParGCAllocBuffer  _tenured_alloc_buffer;
   G1ParGCAllocBuffer* _alloc_buffers[GCAllocPurposeCount];
@@ -1598,10 +1591,6 @@ protected:
   int _queue_num;
 
   size_t _term_attempts;
-#if G1_DETAILED_STATS
-  int _pushes, _pops, _steals, _steal_attempts;
-  int _overflow_pushes;
-#endif
 
   double _start;
   double _start_strong_roots;
@@ -1615,7 +1604,7 @@ protected:
   // this points into the array, as we use the first few entries for padding
   size_t* _surviving_young_words;
 
-#define PADDING_ELEM_NUM (64 / sizeof(size_t))
+#define PADDING_ELEM_NUM (DEFAULT_CACHE_LINE_SIZE / sizeof(size_t))
 
   void   add_to_alloc_buffer_waste(size_t waste) { _alloc_buffer_waste += waste; }
 
@@ -1650,15 +1639,14 @@ public:
   }
 
   RefToScanQueue*   refs()            { return _refs;             }
-  OverflowQueue*    overflowed_refs() { return _overflowed_refs;  }
   ageTable*         age_table()       { return &_age_table;       }
 
   G1ParGCAllocBuffer* alloc_buffer(GCAllocPurpose purpose) {
     return _alloc_buffers[purpose];
   }
 
-  size_t alloc_buffer_waste()                    { return _alloc_buffer_waste; }
-  size_t undo_waste()                            { return _undo_waste; }
+  size_t alloc_buffer_waste() const              { return _alloc_buffer_waste; }
+  size_t undo_waste() const                      { return _undo_waste; }
 
   template  void push_on_queue(T* ref) {
     assert(ref != NULL, "invariant");
@@ -1671,12 +1659,7 @@ public:
       assert(_g1h->obj_in_cs(p), "Should be in CS");
     }
 #endif
-    if (!refs()->push(ref)) {
-      overflowed_refs()->push(ref);
-      IF_G1_DETAILED_STATS(note_overflow_push());
-    } else {
-      IF_G1_DETAILED_STATS(note_push());
-    }
+    refs()->push(ref);
   }
 
   void pop_from_queue(StarTask& ref) {
@@ -1687,7 +1670,6 @@ public:
              _g1h->is_in_g1_reserved(ref.is_narrow() ? oopDesc::load_decode_heap_oop((narrowOop*)ref)
                                                      : oopDesc::load_decode_heap_oop((oop*)ref)),
               "invariant");
-      IF_G1_DETAILED_STATS(note_pop());
     } else {
       StarTask null_task;
       ref = null_task;
@@ -1695,7 +1677,8 @@ public:
   }
 
   void pop_from_overflow_queue(StarTask& ref) {
-    StarTask new_ref = overflowed_refs()->pop();
+    StarTask new_ref;
+    refs()->pop_overflow(new_ref);
     assert((oop*)new_ref != NULL, "pop() from a local non-empty stack");
     assert(UseCompressedOops || !new_ref.is_narrow(), "Error");
     assert(has_partial_array_mask((oop*)new_ref) ||
@@ -1705,8 +1688,8 @@ public:
     ref = new_ref;
   }
 
-  int refs_to_scan()                             { return refs()->size();                 }
-  int overflowed_refs_to_scan()                  { return overflowed_refs()->length();    }
+  int refs_to_scan()            { return refs()->size(); }
+  int overflowed_refs_to_scan() { return refs()->overflow_stack()->length(); }
 
   template  void update_rs(HeapRegion* from, T* p, int tid) {
     if (G1DeferredRSUpdate) {
@@ -1775,30 +1758,16 @@ public:
   int* hash_seed() { return &_hash_seed; }
   int  queue_num() { return _queue_num; }
 
-  size_t term_attempts()   { return _term_attempts; }
+  size_t term_attempts() const  { return _term_attempts; }
   void note_term_attempt() { _term_attempts++; }
 
-#if G1_DETAILED_STATS
-  int pushes()          { return _pushes; }
-  int pops()            { return _pops; }
-  int steals()          { return _steals; }
-  int steal_attempts()  { return _steal_attempts; }
-  int overflow_pushes() { return _overflow_pushes; }
-
-  void note_push()          { _pushes++; }
-  void note_pop()           { _pops++; }
-  void note_steal()         { _steals++; }
-  void note_steal_attempt() { _steal_attempts++; }
-  void note_overflow_push() { _overflow_pushes++; }
-#endif
-
   void start_strong_roots() {
     _start_strong_roots = os::elapsedTime();
   }
   void end_strong_roots() {
     _strong_roots_time += (os::elapsedTime() - _start_strong_roots);
   }
-  double strong_roots_time() { return _strong_roots_time; }
+  double strong_roots_time() const { return _strong_roots_time; }
 
   void start_term_time() {
     note_term_attempt();
@@ -1807,12 +1776,17 @@ public:
   void end_term_time() {
     _term_time += (os::elapsedTime() - _start_term);
   }
-  double term_time() { return _term_time; }
+  double term_time() const { return _term_time; }
 
-  double elapsed() {
+  double elapsed_time() const {
     return os::elapsedTime() - _start;
   }
 
+  static void
+    print_termination_stats_hdr(outputStream* const st = gclog_or_tty);
+  void
+    print_termination_stats(int i, outputStream* const st = gclog_or_tty) const;
+
   size_t* surviving_young_words() {
     // We add on to hide entry 0 which accumulates surviving words for
     // age -1 regions (i.e. non-young ones)
diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp
index a2459d9d0aa..207ee2ffa4c 100644
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp
@@ -81,11 +81,10 @@ inline HeapWord* G1CollectedHeap::attempt_allocation(size_t word_size,
   return attempt_allocation_slow(word_size, permit_collection_pause);
 }
 
-inline RefToScanQueue* G1CollectedHeap::task_queue(int i) {
+inline RefToScanQueue* G1CollectedHeap::task_queue(int i) const {
   return _task_queues->queue(i);
 }
 
-
 inline  bool G1CollectedHeap::isMarkedPrev(oop obj) const {
   return _cm->prevMarkBitMap()->isMarked((HeapWord *)obj);
 }
diff --git a/hotspot/src/share/vm/utilities/taskqueue.cpp b/hotspot/src/share/vm/utilities/taskqueue.cpp
index 7a001803d96..2492b90d1d8 100644
--- a/hotspot/src/share/vm/utilities/taskqueue.cpp
+++ b/hotspot/src/share/vm/utilities/taskqueue.cpp
@@ -36,6 +36,14 @@ const char * const TaskQueueStats::_names[last_stat_id] = {
   "qpush", "qpop", "qpop-s", "qattempt", "qsteal", "opush", "omax"
 };
 
+TaskQueueStats & TaskQueueStats::operator +=(const TaskQueueStats & addend)
+{
+  for (unsigned int i = 0; i < last_stat_id; ++i) {
+    _stats[i] += addend._stats[i];
+  }
+  return *this;
+}
+
 void TaskQueueStats::print_header(unsigned int line, outputStream* const stream,
                                   unsigned int width)
 {
@@ -71,6 +79,29 @@ void TaskQueueStats::print(outputStream* stream, unsigned int width) const
   }
   #undef FMT
 }
+
+#ifdef ASSERT
+// Invariants which should hold after a TaskQueue has been emptied and is
+// quiescent; they do not hold at arbitrary times.
+void TaskQueueStats::verify() const
+{
+  assert(get(push) == get(pop) + get(steal),
+         err_msg("push=" SIZE_FORMAT " pop=" SIZE_FORMAT " steal=" SIZE_FORMAT,
+                 get(push), get(pop), get(steal)));
+  assert(get(pop_slow) <= get(pop),
+         err_msg("pop_slow=" SIZE_FORMAT " pop=" SIZE_FORMAT,
+                 get(pop_slow), get(pop)));
+  assert(get(steal) <= get(steal_attempt),
+         err_msg("steal=" SIZE_FORMAT " steal_attempt=" SIZE_FORMAT,
+                 get(steal), get(steal_attempt)));
+  assert(get(overflow) == 0 || get(push) != 0,
+         err_msg("overflow=" SIZE_FORMAT " push=" SIZE_FORMAT,
+                 get(overflow), get(push)));
+  assert(get(overflow_max_len) == 0 || get(overflow) != 0,
+         err_msg("overflow_max_len=" SIZE_FORMAT " overflow=" SIZE_FORMAT,
+                 get(overflow_max_len), get(overflow)));
+}
+#endif // ASSERT
 #endif // TASKQUEUE_STATS
 
 int TaskQueueSetSuper::randomParkAndMiller(int *seed0) {
diff --git a/hotspot/src/share/vm/utilities/taskqueue.hpp b/hotspot/src/share/vm/utilities/taskqueue.hpp
index 52e89fc8027..1bd1ae72309 100644
--- a/hotspot/src/share/vm/utilities/taskqueue.hpp
+++ b/hotspot/src/share/vm/utilities/taskqueue.hpp
@@ -59,15 +59,21 @@ public:
   inline void record_steal(bool success);
   inline void record_overflow(size_t new_length);
 
+  TaskQueueStats & operator +=(const TaskQueueStats & addend);
+
   inline size_t get(StatId id) const { return _stats[id]; }
   inline const size_t* get() const   { return _stats; }
 
   inline void reset();
 
+  // Print the specified line of the header (does not include a line separator).
   static void print_header(unsigned int line, outputStream* const stream = tty,
                            unsigned int width = 10);
+  // Print the statistics (does not include a line separator).
   void print(outputStream* const stream = tty, unsigned int width = 10) const;
 
+  DEBUG_ONLY(void verify() const;)
+
 private:
   size_t                    _stats[last_stat_id];
   static const char * const _names[last_stat_id];

From b0e98512a72da4ac35f8be9dbf9db32ca878ce3f Mon Sep 17 00:00:00 2001
From: John Coomes 
Date: Mon, 9 Aug 2010 18:03:50 -0700
Subject: [PATCH 014/100] 6970376: ParNew: shared TaskQueue statistics

Reviewed-by: ysr
---
 .../parNew/parNewGeneration.cpp               | 171 +++++++++++-------
 .../parNew/parNewGeneration.hpp               |  49 +++--
 2 files changed, 126 insertions(+), 94 deletions(-)

diff --git a/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp
index dc655aed136..45683b17572 100644
--- a/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp
+++ b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp
@@ -51,9 +51,14 @@ ParScanThreadState::ParScanThreadState(Space* to_space_,
   _is_alive_closure(gen_), _scan_weak_ref_closure(gen_, this),
   _keep_alive_closure(&_scan_weak_ref_closure),
   _promotion_failure_size(0),
-  _pushes(0), _pops(0), _steals(0), _steal_attempts(0), _term_attempts(0),
   _strong_roots_time(0.0), _term_time(0.0)
 {
+  #if TASKQUEUE_STATS
+  _term_attempts = 0;
+  _overflow_refills = 0;
+  _overflow_refill_objs = 0;
+  #endif // TASKQUEUE_STATS
+
   _survivor_chunk_array =
     (ChunkArray*) old_gen()->get_data_recorder(thread_num());
   _hash_seed = 17;  // Might want to take time-based random value.
@@ -100,7 +105,6 @@ void ParScanThreadState::scan_partial_array_and_push_remainder(oop old) {
     // Push remainder.
     bool ok = work_queue()->push(old);
     assert(ok, "just popped, push must be okay");
-    note_push();
   } else {
     // Restore length so that it can be used if there
     // is a promotion failure and forwarding pointers
@@ -126,7 +130,6 @@ void ParScanThreadState::trim_queues(int max_size) {
     while (queue->size() > (juint)max_size) {
       oop obj_to_scan;
       if (queue->pop_local(obj_to_scan)) {
-        note_pop();
         if ((HeapWord *)obj_to_scan < young_old_boundary()) {
           if (obj_to_scan->is_objArray() &&
               obj_to_scan->is_forwarded() &&
@@ -271,20 +274,28 @@ public:
                         GrowableArray**    overflow_stacks_,
                         size_t                  desired_plab_sz,
                         ParallelTaskTerminator& term);
+
+  ~ParScanThreadStateSet() { TASKQUEUE_STATS_ONLY(reset_stats()); }
+
   inline ParScanThreadState& thread_state(int i);
-  int pushes() { return _pushes; }
-  int pops()   { return _pops; }
-  int steals() { return _steals; }
+
   void reset(bool promotion_failed);
   void flush();
+
+  #if TASKQUEUE_STATS
+  static void
+    print_termination_stats_hdr(outputStream* const st = gclog_or_tty);
+  void print_termination_stats(outputStream* const st = gclog_or_tty);
+  static void
+    print_taskqueue_stats_hdr(outputStream* const st = gclog_or_tty);
+  void print_taskqueue_stats(outputStream* const st = gclog_or_tty);
+  void reset_stats();
+  #endif // TASKQUEUE_STATS
+
 private:
   ParallelTaskTerminator& _term;
   ParNewGeneration&       _gen;
   Generation&             _next_gen;
-  // staticstics
-  int _pushes;
-  int _pops;
-  int _steals;
 };
 
 
@@ -294,8 +305,7 @@ ParScanThreadStateSet::ParScanThreadStateSet(
   GrowableArray** overflow_stack_set_,
   size_t desired_plab_sz, ParallelTaskTerminator& term)
   : ResourceArray(sizeof(ParScanThreadState), num_threads),
-    _gen(gen), _next_gen(old_gen), _term(term),
-    _pushes(0), _pops(0), _steals(0)
+    _gen(gen), _next_gen(old_gen), _term(term)
 {
   assert(num_threads > 0, "sanity check!");
   // Initialize states.
@@ -323,6 +333,82 @@ void ParScanThreadStateSet::reset(bool promotion_failed)
   }
 }
 
+#if TASKQUEUE_STATS
+void
+ParScanThreadState::reset_stats()
+{
+  taskqueue_stats().reset();
+  _term_attempts = 0;
+  _overflow_refills = 0;
+  _overflow_refill_objs = 0;
+}
+
+void ParScanThreadStateSet::reset_stats()
+{
+  for (int i = 0; i < length(); ++i) {
+    thread_state(i).reset_stats();
+  }
+}
+
+void
+ParScanThreadStateSet::print_termination_stats_hdr(outputStream* const st)
+{
+  st->print_raw_cr("GC Termination Stats");
+  st->print_raw_cr("     elapsed  --strong roots-- "
+                   "-------termination-------");
+  st->print_raw_cr("thr     ms        ms       %   "
+                   "    ms       %   attempts");
+  st->print_raw_cr("--- --------- --------- ------ "
+                   "--------- ------ --------");
+}
+
+void ParScanThreadStateSet::print_termination_stats(outputStream* const st)
+{
+  print_termination_stats_hdr(st);
+
+  for (int i = 0; i < length(); ++i) {
+    const ParScanThreadState & pss = thread_state(i);
+    const double elapsed_ms = pss.elapsed_time() * 1000.0;
+    const double s_roots_ms = pss.strong_roots_time() * 1000.0;
+    const double term_ms = pss.term_time() * 1000.0;
+    st->print_cr("%3d %9.2f %9.2f %6.2f "
+                 "%9.2f %6.2f " SIZE_FORMAT_W(8),
+                 i, elapsed_ms, s_roots_ms, s_roots_ms * 100 / elapsed_ms,
+                 term_ms, term_ms * 100 / elapsed_ms, pss.term_attempts());
+  }
+}
+
+// Print stats related to work queue activity.
+void ParScanThreadStateSet::print_taskqueue_stats_hdr(outputStream* const st)
+{
+  st->print_raw_cr("GC Task Stats");
+  st->print_raw("thr "); TaskQueueStats::print_header(1, st); st->cr();
+  st->print_raw("--- "); TaskQueueStats::print_header(2, st); st->cr();
+}
+
+void ParScanThreadStateSet::print_taskqueue_stats(outputStream* const st)
+{
+  print_taskqueue_stats_hdr(st);
+
+  TaskQueueStats totals;
+  for (int i = 0; i < length(); ++i) {
+    const ParScanThreadState & pss = thread_state(i);
+    const TaskQueueStats & stats = pss.taskqueue_stats();
+    st->print("%3d ", i); stats.print(st); st->cr();
+    totals += stats;
+
+    if (pss.overflow_refills() > 0) {
+      st->print_cr("    " SIZE_FORMAT_W(10) " overflow refills    "
+                   SIZE_FORMAT_W(10) " overflow objects",
+                   pss.overflow_refills(), pss.overflow_refill_objs());
+    }
+  }
+  st->print("tot "); totals.print(st); st->cr();
+
+  DEBUG_ONLY(totals.verify());
+}
+#endif // TASKQUEUE_STATS
+
 void ParScanThreadStateSet::flush()
 {
   // Work in this loop should be kept as lightweight as
@@ -346,42 +432,8 @@ void ParScanThreadStateSet::flush()
     // Inform old gen that we're done.
     _next_gen.par_promote_alloc_done(i);
     _next_gen.par_oop_since_save_marks_iterate_done(i);
-
-    // Flush stats related to work queue activity (push/pop/steal)
-    // This could conceivably become a bottleneck; if so, we'll put the
-    // stat's gathering under the flag.
-    if (PAR_STATS_ENABLED) {
-      _pushes += par_scan_state.pushes();
-      _pops   += par_scan_state.pops();
-      _steals += par_scan_state.steals();
-      if (ParallelGCVerbose) {
-        gclog_or_tty->print("Thread %d complete:\n"
-                            "  Pushes: %7d    Pops: %7d    Steals %7d (in %d attempts)\n",
-                            i, par_scan_state.pushes(), par_scan_state.pops(),
-                            par_scan_state.steals(), par_scan_state.steal_attempts());
-        if (par_scan_state.overflow_pushes() > 0 ||
-            par_scan_state.overflow_refills() > 0) {
-          gclog_or_tty->print("  Overflow pushes: %7d    "
-                              "Overflow refills: %7d for %d objs.\n",
-                              par_scan_state.overflow_pushes(),
-                              par_scan_state.overflow_refills(),
-                              par_scan_state.overflow_refill_objs());
-        }
-
-        double elapsed = par_scan_state.elapsed();
-        double strong_roots = par_scan_state.strong_roots_time();
-        double term = par_scan_state.term_time();
-        gclog_or_tty->print(
-                            "  Elapsed: %7.2f ms.\n"
-                            "    Strong roots: %7.2f ms (%6.2f%%)\n"
-                            "    Termination:  %7.2f ms (%6.2f%%) (in %d entries)\n",
-                           elapsed * 1000.0,
-                           strong_roots * 1000.0, (strong_roots*100.0/elapsed),
-                           term * 1000.0, (term*100.0/elapsed),
-                           par_scan_state.term_attempts());
-      }
-    }
   }
+
   if (UseConcMarkSweepGC && ParallelGCThreads > 0) {
     // We need to call this even when ResizeOldPLAB is disabled
     // so as to avoid breaking some asserts. While we may be able
@@ -456,15 +508,12 @@ void ParEvacuateFollowersClosure::do_void() {
     // We have no local work, attempt to steal from other threads.
 
     // attempt to steal work from promoted.
-    par_scan_state()->note_steal_attempt();
     if (task_queues()->steal(par_scan_state()->thread_num(),
                              par_scan_state()->hash_seed(),
                              obj_to_scan)) {
-      par_scan_state()->note_steal();
       bool res = work_q->push(obj_to_scan);
       assert(res, "Empty queue should have room for a push.");
 
-      par_scan_state()->note_push();
       //   if successful, goto Start.
       continue;
 
@@ -842,17 +891,6 @@ void ParNewGeneration::collect(bool   full,
   }
   thread_state_set.reset(promotion_failed());
 
-  if (PAR_STATS_ENABLED && ParallelGCVerbose) {
-    gclog_or_tty->print("Thread totals:\n"
-               "  Pushes: %7d    Pops: %7d    Steals %7d (sum = %7d).\n",
-               thread_state_set.pushes(), thread_state_set.pops(),
-               thread_state_set.steals(),
-               thread_state_set.pops()+thread_state_set.steals());
-  }
-  assert(thread_state_set.pushes() == thread_state_set.pops()
-                                    + thread_state_set.steals(),
-         "Or else the queues are leaky.");
-
   // Process (weak) reference objects found during scavenge.
   ReferenceProcessor* rp = ref_processor();
   IsAliveClosure is_alive(this);
@@ -932,6 +970,9 @@ void ParNewGeneration::collect(bool   full,
     gch->print_heap_change(gch_prev_used);
   }
 
+  TASKQUEUE_STATS_ONLY(thread_state_set.print_termination_stats());
+  TASKQUEUE_STATS_ONLY(thread_state_set.print_taskqueue_stats());
+
   if (UseAdaptiveSizePolicy) {
     size_policy->minor_collection_end(gch->gc_cause());
     size_policy->avg_survived()->sample(from()->used());
@@ -1104,9 +1145,8 @@ oop ParNewGeneration::copy_to_survivor_space_avoiding_promotion_undo(
         gclog_or_tty->print("queue overflow!\n");
       }
       push_on_overflow_list(old, par_scan_state);
-      par_scan_state->note_overflow_push();
+      TASKQUEUE_STATS_ONLY(par_scan_state->taskqueue_stats().record_overflow(0));
     }
-    par_scan_state->note_push();
 
     return new_obj;
   }
@@ -1227,9 +1267,8 @@ oop ParNewGeneration::copy_to_survivor_space_with_undo(
     if (simulate_overflow || !par_scan_state->work_queue()->push(obj_to_push)) {
       // Add stats for overflow pushes.
       push_on_overflow_list(old, par_scan_state);
-      par_scan_state->note_overflow_push();
+      TASKQUEUE_STATS_ONLY(par_scan_state->taskqueue_stats().record_overflow(0));
     }
-    par_scan_state->note_push();
 
     return new_obj;
   }
@@ -1466,7 +1505,7 @@ bool ParNewGeneration::take_from_overflow_list_work(ParScanThreadState* par_scan
     cur = next;
     n++;
   }
-  par_scan_state->note_overflow_refill(n);
+  TASKQUEUE_STATS_ONLY(par_scan_state->note_overflow_refill(n));
 #ifndef PRODUCT
   assert(_num_par_pushes >= n, "Too many pops?");
   Atomic::add_ptr(-(intptr_t)n, &_num_par_pushes);
diff --git a/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.hpp b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.hpp
index 8196e621372..a3090ebf452 100644
--- a/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.hpp
+++ b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.hpp
@@ -36,9 +36,6 @@ class ParEvacuateFollowersClosure;
 typedef Padded ObjToScanQueue;
 typedef GenericTaskQueueSet ObjToScanQueueSet;
 
-// Enable this to get push/pop/steal stats.
-const int PAR_STATS_ENABLED = 0;
-
 class ParKeepAliveClosure: public DefNewGeneration::KeepAliveClosure {
  private:
   ParScanWeakRefClosure* _par_cl;
@@ -94,8 +91,11 @@ class ParScanThreadState {
 
   bool _to_space_full;
 
-  int _pushes, _pops, _steals, _steal_attempts, _term_attempts;
-  int _overflow_pushes, _overflow_refills, _overflow_refill_objs;
+#if TASKQUEUE_STATS
+  size_t _term_attempts;
+  size_t _overflow_refills;
+  size_t _overflow_refill_objs;
+#endif // TASKQUEUE_STATS
 
   // Stats for promotion failure
   size_t _promotion_failure_size;
@@ -181,45 +181,38 @@ class ParScanThreadState {
   }
   void print_and_clear_promotion_failure_size();
 
-  int pushes() { return _pushes; }
-  int pops()   { return _pops; }
-  int steals() { return _steals; }
-  int steal_attempts() { return _steal_attempts; }
-  int term_attempts()  { return _term_attempts; }
-  int overflow_pushes() { return _overflow_pushes; }
-  int overflow_refills() { return _overflow_refills; }
-  int overflow_refill_objs() { return _overflow_refill_objs; }
+#if TASKQUEUE_STATS
+  TaskQueueStats & taskqueue_stats() const { return _work_queue->stats; }
 
-  void note_push()  { if (PAR_STATS_ENABLED) _pushes++; }
-  void note_pop()   { if (PAR_STATS_ENABLED) _pops++; }
-  void note_steal() { if (PAR_STATS_ENABLED) _steals++; }
-  void note_steal_attempt() { if (PAR_STATS_ENABLED) _steal_attempts++; }
-  void note_term_attempt()  { if (PAR_STATS_ENABLED) _term_attempts++; }
-  void note_overflow_push() { if (PAR_STATS_ENABLED) _overflow_pushes++; }
-  void note_overflow_refill(int objs) {
-    if (PAR_STATS_ENABLED) {
-      _overflow_refills++;
-      _overflow_refill_objs += objs;
-    }
+  size_t term_attempts() const             { return _term_attempts; }
+  size_t overflow_refills() const          { return _overflow_refills; }
+  size_t overflow_refill_objs() const      { return _overflow_refill_objs; }
+
+  void note_term_attempt()                 { ++_term_attempts; }
+  void note_overflow_refill(size_t objs)   {
+    ++_overflow_refills; _overflow_refill_objs += objs;
   }
 
+  void reset_stats();
+#endif // TASKQUEUE_STATS
+
   void start_strong_roots() {
     _start_strong_roots = os::elapsedTime();
   }
   void end_strong_roots() {
     _strong_roots_time += (os::elapsedTime() - _start_strong_roots);
   }
-  double strong_roots_time() { return _strong_roots_time; }
+  double strong_roots_time() const { return _strong_roots_time; }
   void start_term_time() {
-    note_term_attempt();
+    TASKQUEUE_STATS_ONLY(note_term_attempt());
     _start_term = os::elapsedTime();
   }
   void end_term_time() {
     _term_time += (os::elapsedTime() - _start_term);
   }
-  double term_time() { return _term_time; }
+  double term_time() const { return _term_time; }
 
-  double elapsed() {
+  double elapsed_time() const {
     return os::elapsedTime() - _start;
   }
 };

From 132dde52c305487c664f9c64e7e08a37ba1ecfb2 Mon Sep 17 00:00:00 2001
From: Maurizio Cimadamore 
Date: Tue, 10 Aug 2010 14:52:34 +0100
Subject: [PATCH 015/100] 6975275: diamond implementation needs some cleanup

Resolution issues during diamond inference should be reported through Resolve.logResolveError()

Reviewed-by: jjg
---
 .../com/sun/tools/javac/comp/Attr.java        | 70 +++++++++----------
 .../com/sun/tools/javac/comp/Resolve.java     | 22 ++++--
 2 files changed, 48 insertions(+), 44 deletions(-)

diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
index 70dcc5f6df0..deefa720e22 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java
@@ -1554,7 +1554,7 @@ public class Attr extends JCTree.Visitor {
         List typeargtypes = attribTypes(tree.typeargs, localEnv);
 
         if (TreeInfo.isDiamond(tree)) {
-            clazztype = attribDiamond(localEnv, tree, clazztype, mapping, argtypes, typeargtypes, true);
+            clazztype = attribDiamond(localEnv, tree, clazztype, mapping, argtypes, typeargtypes);
             clazz.type = clazztype;
         }
 
@@ -1692,8 +1692,7 @@ public class Attr extends JCTree.Visitor {
                         Type clazztype,
                         Pair mapping,
                         List argtypes,
-                        List typeargtypes,
-                        boolean reportErrors) {
+                        List typeargtypes) {
         if (clazztype.isErroneous() || mapping == erroneousMapping) {
             //if the type of the instance creation expression is erroneous,
             //or something prevented us to form a valid mapping, return the
@@ -1731,7 +1730,7 @@ public class Attr extends JCTree.Visitor {
                         env,
                         clazztype.tsym.type,
                         argtypes,
-                        typeargtypes, reportErrors);
+                        typeargtypes);
             } finally {
                 ((ClassSymbol) clazztype.tsym).members_field = mapping.fst;
             }
@@ -1760,42 +1759,37 @@ public class Attr extends JCTree.Visitor {
                         Warner.noWarnings);
             } catch (Infer.InferenceException ex) {
                 //an error occurred while inferring uninstantiated type-variables
-                //we need to optionally report an error
-                if (reportErrors) {
-                    log.error(tree.clazz.pos(),
-                            "cant.apply.diamond.1",
-                            diags.fragment("diamond", clazztype.tsym),
-                            ex.diagnostic);
-                }
+                log.error(tree.clazz.pos(),
+                        "cant.apply.diamond.1",
+                        diags.fragment("diamond", clazztype.tsym),
+                        ex.diagnostic);
             }
         }
-        if (reportErrors) {
-            clazztype = chk.checkClassType(tree.clazz.pos(),
-                    clazztype,
-                    true);
-            if (clazztype.tag == CLASS) {
-                List invalidDiamondArgs = chk.checkDiamond((ClassType)clazztype);
-                if (!clazztype.isErroneous() && invalidDiamondArgs.nonEmpty()) {
-                    //one or more types inferred in the previous steps is either a
-                    //captured type or an intersection type --- we need to report an error.
-                    String subkey = invalidDiamondArgs.size() > 1 ?
-                        "diamond.invalid.args" :
-                        "diamond.invalid.arg";
-                    //The error message is of the kind:
-                    //
-                    //cannot infer type arguments for {clazztype}<>;
-                    //reason: {subkey}
-                    //
-                    //where subkey is a fragment of the kind:
-                    //
-                    //type argument(s) {invalidDiamondArgs} inferred for {clazztype}<> is not allowed in this context
-                    log.error(tree.clazz.pos(),
-                                "cant.apply.diamond.1",
-                                diags.fragment("diamond", clazztype.tsym),
-                                diags.fragment(subkey,
-                                               invalidDiamondArgs,
-                                               diags.fragment("diamond", clazztype.tsym)));
-                }
+        clazztype = chk.checkClassType(tree.clazz.pos(),
+                clazztype,
+                true);
+        if (clazztype.tag == CLASS) {
+            List invalidDiamondArgs = chk.checkDiamond((ClassType)clazztype);
+            if (!clazztype.isErroneous() && invalidDiamondArgs.nonEmpty()) {
+                //one or more types inferred in the previous steps is either a
+                //captured type or an intersection type --- we need to report an error.
+                String subkey = invalidDiamondArgs.size() > 1 ?
+                    "diamond.invalid.args" :
+                    "diamond.invalid.arg";
+                //The error message is of the kind:
+                //
+                //cannot infer type arguments for {clazztype}<>;
+                //reason: {subkey}
+                //
+                //where subkey is a fragment of the kind:
+                //
+                //type argument(s) {invalidDiamondArgs} inferred for {clazztype}<> is not allowed in this context
+                log.error(tree.clazz.pos(),
+                            "cant.apply.diamond.1",
+                            diags.fragment("diamond", clazztype.tsym),
+                            diags.fragment(subkey,
+                                           invalidDiamondArgs,
+                                           diags.fragment("diamond", clazztype.tsym)));
             }
         }
         return clazztype;
diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java
index 898e85f7289..84a3d0394c0 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java
@@ -40,6 +40,7 @@ import com.sun.tools.javac.tree.JCTree.*;
 import static com.sun.tools.javac.code.Flags.*;
 import static com.sun.tools.javac.code.Kinds.*;
 import static com.sun.tools.javac.code.TypeTags.*;
+import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType;
 import javax.lang.model.element.ElementVisitor;
 
 import java.util.Map;
@@ -1447,7 +1448,7 @@ public class Resolve {
                               Env env,
                               Type site,
                               List argtypes,
-                              List typeargtypes, boolean reportErrors) {
+                              List typeargtypes) {
         Symbol sym = methodNotFound;
         JCDiagnostic explanation = null;
         List steps = methodResolutionSteps;
@@ -1466,11 +1467,20 @@ public class Resolve {
             }
             steps = steps.tail;
         }
-        if (sym.kind >= AMBIGUOUS && reportErrors) {
-            String key = explanation == null ?
-                "cant.apply.diamond" :
-                "cant.apply.diamond.1";
-            log.error(pos, key, diags.fragment("diamond", site.tsym), explanation);
+        if (sym.kind >= AMBIGUOUS) {
+            final JCDiagnostic details = explanation;
+            Symbol errSym = new ResolveError(WRONG_MTH, "diamond error") {
+                @Override
+                JCDiagnostic getDiagnostic(DiagnosticType dkind, DiagnosticPosition pos, Type site, Name name, List argtypes, List typeargtypes) {
+                    String key = details == null ?
+                        "cant.apply.diamond" :
+                        "cant.apply.diamond.1";
+                    return diags.create(dkind, log.currentSource(), pos, key, diags.fragment("diamond", site.tsym), details);
+                }
+            };
+            MethodResolutionPhase errPhase = firstErroneousResolutionPhase();
+            sym = access(errSym, pos, site, names.init, true, argtypes, typeargtypes);
+            env.info.varArgs = errPhase.isVarargsRequired();
         }
         return sym;
     }

From 23a89dba043723e89be4de21f2700dec2202f322 Mon Sep 17 00:00:00 2001
From: Maurizio Cimadamore 
Date: Tue, 10 Aug 2010 14:53:19 +0100
Subject: [PATCH 016/100] 6975231: Regression test for 6881115 is failing with
 compiler output not matching expected output

Missing symbols are collected in an HashSet which doesn't preserve ordering

Reviewed-by: jjg
---
 .../com/sun/tools/javac/comp/Check.java       | 24 ++++++++++----
 .../tools/javac/resources/compiler.properties |  4 ++-
 .../javac/annotations/6881115/T6881115.out    |  2 +-
 .../examples/AnnotationMissingValues1.java    | 32 +++++++++++++++++++
 4 files changed, 54 insertions(+), 8 deletions(-)
 create mode 100644 langtools/test/tools/javac/diags/examples/AnnotationMissingValues1.java

diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java
index 41f2f280da6..2e5fe04e2fd 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java
@@ -2120,8 +2120,12 @@ public class Check {
     public void validateAnnotation(JCAnnotation a) {
         if (a.type.isErroneous()) return;
 
-        // collect an inventory of the members
-        Set members = new HashSet();
+        // collect an inventory of the members (sorted alphabetically)
+        Set members = new TreeSet(new Comparator() {
+            public int compare(Symbol t, Symbol t1) {
+                return t.name.compareTo(t1.name);
+            }
+        });
         for (Scope.Entry e = a.annotationType.type.tsym.members().elems;
              e != null;
              e = e.sibling)
@@ -2142,10 +2146,18 @@ public class Check {
         }
 
         // all the remaining ones better have default values
-        for (MethodSymbol m : members)
-            if (m.defaultValue == null && !m.type.isErroneous())
-                log.error(a.pos(), "annotation.missing.default.value",
-                          a.type, m.name);
+        ListBuffer missingDefaults = ListBuffer.lb();
+        for (MethodSymbol m : members) {
+            if (m.defaultValue == null && !m.type.isErroneous()) {
+                missingDefaults.append(m.name);
+            }
+        }
+        if (missingDefaults.nonEmpty()) {
+            String key = (missingDefaults.size() > 1)
+                    ? "annotation.missing.default.value.1"
+                    : "annotation.missing.default.value";
+            log.error(a.pos(), key, a.type, missingDefaults);
+        }
 
         // special case: java.lang.annotation.Target must not have
         // repeated values in its value member
diff --git a/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties b/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties
index fb83f62d67f..aa5c03cd038 100644
--- a/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties
+++ b/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties
@@ -42,7 +42,9 @@ compiler.err.already.defined.static.single.import=\
 compiler.err.already.defined.this.unit=\
     {0} is already defined in this compilation unit
 compiler.err.annotation.missing.default.value=\
-    annotation {0} is missing {1}
+    annotation {0} is missing value for the attribute {1}
+compiler.err.annotation.missing.default.value.1=\
+    annotation {0} is missing values for attributes {1}
 compiler.err.annotation.not.valid.for.type=\
     annotation not valid for a value of type {0}
 compiler.err.annotation.type.not.applicable=\
diff --git a/langtools/test/tools/javac/annotations/6881115/T6881115.out b/langtools/test/tools/javac/annotations/6881115/T6881115.out
index 75a54722c16..a28f3af61c0 100644
--- a/langtools/test/tools/javac/annotations/6881115/T6881115.out
+++ b/langtools/test/tools/javac/annotations/6881115/T6881115.out
@@ -1,6 +1,6 @@
 T6881115.java:10:30: compiler.err.duplicate.annotation.member.value: b2, B
 T6881115.java:10:19: compiler.err.annotation.missing.default.value: B, b1
-T6881115.java:11:26: compiler.err.annotation.missing.default.value: B, b1
+T6881115.java:11:26: compiler.err.annotation.missing.default.value.1: B, b1,b2
 T6881115.java:11:43: compiler.err.duplicate.annotation.member.value: b2, B
 T6881115.java:11:32: compiler.err.annotation.missing.default.value: B, b1
 5 errors
diff --git a/langtools/test/tools/javac/diags/examples/AnnotationMissingValues1.java b/langtools/test/tools/javac/diags/examples/AnnotationMissingValues1.java
new file mode 100644
index 00000000000..d9f9338c361
--- /dev/null
+++ b/langtools/test/tools/javac/diags/examples/AnnotationMissingValues1.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+// key: compiler.err.annotation.missing.default.value.1
+
+@interface Anno {
+    String a();
+    String b();
+}
+
+@Anno
+class AnnotationMissingValue { }

From 1716bf859b7c0630a19e4b81b500dbf7395a97a9 Mon Sep 17 00:00:00 2001
From: Lance Andersen 
Date: Tue, 10 Aug 2010 10:07:33 -0400
Subject: [PATCH 017/100] 6898593: java.sql.Date.valueOf no exception if date
 given is not in the JDBC date escape syntax

Reviewed-by: minqi
---
 jdk/src/share/classes/java/sql/Date.java | 45 +++++++++++++++++-------
 1 file changed, 32 insertions(+), 13 deletions(-)

diff --git a/jdk/src/share/classes/java/sql/Date.java b/jdk/src/share/classes/java/sql/Date.java
index 39cf98ce93f..7c2356e75c2 100644
--- a/jdk/src/share/classes/java/sql/Date.java
+++ b/jdk/src/share/classes/java/sql/Date.java
@@ -103,27 +103,46 @@ public class Date extends java.util.Date {
      *         JDBC date escape format (yyyy-mm-dd)
      */
     public static Date valueOf(String s) {
-        int year;
-        int month;
-        int day;
+        final int YEAR_LENGTH = 4;
+        final int MONTH_LENGTH = 2;
+        final int DAY_LENGTH = 2;
+        final int MAX_MONTH = 12;
+        final int MAX_DAY = 31;
         int firstDash;
         int secondDash;
+        Date d = null;
 
-        if (s == null) throw new java.lang.IllegalArgumentException();
-
-        firstDash = s.indexOf('-');
-        secondDash = s.indexOf('-', firstDash+1);
-        if ((firstDash > 0) & (secondDash > 0) & (secondDash < s.length()-1)) {
-            year = Integer.parseInt(s.substring(0, firstDash)) - 1900;
-            month = Integer.parseInt(s.substring(firstDash+1, secondDash)) - 1;
-            day = Integer.parseInt(s.substring(secondDash+1));
-        } else {
+        if (s == null) {
             throw new java.lang.IllegalArgumentException();
         }
 
-        return new Date(year, month, day);
+        firstDash = s.indexOf('-');
+        secondDash = s.indexOf('-', firstDash + 1);
+
+        if ((firstDash > 0) && (secondDash > 0) && (secondDash < s.length() - 1)) {
+            String yyyy = s.substring(0, firstDash);
+            String mm = s.substring(firstDash + 1, secondDash);
+            String dd = s.substring(secondDash + 1);
+            if (yyyy.length() == YEAR_LENGTH && mm.length() == MONTH_LENGTH &&
+                    dd.length() == DAY_LENGTH) {
+                int year = Integer.parseInt(yyyy);
+                int month = Integer.parseInt(mm);
+                int day = Integer.parseInt(dd);
+
+                if ((month >= 1 && month <= MAX_MONTH) && (day >= 1 && day <= MAX_DAY)) {
+                    d = new Date(year - 1900, month - 1, day);
+                }
+            }
+        }
+        if (d == null) {
+            throw new java.lang.IllegalArgumentException();
+        }
+
+        return d;
+
     }
 
+
     /**
      * Formats a date in the date escape format yyyy-mm-dd.
      * 

From e1edb38d9ed7df1d57f8e2f84ce972293640281f Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Tue, 10 Aug 2010 19:29:30 +0400 Subject: [PATCH 018/100] 6960267: JTable.getRowHeight() returns value different from the specified default (16.0) with GTK L&F Reviewed-by: peterz --- jdk/src/share/classes/javax/swing/JTable.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jdk/src/share/classes/javax/swing/JTable.java b/jdk/src/share/classes/javax/swing/JTable.java index 8ca98bbe15a..bcf00a37547 100644 --- a/jdk/src/share/classes/javax/swing/JTable.java +++ b/jdk/src/share/classes/javax/swing/JTable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, 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 @@ -946,7 +946,6 @@ public class JTable extends JComponent implements TableModelListener, Scrollable /** * Returns the height of a table row, in pixels. - * The default row height is 16.0. * * @return the height in pixels of a table row * @see #setRowHeight From 22534d46e9fecc59de8cf18fd3e1bbfcba191e4a Mon Sep 17 00:00:00 2001 From: Chris Hegarty Date: Tue, 10 Aug 2010 17:30:43 +0100 Subject: [PATCH 019/100] 6882910: Unexplained lack of IP4 network ability when transparent IP6 to IP4 is disabled Reviewed-by: alanb --- .../native/java/net/PlainDatagramSocketImpl.c | 36 +++++++++------- .../solaris/native/java/net/PlainSocketImpl.c | 42 +++++++++++++------ jdk/src/solaris/native/sun/nio/ch/Net.c | 16 +++++++ 3 files changed, 67 insertions(+), 27 deletions(-) diff --git a/jdk/src/solaris/native/java/net/PlainDatagramSocketImpl.c b/jdk/src/solaris/native/java/net/PlainDatagramSocketImpl.c index e717322c6ea..48d0d8ecfa8 100644 --- a/jdk/src/solaris/native/java/net/PlainDatagramSocketImpl.c +++ b/jdk/src/solaris/native/java/net/PlainDatagramSocketImpl.c @@ -1052,30 +1052,38 @@ JNIEXPORT void JNICALL Java_java_net_PlainDatagramSocketImpl_datagramSocketCreate(JNIEnv *env, jobject this) { jobject fdObj = (*env)->GetObjectField(env, this, pdsi_fdID); - int fd; - - int t = 1; + int fd, t = 1; +#ifdef AF_INET6 + int domain = ipv6_available() ? AF_INET6 : AF_INET; +#else + int domain = AF_INET; +#endif if (IS_NULL(fdObj)) { JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Socket closed"); return; - } else { -#ifdef AF_INET6 - if (ipv6_available()) { - fd = JVM_Socket(AF_INET6, SOCK_DGRAM, 0); - } else -#endif /* AF_INET6 */ - { - fd = JVM_Socket(AF_INET, SOCK_DGRAM, 0); - } } - if (fd == JVM_IO_ERR) { + + if ((fd = JVM_Socket(domain, SOCK_DGRAM, 0)) == JVM_IO_ERR) { NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "Error creating socket"); return; } +#ifdef AF_INET6 + /* Disable IPV6_V6ONLY to ensure dual-socket support */ + if (domain == AF_INET6) { + int arg = 0; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg, + sizeof(int)) < 0) { + NET_ThrowNew(env, errno, "cannot set IPPROTO_IPV6"); + close(fd); + return; + } + } +#endif /* AF_INET6 */ + setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*) &t, sizeof(int)); #ifdef __linux__ @@ -1088,7 +1096,7 @@ Java_java_net_PlainDatagramSocketImpl_datagramSocketCreate(JNIEnv *env, * On Linux for IPv6 sockets we must set the hop limit * to 1 to be compatible with default ttl of 1 for IPv4 sockets. */ - if (ipv6_available()) { + if (domain == AF_INET6) { int ttl = 1; setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&ttl, sizeof(ttl)); diff --git a/jdk/src/solaris/native/java/net/PlainSocketImpl.c b/jdk/src/solaris/native/java/net/PlainSocketImpl.c index 254a1497ff6..060dacdac86 100644 --- a/jdk/src/solaris/native/java/net/PlainSocketImpl.c +++ b/jdk/src/solaris/native/java/net/PlainSocketImpl.c @@ -181,6 +181,12 @@ Java_java_net_PlainSocketImpl_socketCreate(JNIEnv *env, jobject this, jboolean stream) { jobject fdObj, ssObj; int fd; + int type = (stream ? SOCK_STREAM : SOCK_DGRAM); +#ifdef AF_INET6 + int domain = ipv6_available() ? AF_INET6 : AF_INET; +#else + int domain = AF_INET; +#endif if (socketExceptionCls == NULL) { jclass c = (*env)->FindClass(env, "java/net/SocketException"); @@ -194,25 +200,29 @@ Java_java_net_PlainSocketImpl_socketCreate(JNIEnv *env, jobject this, (*env)->ThrowNew(env, socketExceptionCls, "null fd object"); return; } -#ifdef AF_INET6 - if (ipv6_available()) { - fd = JVM_Socket(AF_INET6, (stream ? SOCK_STREAM: SOCK_DGRAM), 0); - } else -#endif /* AF_INET6 */ - { - fd = JVM_Socket(AF_INET, (stream ? SOCK_STREAM: SOCK_DGRAM), 0); - } - if (fd == JVM_IO_ERR) { + + if ((fd = JVM_Socket(domain, type, 0)) == JVM_IO_ERR) { /* note: if you run out of fds, you may not be able to load * the exception class, and get a NoClassDefFoundError * instead. */ NET_ThrowNew(env, errno, "can't create socket"); return; - } else { - (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd); } +#ifdef AF_INET6 + /* Disable IPV6_V6ONLY to ensure dual-socket support */ + if (domain == AF_INET6) { + int arg = 0; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg, + sizeof(int)) < 0) { + NET_ThrowNew(env, errno, "cannot set IPPROTO_IPV6"); + close(fd); + return; + } + } +#endif /* AF_INET6 */ + /* * If this is a server socket then enable SO_REUSEADDR * automatically and set to non blocking. @@ -221,9 +231,15 @@ Java_java_net_PlainSocketImpl_socketCreate(JNIEnv *env, jobject this, if (ssObj != NULL) { int arg = 1; SET_NONBLOCKING(fd); - JVM_SetSockOpt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, - sizeof(arg)); + if (JVM_SetSockOpt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, + sizeof(arg)) < 0) { + NET_ThrowNew(env, errno, "cannot set SO_REUSEADDR"); + close(fd); + return; + } } + + (*env)->SetIntField(env, fdObj, IO_fd_fdID, fd); } /* diff --git a/jdk/src/solaris/native/sun/nio/ch/Net.c b/jdk/src/solaris/native/sun/nio/ch/Net.c index e0b3d1c1152..dc3d7c4ac34 100644 --- a/jdk/src/solaris/native/sun/nio/ch/Net.c +++ b/jdk/src/solaris/native/sun/nio/ch/Net.c @@ -170,6 +170,22 @@ Java_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6, if (fd < 0) { return handleSocketError(env, errno); } + +#ifdef AF_INET6 + /* Disable IPV6_V6ONLY to ensure dual-socket support */ + if (domain == AF_INET6) { + int arg = 0; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&arg, + sizeof(int)) < 0) { + JNU_ThrowByNameWithLastError(env, + JNU_JAVANETPKG "SocketException", + "sun.nio.ch.Net.setIntOption"); + close(fd); + return -1; + } + } +#endif + if (reuse) { int arg = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, From 55323320b6fe2cda2a04a4a88b8f74dbc2742500 Mon Sep 17 00:00:00 2001 From: Denis Lila Date: Tue, 10 Aug 2010 13:19:44 -0400 Subject: [PATCH 020/100] 6967436: lines longer than 2^15 can fill window 6967433: dashed lines broken when using scaling transforms Converted pisces to floating point. Also, using better AA algorithm Reviewed-by: flar --- .../classes/sun/java2d/pisces/Dasher.java | 199 ++- .../classes/sun/java2d/pisces/LineSink.java | 20 +- .../classes/sun/java2d/pisces/PiscesMath.java | 155 --- .../java2d/pisces/PiscesRenderingEngine.java | 89 +- .../classes/sun/java2d/pisces/Renderer.java | 1109 ++++++----------- .../classes/sun/java2d/pisces/Stroker.java | 393 +++--- .../classes/sun/java2d/pisces/Transform4.java | 84 -- 7 files changed, 662 insertions(+), 1387 deletions(-) delete mode 100644 jdk/src/share/classes/sun/java2d/pisces/PiscesMath.java delete mode 100644 jdk/src/share/classes/sun/java2d/pisces/Transform4.java diff --git a/jdk/src/share/classes/sun/java2d/pisces/Dasher.java b/jdk/src/share/classes/sun/java2d/pisces/Dasher.java index 1a1f328adc4..f5b5e049143 100644 --- a/jdk/src/share/classes/sun/java2d/pisces/Dasher.java +++ b/jdk/src/share/classes/sun/java2d/pisces/Dasher.java @@ -36,117 +36,71 @@ package sun.java2d.pisces; * semantics are unclear. * */ -public class Dasher extends LineSink { +public class Dasher implements LineSink { + private final LineSink output; + private final float[] dash; + private final float startPhase; + private final boolean startDashOn; + private final int startIdx; - LineSink output; - int[] dash; - int startPhase; - boolean startDashOn; - int startIdx; + private final float m00, m10, m01, m11; + private final float det; - int idx; - boolean dashOn; - int phase; + private boolean firstDashOn; + private boolean starting; - int sx, sy; - int x0, y0; + private int idx; + private boolean dashOn; + private float phase; - int m00, m01; - int m10, m11; + private float sx, sy; + private float x0, y0; + private float sx1, sy1; - Transform4 transform; - - boolean symmetric; - long ldet; - - boolean firstDashOn; - boolean starting; - int sx1, sy1; - - /** - * Empty constructor. setOutput and - * setParameters must be called prior to calling any - * other methods. - */ - public Dasher() {} /** * Constructs a Dasher. * * @param output an output LineSink. - * @param dash an array of ints containing the dash - * pattern in S15.16 format. - * @param phase an int containing the dash phase in - * S15.16 format. + * @param dash an array of ints containing the dash pattern + * @param phase an int containing the dash phase * @param transform a Transform4 object indicating * the transform that has been previously applied to all incoming * coordinates. This is required in order to compute dash lengths * properly. */ public Dasher(LineSink output, - int[] dash, int phase, - Transform4 transform) { - setOutput(output); - setParameters(dash, phase, transform); - } - - /** - * Sets the output LineSink of this - * Dasher. - * - * @param output an output LineSink. - */ - public void setOutput(LineSink output) { - this.output = output; - } - - /** - * Sets the parameters of this Dasher. - * - * @param dash an array of ints containing the dash - * pattern in S15.16 format. - * @param phase an int containing the dash phase in - * S15.16 format. - * @param transform a Transform4 object indicating - * the transform that has been previously applied to all incoming - * coordinates. This is required in order to compute dash lengths - * properly. - */ - public void setParameters(int[] dash, int phase, - Transform4 transform) { + float[] dash, float phase, + float a00, float a01, float a10, float a11) { if (phase < 0) { throw new IllegalArgumentException("phase < 0 !"); } + this.output = output; + // Normalize so 0 <= phase < dash[0] int idx = 0; dashOn = true; - int d; + float d; while (phase >= (d = dash[idx])) { phase -= d; idx = (idx + 1) % dash.length; dashOn = !dashOn; } - this.dash = new int[dash.length]; - for (int i = 0; i < dash.length; i++) { - this.dash[i] = dash[i]; - } + this.dash = dash; this.startPhase = this.phase = phase; this.startDashOn = dashOn; this.startIdx = idx; - this.transform = transform; - - this.m00 = transform.m00; - this.m01 = transform.m01; - this.m10 = transform.m10; - this.m11 = transform.m11; - this.ldet = ((long)m00*m11 - (long)m01*m10) >> 16; - this.symmetric = (m00 == m11 && m10 == -m01); + m00 = a00; + m01 = a01; + m10 = a10; + m11 = a11; + det = m00 * m11 - m01 * m10; } - public void moveTo(int x0, int y0) { + public void moveTo(float x0, float y0) { output.moveTo(x0, y0); this.idx = startIdx; this.dashOn = this.startDashOn; @@ -160,7 +114,7 @@ public class Dasher extends LineSink { output.lineJoin(); } - private void goTo(int x1, int y1) { + private void goTo(float x1, float y1) { if (dashOn) { if (starting) { this.sx1 = x1; @@ -180,52 +134,64 @@ public class Dasher extends LineSink { this.y0 = y1; } - public void lineTo(int x1, int y1) { + public void lineTo(float x1, float y1) { + // The widened line is squished to a 0 width one, so no drawing is done + if (det == 0) { + goTo(x1, y1); + return; + } + float dx = x1 - x0; + float dy = y1 - y0; + + + // Compute segment length in the untransformed + // coordinate system + + float la = (dy*m00 - dx*m10)/det; + float lb = (dy*m01 - dx*m11)/det; + float origLen = (float) Math.hypot(la, lb); + + if (origLen == 0) { + // Let the output LineSink deal with cases where dx, dy are 0. + goTo(x1, y1); + return; + } + + // The scaling factors needed to get the dx and dy of the + // transformed dash segments. + float cx = dx / origLen; + float cy = dy / origLen; + while (true) { - int d = dash[idx] - phase; - int lx = x1 - x0; - int ly = y1 - y0; - - // Compute segment length in the untransformed - // coordinate system - // IMPL NOTE - use fixed point - - int l; - if (symmetric) { - l = (int)((PiscesMath.hypot(lx, ly)*65536L)/ldet); - } else{ - long la = ((long)ly*m00 - (long)lx*m10)/ldet; - long lb = ((long)ly*m01 - (long)lx*m11)/ldet; - l = (int)PiscesMath.hypot(la, lb); - } - - if (l < d) { + float leftInThisDashSegment = dash[idx] - phase; + if (origLen < leftInThisDashSegment) { goTo(x1, y1); // Advance phase within current dash segment - phase += l; + phase += origLen; + return; + } else if (origLen == leftInThisDashSegment) { + goTo(x1, y1); + phase = 0f; + idx = (idx + 1) % dash.length; + dashOn = !dashOn; return; } - long t; - int xsplit, ysplit; -// // For zero length dashses, SE appears to move 1/8 unit -// // in device space -// if (d == 0) { -// double dlx = lx/65536.0; -// double dly = ly/65536.0; -// len = PiscesMath.hypot(dlx, dly); -// double dt = 1.0/(8*len); -// double dxsplit = (x0/65536.0) + dt*dlx; -// double dysplit = (y0/65536.0) + dt*dly; -// xsplit = (int)(dxsplit*65536.0); -// ysplit = (int)(dysplit*65536.0); -// } else { - t = ((long)d << 16)/l; - xsplit = x0 + (int)(t*(x1 - x0) >> 16); - ysplit = y0 + (int)(t*(y1 - y0) >> 16); -// } - goTo(xsplit, ysplit); + float dashx, dashy; + float dashdx = dash[idx] * cx; + float dashdy = dash[idx] * cy; + if (phase == 0) { + dashx = x0 + dashdx; + dashy = y0 + dashdy; + } else { + float p = (leftInThisDashSegment) / dash[idx]; + dashx = x0 + p * dashdx; + dashy = y0 + p * dashdy; + } + goTo(dashx, dashy); + + origLen -= (dash[idx] - phase); // Advance to next dash segment idx = (idx + 1) % dash.length; dashOn = !dashOn; @@ -233,6 +199,7 @@ public class Dasher extends LineSink { } } + public void close() { lineTo(sx, sy); if (firstDashOn) { diff --git a/jdk/src/share/classes/sun/java2d/pisces/LineSink.java b/jdk/src/share/classes/sun/java2d/pisces/LineSink.java index 6f92dd4a416..81300a25fa0 100644 --- a/jdk/src/share/classes/sun/java2d/pisces/LineSink.java +++ b/jdk/src/share/classes/sun/java2d/pisces/LineSink.java @@ -39,16 +39,16 @@ package sun.java2d.pisces; * LineSink interface. * */ -public abstract class LineSink { +public interface LineSink { /** * Moves the current drawing position to the point (x0, * y0). * - * @param x0 the X coordinate in S15.16 format - * @param y0 the Y coordinate in S15.16 format + * @param x0 the X coordinate + * @param y0 the Y coordinate */ - public abstract void moveTo(int x0, int y0); + public void moveTo(float x0, float y0); /** * Provides a hint that the current segment should be joined to @@ -65,29 +65,29 @@ public abstract class LineSink { *

Other LineSink classes should simply pass this * hint to their output sink as needed. */ - public abstract void lineJoin(); + public void lineJoin(); /** * Draws a line from the current drawing position to the point * (x1, y1) and sets the current drawing position to * (x1, y1). * - * @param x1 the X coordinate in S15.16 format - * @param y1 the Y coordinate in S15.16 format + * @param x1 the X coordinate + * @param y1 the Y coordinate */ - public abstract void lineTo(int x1, int y1); + public void lineTo(float x1, float y1); /** * Closes the current path by drawing a line from the current * drawing position to the point specified by the moset recent * moveTo command. */ - public abstract void close(); + public void close(); /** * Ends the current path. It may be necessary to end a path in * order to allow end caps to be drawn. */ - public abstract void end(); + public void end(); } diff --git a/jdk/src/share/classes/sun/java2d/pisces/PiscesMath.java b/jdk/src/share/classes/sun/java2d/pisces/PiscesMath.java deleted file mode 100644 index 0a6e9421687..00000000000 --- a/jdk/src/share/classes/sun/java2d/pisces/PiscesMath.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2007, 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 sun.java2d.pisces; - -public class PiscesMath { - - private PiscesMath() {} - - private static final int SINTAB_LG_ENTRIES = 10; - private static final int SINTAB_ENTRIES = 1 << SINTAB_LG_ENTRIES; - private static int[] sintab; - - public static final int PI = (int)(Math.PI*65536.0); - public static final int TWO_PI = (int)(2.0*Math.PI*65536.0); - public static final int PI_OVER_TWO = (int)((Math.PI/2.0)*65536.0); - public static final int SQRT_TWO = (int)(Math.sqrt(2.0)*65536.0); - - static { - sintab = new int[SINTAB_ENTRIES + 1]; - for (int i = 0; i < SINTAB_ENTRIES + 1; i++) { - double theta = i*(Math.PI/2.0)/SINTAB_ENTRIES; - sintab[i] = (int)(Math.sin(theta)*65536.0); - } - } - - public static int sin(int theta) { - int sign = 1; - if (theta < 0) { - theta = -theta; - sign = -1; - } - // 0 <= theta - while (theta >= TWO_PI) { - theta -= TWO_PI; - } - // 0 <= theta < 2*PI - if (theta >= PI) { - theta = TWO_PI - theta; - sign = -sign; - } - // 0 <= theta < PI - if (theta > PI_OVER_TWO) { - theta = PI - theta; - } - // 0 <= theta <= PI/2 - int itheta = (int)((long)theta*SINTAB_ENTRIES/(PI_OVER_TWO)); - return sign*sintab[itheta]; - } - - public static int cos(int theta) { - return sin(PI_OVER_TWO - theta); - } - -// public static double sqrt(double x) { -// double dsqrt = Math.sqrt(x); -// int ix = (int)(x*65536.0); -// Int Isqrt = Isqrt(Ix); - -// Long Lx = (Long)(X*65536.0); -// Long Lsqrt = Lsqrt(Lx); - -// System.Out.Println(); -// System.Out.Println("X = " + X); -// System.Out.Println("Dsqrt = " + Dsqrt); - -// System.Out.Println("Ix = " + Ix); -// System.Out.Println("Isqrt = " + Isqrt/65536.0); - -// System.Out.Println("Lx = " + Lx); -// System.Out.Println("Lsqrt = " + Lsqrt/65536.0); - -// Return Dsqrt; -// } - - // From Ken Turkowski, _Fixed-Point Square Root_, In Graphics Gems V - public static int isqrt(int x) { - int fracbits = 16; - - int root = 0; - int remHi = 0; - int remLo = x; - int count = 15 + fracbits/2; - - do { - remHi = (remHi << 2) | (remLo >>> 30); // N.B. - unsigned shift R - remLo <<= 2; - root <<= 1; - int testdiv = (root << 1) + 1; - if (remHi >= testdiv) { - remHi -= testdiv; - root++; - } - } while (count-- != 0); - - return root; - } - - public static long lsqrt(long x) { - int fracbits = 16; - - long root = 0; - long remHi = 0; - long remLo = x; - int count = 31 + fracbits/2; - - do { - remHi = (remHi << 2) | (remLo >>> 62); // N.B. - unsigned shift R - remLo <<= 2; - root <<= 1; - long testDiv = (root << 1) + 1; - if (remHi >= testDiv) { - remHi -= testDiv; - root++; - } - } while (count-- != 0); - - return root; - } - - public static double hypot(double x, double y) { - // new RuntimeException().printStackTrace(); - return Math.sqrt(x*x + y*y); - } - - public static int hypot(int x, int y) { - return (int)((lsqrt((long)x*x + (long)y*y) + 128) >> 8); - } - - public static long hypot(long x, long y) { - return (lsqrt(x*x + y*y) + 128) >> 8; - } -} diff --git a/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java b/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java index 6446cb109e3..23ef19ee31f 100644 --- a/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java +++ b/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java @@ -37,24 +37,8 @@ import sun.java2d.pipe.RenderingEngine; import sun.java2d.pipe.AATileGenerator; public class PiscesRenderingEngine extends RenderingEngine { - public static Transform4 IdentT4 = new Transform4(); public static double defaultFlat = 0.1; - static int FloatToS15_16(float flt) { - flt = flt * 65536f + 0.5f; - if (flt <= -(65536f * 65536f)) { - return Integer.MIN_VALUE; - } else if (flt >= (65536f * 65536f)) { - return Integer.MAX_VALUE; - } else { - return (int) Math.floor(flt); - } - } - - static float S15_16ToFloat(int fix) { - return (fix / 65536f); - } - /** * Create a widened path as specified by the parameters. *

@@ -85,18 +69,19 @@ public class PiscesRenderingEngine extends RenderingEngine { strokeTo(src, null, width, + false, caps, join, miterlimit, dashes, dashphase, new LineSink() { - public void moveTo(int x0, int y0) { - p2d.moveTo(S15_16ToFloat(x0), S15_16ToFloat(y0)); + public void moveTo(float x0, float y0) { + p2d.moveTo(x0, y0); } public void lineJoin() {} - public void lineTo(int x1, int y1) { - p2d.lineTo(S15_16ToFloat(x1), S15_16ToFloat(y1)); + public void lineTo(float x1, float y1) { + p2d.lineTo(x1, y1); } public void close() { p2d.closePath(); @@ -144,12 +129,12 @@ public class PiscesRenderingEngine extends RenderingEngine { { strokeTo(src, at, bs, thin, normalize, antialias, new LineSink() { - public void moveTo(int x0, int y0) { - consumer.moveTo(S15_16ToFloat(x0), S15_16ToFloat(y0)); + public void moveTo(float x0, float y0) { + consumer.moveTo(x0, y0); } public void lineJoin() {} - public void lineTo(int x1, int y1) { - consumer.lineTo(S15_16ToFloat(x1), S15_16ToFloat(y1)); + public void lineTo(float x1, float y1) { + consumer.lineTo(x1, y1); } public void close() { consumer.closePath(); @@ -181,6 +166,7 @@ public class PiscesRenderingEngine extends RenderingEngine { strokeTo(src, at, lw, + normalize, bs.getEndCap(), bs.getLineJoin(), bs.getMiterLimit(), @@ -258,6 +244,7 @@ public class PiscesRenderingEngine extends RenderingEngine { void strokeTo(Shape src, AffineTransform at, float width, + boolean normalize, int caps, int join, float miterlimit, @@ -265,32 +252,16 @@ public class PiscesRenderingEngine extends RenderingEngine { float dashphase, LineSink lsink) { - Transform4 t4; - - if (at == null || at.isIdentity()) { - t4 = IdentT4; - } else { - t4 = new Transform4(FloatToS15_16((float) at.getScaleX()), - FloatToS15_16((float) at.getShearX()), - FloatToS15_16((float) at.getShearY()), - FloatToS15_16((float) at.getScaleY())); + float a00 = 1f, a01 = 0f, a10 = 0f, a11 = 1f; + if (at != null && !at.isIdentity()) { + a00 = (float)at.getScaleX(); + a01 = (float)at.getShearX(); + a10 = (float)at.getShearY(); + a11 = (float)at.getScaleY(); } - - lsink = new Stroker(lsink, - FloatToS15_16(width), - caps, - join, - FloatToS15_16(miterlimit), - t4); + lsink = new Stroker(lsink, width, caps, join, miterlimit, a00, a01, a10, a11); if (dashes != null) { - int fdashes[] = new int[dashes.length]; - for (int i = 0; i < dashes.length; i++) { - fdashes[i] = FloatToS15_16(dashes[i]); - } - lsink = new Dasher(lsink, - fdashes, - FloatToS15_16(dashphase), - t4); + lsink = new Dasher(lsink, dashes, dashphase, a00, a01, a10, a11); } PathIterator pi = src.getPathIterator(at, defaultFlat); @@ -302,13 +273,11 @@ public class PiscesRenderingEngine extends RenderingEngine { while (!pi.isDone()) { switch (pi.currentSegment(coords)) { case PathIterator.SEG_MOVETO: - lsink.moveTo(FloatToS15_16(coords[0]), - FloatToS15_16(coords[1])); + lsink.moveTo(coords[0], coords[1]); break; case PathIterator.SEG_LINETO: lsink.lineJoin(); - lsink.lineTo(FloatToS15_16(coords[0]), - FloatToS15_16(coords[1])); + lsink.lineTo(coords[0], coords[1]); break; case PathIterator.SEG_CLOSE: lsink.lineJoin(); @@ -378,17 +347,19 @@ public class PiscesRenderingEngine extends RenderingEngine { int bbox[]) { PiscesCache pc = PiscesCache.createInstance(); - Renderer r = new Renderer(); - r.setCache(pc); - r.setAntialiasing(3, 3); - r.beginRendering(clip.getLoX(), clip.getLoY(), - clip.getWidth(), clip.getHeight()); + Renderer r; if (bs == null) { PathIterator pi = s.getPathIterator(at, defaultFlat); - r.setWindingRule(pi.getWindingRule()); + r = new Renderer(3, 3, + clip.getLoX(), clip.getLoY(), + clip.getWidth(), clip.getHeight(), + pi.getWindingRule(), pc); pathTo(pi, r); } else { - r.setWindingRule(PathIterator.WIND_NON_ZERO); + r = new Renderer(3, 3, + clip.getLoX(), clip.getLoY(), + clip.getWidth(), clip.getHeight(), + PathIterator.WIND_NON_ZERO, pc); strokeTo(s, at, bs, thin, normalize, true, r); } r.endRendering(); diff --git a/jdk/src/share/classes/sun/java2d/pisces/Renderer.java b/jdk/src/share/classes/sun/java2d/pisces/Renderer.java index fa58146a5ad..9768e90a77b 100644 --- a/jdk/src/share/classes/sun/java2d/pisces/Renderer.java +++ b/jdk/src/share/classes/sun/java2d/pisces/Renderer.java @@ -25,447 +25,441 @@ package sun.java2d.pisces; -public class Renderer extends LineSink { +import java.util.Arrays; + +public class Renderer implements LineSink { + +/////////////////////////////////////////////////////////////////////////////// +// Scan line iterator and edge crossing data. +////////////////////////////////////////////////////////////////////////////// + + private int[] crossings; + + // This is an array of indices into the edge array. It is initialized to + // [i * SIZEOF_STRUCT_EDGE for i in range(0, edgesSize/SIZEOF_STRUCT_EDGE)] + // (where range(i, j) is i,i+1,...,j-1 -- just like in python). + // The reason for keeping this is because we need the edges array sorted + // by y0, but we don't want to move all that data around, so instead we + // sort the indices into the edge array, and use edgeIndices to access + // the edges array. This is meant to simulate a pointer array (hence the name) + private int[] edgePtrs; + + // crossing bounds. The bounds are not necessarily tight (the scan line + // at minY, for example, might have no crossings). The x bounds will + // be accumulated as crossings are computed. + private int minY, maxY; + private int minX, maxX; + private int nextY; + + // indices into the edge pointer list. They indicate the "active" sublist in + // the edge list (the portion of the list that contains all the edges that + // cross the next scan line). + private int lo, hi; + + private static final int INIT_CROSSINGS_SIZE = 50; + private void ScanLineItInitialize() { + crossings = new int[INIT_CROSSINGS_SIZE]; + edgePtrs = new int[edgesSize / SIZEOF_STRUCT_EDGE]; + for (int i = 0; i < edgePtrs.length; i++) { + edgePtrs[i] = i * SIZEOF_STRUCT_EDGE; + } + + qsort(0, edgePtrs.length - 1); + + // We don't care if we clip some of the line off with ceil, since + // no scan line crossings will be eliminated (in fact, the ceil is + // the y of the first scan line crossing). + nextY = minY = Math.max(boundsMinY, (int)Math.ceil(edgeMinY)); + maxY = Math.min(boundsMaxY, (int)Math.ceil(edgeMaxY)); + + for (lo = 0; lo < edgePtrs.length && edges[edgePtrs[lo]+Y1] <= nextY; lo++) + ; + for (hi = lo; hi < edgePtrs.length && edges[edgePtrs[hi]+CURY] <= nextY; hi++) + ; // the active list is *edgePtrs[lo] (inclusive) *edgePtrs[hi] (exclusive) + for (int i = lo; i < hi; i++) { + setCurY(edgePtrs[i], nextY); + } + + // We accumulate X in the iterator because accumulating it in addEdge + // like we do with Y does not do much good: if there's an edge + // (0,0)->(1000,10000), and if y gets clipped to 1000, then the x + // bound should be 100, but the accumulator from addEdge would say 1000, + // so we'd still have to accumulate the X bounds as we add crossings. + minX = boundsMinX; + maxX = boundsMaxX; + } + + private int ScanLineItCurrentY() { + return nextY - 1; + } + + private int ScanLineItGoToNextYAndComputeCrossings() { + // we go through the active list and remove the ones that don't cross + // the nextY scanline. + int crossingIdx = 0; + for (int i = lo; i < hi; i++) { + if (edges[edgePtrs[i]+Y1] <= nextY) { + edgePtrs[i] = edgePtrs[lo++]; + } + } + if (hi - lo > crossings.length) { + int newSize = Math.max(hi - lo, crossings.length * 2); + crossings = Arrays.copyOf(crossings, newSize); + } + // Now every edge between lo and hi crosses nextY. Compute it's + // crossing and put it in the crossings array. + for (int i = lo; i < hi; i++) { + addCrossing(nextY, getCurCrossing(edgePtrs[i]), (int)edges[edgePtrs[i]+OR], crossingIdx); + gotoNextY(edgePtrs[i]); + crossingIdx++; + } + + nextY++; + // Expand active list to include new edges. + for (; hi < edgePtrs.length && edges[edgePtrs[hi]+CURY] <= nextY; hi++) { + setCurY(edgePtrs[hi], nextY); + } + + Arrays.sort(crossings, 0, crossingIdx); + return crossingIdx; + } + + private boolean ScanLineItHasNext() { + return nextY < maxY; + } + + private void addCrossing(int y, int x, int or, int idx) { + if (x < minX) { + minX = x; + } + if (x > maxX) { + maxX = x; + } + x <<= 1; + crossings[idx] = ((or == 1) ? (x | 0x1) : x); + } + + + // quicksort implementation for sorting the edge indices ("pointers") + // by increasing y0. first, last are indices into the "pointer" array + // It sorts the pointer array from first (inclusive) to last (inclusive) + private void qsort(int first, int last) { + if (last > first) { + int p = partition(first, last); + if (first < p - 1) { + qsort(first, p - 1); + } + if (p < last) { + qsort(p, last); + } + } + } + + // i, j are indices into edgePtrs. + private int partition(int i, int j) { + int pivotVal = edgePtrs[i]; + while (i <= j) { + // edges[edgePtrs[i]+1] is equivalent to (*(edgePtrs[i])).y0 in C + while (edges[edgePtrs[i]+CURY] < edges[pivotVal+CURY]) { i++; } + while (edges[edgePtrs[j]+CURY] > edges[pivotVal+CURY]) { j--; } + if (i <= j) { + int tmp = edgePtrs[i]; + edgePtrs[i] = edgePtrs[j]; + edgePtrs[j] = tmp; + i++; + j--; + } + } + return i; + } + +//============================================================================ + + +////////////////////////////////////////////////////////////////////////////// +// EDGE LIST +////////////////////////////////////////////////////////////////////////////// + + private static final int INIT_NUM_EDGES = 1000; + private static final int SIZEOF_STRUCT_EDGE = 5; + + // The following array is a poor man's struct array: + // it simulates a struct array by having + // edges[SIZEOF_STRUCT_EDGE * i + j] be the jth field in the ith element + // of an array of edge structs. + private float[] edges; + private int edgesSize; // size of the edge list. + private static final int Y1 = 0; + private static final int SLOPE = 1; + private static final int OR = 2; // the orientation. This can be -1 or 1. + // -1 means up, 1 means down. + private static final int CURY = 3; // j = 5 corresponds to the "current Y". + // Each edge keeps track of the last scanline + // crossing it computed, and this is the y coord of + // that scanline. + private static final int CURX = 4; //the x coord of the current crossing. + + // Note that while the array is declared as a float[] not all of it's + // elements should be floats. currentY and Orientation should be ints (or int and + // byte respectively), but they all need to be the same type. This isn't + // really a problem because floats can represent exactly all 23 bit integers, + // which should be more than enough. + // Note, also, that we only need x1 for slope computation, so we don't need + // to store it. x0, y0 don't need to be stored either. They can be put into + // curx, cury, and it's ok if they're lost when curx and cury are changed. + // We take this undeniably ugly and error prone approach (instead of simply + // making an Edge class) for performance reasons. Also, it would probably be nicer + // to have one array for each field, but that would defeat the purpose because + // it would make poor use of the processor cache, since we tend to access + // all the fields for one edge at a time. + + private float edgeMinY; + private float edgeMaxY; + + + private void addEdge(float x0, float y0, float x1, float y1) { + float or = (y0 < y1) ? 1f : -1f; // orientation: 1 = UP; -1 = DOWN + if (or == -1) { + float tmp = y0; + y0 = y1; + y1 = tmp; + tmp = x0; + x0 = x1; + x1 = tmp; + } + // skip edges that don't cross a scanline + if (Math.ceil(y0) >= Math.ceil(y1)) { + return; + } + + int newSize = edgesSize + SIZEOF_STRUCT_EDGE; + if (edges.length < newSize) { + edges = Arrays.copyOf(edges, newSize * 2); + } + edges[edgesSize+CURX] = x0; + edges[edgesSize+CURY] = y0; + edges[edgesSize+Y1] = y1; + edges[edgesSize+SLOPE] = (x1 - x0) / (y1 - y0); + edges[edgesSize+OR] = or; + // the crossing values can't be initialized meaningfully yet. This + // will have to wait until setCurY is called + edgesSize += SIZEOF_STRUCT_EDGE; + + // Accumulate edgeMinY and edgeMaxY + if (y0 < edgeMinY) { edgeMinY = y0; } + if (y1 > edgeMaxY) { edgeMaxY = y1; } + } + + // As far as the following methods care, this edges extends to infinity. + // They can compute the x intersect of any horizontal line. + // precondition: idx is the index to the start of the desired edge. + // So, if the ith edge is wanted, idx should be SIZEOF_STRUCT_EDGE * i + private void setCurY(int idx, int y) { + // compute the x crossing of edge at idx and horizontal line y + // currentXCrossing = (y - y0)*slope + x0 + edges[idx + CURX] = (y - edges[idx + CURY]) * edges[idx + SLOPE] + edges[idx+CURX]; + edges[idx + CURY] = (float)y; + } + + private void gotoNextY(int idx) { + edges[idx + CURY] += 1f; // i.e. curY += 1 + edges[idx + CURX] += edges[idx + SLOPE]; // i.e. curXCrossing += slope + } + + private int getCurCrossing(int idx) { + return (int)edges[idx + CURX]; + } +//==================================================================================== + public static final int WIND_EVEN_ODD = 0; public static final int WIND_NON_ZERO = 1; - // Initial edge list size - // IMPL_NOTE - restore size after growth - public static final int INITIAL_EDGES = 1000; - - // Recommended maximum scratchpad sizes. The arrays will grow - // larger if needed, but when finished() is called they will be released - // if they have grown larger than these sizes. - public static final int DEFAULT_INDICES_SIZE = 8192; - public static final int DEFAULT_CROSSINGS_SIZE = 32*1024; - // Antialiasing - private int SUBPIXEL_LG_POSITIONS_X; - private int SUBPIXEL_LG_POSITIONS_Y; - private int SUBPIXEL_MASK_X; - private int SUBPIXEL_MASK_Y; - private int SUBPIXEL_POSITIONS_X; - private int SUBPIXEL_POSITIONS_Y; - int MAX_AA_ALPHA; - private int MAX_AA_ALPHA_DENOM; - private int HALF_MAX_AA_ALPHA_DENOM; - private int XSHIFT; - private int YSHIFT; - private int YSTEP; - private int HYSTEP; - private int YMASK; - - private static final int MIN_QUAD_OPT_WIDTH = 100 << 16; + final private int SUBPIXEL_LG_POSITIONS_X; + final private int SUBPIXEL_LG_POSITIONS_Y; + final private int SUBPIXEL_POSITIONS_X; + final private int SUBPIXEL_POSITIONS_Y; + final private int SUBPIXEL_MASK_X; + final private int SUBPIXEL_MASK_Y; + final int MAX_AA_ALPHA; // Cache to store RLE-encoded coverage mask of the current primitive - PiscesCache cache; + final PiscesCache cache; - // Bounds of the drawing region, at S15.16 precsion - private int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY; - - // Bounds of the current primitive, at subsample precision - private int rasterMinX, rasterMaxX, rasterMinY, rasterMaxY; + // Bounds of the drawing region, at subpixel precision. + final private int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY; // Pixel bounding box for current primitive - private int bboxX0, bboxY0, bboxX1, bboxY1; + private int pix_bboxX0, pix_bboxY0, pix_bboxX1, pix_bboxY1; // Current winding rule - private int windingRule; + final private int windingRule; // Current drawing position, i.e., final point of last segment - private int x0, y0; + private float x0, y0; // Position of most recent 'moveTo' command - private int sx0, sy0; + private float pix_sx0, pix_sy0; - // Buffer to be filled with one row's worth of alpha values - private byte[] rowAA; // needs to be short if 16x16 subsampling - - // Track the number of vertical extrema of the incoming edge list - // in order to determine the maximum number of crossings of a - // scanline - private int firstOrientation; - private int lastOrientation; - private int flips; - - // Parameters for emitRow - private int alphaWidth; - - public Renderer() { - } - - public void setAntialiasing(int subpixelLgPositionsX, - int subpixelLgPositionsY) { + public Renderer(int subpixelLgPositionsX, int subpixelLgPositionsY, + int pix_boundsX, int pix_boundsY, + int pix_boundsWidth, int pix_boundsHeight, + int windingRule, + PiscesCache cache) { this.SUBPIXEL_LG_POSITIONS_X = subpixelLgPositionsX; this.SUBPIXEL_LG_POSITIONS_Y = subpixelLgPositionsY; + this.SUBPIXEL_MASK_X = (1 << (SUBPIXEL_LG_POSITIONS_X)) - 1; + this.SUBPIXEL_MASK_Y = (1 << (SUBPIXEL_LG_POSITIONS_Y)) - 1; + this.SUBPIXEL_POSITIONS_X = 1 << (SUBPIXEL_LG_POSITIONS_X); + this.SUBPIXEL_POSITIONS_Y = 1 << (SUBPIXEL_LG_POSITIONS_Y); + this.MAX_AA_ALPHA = (SUBPIXEL_POSITIONS_X * SUBPIXEL_POSITIONS_Y); - this.SUBPIXEL_MASK_X = - (1 << (SUBPIXEL_LG_POSITIONS_X)) - 1; - this.SUBPIXEL_MASK_Y = - (1 << (SUBPIXEL_LG_POSITIONS_Y)) - 1; - this.SUBPIXEL_POSITIONS_X = - 1 << (SUBPIXEL_LG_POSITIONS_X); - this.SUBPIXEL_POSITIONS_Y = - 1 << (SUBPIXEL_LG_POSITIONS_Y); - this.MAX_AA_ALPHA = - (SUBPIXEL_POSITIONS_X*SUBPIXEL_POSITIONS_Y); - this.MAX_AA_ALPHA_DENOM = 255*MAX_AA_ALPHA; - this.HALF_MAX_AA_ALPHA_DENOM = MAX_AA_ALPHA_DENOM/2; - this.XSHIFT = 16 - SUBPIXEL_LG_POSITIONS_X; - this.YSHIFT = 16 - SUBPIXEL_LG_POSITIONS_Y; - this.YSTEP = 1 << YSHIFT; - this.HYSTEP = 1 << (YSHIFT - 1); - this.YMASK = ~(YSTEP - 1); - } + this.edges = new float[SIZEOF_STRUCT_EDGE * INIT_NUM_EDGES]; + edgeMinY = Float.POSITIVE_INFINITY; + edgeMaxY = Float.NEGATIVE_INFINITY; + edgesSize = 0; - public int getSubpixelLgPositionsX() { - return SUBPIXEL_LG_POSITIONS_X; - } - - public int getSubpixelLgPositionsY() { - return SUBPIXEL_LG_POSITIONS_Y; - } - - public void setWindingRule(int windingRule) { this.windingRule = windingRule; + this.cache = cache; + + this.boundsMinX = pix_boundsX * SUBPIXEL_POSITIONS_X; + this.boundsMinY = pix_boundsY * SUBPIXEL_POSITIONS_Y; + this.boundsMaxX = (pix_boundsX + pix_boundsWidth) * SUBPIXEL_POSITIONS_X; + this.boundsMaxY = (pix_boundsY + pix_boundsHeight) * SUBPIXEL_POSITIONS_Y; + + this.pix_bboxX0 = pix_boundsX; + this.pix_bboxY0 = pix_boundsY; + this.pix_bboxX1 = pix_boundsX + pix_boundsWidth; + this.pix_bboxY1 = pix_boundsY + pix_boundsHeight; } - public int getWindingRule() { - return windingRule; + private float tosubpixx(float pix_x) { + return pix_x * SUBPIXEL_POSITIONS_X; + } + private float tosubpixy(float pix_y) { + return pix_y * SUBPIXEL_POSITIONS_Y; } - public void beginRendering(int boundsX, int boundsY, - int boundsWidth, int boundsHeight) { - lastOrientation = 0; - flips = 0; - - resetEdges(); - - this.boundsMinX = boundsX << 16; - this.boundsMinY = boundsY << 16; - this.boundsMaxX = (boundsX + boundsWidth) << 16; - this.boundsMaxY = (boundsY + boundsHeight) << 16; - - this.bboxX0 = boundsX; - this.bboxY0 = boundsY; - this.bboxX1 = boundsX + boundsWidth; - this.bboxY1 = boundsY + boundsHeight; - } - - public void moveTo(int x0, int y0) { - // System.out.println("Renderer: moveTo " + x0/65536.0 + " " + y0/65536.0); + public void moveTo(float pix_x0, float pix_y0) { close(); - this.sx0 = this.x0 = x0; - this.sy0 = this.y0 = y0; - this.lastOrientation = 0; + this.pix_sx0 = pix_x0; + this.pix_sy0 = pix_y0; + this.y0 = tosubpixy(pix_y0); + this.x0 = tosubpixx(pix_x0); } - public void lineJoin() { - // System.out.println("Renderer: lineJoin"); - // do nothing - } + public void lineJoin() { /* do nothing */ } - public void lineTo(int x1, int y1) { - // System.out.println("Renderer: lineTo " + x1/65536.0 + " " + y1/65536.0); + public void lineTo(float pix_x1, float pix_y1) { + float x1 = tosubpixx(pix_x1); + float y1 = tosubpixy(pix_y1); // Ignore horizontal lines - // Next line will count flip if (y0 == y1) { this.x0 = x1; return; } - int orientation = (y0 < y1) ? 1 : -1; - if (lastOrientation == 0) { - firstOrientation = orientation; - } else if (orientation != lastOrientation) { - ++flips; - } - lastOrientation = orientation; - - // Bias Y by 1 ULP so endpoints never lie on a scanline - addEdge(x0, y0 | 0x1, x1, y1 | 0x1); + addEdge(x0, y0, x1, y1); this.x0 = x1; this.y0 = y1; } public void close() { - // System.out.println("Renderer: close"); - - int orientation = lastOrientation; - if (y0 != sy0) { - orientation = (y0 < sy0) ? 1 : -1; - } - if (orientation != firstOrientation) { - ++flips; - } - lineTo(sx0, sy0); + // lineTo expects its input in pixel coordinates. + lineTo(pix_sx0, pix_sy0); } public void end() { close(); - // System.out.println("Renderer: end"); - // do nothing - } - - // Scan convert a single edge - private void computeCrossingsForEdge(int index, - int boundsMinY, int boundsMaxY) { - int iy0 = edges[index + 1]; - int iy1 = edges[index + 3]; - - // Clip to valid Y range - int clipy0 = (iy0 > boundsMinY) ? iy0 : boundsMinY; - int clipy1 = (iy1 < boundsMaxY) ? iy1 : boundsMaxY; - - int minY = ((clipy0 + HYSTEP) & YMASK) + HYSTEP; - int maxY = ((clipy1 - HYSTEP) & YMASK) + HYSTEP; - - // IMPL_NOTE - If line falls outside the valid X range, could - // draw a vertical line instead - - // Exit if no scanlines are crossed - if (minY > maxY) { - return; - } - - // Scan convert line using a DDA approach - - int ix0 = edges[index]; - int ix1 = edges[index + 2]; - long dx = ((long) ix1) - ix0; - long dy = ((long) iy1) - iy0; - - // Compute first crossing point at y = minY - int orientation = edges[index + 4]; - int y = minY; - long lx = (((long) y) - iy0)*dx/dy + ix0; - addCrossing(y >> YSHIFT, (int)(lx >> XSHIFT), orientation); - - // Advance y to next scanline, exit if past endpoint - y += YSTEP; - if (y > maxY) { - return; - } - - // Compute xstep only if additional scanlines are crossed - // For each scanline, add xstep to lx and YSTEP to y and - // emit the new crossing - long xstep = ((long)YSTEP*dx)/dy; - for (; y <= maxY; y += YSTEP) { - lx += xstep; - addCrossing(y >> YSHIFT, (int)(lx >> XSHIFT), orientation); - } - } - - private void computeBounds() { - rasterMinX = crossingMinX & ~SUBPIXEL_MASK_X; - rasterMaxX = crossingMaxX | SUBPIXEL_MASK_X; - rasterMinY = crossingMinY & ~SUBPIXEL_MASK_Y; - rasterMaxY = crossingMaxY | SUBPIXEL_MASK_Y; - - // If nothing was drawn, we have: - // minX = Integer.MAX_VALUE and maxX = Integer.MIN_VALUE - // so nothing to render - if (rasterMinX > rasterMaxX || rasterMinY > rasterMaxY) { - rasterMinX = 0; - rasterMaxX = -1; - rasterMinY = 0; - rasterMaxY = -1; - return; - } - - if (rasterMinX < boundsMinX >> XSHIFT) { - rasterMinX = boundsMinX >> XSHIFT; - } - if (rasterMinY < boundsMinY >> YSHIFT) { - rasterMinY = boundsMinY >> YSHIFT; - } - if (rasterMaxX > boundsMaxX >> XSHIFT) { - rasterMaxX = boundsMaxX >> XSHIFT; - } - if (rasterMaxY > boundsMaxY >> YSHIFT) { - rasterMaxY = boundsMaxY >> YSHIFT; - } - } - - private int clamp(int x, int min, int max) { - if (x < min) { - return min; - } else if (x > max) { - return max; - } - return x; } private void _endRendering() { - if (flips == 0) { - bboxX0 = bboxY0 = 0; - bboxX1 = bboxY1 = -1; - return; - } + // Mask to determine the relevant bit of the crossing sum + // 0x1 if EVEN_ODD, all bits if NON_ZERO + int mask = (windingRule == WIND_EVEN_ODD) ? 0x1 : ~0x0; - // Special case for filling a single rect with a flat, opaque color - // REMIND: This special case was not originally written to fill a - // cache object and called directly to a Blit - it needs some code - // to fill the cache instead to be useful for this usage... - if (false /* Does not work with cache (yet?) */ && - edgeIdx == 10 && - edges[0] == edges[2] && - edges[1] == edges[6] && - edges[3] == edges[8] && - edges[5] == edges[7] && - Math.abs(edges[0] - edges[5]) > MIN_QUAD_OPT_WIDTH) - { + // add 1 to better deal with the last pixel in a pixel row. + int width = ((boundsMaxX - boundsMinX) >> SUBPIXEL_LG_POSITIONS_X) + 1; + byte[] alpha = new byte[width+1]; - int x0 = edges[0] >> XSHIFT; - int y0 = edges[1] >> YSHIFT; - int x1 = edges[5] >> XSHIFT; - int y1 = edges[3] >> YSHIFT; + // Now we iterate through the scanlines. We must tell emitRow the coord + // of the first non-transparent pixel, so we must keep accumulators for + // the first and last pixels of the section of the current pixel row + // that we will emit. + // We also need to accumulate pix_bbox*, but the iterator does it + // for us. We will just get the values from it once this loop is done + int pix_maxX = Integer.MIN_VALUE; + int pix_minX = Integer.MAX_VALUE; - if (x0 > x1) { - int tmp = x0; - x0 = x1; - x1 = tmp; - } - if (y0 > y1) { - int tmp = y0; - y0 = y1; - y1 = tmp; + int y = boundsMinY; // needs to be declared here so we emit the last row properly. + ScanLineItInitialize(); + for ( ; ScanLineItHasNext(); ) { + int numCrossings = ScanLineItGoToNextYAndComputeCrossings(); + y = ScanLineItCurrentY(); + + if (numCrossings > 0) { + int lowx = crossings[0] >> 1; + int highx = crossings[numCrossings - 1] >> 1; + int x0 = Math.max(lowx, boundsMinX); + int x1 = Math.min(highx, boundsMaxX); + + pix_minX = Math.min(pix_minX, x0 >> SUBPIXEL_LG_POSITIONS_X); + pix_maxX = Math.max(pix_maxX, x1 >> SUBPIXEL_LG_POSITIONS_X); } - int bMinX = this.boundsMinX >> XSHIFT; - int bMinY = this.boundsMinY >> YSHIFT; - int bMaxX = this.boundsMaxX >> XSHIFT; - int bMaxY = this.boundsMaxY >> YSHIFT; + int sum = 0; + int prev = boundsMinX; + for (int i = 0; i < numCrossings; i++) { + int curxo = crossings[i]; + int curx = curxo >> 1; + int crorientation = ((curxo & 0x1) == 0x1) ? 1 : -1; + if ((sum & mask) != 0) { + int x0 = Math.max(prev, boundsMinX); + int x1 = Math.min(curx, boundsMaxX); + if (x0 < x1) { + x0 -= boundsMinX; // turn x0, x1 from coords to indeces + x1 -= boundsMinX; // in the alpha array. - // Clip to image bounds in supersampled coordinates - x0 = clamp(x0, bMinX, bMaxX); - x1 = clamp(x1, bMinX, bMaxX); - y0 = clamp(y0, bMinY, bMaxY); - y1 = clamp(y1, bMinY, bMaxY); + int pix_x = x0 >> SUBPIXEL_LG_POSITIONS_X; + int pix_xmaxm1 = (x1 - 1) >> SUBPIXEL_LG_POSITIONS_X; - /* - * REMIND: Need to fill the cache here instead... - Blit.fillRectSrcOver(this, - imageData, imageType, - imageOffset, - imageScanlineStride, imagePixelStride, - width, height, - x0, y0, x1, y1, - cred, cgreen, cblue); - */ - - bboxX0 = x0 >> SUBPIXEL_LG_POSITIONS_X; - bboxY0 = y0 >> SUBPIXEL_LG_POSITIONS_Y; - bboxX1 = (x1 + SUBPIXEL_POSITIONS_X - 1) - >> SUBPIXEL_LG_POSITIONS_X; - bboxY1 = (y1 + SUBPIXEL_POSITIONS_Y - 1) - >> SUBPIXEL_LG_POSITIONS_Y; - - return; - } - - int minY = (edgeMinY > boundsMinY) ? edgeMinY : boundsMinY; - int maxY = (edgeMaxY < boundsMaxY) ? edgeMaxY : boundsMaxY; - - // Check for empty intersection of primitive with the drawing area - if (minY > maxY) { - bboxX0 = bboxY0 = 0; - bboxX1 = bboxY1 = -1; - return; - } - - // Compute Y extent in subpixel coordinates - int iminY = (minY >> YSHIFT) & ~SUBPIXEL_MASK_Y; - int imaxY = (maxY >> YSHIFT) | SUBPIXEL_MASK_Y; - int yextent = (imaxY - iminY) + 1; - - // Maximum number of crossings - int size = flips*yextent; - - int bmax = (boundsMaxY >> YSHIFT) - 1; - if (imaxY > bmax) { - imaxY = bmax; - } - - // Initialize X bounds, will be refined for each strip - bboxX0 = Integer.MAX_VALUE; - bboxX1 = Integer.MIN_VALUE; - - // Set Y bounds - bboxY0 = iminY >> SUBPIXEL_LG_POSITIONS_Y; - bboxY1 = (imaxY + SUBPIXEL_POSITIONS_Y - 1) >> SUBPIXEL_LG_POSITIONS_Y; - - // Compute number of rows that can be processing using - // a crossings table no larger than DEFAULT_CROSSINGS_SIZE. - // However, we must process at least one row, so we grow the table - // temporarily if needed. This would require an object with a - // huge number of flips. - int rows = DEFAULT_CROSSINGS_SIZE/(flips*SUBPIXEL_POSITIONS_Y); - rows = Math.min(rows, yextent); - rows = Math.max(rows, 1); - for (int i = iminY; i <= imaxY; i += rows*SUBPIXEL_POSITIONS_Y) { - // Compute index of last scanline to be processed in this pass - int last = Math.min(i + rows*SUBPIXEL_POSITIONS_Y - 1, imaxY); - setCrossingsExtents(i, last, flips); - - int bminY = i << YSHIFT; - int bmaxY = (last << YSHIFT) | ~YMASK; - - // Process edges from the edge list - int maxIdx = edgeIdx; - for (int index = 0; index < maxIdx; index += 5) { - // Test y1 < min: - // - // If edge lies entirely above current strip, - // discard it - if (edges[index + 3] < bminY) { - // Overwrite the edge with the last edge - edgeIdx -= 5; - int fidx = edgeIdx; - int tidx = index; - edges[tidx++] = edges[fidx++]; - edges[tidx++] = edges[fidx++]; - edges[tidx++] = edges[fidx++]; - edges[tidx++] = edges[fidx++]; - edges[tidx ] = edges[fidx ]; - - maxIdx -= 5; - index -= 5; - continue; + if (pix_x == pix_xmaxm1) { + // Start and end in same pixel + alpha[pix_x] += (x1 - x0); + alpha[pix_x+1] -= (x1 - x0); + } else { + int pix_xmax = x1 >> SUBPIXEL_LG_POSITIONS_X; + alpha[pix_x] += SUBPIXEL_POSITIONS_X - (x0 & SUBPIXEL_MASK_X); + alpha[pix_x+1] += (x0 & SUBPIXEL_MASK_X); + alpha[pix_xmax] -= SUBPIXEL_POSITIONS_X - (x1 & SUBPIXEL_MASK_X); + alpha[pix_xmax+1] -= (x1 & SUBPIXEL_MASK_X); + } + } } - - // Test y0 > max: - // - // If edge lies entirely below current strip, - // skip it for now - if (edges[index + 1] > bmaxY) { - continue; - } - - computeCrossingsForEdge(index, bminY, bmaxY); + sum += crorientation; + prev = curx; } - computeBounds(); - if (rasterMaxX < rasterMinX) { - continue; + if ((y & SUBPIXEL_MASK_Y) == SUBPIXEL_MASK_Y) { + emitRow(alpha, y >> SUBPIXEL_LG_POSITIONS_Y, pix_minX, pix_maxX); + pix_minX = Integer.MAX_VALUE; + pix_maxX = Integer.MIN_VALUE; } - - bboxX0 = Math.min(bboxX0, - rasterMinX >> SUBPIXEL_LG_POSITIONS_X); - bboxX1 = Math.max(bboxX1, - (rasterMaxX + SUBPIXEL_POSITIONS_X - 1) - >> SUBPIXEL_LG_POSITIONS_X); - renderStrip(); } - // Free up any unusually large scratchpad memory used by the - // preceding primitive - crossingListFinished(); + // Emit final row + if (pix_maxX >= pix_minX) { + emitRow(alpha, y >> SUBPIXEL_LG_POSITIONS_Y, pix_minX, pix_maxX); + } + pix_bboxX0 = minX >> SUBPIXEL_LG_POSITIONS_X; + pix_bboxX1 = maxX >> SUBPIXEL_LG_POSITIONS_X; + pix_bboxY0 = minY >> SUBPIXEL_LG_POSITIONS_Y; + pix_bboxY1 = maxY >> SUBPIXEL_LG_POSITIONS_Y; } + public void endRendering() { // Set up the cache to accumulate the bounding box if (cache != null) { @@ -478,176 +472,31 @@ public class Renderer extends LineSink { _endRendering(); } - public void getBoundingBox(int[] bbox) { - bbox[0] = bboxX0; - bbox[1] = bboxY0; - bbox[2] = bboxX1 - bboxX0; - bbox[3] = bboxY1 - bboxY0; + public void getBoundingBox(int[] pix_bbox) { + pix_bbox[0] = pix_bboxX0; + pix_bbox[1] = pix_bboxY0; + pix_bbox[2] = pix_bboxX1 - pix_bboxX0; + pix_bbox[3] = pix_bboxY1 - pix_bboxY0; } - private void renderStrip() { - // Grow rowAA according to the raster width - int width = (rasterMaxX - rasterMinX + 1) >> SUBPIXEL_LG_POSITIONS_X; - alphaWidth = width; - - // Allocate one extra entry in rowAA to avoid a conditional in - // the rendering loop - int bufLen = width + 1; - if (this.rowAA == null || this.rowAA.length < bufLen) { - this.rowAA = new byte[bufLen]; - } - - // Mask to determine the relevant bit of the crossing sum - // 0x1 if EVEN_ODD, all bits if NON_ZERO - int mask = (windingRule == WIND_EVEN_ODD) ? 0x1 : ~0x0; - - int y = 0; - int prevY = rasterMinY - 1; - - int minX = Integer.MAX_VALUE; - int maxX = Integer.MIN_VALUE; - - iterateCrossings(); - while (hasMoreCrossingRows()) { - y = crossingY; - - // Emit any skipped rows - for (int j = prevY + 1; j < y; j++) { - if (((j & SUBPIXEL_MASK_Y) == SUBPIXEL_MASK_Y) || - (j == rasterMaxY)) { - emitRow(j >> SUBPIXEL_LG_POSITIONS_Y, 0, -1); - } - } - prevY = y; - - if (crossingRowIndex < crossingRowCount) { - int lx = crossings[crossingRowOffset + crossingRowIndex]; - lx >>= 1; - int hx = crossings[crossingRowOffset + crossingRowCount - 1]; - hx >>= 1; - int x0 = lx > rasterMinX ? lx : rasterMinX; - int x1 = hx < rasterMaxX ? hx : rasterMaxX; - x0 -= rasterMinX; - x1 -= rasterMinX; - - minX = Math.min(minX, x0 >> SUBPIXEL_LG_POSITIONS_X); - maxX = Math.max(maxX, x1 >> SUBPIXEL_LG_POSITIONS_X); - } - - int sum = 0; - int prev = rasterMinX; - while (crossingRowIndex < crossingRowCount) { - int crxo = crossings[crossingRowOffset + crossingRowIndex]; - crossingRowIndex++; - - int crx = crxo >> 1; - int crorientation = ((crxo & 0x1) == 0x1) ? 1 : -1; - - if ((sum & mask) != 0) { - // Clip to active X range, if x1 < x0 loop will - // have no effect - int x0 = prev > rasterMinX ? prev : rasterMinX; - int x1 = crx < rasterMaxX ? crx : rasterMaxX; - - // Empty spans - if (x1 > x0) { - x0 -= rasterMinX; - x1 -= rasterMinX; - - // Accumulate alpha, equivalent to: - // for (int x = x0; x < x1; x++) { - // ++rowAA[x >> SUBPIXEL_LG_POSITIONS_X]; - // } - // - // In the middle of the span, we can update a full - // pixel at a time (i.e., SUBPIXEL_POSITIONS_X - // subpixels) - - int x = x0 >> SUBPIXEL_LG_POSITIONS_X; - int xmaxm1 = (x1 - 1) >> SUBPIXEL_LG_POSITIONS_X; - if (x == xmaxm1) { - // Start and end in same pixel - rowAA[x] += x1 - x0; - } else { - // Start and end in different pixels - rowAA[x++] += SUBPIXEL_POSITIONS_X - - (x0 & SUBPIXEL_MASK_X); - int xmax = x1 >> SUBPIXEL_LG_POSITIONS_X; - while (x < xmax) { - rowAA[x++] += SUBPIXEL_POSITIONS_X; - } - // Note - at this point it is possible that - // x == width, which implies that - // x1 & SUBPIXEL_MASK_X == 0. We allocate - // one extra entry in rowAA so this - // assignment will be harmless. The alternative - // is an extra conditional here, or some other - // scheme to deal with the last pixel better. - rowAA[x] += x1 & SUBPIXEL_MASK_X; - } - } - } - sum += crorientation; - prev = crx; - } - - // Every SUBPIXEL_POSITIONS rows, output an antialiased row - if (((y & SUBPIXEL_MASK_Y) == SUBPIXEL_MASK_Y) || - (y == rasterMaxY)) { - emitRow(y >> SUBPIXEL_LG_POSITIONS_Y, minX, maxX); - minX = Integer.MAX_VALUE; - maxX = Integer.MIN_VALUE; - } - } - - // Emit final row - for (int j = prevY + 1; j <= rasterMaxY; j++) { - if (((j & SUBPIXEL_MASK_Y) == SUBPIXEL_MASK_Y) || - (j == rasterMaxY)) { - emitRow(j >> SUBPIXEL_LG_POSITIONS_Y, minX, maxX); - minX = Integer.MAX_VALUE; - maxX = Integer.MIN_VALUE; - } - } - } - - private void clearAlpha(byte[] alpha, - int width, - int minX, int maxX) { - if (maxX >= minX) { - int w = maxX - minX + 1; - if (w + minX > width) { - w = width - minX; - } - - int aidx = minX; - for (int i = 0; i < w; i++, aidx++) { - alpha[aidx] = (byte)0; - } - } - } - - private void emitRow(int y, int minX, int maxX) { + private void emitRow(byte[] alphaRow, int pix_y, int pix_from, int pix_to) { // Copy rowAA data into the cache if one is present if (cache != null) { - if (maxX >= minX) { - int x0 = minX + (rasterMinX >> SUBPIXEL_LG_POSITIONS_X); - int x1 = maxX + (rasterMinX >> SUBPIXEL_LG_POSITIONS_X); + if (pix_to >= pix_from) { + cache.startRow(pix_y, pix_from, pix_to); - cache.startRow(y, x0, x1); - int srcIdx = minX; + // Perform run-length encoding and store results in the cache + int from = pix_from - (boundsMinX >> SUBPIXEL_LG_POSITIONS_X); + int to = pix_to - (boundsMinX >> SUBPIXEL_LG_POSITIONS_X); - // Perform run-length encoding - // and store results in the cache - byte startVal = rowAA[srcIdx++]; int runLen = 1; - while (srcIdx <= maxX) { - byte nextVal = rowAA[srcIdx++]; + byte startVal = alphaRow[from]; + for (int i = from + 1; i <= to; i++) { + byte nextVal = (byte)(startVal + alphaRow[i]); if (nextVal == startVal && runLen < 255) { - ++runLen; + runLen++; } else { cache.addRLERun(startVal, runLen); - runLen = 1; startVal = nextVal; } @@ -656,190 +505,6 @@ public class Renderer extends LineSink { cache.addRLERun((byte)0, 0); } } - - clearAlpha(rowAA, - alphaWidth, - minX, maxX); - } - - public void setCache(PiscesCache cache) { - this.cache = cache; - } - - // Edge list data - - private int[] edges = new int[5*INITIAL_EDGES]; - private int edgeIdx = 0; - private int edgeMinY = Integer.MAX_VALUE; - private int edgeMaxY = Integer.MIN_VALUE; - - private void addEdge(int x0, int y0, int x1, int y1) { - int newLen = edgeIdx + 5; - if (edges.length < newLen) { - int[] tmp = new int[Math.max(11*edges.length/10, newLen)]; - System.arraycopy(edges, 0, tmp, 0, edgeIdx); - this.edges = tmp; - } - - int orientation = 1; - if (y0 > y1) { - int tmp = y0; - y0 = y1; - y1 = tmp; - - orientation = -1; - } - - // Skip edges that don't cross a subsampled scanline - int eminY = ((y0 + HYSTEP) & YMASK); - int emaxY = ((y1 - HYSTEP) & YMASK); - if (eminY > emaxY) { - return; - } - - if (orientation == -1) { - int tmp = x0; - x0 = x1; - x1 = tmp; - } - - edges[edgeIdx++] = x0; - edges[edgeIdx++] = y0; - edges[edgeIdx++] = x1; - edges[edgeIdx++] = y1; - edges[edgeIdx++] = orientation; - - // Update Y bounds of primitive - if (y0 < edgeMinY) { - edgeMinY = y0; - } - if (y1 > edgeMaxY) { - edgeMaxY = y1; - } - } - - private void resetEdges() { - this.edgeIdx = 0; - this.edgeMinY = Integer.MAX_VALUE; - this.edgeMaxY = Integer.MIN_VALUE; - } - - // Crossing list data - - private int[] crossingIndices; - private int[] crossings; - private int crossingMinY; - private int crossingMaxY; - private int crossingMinX = Integer.MAX_VALUE; - private int crossingMaxX = Integer.MIN_VALUE; - private int crossingMaxXEntries; - private int numCrossings = 0; - private boolean crossingsSorted = false; - - private int crossingY; - private int crossingRowCount; - private int crossingRowOffset; - private int crossingRowIndex; - - private void setCrossingsExtents(int minY, int maxY, int maxXEntries) { - int yextent = maxY - minY + 1; - - // Grow indices array as needed - if (crossingIndices == null || crossingIndices.length < yextent) { - this.crossingIndices = - new int[Math.max(yextent, DEFAULT_INDICES_SIZE)]; - } - // Grow crossings array as needed - if (crossings == null || crossings.length < yextent*maxXEntries) { - this.crossings = new int[Math.max(yextent*maxXEntries, - DEFAULT_CROSSINGS_SIZE)]; - } - this.crossingMinY = minY; - this.crossingMaxY = maxY; - this.crossingMaxXEntries = maxXEntries; - resetCrossings(); - } - - private void resetCrossings() { - int yextent = crossingMaxY - crossingMinY + 1; - int start = 0; - for (int i = 0; i < yextent; i++) { - crossingIndices[i] = start; - start += crossingMaxXEntries; - } - crossingMinX = Integer.MAX_VALUE; - crossingMaxX = Integer.MIN_VALUE; - numCrossings = 0; - crossingsSorted = false; - } - - // Free sorting arrays if larger than maximum size - private void crossingListFinished() { - if (crossings != null && crossings.length > DEFAULT_CROSSINGS_SIZE) { - crossings = new int[DEFAULT_CROSSINGS_SIZE]; - } - if (crossingIndices != null && - crossingIndices.length > DEFAULT_INDICES_SIZE) - { - crossingIndices = new int[DEFAULT_INDICES_SIZE]; - } - } - - private void sortCrossings(int[] x, int off, int len) { - for (int i = off + 1; i < off + len; i++) { - int j = i; - int xj = x[j]; - int xjm1; - - while (j > off && (xjm1 = x[j - 1]) > xj) { - x[j] = xjm1; - x[j - 1] = xj; - j--; - } - } - } - - private void sortCrossings() { - int start = 0; - for (int i = 0; i <= crossingMaxY - crossingMinY; i++) { - sortCrossings(crossings, start, crossingIndices[i] - start); - start += crossingMaxXEntries; - } - } - - private void addCrossing(int y, int x, int orientation) { - if (x < crossingMinX) { - crossingMinX = x; - } - if (x > crossingMaxX) { - crossingMaxX = x; - } - - int index = crossingIndices[y - crossingMinY]++; - x <<= 1; - crossings[index] = (orientation == 1) ? (x | 0x1) : x; - - ++numCrossings; - } - - private void iterateCrossings() { - if (!crossingsSorted) { - sortCrossings(); - crossingsSorted = true; - } - crossingY = crossingMinY - 1; - crossingRowOffset = -crossingMaxXEntries; - } - - private boolean hasMoreCrossingRows() { - if (++crossingY <= crossingMaxY) { - crossingRowOffset += crossingMaxXEntries; - int y = crossingY - crossingMinY; - crossingRowCount = crossingIndices[y] - y*crossingMaxXEntries; - crossingRowIndex = 0; - return true; - } else { - return false; - } + java.util.Arrays.fill(alphaRow, (byte)0); } } diff --git a/jdk/src/share/classes/sun/java2d/pisces/Stroker.java b/jdk/src/share/classes/sun/java2d/pisces/Stroker.java index e17283a3bdc..574c460fea9 100644 --- a/jdk/src/share/classes/sun/java2d/pisces/Stroker.java +++ b/jdk/src/share/classes/sun/java2d/pisces/Stroker.java @@ -25,7 +25,7 @@ package sun.java2d.pisces; -public class Stroker extends LineSink { +public class Stroker implements LineSink { private static final int MOVE_TO = 0; private static final int LINE_TO = 1; @@ -61,19 +61,15 @@ public class Stroker extends LineSink { */ public static final int CAP_SQUARE = 2; - LineSink output; + private final LineSink output; - int lineWidth; - int capStyle; - int joinStyle; - int miterLimit; + private final int capStyle; + private final int joinStyle; - Transform4 transform; - int m00, m01; - int m10, m11; + private final float m00, m01, m10, m11, det; - int lineWidth2; - long scaledLineWidth2; + private final float lineWidth2; + private final float scaledLineWidth2; // For any pen offset (pen_dx, pen_dy) that does not depend on // the line orientation, the pen should be transformed so that: @@ -88,143 +84,86 @@ public class Stroker extends LineSink { // // pen_dx'(r, theta) = r*(m00*cos(theta) + m01*sin(theta)) // pen_dy'(r, theta) = r*(m10*cos(theta) + m11*sin(theta)) - int numPenSegments; - int[] pen_dx; - int[] pen_dy; - boolean[] penIncluded; - int[] join; + private int numPenSegments; + private final float[] pen_dx; + private final float[] pen_dy; + private boolean[] penIncluded; + private final float[] join; - int[] offset = new int[2]; - int[] reverse = new int[100]; - int[] miter = new int[2]; - long miterLimitSq; + private final float[] offset = new float[2]; + private float[] reverse = new float[100]; + private final float[] miter = new float[2]; + private final float miterLimitSq; - int prev; - int rindex; - boolean started; - boolean lineToOrigin; - boolean joinToOrigin; + private int prev; + private int rindex; + private boolean started; + private boolean lineToOrigin; + private boolean joinToOrigin; - int sx0, sy0, sx1, sy1, x0, y0, x1, y1; - int mx0, my0, mx1, my1, omx, omy; - int lx0, ly0, lx1, ly1, lx0p, ly0p, px0, py0; + private float sx0, sy0, sx1, sy1, x0, y0, px0, py0; + private float mx0, my0, omx, omy; - double m00_2_m01_2; - double m10_2_m11_2; - double m00_m10_m01_m11; - - /** - * Empty constructor. setOutput and - * setParameters must be called prior to calling any - * other methods. - */ - public Stroker() {} + private float m00_2_m01_2; + private float m10_2_m11_2; + private float m00_m10_m01_m11; /** * Constructs a Stroker. * * @param output an output LineSink. - * @param lineWidth the desired line width in pixels, in S15.16 - * format. + * @param lineWidth the desired line width in pixels * @param capStyle the desired end cap style, one of * CAP_BUTT, CAP_ROUND or * CAP_SQUARE. * @param joinStyle the desired line join style, one of * JOIN_MITER, JOIN_ROUND or * JOIN_BEVEL. - * @param miterLimit the desired miter limit, in S15.16 format. + * @param miterLimit the desired miter limit * @param transform a Transform4 object indicating * the transform that has been previously applied to all incoming * coordinates. This is required in order to produce consistently * shaped end caps and joins. */ public Stroker(LineSink output, - int lineWidth, + float lineWidth, int capStyle, int joinStyle, - int miterLimit, - Transform4 transform) { - setOutput(output); - setParameters(lineWidth, capStyle, joinStyle, miterLimit, transform); - } - - /** - * Sets the output LineSink of this - * Stroker. - * - * @param output an output LineSink. - */ - public void setOutput(LineSink output) { + float miterLimit, + float m00, float m01, float m10, float m11) { this.output = output; - } - /** - * Sets the parameters of this Stroker. - * @param lineWidth the desired line width in pixels, in S15.16 - * format. - * @param capStyle the desired end cap style, one of - * CAP_BUTT, CAP_ROUND or - * CAP_SQUARE. - * @param joinStyle the desired line join style, one of - * JOIN_MITER, JOIN_ROUND or - * JOIN_BEVEL. - * @param miterLimit the desired miter limit, in S15.16 format. - * @param transform a Transform4 object indicating - * the transform that has been previously applied to all incoming - * coordinates. This is required in order to produce consistently - * shaped end caps and joins. - */ - public void setParameters(int lineWidth, - int capStyle, - int joinStyle, - int miterLimit, - Transform4 transform) { - this.lineWidth = lineWidth; - this.lineWidth2 = lineWidth >> 1; - this.scaledLineWidth2 = ((long)transform.m00*lineWidth2) >> 16; + this.lineWidth2 = lineWidth / 2; + this.scaledLineWidth2 = m00 * lineWidth2; this.capStyle = capStyle; this.joinStyle = joinStyle; - this.miterLimit = miterLimit; - this.transform = transform; - this.m00 = transform.m00; - this.m01 = transform.m01; - this.m10 = transform.m10; - this.m11 = transform.m11; + m00_2_m01_2 = m00*m00 + m01*m01; + m10_2_m11_2 = m10*m10 + m11*m11; + m00_m10_m01_m11 = m00*m10 + m01*m11; - this.m00_2_m01_2 = (double)m00*m00 + (double)m01*m01; - this.m10_2_m11_2 = (double)m10*m10 + (double)m11*m11; - this.m00_m10_m01_m11 = (double)m00*m10 + (double)m01*m11; + this.m00 = m00; + this.m01 = m01; + this.m10 = m10; + this.m11 = m11; + det = m00*m11 - m01*m10; - double dm00 = m00/65536.0; - double dm01 = m01/65536.0; - double dm10 = m10/65536.0; - double dm11 = m11/65536.0; - double determinant = dm00*dm11 - dm01*dm10; + float limit = miterLimit * lineWidth2 * det; + this.miterLimitSq = limit*limit; - if (joinStyle == JOIN_MITER) { - double limit = - (miterLimit/65536.0)*(lineWidth2/65536.0)*determinant; - double limitSq = limit*limit; - this.miterLimitSq = (long)(limitSq*65536.0*65536.0); - } - - this.numPenSegments = (int)(3.14159f*lineWidth/65536.0f); - if (pen_dx == null || pen_dx.length < numPenSegments) { - this.pen_dx = new int[numPenSegments]; - this.pen_dy = new int[numPenSegments]; - this.penIncluded = new boolean[numPenSegments]; - this.join = new int[2*numPenSegments]; - } + this.numPenSegments = (int)(3.14159f * lineWidth); + this.pen_dx = new float[numPenSegments]; + this.pen_dy = new float[numPenSegments]; + this.penIncluded = new boolean[numPenSegments]; + this.join = new float[2*numPenSegments]; for (int i = 0; i < numPenSegments; i++) { - double r = lineWidth/2.0; - double theta = (double)i*2.0*Math.PI/numPenSegments; + double theta = (i * 2.0 * Math.PI)/numPenSegments; double cos = Math.cos(theta); double sin = Math.sin(theta); - pen_dx[i] = (int)(r*(dm00*cos + dm01*sin)); - pen_dy[i] = (int)(r*(dm10*cos + dm11*sin)); + pen_dx[i] = (float)(lineWidth2 * (m00*cos + m01*sin)); + pen_dy[i] = (float)(lineWidth2 * (m10*cos + m11*sin)); } prev = CLOSE; @@ -233,32 +172,31 @@ public class Stroker extends LineSink { lineToOrigin = false; } - private void computeOffset(int x0, int y0, int x1, int y1, int[] m) { - long lx = (long)x1 - (long)x0; - long ly = (long)y1 - (long)y0; + private void computeOffset(float x0, float y0, + float x1, float y1, float[] m) { + float lx = x1 - x0; + float ly = y1 - y0; - int dx, dy; + float dx, dy; if (m00 > 0 && m00 == m11 && m01 == 0 & m10 == 0) { - long ilen = PiscesMath.hypot(lx, ly); + float ilen = (float)Math.hypot(lx, ly); if (ilen == 0) { dx = dy = 0; } else { - dx = (int)( (ly*scaledLineWidth2)/ilen); - dy = (int)(-(lx*scaledLineWidth2)/ilen); + dx = (ly * scaledLineWidth2)/ilen; + dy = -(lx * scaledLineWidth2)/ilen; } } else { - double dlx = x1 - x0; - double dly = y1 - y0; - double det = (double)m00*m11 - (double)m01*m10; int sdet = (det > 0) ? 1 : -1; - double a = dly*m00 - dlx*m10; - double b = dly*m01 - dlx*m11; - double dh = PiscesMath.hypot(a, b); - double div = sdet*lineWidth2/(65536.0*dh); - double ddx = dly*m00_2_m01_2 - dlx*m00_m10_m01_m11; - double ddy = dly*m00_m10_m01_m11 - dlx*m10_2_m11_2; - dx = (int)(ddx*div); - dy = (int)(ddy*div); + float a = ly * m00 - lx * m10; + float b = ly * m01 - lx * m11; + float dh = (float)Math.hypot(a, b); + float div = sdet * lineWidth2/dh; + + float ddx = ly * m00_2_m01_2 - lx * m00_m10_m01_m11; + float ddy = ly * m00_m10_m01_m11 - lx * m10_2_m11_2; + dx = ddx*div; + dy = ddy*div; } m[0] = dx; @@ -267,58 +205,43 @@ public class Stroker extends LineSink { private void ensureCapacity(int newrindex) { if (reverse.length < newrindex) { - int[] tmp = new int[Math.max(newrindex, 6*reverse.length/5)]; - System.arraycopy(reverse, 0, tmp, 0, rindex); - this.reverse = tmp; + reverse = java.util.Arrays.copyOf(reverse, 6*reverse.length/5); } } - private boolean isCCW(int x0, int y0, - int x1, int y1, - int x2, int y2) { - int dx0 = x1 - x0; - int dy0 = y1 - y0; - int dx1 = x2 - x1; - int dy1 = y2 - y1; - return (long)dx0*dy1 < (long)dy0*dx1; + private boolean isCCW(float x0, float y0, + float x1, float y1, + float x2, float y2) { + return (x1 - x0) * (y2 - y1) < (y1 - y0) * (x2 - x1); } - private boolean side(int x, int y, int x0, int y0, int x1, int y1) { - long lx = x; - long ly = y; - long lx0 = x0; - long ly0 = y0; - long lx1 = x1; - long ly1 = y1; - - return (ly0 - ly1)*lx + (lx1 - lx0)*ly + (lx0*ly1 - lx1*ly0) > 0; + private boolean side(float x, float y, + float x0, float y0, + float x1, float y1) { + return (y0 - y1)*x + (x1 - x0)*y + (x0*y1 - x1*y0) > 0; } - private int computeRoundJoin(int cx, int cy, - int xa, int ya, - int xb, int yb, + private int computeRoundJoin(float cx, float cy, + float xa, float ya, + float xb, float yb, int side, boolean flip, - int[] join) { - int px, py; + float[] join) { + float px, py; int ncoords = 0; boolean centerSide; if (side == 0) { centerSide = side(cx, cy, xa, ya, xb, yb); } else { - centerSide = (side == 1) ? true : false; + centerSide = (side == 1); } for (int i = 0; i < numPenSegments; i++) { px = cx + pen_dx[i]; py = cy + pen_dy[i]; boolean penSide = side(px, py, xa, ya, xb, yb); - if (penSide != centerSide) { - penIncluded[i] = true; - } else { - penIncluded[i] = false; - } + penIncluded[i] = (penSide != centerSide); } int start = -1, end = -1; @@ -338,10 +261,10 @@ public class Stroker extends LineSink { } if (start != -1 && end != -1) { - long dxa = cx + pen_dx[start] - xa; - long dya = cy + pen_dy[start] - ya; - long dxb = cx + pen_dx[start] - xb; - long dyb = cy + pen_dy[start] - yb; + float dxa = cx + pen_dx[start] - xa; + float dya = cy + pen_dy[start] - ya; + float dxb = cx + pen_dx[start] - xb; + float dyb = cy + pen_dy[start] - yb; boolean rev = (dxa*dxa + dya*dya > dxb*dxb + dyb*dyb); int i = rev ? end : start; @@ -362,22 +285,25 @@ public class Stroker extends LineSink { return ncoords/2; } - private static final long ROUND_JOIN_THRESHOLD = 1000L; - private static final long ROUND_JOIN_INTERNAL_THRESHOLD = 1000000000L; + // pisces used to use fixed point arithmetic with 16 decimal digits. I + // didn't want to change the values of the constants below when I converted + // it to floating point, so that's why the divisions by 2^16 are there. + private static final float ROUND_JOIN_THRESHOLD = 1000/65536f; + private static final float ROUND_JOIN_INTERNAL_THRESHOLD = 1000000000/65536f; - private void drawRoundJoin(int x, int y, - int omx, int omy, int mx, int my, + private void drawRoundJoin(float x, float y, + float omx, float omy, float mx, float my, int side, boolean flip, boolean rev, - long threshold) { + float threshold) { if ((omx == 0 && omy == 0) || (mx == 0 && my == 0)) { return; } - long domx = (long)omx - mx; - long domy = (long)omy - my; - long len = domx*domx + domy*domy; + float domx = omx - mx; + float domy = omy - my; + float len = domx*domx + domy*domy; if (len < threshold) { return; } @@ -389,10 +315,10 @@ public class Stroker extends LineSink { my = -my; } - int bx0 = x + omx; - int by0 = y + omy; - int bx1 = x + mx; - int by1 = y + my; + float bx0 = x + omx; + float by0 = y + omy; + float bx1 = x + mx; + float by1 = y + my; int npoints = computeRoundJoin(x, y, bx0, by0, bx1, by1, side, flip, @@ -404,40 +330,30 @@ public class Stroker extends LineSink { // Return the intersection point of the lines (ix0, iy0) -> (ix1, iy1) // and (ix0p, iy0p) -> (ix1p, iy1p) in m[0] and m[1] - private void computeMiter(int ix0, int iy0, int ix1, int iy1, - int ix0p, int iy0p, int ix1p, int iy1p, - int[] m) { - long x0 = ix0; - long y0 = iy0; - long x1 = ix1; - long y1 = iy1; + private void computeMiter(float x0, float y0, float x1, float y1, + float x0p, float y0p, float x1p, float y1p, + float[] m) { + float x10 = x1 - x0; + float y10 = y1 - y0; + float x10p = x1p - x0p; + float y10p = y1p - y0p; - long x0p = ix0p; - long y0p = iy0p; - long x1p = ix1p; - long y1p = iy1p; - - long x10 = x1 - x0; - long y10 = y1 - y0; - long x10p = x1p - x0p; - long y10p = y1p - y0p; - - long den = (x10*y10p - x10p*y10) >> 16; + float den = x10*y10p - x10p*y10; if (den == 0) { - m[0] = ix0; - m[1] = iy0; + m[0] = x0; + m[1] = y0; return; } - long t = (x1p*(y0 - y0p) - x0*y10p + x0p*(y1p - y0)) >> 16; - m[0] = (int)(x0 + (t*x10)/den); - m[1] = (int)(y0 + (t*y10)/den); + float t = x1p*(y0 - y0p) - x0*y10p + x0p*(y1p - y0); + m[0] = x0 + (t*x10)/den; + m[1] = y0 + (t*y10)/den; } - private void drawMiter(int px0, int py0, - int x0, int y0, - int x1, int y1, - int omx, int omy, int mx, int my, + private void drawMiter(float px0, float py0, + float x0, float y0, + float x1, float y1, + float omx, float omy, float mx, float my, boolean rev) { if (mx == omx && my == omy) { return; @@ -461,11 +377,11 @@ public class Stroker extends LineSink { miter); // Compute miter length in untransformed coordinates - long dx = (long)miter[0] - x0; - long dy = (long)miter[1] - y0; - long a = (dy*m00 - dx*m10) >> 16; - long b = (dy*m01 - dx*m11) >> 16; - long lenSq = a*a + b*b; + float dx = miter[0] - x0; + float dy = miter[1] - y0; + float a = dy*m00 - dx*m10; + float b = dy*m01 - dx*m11; + float lenSq = a*a + b*b; if (lenSq < miterLimitSq) { emitLineTo(miter[0], miter[1], rev); @@ -473,7 +389,7 @@ public class Stroker extends LineSink { } - public void moveTo(int x0, int y0) { + public void moveTo(float x0, float y0) { // System.out.println("Stroker.moveTo(" + x0/65536.0 + ", " + y0/65536.0 + ")"); if (lineToOrigin) { @@ -501,7 +417,7 @@ public class Stroker extends LineSink { this.joinSegment = true; } - public void lineTo(int x1, int y1) { + public void lineTo(float x1, float y1) { // System.out.println("Stroker.lineTo(" + x1/65536.0 + ", " + y1/65536.0 + ")"); if (lineToOrigin) { @@ -526,10 +442,10 @@ public class Stroker extends LineSink { joinSegment = false; } - private void lineToImpl(int x1, int y1, boolean joinSegment) { + private void lineToImpl(float x1, float y1, boolean joinSegment) { computeOffset(x0, y0, x1, y1, offset); - int mx = offset[0]; - int my = offset[1]; + float mx = offset[0]; + float my = offset[1]; if (!started) { emitMoveTo(x0 + mx, y0 + my); @@ -567,10 +483,6 @@ public class Stroker extends LineSink { emitLineTo(x0 - mx, y0 - my, true); emitLineTo(x1 - mx, y1 - my, true); - lx0 = x1 + mx; ly0 = y1 + my; - lx0p = x1 - mx; ly0p = y1 - my; - lx1 = x1; ly1 = y1; - this.omx = mx; this.omy = my; this.px0 = x0; @@ -594,8 +506,8 @@ public class Stroker extends LineSink { } computeOffset(x0, y0, sx0, sy0, offset); - int mx = offset[0]; - int my = offset[1]; + float mx = offset[0]; + float my = offset[1]; // Draw penultimate join boolean ccw = isCCW(px0, py0, x0, y0, sx0, sy0); @@ -678,12 +590,10 @@ public class Stroker extends LineSink { this.prev = MOVE_TO; } - long lineLength(long ldx, long ldy) { - long ldet = ((long)m00*m11 - (long)m01*m10) >> 16; - long la = ((long)ldy*m00 - (long)ldx*m10)/ldet; - long lb = ((long)ldy*m01 - (long)ldx*m11)/ldet; - long llen = (int)PiscesMath.hypot(la, lb); - return llen; + double userSpaceLineLength(double dx, double dy) { + double a = (dy*m00 - dx*m10)/det; + double b = (dy*m01 - dx*m11)/det; + return Math.hypot(a, b); } private void finish() { @@ -692,13 +602,13 @@ public class Stroker extends LineSink { omx, omy, -omx, -omy, 1, false, false, ROUND_JOIN_THRESHOLD); } else if (capStyle == CAP_SQUARE) { - long ldx = (long)(px0 - x0); - long ldy = (long)(py0 - y0); - long llen = lineLength(ldx, ldy); - long s = (long)lineWidth2*65536/llen; + float dx = px0 - x0; + float dy = py0 - y0; + float len = (float)userSpaceLineLength(dx, dy); + float s = lineWidth2/len; - int capx = x0 - (int)(ldx*s >> 16); - int capy = y0 - (int)(ldy*s >> 16); + float capx = x0 - dx*s; + float capy = y0 - dy*s; emitLineTo(capx + omx, capy + omy); emitLineTo(capx - omx, capy - omy); @@ -714,13 +624,13 @@ public class Stroker extends LineSink { -mx0, -my0, mx0, my0, 1, false, false, ROUND_JOIN_THRESHOLD); } else if (capStyle == CAP_SQUARE) { - long ldx = (long)(sx1 - sx0); - long ldy = (long)(sy1 - sy0); - long llen = lineLength(ldx, ldy); - long s = (long)lineWidth2*65536/llen; + float dx = sx1 - sx0; + float dy = sy1 - sy0; + float len = (float)userSpaceLineLength(dx, dy); + float s = lineWidth2/len; - int capx = sx0 - (int)(ldx*s >> 16); - int capy = sy0 - (int)(ldy*s >> 16); + float capx = sx0 - dx*s; + float capy = sy0 - dy*s; emitLineTo(capx - mx0, capy - my0); emitLineTo(capx + mx0, capy + my0); @@ -730,17 +640,17 @@ public class Stroker extends LineSink { this.joinSegment = false; } - private void emitMoveTo(int x0, int y0) { + private void emitMoveTo(float x0, float y0) { // System.out.println("Stroker.emitMoveTo(" + x0/65536.0 + ", " + y0/65536.0 + ")"); output.moveTo(x0, y0); } - private void emitLineTo(int x1, int y1) { + private void emitLineTo(float x1, float y1) { // System.out.println("Stroker.emitLineTo(" + x0/65536.0 + ", " + y0/65536.0 + ")"); output.lineTo(x1, y1); } - private void emitLineTo(int x1, int y1, boolean rev) { + private void emitLineTo(float x1, float y1, boolean rev) { if (rev) { ensureCapacity(rindex + 2); reverse[rindex++] = x1; @@ -755,3 +665,4 @@ public class Stroker extends LineSink { output.close(); } } + diff --git a/jdk/src/share/classes/sun/java2d/pisces/Transform4.java b/jdk/src/share/classes/sun/java2d/pisces/Transform4.java deleted file mode 100644 index bd79b762dba..00000000000 --- a/jdk/src/share/classes/sun/java2d/pisces/Transform4.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2007, 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 sun.java2d.pisces; - -public class Transform4 { - - public int m00, m01, m10, m11; -// double det; // det*65536 - - public Transform4() { - this(1 << 16, 0, 0, 1 << 16); - } - - public Transform4(int m00, int m01, - int m10, int m11) { - this.m00 = m00; - this.m01 = m01; - this.m10 = m10; - this.m11 = m11; - -// this.det = (double)m00*m11 - (double)m01*m10; - } - -// public Transform4 createInverse() { -// double dm00 = m00/65536.0; -// double dm01 = m01/65536.0; -// double dm10 = m10/65536.0; -// double dm11 = m11/65536.0; - -// double invdet = 65536.0/(dm00*dm11 - dm01*dm10); - -// int im00 = (int)( dm11*invdet); -// int im01 = (int)(-dm01*invdet); -// int im10 = (int)(-dm10*invdet); -// int im11 = (int)( dm00*invdet); - -// return new Transform4(im00, im01, im10, im11); -// } - -// public void transform(int[] point) { -// } - -// /** -// * Returns the length of the line segment obtained by inverse -// * transforming the points (x0, y0) and (x1, -// * y1). -// */ -// public int getTransformedLength(int x0, int x1, int y0, int y1) { -// int lx = x1 - x0; -// int ly = y1 - y0; - -// double a = (double)m00*ly - (double)m10*lx; -// double b = (double)m01*ly - (double)m11*lx; -// double len = PiscesMath.sqrt((a*a + b*b)/(det*det)); -// return (int)(len*65536.0); -// } - -// public int getType() { -// } - -} From 46e4eafc0cfbf646a0c3c6a0d6fa2b393b8d73a5 Mon Sep 17 00:00:00 2001 From: Xueming Shen Date: Tue, 10 Aug 2010 13:15:40 -0700 Subject: [PATCH 021/100] 6923794: About 40 JCK test case fail with AssertionError if -esa option is specified Removed the assert Reviewed-by: alanb --- jdk/src/solaris/classes/java/io/UnixFileSystem.java | 1 - jdk/src/windows/classes/java/io/Win32FileSystem.java | 1 - 2 files changed, 2 deletions(-) diff --git a/jdk/src/solaris/classes/java/io/UnixFileSystem.java b/jdk/src/solaris/classes/java/io/UnixFileSystem.java index df070bd0039..2c0feb2d5ef 100644 --- a/jdk/src/solaris/classes/java/io/UnixFileSystem.java +++ b/jdk/src/solaris/classes/java/io/UnixFileSystem.java @@ -187,7 +187,6 @@ class UnixFileSystem extends FileSystem { } } } - assert canonicalize0(path).equals(res) || path.startsWith(javaHome); return res; } } diff --git a/jdk/src/windows/classes/java/io/Win32FileSystem.java b/jdk/src/windows/classes/java/io/Win32FileSystem.java index 510b84abf62..490c411b072 100644 --- a/jdk/src/windows/classes/java/io/Win32FileSystem.java +++ b/jdk/src/windows/classes/java/io/Win32FileSystem.java @@ -424,7 +424,6 @@ class Win32FileSystem extends FileSystem { } } } - assert canonicalize0(path).equalsIgnoreCase(res); return res; } } From 7ee29eeb6a358088bb2821d754c6684db66544f2 Mon Sep 17 00:00:00 2001 From: "Y. Srinivas Ramakrishna" Date: Tue, 10 Aug 2010 14:53:35 -0700 Subject: [PATCH 022/100] 6973570: OrderAccess::storestore() scales poorly on multi-socket x64 and sparc: cache-line ping-ponging Volatile store to static variable removed in favour of a volatile store to stack to avoid excessive cache coherency traffic; verified that the volatile store is not elided by any of our current compilers. Reviewed-by: dholmes, dice, jcoomes, kvn --- .../vm/orderAccess_linux_sparc.inline.hpp | 6 +++--- .../linux_x86/vm/orderAccess_linux_x86.inline.hpp | 12 +++++++----- .../vm/orderAccess_solaris_sparc.inline.hpp | 10 ++++++---- .../vm/orderAccess_solaris_x86.inline.hpp | 12 +++++++----- .../vm/orderAccess_windows_x86.inline.hpp | 4 ++-- hotspot/src/share/vm/runtime/orderAccess.cpp | 4 +--- hotspot/src/share/vm/runtime/orderAccess.hpp | 12 +++++++----- 7 files changed, 33 insertions(+), 27 deletions(-) diff --git a/hotspot/src/os_cpu/linux_sparc/vm/orderAccess_linux_sparc.inline.hpp b/hotspot/src/os_cpu/linux_sparc/vm/orderAccess_linux_sparc.inline.hpp index 4ff88cfdbbe..2770baaabef 100644 --- a/hotspot/src/os_cpu/linux_sparc/vm/orderAccess_linux_sparc.inline.hpp +++ b/hotspot/src/os_cpu/linux_sparc/vm/orderAccess_linux_sparc.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -36,8 +36,8 @@ inline void OrderAccess::acquire() { } inline void OrderAccess::release() { - jint* dummy = (jint*)&dummy; - __asm__ volatile("stw %%g0, [%0]" : : "r" (dummy) : "memory"); + jint* local_dummy = (jint*)&local_dummy; + __asm__ volatile("stw %%g0, [%0]" : : "r" (local_dummy) : "memory"); } inline void OrderAccess::fence() { diff --git a/hotspot/src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp b/hotspot/src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp index a4f6c17d7ae..d487ece69a3 100644 --- a/hotspot/src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp +++ b/hotspot/src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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,16 +30,18 @@ inline void OrderAccess::loadstore() { acquire(); } inline void OrderAccess::storeload() { fence(); } inline void OrderAccess::acquire() { - volatile intptr_t dummy; + volatile intptr_t local_dummy; #ifdef AMD64 - __asm__ volatile ("movq 0(%%rsp), %0" : "=r" (dummy) : : "memory"); + __asm__ volatile ("movq 0(%%rsp), %0" : "=r" (local_dummy) : : "memory"); #else - __asm__ volatile ("movl 0(%%esp),%0" : "=r" (dummy) : : "memory"); + __asm__ volatile ("movl 0(%%esp),%0" : "=r" (local_dummy) : : "memory"); #endif // AMD64 } inline void OrderAccess::release() { - dummy = 0; + // Avoid hitting the same cache-line from + // different threads. + volatile jint local_dummy = 0; } inline void OrderAccess::fence() { diff --git a/hotspot/src/os_cpu/solaris_sparc/vm/orderAccess_solaris_sparc.inline.hpp b/hotspot/src/os_cpu/solaris_sparc/vm/orderAccess_solaris_sparc.inline.hpp index 2646977ce8f..6075e010687 100644 --- a/hotspot/src/os_cpu/solaris_sparc/vm/orderAccess_solaris_sparc.inline.hpp +++ b/hotspot/src/os_cpu/solaris_sparc/vm/orderAccess_solaris_sparc.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,8 +42,8 @@ inline void OrderAccess::acquire() { } inline void OrderAccess::release() { - jint* dummy = (jint*)&dummy; - __asm__ volatile("stw %%g0, [%0]" : : "r" (dummy) : "memory"); + jint* local_dummy = (jint*)&local_dummy; + __asm__ volatile("stw %%g0, [%0]" : : "r" (local_dummy) : "memory"); } inline void OrderAccess::fence() { @@ -57,7 +57,9 @@ inline void OrderAccess::acquire() { } inline void OrderAccess::release() { - dummy = 0; + // Avoid hitting the same cache-line from + // different threads. + volatile jint local_dummy = 0; } inline void OrderAccess::fence() { diff --git a/hotspot/src/os_cpu/solaris_x86/vm/orderAccess_solaris_x86.inline.hpp b/hotspot/src/os_cpu/solaris_x86/vm/orderAccess_solaris_x86.inline.hpp index 7165cde66d0..84c1ce9238d 100644 --- a/hotspot/src/os_cpu/solaris_x86/vm/orderAccess_solaris_x86.inline.hpp +++ b/hotspot/src/os_cpu/solaris_x86/vm/orderAccess_solaris_x86.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -40,7 +40,9 @@ inline void OrderAccess::acquire() { } inline void OrderAccess::release() { - dummy = 0; + // Avoid hitting the same cache-line from + // different threads. + volatile jint local_dummy = 0; } inline void OrderAccess::fence() { @@ -53,11 +55,11 @@ inline void OrderAccess::fence() { extern "C" { inline void _OrderAccess_acquire() { - volatile intptr_t dummy; + volatile intptr_t local_dummy; #ifdef AMD64 - __asm__ volatile ("movq 0(%%rsp), %0" : "=r" (dummy) : : "memory"); + __asm__ volatile ("movq 0(%%rsp), %0" : "=r" (local_dummy) : : "memory"); #else - __asm__ volatile ("movl 0(%%esp),%0" : "=r" (dummy) : : "memory"); + __asm__ volatile ("movl 0(%%esp),%0" : "=r" (local_dummy) : : "memory"); #endif // AMD64 } inline void _OrderAccess_fence() { diff --git a/hotspot/src/os_cpu/windows_x86/vm/orderAccess_windows_x86.inline.hpp b/hotspot/src/os_cpu/windows_x86/vm/orderAccess_windows_x86.inline.hpp index 4a5ac91b18d..1ccddc4d397 100644 --- a/hotspot/src/os_cpu/windows_x86/vm/orderAccess_windows_x86.inline.hpp +++ b/hotspot/src/os_cpu/windows_x86/vm/orderAccess_windows_x86.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -41,7 +41,7 @@ inline void OrderAccess::acquire() { inline void OrderAccess::release() { // A volatile store has release semantics. - dummy = 0; + volatile jint local_dummy = 0; } inline void OrderAccess::fence() { diff --git a/hotspot/src/share/vm/runtime/orderAccess.cpp b/hotspot/src/share/vm/runtime/orderAccess.cpp index b61c481607e..12124f964ba 100644 --- a/hotspot/src/share/vm/runtime/orderAccess.cpp +++ b/hotspot/src/share/vm/runtime/orderAccess.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,6 @@ # include "incls/_precompiled.incl" # include "incls/_orderAccess.cpp.incl" -volatile intptr_t OrderAccess::dummy = 0; - void OrderAccess::StubRoutines_fence() { // Use a stub if it exists. It may not exist during bootstrap so do // nothing in that case but assert if no fence code exists after threads have been created diff --git a/hotspot/src/share/vm/runtime/orderAccess.hpp b/hotspot/src/share/vm/runtime/orderAccess.hpp index 42c9227f5a2..28b049a43fc 100644 --- a/hotspot/src/share/vm/runtime/orderAccess.hpp +++ b/hotspot/src/share/vm/runtime/orderAccess.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -166,6 +166,12 @@ // and release must include a sequence point, usually via a volatile memory // access. Other ways to guarantee a sequence point are, e.g., use of // indirect calls and linux's __asm__ volatile. +// Note: as of 6973570, we have replaced the originally static "dummy" field +// (see above) by a volatile store to the stack. All of the versions of the +// compilers that we currently use (SunStudio, gcc and VC++) respect the +// semantics of volatile here. If you build HotSpot using other +// compilers, you may need to verify that no compiler reordering occurs +// across the sequence point respresented by the volatile access. // // // os::is_MP Considered Redundant @@ -297,10 +303,6 @@ class OrderAccess : AllStatic { static void release_store_ptr_fence(volatile intptr_t* p, intptr_t v); static void release_store_ptr_fence(volatile void* p, void* v); - // In order to force a memory access, implementations may - // need a volatile externally visible dummy variable. - static volatile intptr_t dummy; - private: // This is a helper that invokes the StubRoutines::fence_entry() // routine if it exists, It should only be used by platforms that From a3c07597f250e90c84c8dd77ba61d622c4bfed9e Mon Sep 17 00:00:00 2001 From: Denis Lila Date: Wed, 11 Aug 2010 10:05:56 -0400 Subject: [PATCH 023/100] 6976265: No STROKE_CONTROL Implemented it in sun.java2d.pisces by adding a PathIterator. Reviewed-by: flar --- .../java2d/pisces/PiscesRenderingEngine.java | 150 +++++++++++++++++- 1 file changed, 142 insertions(+), 8 deletions(-) diff --git a/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java b/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java index 23ef19ee31f..ee2b35e6809 100644 --- a/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java +++ b/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java @@ -27,6 +27,7 @@ package sun.java2d.pisces; import java.awt.Shape; import java.awt.BasicStroke; +import java.awt.geom.FlatteningPathIterator; import java.awt.geom.Path2D; import java.awt.geom.AffineTransform; import java.awt.geom.PathIterator; @@ -39,6 +40,8 @@ import sun.java2d.pipe.AATileGenerator; public class PiscesRenderingEngine extends RenderingEngine { public static double defaultFlat = 0.1; + private static enum NormMode {OFF, ON_NO_AA, ON_WITH_AA} + /** * Create a widened path as specified by the parameters. *

@@ -69,7 +72,7 @@ public class PiscesRenderingEngine extends RenderingEngine { strokeTo(src, null, width, - false, + NormMode.OFF, caps, join, miterlimit, @@ -127,7 +130,10 @@ public class PiscesRenderingEngine extends RenderingEngine { boolean antialias, final PathConsumer2D consumer) { - strokeTo(src, at, bs, thin, normalize, antialias, + NormMode norm = (normalize) ? + ((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA) + : NormMode.OFF; + strokeTo(src, at, bs, thin, norm, antialias, new LineSink() { public void moveTo(float x0, float y0) { consumer.moveTo(x0, y0); @@ -149,7 +155,7 @@ public class PiscesRenderingEngine extends RenderingEngine { AffineTransform at, BasicStroke bs, boolean thin, - boolean normalize, + NormMode normalize, boolean antialias, LineSink lsink) { @@ -244,7 +250,7 @@ public class PiscesRenderingEngine extends RenderingEngine { void strokeTo(Shape src, AffineTransform at, float width, - boolean normalize, + NormMode normalize, int caps, int join, float miterlimit, @@ -263,11 +269,130 @@ public class PiscesRenderingEngine extends RenderingEngine { if (dashes != null) { lsink = new Dasher(lsink, dashes, dashphase, a00, a01, a10, a11); } - - PathIterator pi = src.getPathIterator(at, defaultFlat); + PathIterator pi; + if (normalize != NormMode.OFF) { + pi = new FlatteningPathIterator( + new NormalizingPathIterator(src.getPathIterator(at), normalize), + defaultFlat); + } else { + pi = src.getPathIterator(at, defaultFlat); + } pathTo(pi, lsink); } + private static class NormalizingPathIterator implements PathIterator { + + private final PathIterator src; + + // the adjustment applied to the current position. + private float curx_adjust, cury_adjust; + // the adjustment applied to the last moveTo position. + private float movx_adjust, movy_adjust; + + // constants used in normalization computations + private final float lval, rval; + + NormalizingPathIterator(PathIterator src, NormMode mode) { + this.src = src; + switch (mode) { + case ON_NO_AA: + // round to nearest (0.25, 0.25) pixel + lval = rval = 0.25f; + break; + case ON_WITH_AA: + // round to nearest pixel center + lval = 0f; + rval = 0.5f; + break; + case OFF: + throw new InternalError("A NormalizingPathIterator should " + + "not be created if no normalization is being done"); + default: + throw new InternalError("Unrecognized normalization mode"); + } + } + + public int currentSegment(float[] coords) { + int type = src.currentSegment(coords); + + int lastCoord; + switch(type) { + case PathIterator.SEG_CUBICTO: + lastCoord = 4; + break; + case PathIterator.SEG_QUADTO: + lastCoord = 2; + break; + case PathIterator.SEG_LINETO: + case PathIterator.SEG_MOVETO: + lastCoord = 0; + break; + case PathIterator.SEG_CLOSE: + // we don't want to deal with this case later. We just exit now + curx_adjust = movx_adjust; + cury_adjust = movy_adjust; + return type; + default: + throw new InternalError("Unrecognized curve type"); + } + + // normalize endpoint + float x_adjust = (float)Math.floor(coords[lastCoord] + lval) + rval - + coords[lastCoord]; + float y_adjust = (float)Math.floor(coords[lastCoord+1] + lval) + rval - + coords[lastCoord + 1]; + + coords[lastCoord ] += x_adjust; + coords[lastCoord + 1] += y_adjust; + + // now that the end points are done, normalize the control points + switch(type) { + case PathIterator.SEG_CUBICTO: + coords[0] += curx_adjust; + coords[1] += cury_adjust; + coords[2] += x_adjust; + coords[3] += y_adjust; + break; + case PathIterator.SEG_QUADTO: + coords[0] += (curx_adjust + x_adjust) / 2; + coords[1] += (cury_adjust + y_adjust) / 2; + break; + case PathIterator.SEG_LINETO: + break; + case PathIterator.SEG_MOVETO: + movx_adjust = x_adjust; + movy_adjust = y_adjust; + break; + case PathIterator.SEG_CLOSE: + throw new InternalError("This should be handled earlier."); + } + curx_adjust = x_adjust; + cury_adjust = y_adjust; + return type; + } + + public int currentSegment(double[] coords) { + float[] tmp = new float[6]; + int type = this.currentSegment(tmp); + for (int i = 0; i < 6; i++) { + coords[i] = (float) tmp[i]; + } + return type; + } + + public int getWindingRule() { + return src.getWindingRule(); + } + + public boolean isDone() { + return src.isDone(); + } + + public void next() { + src.next(); + } + } + void pathTo(PathIterator pi, LineSink lsink) { float coords[] = new float[2]; while (!pi.isDone()) { @@ -348,8 +473,16 @@ public class PiscesRenderingEngine extends RenderingEngine { { PiscesCache pc = PiscesCache.createInstance(); Renderer r; + NormMode norm = (normalize) ? NormMode.ON_WITH_AA : NormMode.OFF; if (bs == null) { - PathIterator pi = s.getPathIterator(at, defaultFlat); + PathIterator pi; + if (normalize) { + pi = new FlatteningPathIterator( + new NormalizingPathIterator(s.getPathIterator(at), norm), + defaultFlat); + } else { + pi = s.getPathIterator(at, defaultFlat); + } r = new Renderer(3, 3, clip.getLoX(), clip.getLoY(), clip.getWidth(), clip.getHeight(), @@ -360,7 +493,7 @@ public class PiscesRenderingEngine extends RenderingEngine { clip.getLoX(), clip.getLoY(), clip.getWidth(), clip.getHeight(), PathIterator.WIND_NON_ZERO, pc); - strokeTo(s, at, bs, thin, normalize, true, r); + strokeTo(s, at, bs, thin, norm, true, r); } r.endRendering(); PiscesTileGenerator ptg = new PiscesTileGenerator(pc, r.MAX_AA_ALPHA); @@ -391,3 +524,4 @@ public class PiscesRenderingEngine extends RenderingEngine { } } } + From bb7d4b2b35cefcf9d7129c5f6cf6f6ba156b7b79 Mon Sep 17 00:00:00 2001 From: John Coomes Date: Wed, 11 Aug 2010 13:12:28 -0700 Subject: [PATCH 024/100] 6976378: ParNew: stats are printed unconditionally in debug builds Reviewed-by: tonyp --- .../share/vm/gc_implementation/parNew/parNewGeneration.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp index 45683b17572..e31e2854664 100644 --- a/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp +++ b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp @@ -970,8 +970,10 @@ void ParNewGeneration::collect(bool full, gch->print_heap_change(gch_prev_used); } - TASKQUEUE_STATS_ONLY(thread_state_set.print_termination_stats()); - TASKQUEUE_STATS_ONLY(thread_state_set.print_taskqueue_stats()); + if (PrintGCDetails && ParallelGCVerbose) { + TASKQUEUE_STATS_ONLY(thread_state_set.print_termination_stats()); + TASKQUEUE_STATS_ONLY(thread_state_set.print_taskqueue_stats()); + } if (UseAdaptiveSizePolicy) { size_policy->minor_collection_end(gch->gc_cause()); From 49d555edeeb77d020d4637a9dd088478229576f4 Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Thu, 12 Aug 2010 19:53:25 +0100 Subject: [PATCH 025/100] 6971825: (so) improve scatter/gather implementation Reviewed-by: chegar, sherman --- .../sun/nio/ch/DatagramChannelImpl.java | 38 +-- .../classes/sun/nio/ch/FileChannelImpl.java | 35 +- jdk/src/share/classes/sun/nio/ch/IOUtil.java | 302 ++++++++---------- .../classes/sun/nio/ch/IOVecWrapper.java | 98 +++++- .../classes/sun/nio/ch/SocketChannelImpl.java | 38 +-- jdk/src/share/classes/sun/nio/ch/Util.java | 207 +++++++++--- 6 files changed, 411 insertions(+), 307 deletions(-) diff --git a/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java b/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java index 41b37c9e960..1d7d6758d8e 100644 --- a/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java +++ b/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java @@ -536,9 +536,11 @@ class DatagramChannelImpl } } - private long read0(ByteBuffer[] bufs) throws IOException { - if (bufs == null) - throw new NullPointerException(); + public long read(ByteBuffer[] dsts, int offset, int length) + throws IOException + { + if ((offset < 0) || (length < 0) || (offset > dsts.length - length)) + throw new IndexOutOfBoundsException(); synchronized (readLock) { synchronized (stateLock) { ensureOpen(); @@ -552,7 +554,7 @@ class DatagramChannelImpl return 0; readerThread = NativeThread.current(); do { - n = IOUtil.read(fd, bufs, nd); + n = IOUtil.read(fd, dsts, offset, length, nd); } while ((n == IOStatus.INTERRUPTED) && isOpen()); return IOStatus.normalize(n); } finally { @@ -563,15 +565,6 @@ class DatagramChannelImpl } } - public long read(ByteBuffer[] dsts, int offset, int length) - throws IOException - { - if ((offset < 0) || (length < 0) || (offset > dsts.length - length)) - throw new IndexOutOfBoundsException(); - // ## Fix IOUtil.write so that we can avoid this array copy - return read0(Util.subsequence(dsts, offset, length)); - } - public int write(ByteBuffer buf) throws IOException { if (buf == null) throw new NullPointerException(); @@ -599,9 +592,11 @@ class DatagramChannelImpl } } - private long write0(ByteBuffer[] bufs) throws IOException { - if (bufs == null) - throw new NullPointerException(); + public long write(ByteBuffer[] srcs, int offset, int length) + throws IOException + { + if ((offset < 0) || (length < 0) || (offset > srcs.length - length)) + throw new IndexOutOfBoundsException(); synchronized (writeLock) { synchronized (stateLock) { ensureOpen(); @@ -615,7 +610,7 @@ class DatagramChannelImpl return 0; writerThread = NativeThread.current(); do { - n = IOUtil.write(fd, bufs, nd); + n = IOUtil.write(fd, srcs, offset, length, nd); } while ((n == IOStatus.INTERRUPTED) && isOpen()); return IOStatus.normalize(n); } finally { @@ -626,15 +621,6 @@ class DatagramChannelImpl } } - public long write(ByteBuffer[] srcs, int offset, int length) - throws IOException - { - if ((offset < 0) || (length < 0) || (offset > srcs.length - length)) - throw new IndexOutOfBoundsException(); - // ## Fix IOUtil.write so that we can avoid this array copy - return write0(Util.subsequence(srcs, offset, length)); - } - protected void implConfigureBlocking(boolean block) throws IOException { IOUtil.configureBlocking(fd, block); } diff --git a/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java b/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java index 1bbd5e025ef..af732f5b3a3 100644 --- a/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java +++ b/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java @@ -143,7 +143,11 @@ public class FileChannelImpl } } - private long read0(ByteBuffer[] dsts) throws IOException { + public long read(ByteBuffer[] dsts, int offset, int length) + throws IOException + { + if ((offset < 0) || (length < 0) || (offset > dsts.length - length)) + throw new IndexOutOfBoundsException(); ensureOpen(); if (!readable) throw new NonReadableChannelException(); @@ -156,7 +160,7 @@ public class FileChannelImpl if (!isOpen()) return 0; do { - n = IOUtil.read(fd, dsts, nd); + n = IOUtil.read(fd, dsts, offset, length, nd); } while ((n == IOStatus.INTERRUPTED) && isOpen()); return IOStatus.normalize(n); } finally { @@ -167,15 +171,6 @@ public class FileChannelImpl } } - public long read(ByteBuffer[] dsts, int offset, int length) - throws IOException - { - if ((offset < 0) || (length < 0) || (offset > dsts.length - length)) - throw new IndexOutOfBoundsException(); - // ## Fix IOUtil.write so that we can avoid this array copy - return read0(Util.subsequence(dsts, offset, length)); - } - public int write(ByteBuffer src) throws IOException { ensureOpen(); if (!writable) @@ -200,7 +195,11 @@ public class FileChannelImpl } } - private long write0(ByteBuffer[] srcs) throws IOException { + public long write(ByteBuffer[] srcs, int offset, int length) + throws IOException + { + if ((offset < 0) || (length < 0) || (offset > srcs.length - length)) + throw new IndexOutOfBoundsException(); ensureOpen(); if (!writable) throw new NonWritableChannelException(); @@ -213,7 +212,7 @@ public class FileChannelImpl if (!isOpen()) return 0; do { - n = IOUtil.write(fd, srcs, nd); + n = IOUtil.write(fd, srcs, offset, length, nd); } while ((n == IOStatus.INTERRUPTED) && isOpen()); return IOStatus.normalize(n); } finally { @@ -224,16 +223,6 @@ public class FileChannelImpl } } - public long write(ByteBuffer[] srcs, int offset, int length) - throws IOException - { - if ((offset < 0) || (length < 0) || (offset > srcs.length - length)) - throw new IndexOutOfBoundsException(); - // ## Fix IOUtil.write so that we can avoid this array copy - return write0(Util.subsequence(srcs, offset, length)); - } - - // -- Other operations -- public long position() throws IOException { diff --git a/jdk/src/share/classes/sun/nio/ch/IOUtil.java b/jdk/src/share/classes/sun/nio/ch/IOUtil.java index 5b1dd95cddc..86acff63566 100644 --- a/jdk/src/share/classes/sun/nio/ch/IOUtil.java +++ b/jdk/src/share/classes/sun/nio/ch/IOUtil.java @@ -38,34 +38,6 @@ class IOUtil { private IOUtil() { } // No instantiation - /* - * Returns the index of first buffer in bufs with remaining, - * or -1 if there is nothing left - */ - private static int remaining(ByteBuffer[] bufs) { - int numBufs = bufs.length; - for (int i=0; i 0) - bufs = skipBufs(bufs, nextWithRemaining); + return write(fd, bufs, 0, bufs.length, nd); + } - int numBufs = bufs.length; + static long write(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length, + NativeDispatcher nd) + throws IOException + { + IOVecWrapper vec = IOVecWrapper.get(length); - // Create shadow to ensure DirectByteBuffers are used - ByteBuffer[] shadow = new ByteBuffer[numBufs]; + boolean completed = false; + int iov_len = 0; try { - for (int i=0; i 0) { + vec.setBuffer(iov_len, buf, pos, rem); + + // allocate shadow buffer to ensure I/O is done with direct buffer + if (!(buf instanceof DirectBuffer)) { + ByteBuffer shadow = Util.getTemporaryDirectBuffer(rem); + shadow.put(buf); + shadow.flip(); + vec.setShadow(iov_len, shadow); + buf.position(pos); // temporarily restore position in user buffer + buf = shadow; + pos = shadow.position(); + } + + vec.putBase(iov_len, ((DirectBuffer)buf).address() + pos); + vec.putLen(iov_len, rem); + iov_len++; } } + if (iov_len == 0) + return 0L; - IOVecWrapper vec = null; - long bytesWritten = 0; - try { - // Create a native iovec array - vec= new IOVecWrapper(numBufs); - - // Fill in the iovec array with appropriate data - for (int i=0; i= len) { - bytesWritten -= len; - int newPosition = pos + len; - nextBuffer.position(newPosition); - } else { // Buffers not completely filled - if (bytesWritten > 0) { - assert(pos + bytesWritten < (long)Integer.MAX_VALUE); - int newPosition = (int)(pos + bytesWritten); - nextBuffer.position(newPosition); - } - break; + long left = bytesWritten; + for (int j=0; j 0) { + ByteBuffer buf = vec.getBuffer(j); + int pos = vec.getPosition(j); + int rem = vec.getRemaining(j); + int n = (left > rem) ? rem : (int)left; + buf.position(pos + n); + left -= n; } + // return shadow buffers to buffer pool + ByteBuffer shadow = vec.getShadow(j); + if (shadow != null) + Util.offerLastTemporaryDirectBuffer(shadow); + vec.clearRefs(j); } - return returnVal; + + completed = true; + return bytesWritten; + } finally { - // return any substituted buffers to cache - for (int i=0; i 0) - bufs = skipBufs(bufs, nextWithRemaining); + return read(fd, bufs, 0, bufs.length, nd); + } - int numBufs = bufs.length; + static long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length, + NativeDispatcher nd) + throws IOException + { + IOVecWrapper vec = IOVecWrapper.get(length); - // Read into the shadow to ensure DirectByteBuffers are used - ByteBuffer[] shadow = new ByteBuffer[numBufs]; - boolean usingSlowBuffers = false; + boolean completed = false; + int iov_len = 0; try { - for (int i=0; i 0) { + vec.setBuffer(iov_len, buf, pos, rem); + + // allocate shadow buffer to ensure I/O is done with direct buffer + if (!(buf instanceof DirectBuffer)) { + ByteBuffer shadow = Util.getTemporaryDirectBuffer(rem); + vec.setShadow(iov_len, shadow); + buf = shadow; + pos = shadow.position(); + } + + vec.putBase(iov_len, ((DirectBuffer)buf).address() + pos); + vec.putLen(iov_len, rem); + iov_len++; } } + if (iov_len == 0) + return 0L; - IOVecWrapper vec = null; - long bytesRead = 0; - try { - // Create a native iovec array - vec = new IOVecWrapper(numBufs); - - // Fill in the iovec array with appropriate data - for (int i=0; i= len) { - bytesRead -= len; - int newPosition = pos + len; - nextBuffer.position(newPosition); - } else { // Buffers not completely filled - if (bytesRead > 0) { - assert(pos + bytesRead < (long)Integer.MAX_VALUE); - int newPosition = (int)(pos + bytesRead); - nextBuffer.position(newPosition); + long left = bytesRead; + for (int j=0; j 0) { + ByteBuffer buf = vec.getBuffer(j); + int rem = vec.getRemaining(j); + int n = (left > rem) ? rem : (int)left; + if (shadow == null) { + int pos = vec.getPosition(j); + buf.position(pos + n); + } else { + shadow.limit(shadow.position() + n); + buf.put(shadow); } - break; + left -= n; } + if (shadow != null) + Util.offerLastTemporaryDirectBuffer(shadow); + vec.clearRefs(j); } - // Put results from shadow into the slow buffers - if (usingSlowBuffers) { - for (int i=0; i cached = + new ThreadLocal(); + + private IOVecWrapper(int size) { + this.size = size; + this.buf = new ByteBuffer[size]; + this.position = new int[size]; + this.remaining = new int[size]; + this.shadow = new ByteBuffer[size]; + this.vecArray = new AllocatedNativeObject(size * SIZE_IOVEC, false); + this.address = vecArray.address(); + } + + static IOVecWrapper get(int size) { + IOVecWrapper wrapper = cached.get(); + if (wrapper != null && wrapper.size < size) { + // not big enough; eagerly release memory + wrapper.vecArray.free(); + wrapper = null; + } + if (wrapper == null) { + wrapper = new IOVecWrapper(size); + Cleaner.create(wrapper, new Deallocator(wrapper.vecArray)); + cached.set(wrapper); + } + return wrapper; + } + + void setBuffer(int i, ByteBuffer buf, int pos, int rem) { + this.buf[i] = buf; + this.position[i] = pos; + this.remaining[i] = rem; + } + + void setShadow(int i, ByteBuffer buf) { + shadow[i] = buf; + } + + ByteBuffer getBuffer(int i) { + return buf[i]; + } + + int getPosition(int i) { + return position[i]; + } + + int getRemaining(int i) { + return remaining[i]; + } + + ByteBuffer getShadow(int i) { + return shadow[i]; + } + + void clearRefs(int i) { + buf[i] = null; + shadow[i] = null; } void putBase(int i, long base) { @@ -78,10 +154,6 @@ class IOVecWrapper { vecArray.putLong(offset, len); } - void free() { - vecArray.free(); - } - static { addressSize = Util.unsafe().addressSize(); LEN_OFFSET = addressSize; diff --git a/jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java b/jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java index ee84a0e626c..7d42d7d0096 100644 --- a/jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java +++ b/jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java @@ -385,9 +385,11 @@ class SocketChannelImpl } } - private long read0(ByteBuffer[] bufs) throws IOException { - if (bufs == null) - throw new NullPointerException(); + public long read(ByteBuffer[] dsts, int offset, int length) + throws IOException + { + if ((offset < 0) || (length < 0) || (offset > dsts.length - length)) + throw new IndexOutOfBoundsException(); synchronized (readLock) { if (!ensureReadOpen()) return -1; @@ -401,7 +403,7 @@ class SocketChannelImpl } for (;;) { - n = IOUtil.read(fd, bufs, nd); + n = IOUtil.read(fd, dsts, offset, length, nd); if ((n == IOStatus.INTERRUPTED) && isOpen()) continue; return IOStatus.normalize(n); @@ -418,15 +420,6 @@ class SocketChannelImpl } } - public long read(ByteBuffer[] dsts, int offset, int length) - throws IOException - { - if ((offset < 0) || (length < 0) || (offset > dsts.length - length)) - throw new IndexOutOfBoundsException(); - // ## Fix IOUtil.write so that we can avoid this array copy - return read0(Util.subsequence(dsts, offset, length)); - } - public int write(ByteBuffer buf) throws IOException { if (buf == null) throw new NullPointerException(); @@ -458,9 +451,11 @@ class SocketChannelImpl } } - public long write0(ByteBuffer[] bufs) throws IOException { - if (bufs == null) - throw new NullPointerException(); + public long write(ByteBuffer[] srcs, int offset, int length) + throws IOException + { + if ((offset < 0) || (length < 0) || (offset > srcs.length - length)) + throw new IndexOutOfBoundsException(); synchronized (writeLock) { ensureWriteOpen(); long n = 0; @@ -472,7 +467,7 @@ class SocketChannelImpl writerThread = NativeThread.current(); } for (;;) { - n = IOUtil.write(fd, bufs, nd); + n = IOUtil.write(fd, srcs, offset, length, nd); if ((n == IOStatus.INTERRUPTED) && isOpen()) continue; return IOStatus.normalize(n); @@ -489,15 +484,6 @@ class SocketChannelImpl } } - public long write(ByteBuffer[] srcs, int offset, int length) - throws IOException - { - if ((offset < 0) || (length < 0) || (offset > srcs.length - length)) - throw new IndexOutOfBoundsException(); - // ## Fix IOUtil.write so that we can avoid this array copy - return write0(Util.subsequence(srcs, offset, length)); - } - // package-private int sendOutOfBandData(byte b) throws IOException { synchronized (writeLock) { diff --git a/jdk/src/share/classes/sun/nio/ch/Util.java b/jdk/src/share/classes/sun/nio/ch/Util.java index b17801cbed7..50d280865cd 100644 --- a/jdk/src/share/classes/sun/nio/ch/Util.java +++ b/jdk/src/share/classes/sun/nio/ch/Util.java @@ -41,67 +41,180 @@ import sun.security.action.GetPropertyAction; class Util { - // -- Caches -- // The number of temp buffers in our pool - private static final int TEMP_BUF_POOL_SIZE = 3; + private static final int TEMP_BUF_POOL_SIZE = 8; - // Per-thread soft cache of the last temporary direct buffer - private static ThreadLocal>[] bufferPool; + // Per-thread cache of temporary direct buffers + private static ThreadLocal bufferCache = + new ThreadLocal() + { + @Override + protected BufferCache initialValue() { + return new BufferCache(); + } + }; - @SuppressWarnings("unchecked") - static ThreadLocal>[] createThreadLocalBufferPool() { - return new ThreadLocal[TEMP_BUF_POOL_SIZE]; - } - - static { - bufferPool = createThreadLocalBufferPool(); - for (int i=0; i>(); + /** + * A simple cache of direct buffers. + */ + private static class BufferCache { + // the array of buffers + private ByteBuffer[] buffers; + + // the number of buffers in the cache + private int count; + + // the index of the first valid buffer (undefined if count == 0) + private int start; + + private int next(int i) { + return (i + 1) % TEMP_BUF_POOL_SIZE; + } + + BufferCache() { + buffers = new ByteBuffer[TEMP_BUF_POOL_SIZE]; + } + + /** + * Removes and returns a buffer from the cache of at least the given + * size (or null if no suitable buffer is found). + */ + ByteBuffer get(int size) { + if (count == 0) + return null; // cache is empty + + ByteBuffer[] buffers = this.buffers; + + // search for suitable buffer (often the first buffer will do) + ByteBuffer buf = buffers[start]; + if (buf.capacity() < size) { + buf = null; + int i = start; + while ((i = next(i)) != start) { + ByteBuffer bb = buffers[i]; + if (bb == null) + break; + if (bb.capacity() >= size) { + buf = bb; + break; + } + } + if (buf == null) + return null; + // move first element to here to avoid re-packing + buffers[i] = buffers[start]; + } + + // remove first element + buffers[start] = null; + start = next(start); + count--; + + // prepare the buffer and return it + buf.rewind(); + buf.limit(size); + return buf; + } + + boolean offerFirst(ByteBuffer buf) { + if (count >= TEMP_BUF_POOL_SIZE) { + return false; + } else { + start = (start + TEMP_BUF_POOL_SIZE - 1) % TEMP_BUF_POOL_SIZE; + buffers[start] = buf; + count++; + return true; + } + } + + boolean offerLast(ByteBuffer buf) { + if (count >= TEMP_BUF_POOL_SIZE) { + return false; + } else { + int next = (start + count) % TEMP_BUF_POOL_SIZE; + buffers[next] = buf; + count++; + return true; + } + } + + boolean isEmpty() { + return count == 0; + } + + ByteBuffer removeFirst() { + assert count > 0; + ByteBuffer buf = buffers[start]; + buffers[start] = null; + start = next(start); + count--; + return buf; + } } + /** + * Returns a temporary buffer of at least the given size + */ static ByteBuffer getTemporaryDirectBuffer(int size) { - ByteBuffer buf = null; - // Grab a buffer if available - for (int i=0; i ref = bufferPool[i].get(); - if ((ref != null) && ((buf = ref.get()) != null) && - (buf.capacity() >= size)) { - buf.rewind(); - buf.limit(size); - bufferPool[i].set(null); - return buf; + BufferCache cache = bufferCache.get(); + ByteBuffer buf = cache.get(size); + if (buf != null) { + return buf; + } else { + // No suitable buffer in the cache so we need to allocate a new + // one. To avoid the cache growing then we remove the first + // buffer from the cache and free it. + if (!cache.isEmpty()) { + buf = cache.removeFirst(); + free(buf); } + return ByteBuffer.allocateDirect(size); } - - // Make a new one - return ByteBuffer.allocateDirect(size); } + /** + * Releases a temporary buffer by returning to the cache or freeing it. + */ static void releaseTemporaryDirectBuffer(ByteBuffer buf) { - if (buf == null) - return; - // Put it in an empty slot if such exists - for (int i=0; i ref = bufferPool[i].get(); - if ((ref == null) || (ref.get() == null)) { - bufferPool[i].set(new SoftReference(buf)); - return; - } - } - // Otherwise replace a smaller one in the cache if such exists - for (int i=0; i ref = bufferPool[i].get(); - ByteBuffer inCacheBuf = ref.get(); - if ((inCacheBuf == null) || (buf.capacity() > inCacheBuf.capacity())) { - bufferPool[i].set(new SoftReference(buf)); - return; - } - } + offerFirstTemporaryDirectBuffer(buf); + } - // release memory - ((DirectBuffer)buf).cleaner().clean(); + /** + * Releases a temporary buffer by returning to the cache or freeing it. If + * returning to the cache then insert it at the start so that it is + * likely to be returned by a subsequent call to getTemporaryDirectBuffer. + */ + static void offerFirstTemporaryDirectBuffer(ByteBuffer buf) { + assert buf != null; + BufferCache cache = bufferCache.get(); + if (!cache.offerFirst(buf)) { + // cache is full + free(buf); + } + } + + /** + * Releases a temporary buffer by returning to the cache or freeing it. If + * returning to the cache then insert it at the end. This makes it + * suitable for scatter/gather operations where the buffers are returned to + * cache in same order that they were obtained. + */ + static void offerLastTemporaryDirectBuffer(ByteBuffer buf) { + assert buf != null; + BufferCache cache = bufferCache.get(); + if (!cache.offerLast(buf)) { + // cache is full + free(buf); + } + } + + /** + * Frees the memory for the given direct buffer + */ + private static void free(ByteBuffer buf) { + ((DirectBuffer)buf).cleaner().clean(); } private static class SelectorWrapper { From abc4e4d7d03bd84a32e8030d78ebc1ee4cac73f5 Mon Sep 17 00:00:00 2001 From: Mandy Chung Date: Thu, 12 Aug 2010 16:36:49 -0700 Subject: [PATCH 026/100] 6973831: NPE when printing stack trace of OOME Initialize suppressedExceptions field to null Reviewed-by: briangoetz, dholmes, forax --- .../share/classes/java/lang/Throwable.java | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/jdk/src/share/classes/java/lang/Throwable.java b/jdk/src/share/classes/java/lang/Throwable.java index 213f5f86d5c..4d4169139e8 100644 --- a/jdk/src/share/classes/java/lang/Throwable.java +++ b/jdk/src/share/classes/java/lang/Throwable.java @@ -200,7 +200,16 @@ public class Throwable implements Serializable { * @serial * @since 1.7 */ - private List suppressedExceptions = Collections.emptyList(); + private List suppressedExceptions = null; + /* + * This field is lazily initialized when the first suppressed + * exception is added. + * + * OutOfMemoryError is preallocated in the VM for better OOM + * diagnosability during VM initialization. Constructor can't + * be not invoked. If a new field to be added in the future must + * be initialized to non-null, it requires a synchronized VM change. + */ /** Message for trying to suppress a null exception. */ private static final String NULL_CAUSE_MESSAGE = "Cannot suppress a null exception."; @@ -329,7 +338,7 @@ public class Throwable implements Serializable { * cause is nonexistent or unknown. * @since 1.4 */ - public Throwable getCause() { + public synchronized Throwable getCause() { return (cause==this ? null : cause); } @@ -563,7 +572,7 @@ public class Throwable implements Serializable { s.println("\tat " + traceElement); // Print suppressed exceptions, if any - for (Throwable se : suppressedExceptions) + for (Throwable se : getSuppressedExceptions()) se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, "\t", dejaVu); // Print cause, if any @@ -604,7 +613,7 @@ public class Throwable implements Serializable { s.println(prefix + "\t... " + framesInCommon + " more"); // Print suppressed exceptions, if any - for (Throwable se : suppressedExceptions) + for (Throwable se : getSuppressedExceptions()) se.printEnclosedStackTrace(s, trace, SUPPRESSED_CAPTION, prefix +"\t", dejaVu); @@ -747,7 +756,9 @@ public class Throwable implements Serializable { if (defensiveCopy[i] == null) throw new NullPointerException("stackTrace[" + i + "]"); - this.stackTrace = defensiveCopy; + synchronized (this) { + this.stackTrace = defensiveCopy; + } } /** @@ -772,11 +783,11 @@ public class Throwable implements Serializable { private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); // read in all fields - List suppressed = Collections.emptyList(); + List suppressed = null; if (suppressedExceptions != null && !suppressedExceptions.isEmpty()) { // Copy Throwables to new list suppressed = new ArrayList(); - for(Throwable t : suppressedExceptions) { + for (Throwable t : suppressedExceptions) { if (t == null) throw new NullPointerException(NULL_CAUSE_MESSAGE); suppressed.add(t); @@ -819,7 +830,7 @@ public class Throwable implements Serializable { if (exception == this) throw new IllegalArgumentException("Self-suppression not permitted"); - if (suppressedExceptions.size() == 0) + if (suppressedExceptions == null) suppressedExceptions = new ArrayList(); suppressedExceptions.add(exception); } @@ -835,7 +846,10 @@ public class Throwable implements Serializable { * suppressed to deliver this exception. * @since 1.7 */ - public Throwable[] getSuppressedExceptions() { - return suppressedExceptions.toArray(EMPTY_THROWABLE_ARRAY); + public synchronized Throwable[] getSuppressedExceptions() { + if (suppressedExceptions == null) + return EMPTY_THROWABLE_ARRAY; + else + return suppressedExceptions.toArray(EMPTY_THROWABLE_ARRAY); } } From 1d736d28510e5ee2ce89504f1d54d1c3115c80de Mon Sep 17 00:00:00 2001 From: Tom Rodriguez Date: Thu, 12 Aug 2010 16:38:23 -0700 Subject: [PATCH 027/100] 6974176: ShouldNotReachHere, instanceKlass.cpp:1426 Reviewed-by: kvn, twisti --- hotspot/src/share/vm/code/nmethod.cpp | 33 ++++++++++++++++++++------- hotspot/src/share/vm/code/nmethod.hpp | 5 ++++ 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/hotspot/src/share/vm/code/nmethod.cpp b/hotspot/src/share/vm/code/nmethod.cpp index af0f1833368..1b6472a3c05 100644 --- a/hotspot/src/share/vm/code/nmethod.cpp +++ b/hotspot/src/share/vm/code/nmethod.cpp @@ -433,6 +433,10 @@ void nmethod::init_defaults() { _unload_reported = false; // jvmti state NOT_PRODUCT(_has_debug_info = false); +#ifdef ASSERT + _oops_are_stale = false; +#endif + _oops_do_mark_link = NULL; _jmethod_id = NULL; _osr_link = NULL; @@ -1230,11 +1234,10 @@ void nmethod::log_state_change() const { bool nmethod::make_not_entrant_or_zombie(unsigned int state) { assert(state == zombie || state == not_entrant, "must be zombie or not_entrant"); - bool was_alive = false; - // Make sure neither the nmethod nor the method is flushed in case of a safepoint in code below. nmethodLocker nml(this); methodHandle the_method(method()); + No_Safepoint_Verifier nsv; { // If the method is already zombie there is nothing to do @@ -1303,13 +1306,27 @@ bool nmethod::make_not_entrant_or_zombie(unsigned int state) { // state will be flushed later when the transition to zombie // happens or they get unloaded. if (state == zombie) { - // zombie only - if a JVMTI agent has enabled the CompiledMethodUnload event - // and it hasn't already been reported for this nmethod then report it now. - // (the event may have been reported earilier if the GC marked it for unloading). - post_compiled_method_unload(); + { + // Flushing dependecies must be done before any possible + // safepoint can sneak in, otherwise the oops used by the + // dependency logic could have become stale. + MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); + flush_dependencies(NULL); + } - MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag); - flush_dependencies(NULL); + { + // zombie only - if a JVMTI agent has enabled the CompiledMethodUnload event + // and it hasn't already been reported for this nmethod then report it now. + // (the event may have been reported earilier if the GC marked it for unloading). + Pause_No_Safepoint_Verifier pnsv(&nsv); + post_compiled_method_unload(); + } + +#ifdef ASSERT + // It's no longer safe to access the oops section since zombie + // nmethods aren't scanned for GC. + _oops_are_stale = true; +#endif } else { assert(state == not_entrant, "other cases may need to be handled differently"); } diff --git a/hotspot/src/share/vm/code/nmethod.hpp b/hotspot/src/share/vm/code/nmethod.hpp index b57cb5e3dc7..88fe784cffa 100644 --- a/hotspot/src/share/vm/code/nmethod.hpp +++ b/hotspot/src/share/vm/code/nmethod.hpp @@ -177,6 +177,10 @@ class nmethod : public CodeBlob { // Protected by Patching_lock unsigned char _state; // {alive, not_entrant, zombie, unloaded) +#ifdef ASSERT + bool _oops_are_stale; // indicates that it's no longer safe to access oops section +#endif + enum { alive = 0, not_entrant = 1, // uncommon trap has happened but activations may still exist zombie = 2, @@ -434,6 +438,7 @@ class nmethod : public CodeBlob { oop* oop_addr_at(int index) const { // for GC // relocation indexes are biased by 1 (because 0 is reserved) assert(index > 0 && index <= oops_size(), "must be a valid non-zero index"); + assert(!_oops_are_stale, "oops are stale"); return &oops_begin()[index - 1]; } From a96301c84e88d07a0dcec155a2d784a0ffb18303 Mon Sep 17 00:00:00 2001 From: Tom Rodriguez Date: Thu, 12 Aug 2010 23:34:34 -0700 Subject: [PATCH 028/100] 6975006: assert(check.is_deoptimized_frame()) failed: missed deopt Reviewed-by: kvn, twisti --- hotspot/src/share/vm/runtime/frame.cpp | 18 ++++++++++-------- hotspot/src/share/vm/runtime/frame.hpp | 2 +- hotspot/src/share/vm/runtime/safepoint.cpp | 3 +++ hotspot/src/share/vm/runtime/safepoint.hpp | 2 ++ hotspot/src/share/vm/runtime/thread.cpp | 3 +-- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/hotspot/src/share/vm/runtime/frame.cpp b/hotspot/src/share/vm/runtime/frame.cpp index 89de3ca1a99..69d28798499 100644 --- a/hotspot/src/share/vm/runtime/frame.cpp +++ b/hotspot/src/share/vm/runtime/frame.cpp @@ -215,17 +215,15 @@ bool frame::can_be_deoptimized() const { return !nm->is_at_poll_return(pc()); } -void frame::deoptimize(JavaThread* thread, bool thread_is_known_safe) { -// Schedule deoptimization of an nmethod activation with this frame. - - // Store the original pc before an patch (or request to self-deopt) - // in the published location of the frame. - +void frame::deoptimize(JavaThread* thread) { + // Schedule deoptimization of an nmethod activation with this frame. assert(_cb != NULL && _cb->is_nmethod(), "must be"); nmethod* nm = (nmethod*)_cb; // This is a fix for register window patching race - if (NeedsDeoptSuspend && !thread_is_known_safe) { + if (NeedsDeoptSuspend && Thread::current() != thread) { + assert(SafepointSynchronize::is_at_safepoint(), + "patching other threads for deopt may only occur at a safepoint"); // It is possible especially with DeoptimizeALot/DeoptimizeRandom that // we could see the frame again and ask for it to be deoptimized since @@ -248,7 +246,11 @@ void frame::deoptimize(JavaThread* thread, bool thread_is_known_safe) { // whether to spin or block. It isn't worth it. Just treat it like // native and be done with it. // - JavaThreadState state = thread->thread_state(); + // Examine the state of the thread at the start of safepoint since + // threads that were in native at the start of the safepoint could + // come to a halt during the safepoint, changing the current value + // of the safepoint_state. + JavaThreadState state = thread->safepoint_state()->orig_thread_state(); if (state == _thread_in_native || state == _thread_in_native_trans) { // Since we are at a safepoint the target thread will stop itself // before it can return to java as long as we remain at the safepoint. diff --git a/hotspot/src/share/vm/runtime/frame.hpp b/hotspot/src/share/vm/runtime/frame.hpp index 4f2df2ad964..a2ac51f3669 100644 --- a/hotspot/src/share/vm/runtime/frame.hpp +++ b/hotspot/src/share/vm/runtime/frame.hpp @@ -174,7 +174,7 @@ class frame VALUE_OBJ_CLASS_SPEC { address sender_pc() const; // Support for deoptimization - void deoptimize(JavaThread* thread, bool thread_is_known_safe = false); + void deoptimize(JavaThread* thread); // The frame's original SP, before any extension by an interpreted callee; // used for packing debug info into vframeArray objects and vframeArray lookup. diff --git a/hotspot/src/share/vm/runtime/safepoint.cpp b/hotspot/src/share/vm/runtime/safepoint.cpp index 65d130de103..2b51d303716 100644 --- a/hotspot/src/share/vm/runtime/safepoint.cpp +++ b/hotspot/src/share/vm/runtime/safepoint.cpp @@ -782,6 +782,9 @@ void ThreadSafepointState::examine_state_of_thread() { JavaThreadState state = _thread->thread_state(); + // Save the state at the start of safepoint processing. + _orig_thread_state = state; + // Check for a thread that is suspended. Note that thread resume tries // to grab the Threads_lock which we own here, so a thread cannot be // resumed during safepoint synchronization. diff --git a/hotspot/src/share/vm/runtime/safepoint.hpp b/hotspot/src/share/vm/runtime/safepoint.hpp index 46d7f3c3b97..740cbd61c44 100644 --- a/hotspot/src/share/vm/runtime/safepoint.hpp +++ b/hotspot/src/share/vm/runtime/safepoint.hpp @@ -185,6 +185,7 @@ class ThreadSafepointState: public CHeapObj { JavaThread * _thread; volatile suspend_type _type; + JavaThreadState _orig_thread_state; public: @@ -199,6 +200,7 @@ class ThreadSafepointState: public CHeapObj { JavaThread* thread() const { return _thread; } suspend_type type() const { return _type; } bool is_running() const { return (_type==_running); } + JavaThreadState orig_thread_state() const { return _orig_thread_state; } // Support for safepoint timeout (debugging) bool has_called_back() const { return _has_called_back; } diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp index e2ac4f86be8..d9fdbbd7511 100644 --- a/hotspot/src/share/vm/runtime/thread.cpp +++ b/hotspot/src/share/vm/runtime/thread.cpp @@ -2110,8 +2110,7 @@ void JavaThread::check_safepoint_and_suspend_for_native_trans(JavaThread *thread } if (f.id() == thread->must_deopt_id()) { thread->clear_must_deopt_id(); - // Since we know we're safe to deopt the current state is a safe state - f.deoptimize(thread, true); + f.deoptimize(thread); } else { fatal("missed deoptimization!"); } From 5223492f6b1315267010f6c1fbf2faceaa189da8 Mon Sep 17 00:00:00 2001 From: Gary Benson Date: Fri, 13 Aug 2010 22:26:27 +0100 Subject: [PATCH 029/100] 6976186: Integrate Shark Shark is a JIT compiler for Zero that uses the LLVM compiler infrastructure. Reviewed-by: ohair --- jdk/make/jdk_generic_profile.sh | 55 +++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/jdk/make/jdk_generic_profile.sh b/jdk/make/jdk_generic_profile.sh index 3f2e08446f0..4363a1b710e 100644 --- a/jdk/make/jdk_generic_profile.sh +++ b/jdk/make/jdk_generic_profile.sh @@ -340,6 +340,10 @@ PATH="${path4sdk}" export PATH # Export variables required for Zero +if [ "${SHARK_BUILD}" = true ] ; then + ZERO_BUILD=true + export ZERO_BUILD +fi if [ "${ZERO_BUILD}" = true ] ; then # ZERO_LIBARCH is the name of the architecture-specific # subdirectory under $JAVA_HOME/jre/lib @@ -417,4 +421,55 @@ if [ "${ZERO_BUILD}" = true ] ; then fi export LIBFFI_CFLAGS export LIBFFI_LIBS + + # LLVM_CFLAGS, LLVM_LDFLAGS and LLVM_LIBS tell the compiler how to + # compile and link against LLVM + if [ "${SHARK_BUILD}" = true ] ; then + if [ "${LLVM_CONFIG}" = "" ] ; then + LLVM_CONFIG=$(which llvm-config 2>/dev/null) + fi + if [ ! -x "${LLVM_CONFIG}" ] ; then + echo "ERROR: Unable to locate llvm-config" + exit 1 + fi + llvm_components="jit engine nativecodegen" + + unset LLVM_CFLAGS + for flag in $("${LLVM_CONFIG}" --cxxflags $llvm_components); do + if echo "${flag}" | grep -q '^-[ID]'; then + if [ "${flag}" != "-D_DEBUG" ] ; then + if [ "${LLVM_CFLAGS}" != "" ] ; then + LLVM_CFLAGS="${LLVM_CFLAGS} " + fi + LLVM_CFLAGS="${LLVM_CFLAGS}${flag}" + fi + fi + done + llvm_version=$("${LLVM_CONFIG}" --version | sed 's/\.//; s/svn.*//') + LLVM_CFLAGS="${LLVM_CFLAGS} -DSHARK_LLVM_VERSION=${llvm_version}" + + unset LLVM_LDFLAGS + for flag in $("${LLVM_CONFIG}" --ldflags $llvm_components); do + if echo "${flag}" | grep -q '^-L'; then + if [ "${LLVM_LDFLAGS}" != "" ] ; then + LLVM_LDFLAGS="${LLVM_LDFLAGS} " + fi + LLVM_LDFLAGS="${LLVM_LDFLAGS}${flag}" + fi + done + + unset LLVM_LIBS + for flag in $("${LLVM_CONFIG}" --libs $llvm_components); do + if echo "${flag}" | grep -q '^-l'; then + if [ "${LLVM_LIBS}" != "" ] ; then + LLVM_LIBS="${LLVM_LIBS} " + fi + LLVM_LIBS="${LLVM_LIBS}${flag}" + fi + done + + export LLVM_CFLAGS + export LLVM_LDFLAGS + export LLVM_LIBS + fi fi From 0f1f7c3b7f766d2c6e1bfafce2d262ec216d06a2 Mon Sep 17 00:00:00 2001 From: Tom Rodriguez Date: Fri, 13 Aug 2010 15:14:00 -0700 Subject: [PATCH 030/100] 6976372: # assert(_owner == Thread::current()) failed: invariant Reviewed-by: kvn, twisti --- hotspot/src/share/vm/compiler/compileBroker.cpp | 10 ++++------ hotspot/src/share/vm/runtime/sharedRuntime.cpp | 4 +--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/hotspot/src/share/vm/compiler/compileBroker.cpp b/hotspot/src/share/vm/compiler/compileBroker.cpp index 7288cac07ba..478d3f47bf1 100644 --- a/hotspot/src/share/vm/compiler/compileBroker.cpp +++ b/hotspot/src/share/vm/compiler/compileBroker.cpp @@ -1652,12 +1652,10 @@ void CompileBroker::invoke_compiler_on_method(CompileTask* task) { void CompileBroker::handle_full_code_cache() { UseInterpreter = true; if (UseCompiler || AlwaysCompileLoopMethods ) { - CompilerThread* thread = CompilerThread::current(); - CompileLog* log = thread->log(); - if (log != NULL) { - log->begin_elem("code_cache_full"); - log->stamp(); - log->end_elem(); + if (xtty != NULL) { + xtty->begin_elem("code_cache_full"); + xtty->stamp(); + xtty->end_elem(); } warning("CodeCache is full. Compiler has been disabled."); warning("Try increasing the code cache size using -XX:ReservedCodeCacheSize="); diff --git a/hotspot/src/share/vm/runtime/sharedRuntime.cpp b/hotspot/src/share/vm/runtime/sharedRuntime.cpp index 4ff5aa0fa88..5e9325399e4 100644 --- a/hotspot/src/share/vm/runtime/sharedRuntime.cpp +++ b/hotspot/src/share/vm/runtime/sharedRuntime.cpp @@ -2493,15 +2493,13 @@ nmethod *AdapterHandlerLibrary::create_native_wrapper(methodHandle method) { } // Must unlock before calling set_code + // Install the generated code. if (nm != NULL) { method->set_code(method, nm); nm->post_compiled_method_load_event(); } else { // CodeCache is full, disable compilation - // Ought to log this but compile log is only per compile thread - // and we're some non descript Java thread. - MutexUnlocker mu(AdapterHandlerLibrary_lock); CompileBroker::handle_full_code_cache(); } return nm; From 91bd86216a7c113b8b85c73a2902d9372a3daebf Mon Sep 17 00:00:00 2001 From: Maurizio Cimadamore Date: Mon, 16 Aug 2010 14:56:23 +0100 Subject: [PATCH 031/100] 6976649: javac does not enforce required annotation elements in arrays Type annotation should take advantage of recursive annotation checking Reviewed-by: jjg --- .../classes/com/sun/tools/javac/comp/Attr.java | 8 +++++--- .../com/sun/tools/javac/comp/Check.java | 18 +++++++----------- .../javac/annotations/6881115/T6881115.java | 8 +++++--- .../javac/annotations/6881115/T6881115.out | 12 +++++++++++- .../javac/annotations/pos/TrailingComma.java | 2 +- 5 files changed, 29 insertions(+), 19 deletions(-) diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java index deefa720e22..4a7ab59fd16 100644 --- a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java @@ -696,9 +696,11 @@ public class Attr extends JCTree.Visitor { // ensure that annotation method does not clash with members of Object/Annotation chk.validateAnnotationMethod(tree.pos(), m); - // if default value is an annotation, check it is a well-formed - // annotation value (e.g. no duplicate values, no missing values, etc.) - chk.validateAnnotationDefaultValue(tree.defaultValue); + if (tree.defaultValue != null) { + // if default value is an annotation, check it is a well-formed + // annotation value (e.g. no duplicate values, no missing values, etc.) + chk.validateAnnotationTree(tree.defaultValue); + } } for (List l = tree.thrown; l.nonEmpty(); l = l.tail) diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java index 2e5fe04e2fd..4edbf72443a 100644 --- a/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java +++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java @@ -25,6 +25,7 @@ package com.sun.tools.javac.comp; +import com.sun.source.tree.AssignmentTree; import java.util.*; import java.util.Set; @@ -1930,20 +1931,17 @@ public class Check { **************************************************************************/ /** - * Validate annotations in default values + * Recursively validate annotations values */ - void validateAnnotationDefaultValue(JCTree defaultValue) { - class DefaultValueValidator extends TreeScanner { + void validateAnnotationTree(JCTree tree) { + class AnnotationValidator extends TreeScanner { @Override public void visitAnnotation(JCAnnotation tree) { super.visitAnnotation(tree); validateAnnotation(tree); } } - // defaultValue may be null if an error occurred, so don't bother validating it - if (defaultValue != null) { - defaultValue.accept(new DefaultValueValidator()); - } + tree.accept(new AnnotationValidator()); } /** Annotation types are restricted to primitives, String, an @@ -2009,7 +2007,7 @@ public class Check { /** Check an annotation of a symbol. */ public void validateAnnotation(JCAnnotation a, Symbol s) { - validateAnnotation(a); + validateAnnotationTree(a); if (!annotationApplicable(a, s)) log.error(a.pos(), "annotation.type.not.applicable"); @@ -2023,7 +2021,7 @@ public class Check { public void validateTypeAnnotation(JCTypeAnnotation a, boolean isTypeParameter) { if (a.type == null) throw new AssertionError("annotation tree hasn't been attributed yet: " + a); - validateAnnotation(a); + validateAnnotationTree(a); if (!isTypeAnnotation(a, isTypeParameter)) log.error(a.pos(), "annotation.type.not.applicable"); @@ -2141,8 +2139,6 @@ public class Check { if (!members.remove(m)) log.error(assign.lhs.pos(), "duplicate.annotation.member.value", m.name, a.type); - if (assign.rhs.getTag() == ANNOTATION) - validateAnnotation((JCAnnotation)assign.rhs); } // all the remaining ones better have default values diff --git a/langtools/test/tools/javac/annotations/6881115/T6881115.java b/langtools/test/tools/javac/annotations/6881115/T6881115.java index 43c896b62d6..a0b9960c2fe 100644 --- a/langtools/test/tools/javac/annotations/6881115/T6881115.java +++ b/langtools/test/tools/javac/annotations/6881115/T6881115.java @@ -1,6 +1,6 @@ /* * @test /nodynamiccopyright/ - * @bug 6881115 + * @bug 6881115 6976649 * @summary javac permits nested anno w/o mandatory attrs => IncompleteAnnotationException * @author mcimadamore * @compile/fail/ref=T6881115.out -XDrawDiagnostics T6881115.java @@ -14,5 +14,7 @@ String b1(); int b2(); } -@A -class T6881115 {} +@A(b = @B(b2 = 1, b2 = 2), + b_arr = {@B(), @B(b2 = 1, b2 = 2)}) +class T6881115<@A(b = @B(b2 = 1, b2 = 2), + b_arr = {@B(), @B(b2 = 1, b2 = 2)}) X> {} diff --git a/langtools/test/tools/javac/annotations/6881115/T6881115.out b/langtools/test/tools/javac/annotations/6881115/T6881115.out index a28f3af61c0..4cece230577 100644 --- a/langtools/test/tools/javac/annotations/6881115/T6881115.out +++ b/langtools/test/tools/javac/annotations/6881115/T6881115.out @@ -3,4 +3,14 @@ T6881115.java:10:19: compiler.err.annotation.missing.default.value: B, b1 T6881115.java:11:26: compiler.err.annotation.missing.default.value.1: B, b1,b2 T6881115.java:11:43: compiler.err.duplicate.annotation.member.value: b2, B T6881115.java:11:32: compiler.err.annotation.missing.default.value: B, b1 -5 errors +T6881115.java:17:19: compiler.err.duplicate.annotation.member.value: b2, B +T6881115.java:17:8: compiler.err.annotation.missing.default.value: B, b1 +T6881115.java:18:13: compiler.err.annotation.missing.default.value.1: B, b1,b2 +T6881115.java:18:30: compiler.err.duplicate.annotation.member.value: b2, B +T6881115.java:18:19: compiler.err.annotation.missing.default.value: B, b1 +T6881115.java:19:34: compiler.err.duplicate.annotation.member.value: b2, B +T6881115.java:19:23: compiler.err.annotation.missing.default.value: B, b1 +T6881115.java:20:28: compiler.err.annotation.missing.default.value.1: B, b1,b2 +T6881115.java:20:45: compiler.err.duplicate.annotation.member.value: b2, B +T6881115.java:20:34: compiler.err.annotation.missing.default.value: B, b1 +15 errors diff --git a/langtools/test/tools/javac/annotations/pos/TrailingComma.java b/langtools/test/tools/javac/annotations/pos/TrailingComma.java index 157a41e9777..20345b56f89 100644 --- a/langtools/test/tools/javac/annotations/pos/TrailingComma.java +++ b/langtools/test/tools/javac/annotations/pos/TrailingComma.java @@ -36,7 +36,7 @@ import java.lang.annotation.*; } -@TestAnnotation({@SuppressWarnings(), +@TestAnnotation({@SuppressWarnings({}), @SuppressWarnings({"Beware the ides of March.",}), @SuppressWarnings({"Look both ways", "Before Crossing",}), }) public class TrailingComma { From 2801325742ac33c5059b7d87a7886dd332cad61a Mon Sep 17 00:00:00 2001 From: Maurizio Cimadamore Date: Mon, 16 Aug 2010 14:58:10 +0100 Subject: [PATCH 032/100] 6369605: Unconstrained type variables fails to include bounds Unconstrained type-variables with recursive bounds are not inferred properly Reviewed-by: jjg --- .../com/sun/tools/javac/code/Type.java | 22 +++- .../com/sun/tools/javac/comp/Infer.java | 101 +++++++++++++++--- .../javac/Diagnostics/6862608/T6862608a.out | 2 +- .../tools/javac/diags/examples.not-yet.txt | 1 + .../diags/examples/InvalidInferredTypes.java | 8 +- .../generics/inference/6369605/T6369605a.java | 50 +++++++++ .../generics/inference/6369605/T6369605b.java | 49 +++++++++ .../generics/inference/6638712/T6638712a.out | 2 +- 8 files changed, 209 insertions(+), 26 deletions(-) create mode 100644 langtools/test/tools/javac/generics/inference/6369605/T6369605a.java create mode 100644 langtools/test/tools/javac/generics/inference/6369605/T6369605b.java diff --git a/langtools/src/share/classes/com/sun/tools/javac/code/Type.java b/langtools/src/share/classes/com/sun/tools/javac/code/Type.java index b3fae7d131c..541856c45f5 100644 --- a/langtools/src/share/classes/com/sun/tools/javac/code/Type.java +++ b/langtools/src/share/classes/com/sun/tools/javac/code/Type.java @@ -347,11 +347,17 @@ public class Type implements PrimitiveType { return false; } - /** Does this type contain an occurrence of some type in `elems'? + /** Does this type contain an occurrence of some type in 'ts'? */ - public boolean containsSome(List ts) { - for (List l = ts; l.nonEmpty(); l = l.tail) - if (this.contains(ts.head)) return true; + public boolean containsAny(List ts) { + for (Type t : ts) + if (this.contains(t)) return true; + return false; + } + + public static boolean containsAny(List ts1, List ts2) { + for (Type t : ts1) + if (t.containsAny(ts2)) return true; return false; } @@ -431,6 +437,10 @@ public class Type implements PrimitiveType { this.bound = bound; } + public boolean contains(Type t) { + return kind != UNBOUND && type.contains(t); + } + public boolean isSuperBound() { return kind == SUPER || kind == UNBOUND; @@ -681,7 +691,9 @@ public class Type implements PrimitiveType { return elem == this || (isParameterized() - && (getEnclosingType().contains(elem) || contains(getTypeArguments(), elem))); + && (getEnclosingType().contains(elem) || contains(getTypeArguments(), elem))) + || (isCompound() + && (supertype_field.contains(elem) || contains(interfaces_field, elem))); } public void complete() { diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Infer.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Infer.java index eb53fbcb319..de6803a1896 100644 --- a/langtools/src/share/classes/com/sun/tools/javac/comp/Infer.java +++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Infer.java @@ -138,24 +138,73 @@ public class Infer { /** A mapping that returns its type argument with every UndetVar replaced * by its `inst' field. Throws a NoInstanceException * if this not possible because an `inst' field is null. + * Note: mutually referring undertvars will be left uninstantiated + * (that is, they will be replaced by the underlying type-variable). */ + Mapping getInstFun = new Mapping("getInstFun") { public Type apply(Type t) { switch (t.tag) { - case UNKNOWN: - throw ambiguousNoInstanceException - .setMessage("undetermined.type"); - case UNDETVAR: - UndetVar that = (UndetVar) t; - if (that.inst == null) + case UNKNOWN: throw ambiguousNoInstanceException - .setMessage("type.variable.has.undetermined.type", - that.qtype); - return apply(that.inst); - default: - return t.map(this); + .setMessage("undetermined.type"); + case UNDETVAR: + UndetVar that = (UndetVar) t; + if (that.inst == null) + throw ambiguousNoInstanceException + .setMessage("type.variable.has.undetermined.type", + that.qtype); + return isConstraintCyclic(that) ? + that.qtype : + apply(that.inst); + default: + return t.map(this); } } + + private boolean isConstraintCyclic(UndetVar uv) { + Types.UnaryVisitor constraintScanner = + new Types.UnaryVisitor() { + + List seen = List.nil(); + + Boolean visit(List ts) { + for (Type t : ts) { + if (visit(t)) return true; + } + return false; + } + + public Boolean visitType(Type t, Void ignored) { + return false; + } + + @Override + public Boolean visitClassType(ClassType t, Void ignored) { + if (t.isCompound()) { + return visit(types.supertype(t)) || + visit(types.interfaces(t)); + } else { + return visit(t.getTypeArguments()); + } + } + @Override + public Boolean visitWildcardType(WildcardType t, Void ignored) { + return visit(t.type); + } + + @Override + public Boolean visitUndetVar(UndetVar t, Void ignored) { + if (seen.contains(t)) { + return true; + } else { + seen = seen.prepend(t); + return visit(t.inst); + } + } + }; + return constraintScanner.visit(uv); + } }; /*************************************************************************** @@ -257,10 +306,9 @@ public class Infer { TypeVar tv = (TypeVar)uv.qtype; ListBuffer hibounds = new ListBuffer(); for (Type t : that.getConstraints(tv, ConstraintKind.EXTENDS)) { - if (!t.containsSome(that.tvars) && t.tag != BOT) { - hibounds.append(t); - } + hibounds.append(types.subst(t, that.tvars, undetvars)); } + List inst = that.getConstraints(tv, ConstraintKind.EQUAL); if (inst.nonEmpty() && inst.head.tag != BOT) { uv.inst = inst.head; @@ -279,9 +327,32 @@ public class Infer { // check bounds List targs = Type.map(undetvars, getInstFun); - targs = types.subst(targs, that.tvars, targs); + if (Type.containsAny(targs, that.tvars)) { + //replace uninferred type-vars + targs = types.subst(targs, + that.tvars, + instaniateAsUninferredVars(undetvars, that.tvars)); + } return chk.checkType(warn.pos(), that.inst(targs, types), to); } + //where + private List instaniateAsUninferredVars(List undetvars, List tvars) { + ListBuffer new_targs = ListBuffer.lb(); + //step 1 - create syntethic captured vars + for (Type t : undetvars) { + UndetVar uv = (UndetVar)t; + Type newArg = new CapturedType(t.tsym.name, t.tsym, uv.inst, syms.botType, null); + new_targs = new_targs.append(newArg); + } + //step 2 - replace synthetic vars in their bounds + for (Type t : new_targs.toList()) { + CapturedType ct = (CapturedType)t; + ct.bound = types.subst(ct.bound, tvars, new_targs.toList()); + WildcardType wt = new WildcardType(ct.bound, BoundKind.EXTENDS, syms.boundClass); + ct.wildcard = wt; + } + return new_targs.toList(); + } /** Instantiate method type `mt' by finding instantiations of * `tvars' so that method can be applied to `argtypes'. diff --git a/langtools/test/tools/javac/Diagnostics/6862608/T6862608a.out b/langtools/test/tools/javac/Diagnostics/6862608/T6862608a.out index 41d11d2d5b5..59d1c9b5c3f 100644 --- a/langtools/test/tools/javac/Diagnostics/6862608/T6862608a.out +++ b/langtools/test/tools/javac/Diagnostics/6862608/T6862608a.out @@ -1,3 +1,3 @@ -T6862608a.java:19:41: compiler.err.invalid.inferred.types: T, (compiler.misc.inferred.do.not.conform.to.params: java.lang.Iterable>, java.util.List>) +T6862608a.java:19:41: compiler.err.prob.found.req: (compiler.misc.incompatible.types.1: (compiler.misc.no.conforming.instance.exists: T, java.util.Comparator, java.util.Comparator)), java.util.Comparator, java.util.Comparator - compiler.misc.where.description.typevar: T,{(compiler.misc.where.typevar: T, java.lang.Object, kindname.method, compound(java.lang.Iterable>))} 1 error diff --git a/langtools/test/tools/javac/diags/examples.not-yet.txt b/langtools/test/tools/javac/diags/examples.not-yet.txt index 1fe0abdbfc0..5b83aa058c7 100644 --- a/langtools/test/tools/javac/diags/examples.not-yet.txt +++ b/langtools/test/tools/javac/diags/examples.not-yet.txt @@ -64,6 +64,7 @@ compiler.misc.fatal.err.cant.locate.field # Resolve, from Lower compiler.misc.fatal.err.cant.locate.meth # Resolve, from Lower compiler.misc.file.does.not.contain.package compiler.misc.illegal.start.of.class.file +compiler.misc.inferred.do.not.conform.to.params # UNUSED (hard to see if very complex inference scenario might require this though, so leaving it in, as per JLS3) compiler.misc.kindname.annotation compiler.misc.kindname.enum compiler.misc.kindname.package diff --git a/langtools/test/tools/javac/diags/examples/InvalidInferredTypes.java b/langtools/test/tools/javac/diags/examples/InvalidInferredTypes.java index 2ff3035d74c..22dfb6b0d98 100644 --- a/langtools/test/tools/javac/diags/examples/InvalidInferredTypes.java +++ b/langtools/test/tools/javac/diags/examples/InvalidInferredTypes.java @@ -22,17 +22,17 @@ */ // key: compiler.err.invalid.inferred.types -// key: compiler.misc.inferred.do.not.conform.to.params +// key: compiler.misc.inferred.do.not.conform.to.bounds import java.util.*; class InvalidInferredTypes { - Comparator compound(Iterable> it) { + > T makeList() { return null; } - public void test(List> x) { - Comparator c3 = compound(x); + public void test() { + List l = makeList(); } } diff --git a/langtools/test/tools/javac/generics/inference/6369605/T6369605a.java b/langtools/test/tools/javac/generics/inference/6369605/T6369605a.java new file mode 100644 index 00000000000..5f31fb657ce --- /dev/null +++ b/langtools/test/tools/javac/generics/inference/6369605/T6369605a.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010, 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 6369605 + * @summary Unconstrained type variables fails to include bounds + * @author mcimadamore + * @compile T6369605a.java + */ +import java.util.List; + +class T6369605a { + static > T m1() { + return null; + } + + static , U extends List> T m2() { + return null; + } + + static , U extends List, V extends List> T m3() { + return null; + } + + List l1 = m1(); + List l2 = m2(); + List l3 = m3(); +} + diff --git a/langtools/test/tools/javac/generics/inference/6369605/T6369605b.java b/langtools/test/tools/javac/generics/inference/6369605/T6369605b.java new file mode 100644 index 00000000000..7666b5780bb --- /dev/null +++ b/langtools/test/tools/javac/generics/inference/6369605/T6369605b.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2010, 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 6369605 + * @summary Unconstrained type variables fails to include bounds + * @author mcimadamore + * @compile T6369605b.java + */ +import java.util.List; + +class T6369605b { + static , X> List m1() { + return null; + } + + static , U extends List, X> List m2() { + return null; + } + + static , U extends List, V extends List, X> List m3() { + return null; + } + + List l1 = m1(); + List l2 = m2(); + List l3 = m3(); +} diff --git a/langtools/test/tools/javac/generics/inference/6638712/T6638712a.out b/langtools/test/tools/javac/generics/inference/6638712/T6638712a.out index 420b2f72c9f..e20744cc1b8 100644 --- a/langtools/test/tools/javac/generics/inference/6638712/T6638712a.out +++ b/langtools/test/tools/javac/generics/inference/6638712/T6638712a.out @@ -1,2 +1,2 @@ -T6638712a.java:16:41: compiler.err.invalid.inferred.types: T, (compiler.misc.inferred.do.not.conform.to.params: java.lang.Iterable>, java.util.List>) +T6638712a.java:16:41: compiler.err.prob.found.req: (compiler.misc.incompatible.types.1: (compiler.misc.no.conforming.instance.exists: T, java.util.Comparator, java.util.Comparator)), java.util.Comparator, java.util.Comparator 1 error From d7787c25cff6038433080895bd33a8353cd9836d Mon Sep 17 00:00:00 2001 From: Jonathan Gibbons Date: Mon, 16 Aug 2010 14:59:21 -0700 Subject: [PATCH 033/100] 6976833: options included twice in Example SimpleCompiler Reviewed-by: darcy --- langtools/test/tools/javac/diags/Example.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/langtools/test/tools/javac/diags/Example.java b/langtools/test/tools/javac/diags/Example.java index 9ff905a8e5a..9b833f65989 100644 --- a/langtools/test/tools/javac/diags/Example.java +++ b/langtools/test/tools/javac/diags/Example.java @@ -421,7 +421,7 @@ class Example implements Comparable { if (verbose) System.err.println("run_simple: " + opts + " " + files); - List args = new ArrayList(opts); + List args = new ArrayList(); if (keys != null || raw) args.add("-XDrawDiagnostics"); From a33a3ae92b5e30ad86474dba4f9d355d09b74c0e Mon Sep 17 00:00:00 2001 From: Mandy Chung Date: Mon, 16 Aug 2010 15:36:13 -0700 Subject: [PATCH 034/100] 6921234: TEST_BUG: java/lang/ClassLoader/deadlock/TestCrossDelegate.sh needs to be modified for Cygwin Add check for CYGWIN Reviewed-by: ohair --- jdk/test/java/lang/ClassLoader/deadlock/TestCrossDelegate.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdk/test/java/lang/ClassLoader/deadlock/TestCrossDelegate.sh b/jdk/test/java/lang/ClassLoader/deadlock/TestCrossDelegate.sh index 47367f7de82..71e07441ce2 100644 --- a/jdk/test/java/lang/ClassLoader/deadlock/TestCrossDelegate.sh +++ b/jdk/test/java/lang/ClassLoader/deadlock/TestCrossDelegate.sh @@ -55,7 +55,7 @@ case "$OS" in Linux ) FS="/" ;; - Windows* ) + Windows* | CYGWIN* ) FS="\\" ;; esac From 77f845359aca6c3050ea7211736198aa2f1722a9 Mon Sep 17 00:00:00 2001 From: "Y. Srinivas Ramakrishna" Date: Mon, 16 Aug 2010 15:58:42 -0700 Subject: [PATCH 035/100] 6948538: CMS: BOT walkers can fall into object allocation and initialization cracks GC workers now recognize an intermediate transient state of blocks which are allocated but have not yet completed initialization. blk_start() calls do not attempt to determine the size of a block in the transient state, rather waiting for the block to become initialized so that it is safe to query its size. Audited and ensured the order of initialization of object fields (klass, free bit and size) to respect block state transition protocol. Also included some new assertion checking code enabled in debug mode. Reviewed-by: chrisphi, johnc, poonam --- .../compactibleFreeListSpace.cpp | 118 ++++++++++++++---- .../compactibleFreeListSpace.hpp | 17 ++- .../concurrentMarkSweepGeneration.cpp | 105 +++++++++++++--- .../concurrentMarkSweepGeneration.hpp | 8 +- .../concurrentMarkSweep/freeChunk.hpp | 18 ++- .../concurrentMarkSweep/promotionInfo.cpp | 4 +- .../includeDB_gc_concurrentMarkSweep | 7 +- hotspot/src/share/vm/includeDB_core | 12 +- .../src/share/vm/memory/blockOffsetTable.cpp | 66 +++++----- .../src/share/vm/memory/blockOffsetTable.hpp | 70 ++++++++--- .../vm/memory/blockOffsetTable.inline.hpp | 14 ++- hotspot/src/share/vm/runtime/globals.hpp | 2 +- 12 files changed, 318 insertions(+), 123 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp index 28f6ef4b0a3..30982f3dd91 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2010, 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 @@ -402,6 +402,29 @@ size_t CompactibleFreeListSpace::max_alloc_in_words() const { return res; } +void LinearAllocBlock::print_on(outputStream* st) const { + st->print_cr(" LinearAllocBlock: ptr = " PTR_FORMAT ", word_size = " SIZE_FORMAT + ", refillsize = " SIZE_FORMAT ", allocation_size_limit = " SIZE_FORMAT, + _ptr, _word_size, _refillSize, _allocation_size_limit); +} + +void CompactibleFreeListSpace::print_on(outputStream* st) const { + st->print_cr("COMPACTIBLE FREELIST SPACE"); + st->print_cr(" Space:"); + Space::print_on(st); + + st->print_cr("promoInfo:"); + _promoInfo.print_on(st); + + st->print_cr("_smallLinearAllocBlock"); + _smallLinearAllocBlock.print_on(st); + + // dump_memory_block(_smallLinearAllocBlock->_ptr, 128); + + st->print_cr(" _fitStrategy = %s, _adaptive_freelists = %s", + _fitStrategy?"true":"false", _adaptive_freelists?"true":"false"); +} + void CompactibleFreeListSpace::print_indexed_free_lists(outputStream* st) const { reportIndexedFreeListStatistics(); @@ -557,13 +580,15 @@ size_t CompactibleFreeListSpace::maxChunkSizeInIndexedFreeLists() const { void CompactibleFreeListSpace::set_end(HeapWord* value) { HeapWord* prevEnd = end(); assert(prevEnd != value, "unnecessary set_end call"); - assert(prevEnd == NULL || value >= unallocated_block(), "New end is below unallocated block"); + assert(prevEnd == NULL || !BlockOffsetArrayUseUnallocatedBlock || value >= unallocated_block(), + "New end is below unallocated block"); _end = value; if (prevEnd != NULL) { // Resize the underlying block offset table. _bt.resize(pointer_delta(value, bottom())); if (value <= prevEnd) { - assert(value >= unallocated_block(), "New end is below unallocated block"); + assert(!BlockOffsetArrayUseUnallocatedBlock || value >= unallocated_block(), + "New end is below unallocated block"); } else { // Now, take this new chunk and add it to the free blocks. // Note that the BOT has not yet been updated for this block. @@ -938,7 +963,6 @@ HeapWord* CompactibleFreeListSpace::block_start_careful(const void* p) const { size_t CompactibleFreeListSpace::block_size(const HeapWord* p) const { NOT_PRODUCT(verify_objects_initialized()); - assert(MemRegion(bottom(), end()).contains(p), "p not in space"); // This must be volatile, or else there is a danger that the compiler // will compile the code below into a sometimes-infinite loop, by keeping // the value read the first time in a register. @@ -957,7 +981,7 @@ size_t CompactibleFreeListSpace::block_size(const HeapWord* p) const { // must read from what 'p' points to in each loop. klassOop k = ((volatile oopDesc*)p)->klass_or_null(); if (k != NULL) { - assert(k->is_oop(true /* ignore mark word */), "Should really be klass oop."); + assert(k->is_oop(true /* ignore mark word */), "Should be klass oop"); oop o = (oop)p; assert(o->is_parsable(), "Should be parsable"); assert(o->is_oop(true /* ignore mark word */), "Should be an oop."); @@ -1231,7 +1255,6 @@ HeapWord* CompactibleFreeListSpace::allocate_adaptive_freelists(size_t size) { // satisfy the request. This is different that // evm. // Don't record chunk off a LinAB? smallSplitBirth(size); - } else { // Raid the exact free lists larger than size, even if they are not // overpopulated. @@ -1449,6 +1472,7 @@ CompactibleFreeListSpace::getChunkFromLinearAllocBlock(LinearAllocBlock *blk, // Update BOT last so that other (parallel) GC threads see a consistent // view of the BOT and free blocks. // Above must occur before BOT is updated below. + OrderAccess::storestore(); _bt.split_block(res, blk_size, size); // adjust block offset table } return res; @@ -1477,6 +1501,7 @@ HeapWord* CompactibleFreeListSpace::getChunkFromLinearAllocBlockRemainder( // Update BOT last so that other (parallel) GC threads see a consistent // view of the BOT and free blocks. // Above must occur before BOT is updated below. + OrderAccess::storestore(); _bt.split_block(res, blk_size, size); // adjust block offset table _bt.allocated(res, size); } @@ -1856,6 +1881,8 @@ CompactibleFreeListSpace::splitChunkAndReturnRemainder(FreeChunk* chunk, ffc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads. // Above must occur before BOT is updated below. // adjust block offset table + OrderAccess::storestore(); + assert(chunk->isFree() && ffc->isFree(), "Error"); _bt.split_block((HeapWord*)chunk, chunk->size(), new_size); if (rem_size < SmallForDictionary) { bool is_par = (SharedHeap::heap()->n_par_threads() > 0); @@ -1911,8 +1938,7 @@ void CompactibleFreeListSpace::save_marks() { // mark the "end" of the used space at the time of this call; // note, however, that promoted objects from this point // on are tracked in the _promoInfo below. - set_saved_mark_word(BlockOffsetArrayUseUnallocatedBlock ? - unallocated_block() : end()); + set_saved_mark_word(unallocated_block()); // inform allocator that promotions should be tracked. assert(_promoInfo.noPromotions(), "_promoInfo inconsistency"); _promoInfo.startTrackingPromotions(); @@ -2238,8 +2264,7 @@ void CompactibleFreeListSpace::split(size_t from, size_t to1) { } void CompactibleFreeListSpace::print() const { - tty->print(" CompactibleFreeListSpace"); - Space::print(); + Space::print_on(tty); } void CompactibleFreeListSpace::prepare_for_verify() { @@ -2253,18 +2278,28 @@ class VerifyAllBlksClosure: public BlkClosure { private: const CompactibleFreeListSpace* _sp; const MemRegion _span; + HeapWord* _last_addr; + size_t _last_size; + bool _last_was_obj; + bool _last_was_live; public: VerifyAllBlksClosure(const CompactibleFreeListSpace* sp, - MemRegion span) : _sp(sp), _span(span) { } + MemRegion span) : _sp(sp), _span(span), + _last_addr(NULL), _last_size(0), + _last_was_obj(false), _last_was_live(false) { } virtual size_t do_blk(HeapWord* addr) { size_t res; + bool was_obj = false; + bool was_live = false; if (_sp->block_is_obj(addr)) { + was_obj = true; oop p = oop(addr); guarantee(p->is_oop(), "Should be an oop"); res = _sp->adjustObjectSize(p->size()); if (_sp->obj_is_alive(addr)) { + was_live = true; p->verify(); } } else { @@ -2275,7 +2310,20 @@ class VerifyAllBlksClosure: public BlkClosure { "Chunk should be on a free list"); } } - guarantee(res != 0, "Livelock: no rank reduction!"); + if (res == 0) { + gclog_or_tty->print_cr("Livelock: no rank reduction!"); + gclog_or_tty->print_cr( + " Current: addr = " PTR_FORMAT ", size = " SIZE_FORMAT ", obj = %s, live = %s \n" + " Previous: addr = " PTR_FORMAT ", size = " SIZE_FORMAT ", obj = %s, live = %s \n", + addr, res, was_obj ?"true":"false", was_live ?"true":"false", + _last_addr, _last_size, _last_was_obj?"true":"false", _last_was_live?"true":"false"); + _sp->print_on(gclog_or_tty); + guarantee(false, "Seppuku!"); + } + _last_addr = addr; + _last_size = res; + _last_was_obj = was_obj; + _last_was_live = was_live; return res; } }; @@ -2521,7 +2569,7 @@ void CFLS_LAB::modify_initialization(size_t n, unsigned wt) { HeapWord* CFLS_LAB::alloc(size_t word_sz) { FreeChunk* res; - word_sz = _cfls->adjustObjectSize(word_sz); + guarantee(word_sz == _cfls->adjustObjectSize(word_sz), "Error"); if (word_sz >= CompactibleFreeListSpace::IndexSetSize) { // This locking manages sync with other large object allocations. MutexLockerEx x(_cfls->parDictionaryAllocLock(), @@ -2667,12 +2715,12 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n (cur_sz < CompactibleFreeListSpace::IndexSetSize) && (CMSSplitIndexedFreeListBlocks || k <= 1); k++, cur_sz = k * word_sz) { - FreeList* gfl = &_indexedFreeList[cur_sz]; FreeList fl_for_cur_sz; // Empty. fl_for_cur_sz.set_size(cur_sz); { MutexLockerEx x(_indexedFreeListParLocks[cur_sz], Mutex::_no_safepoint_check_flag); + FreeList* gfl = &_indexedFreeList[cur_sz]; if (gfl->count() != 0) { // nn is the number of chunks of size cur_sz that // we'd need to split k-ways each, in order to create @@ -2685,9 +2733,9 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n // we increment the split death count by the number of blocks // we just took from the cur_sz-size blocks list and which // we will be splitting below. - ssize_t deaths = _indexedFreeList[cur_sz].splitDeaths() + + ssize_t deaths = gfl->splitDeaths() + fl_for_cur_sz.count(); - _indexedFreeList[cur_sz].set_splitDeaths(deaths); + gfl->set_splitDeaths(deaths); } } } @@ -2703,18 +2751,25 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n // access the main chunk sees it as a single free block until we // change it. size_t fc_size = fc->size(); + assert(fc->isFree(), "Error"); for (int i = k-1; i >= 0; i--) { FreeChunk* ffc = (FreeChunk*)((HeapWord*)fc + i * word_sz); + assert((i != 0) || + ((fc == ffc) && ffc->isFree() && + (ffc->size() == k*word_sz) && (fc_size == word_sz)), + "Counting error"); ffc->setSize(word_sz); - ffc->linkNext(NULL); ffc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads. + ffc->linkNext(NULL); // Above must occur before BOT is updated below. - // splitting from the right, fc_size == (k - i + 1) * wordsize - _bt.mark_block((HeapWord*)ffc, word_sz); + OrderAccess::storestore(); + // splitting from the right, fc_size == i * word_sz + _bt.mark_block((HeapWord*)ffc, word_sz, true /* reducing */); fc_size -= word_sz; - _bt.verify_not_unallocated((HeapWord*)ffc, ffc->size()); + assert(fc_size == i*word_sz, "Error"); + _bt.verify_not_unallocated((HeapWord*)ffc, word_sz); _bt.verify_single_block((HeapWord*)fc, fc_size); - _bt.verify_single_block((HeapWord*)ffc, ffc->size()); + _bt.verify_single_block((HeapWord*)ffc, word_sz); // Push this on "fl". fl->returnChunkAtHead(ffc); } @@ -2744,7 +2799,7 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n _dictionary->minSize()), FreeBlockDictionary::atLeast); if (fc != NULL) { - _bt.allocated((HeapWord*)fc, fc->size()); // update _unallocated_blk + _bt.allocated((HeapWord*)fc, fc->size(), true /* reducing */); // update _unallocated_blk dictionary()->dictCensusUpdate(fc->size(), true /*split*/, false /*birth*/); @@ -2754,8 +2809,10 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n } } if (fc == NULL) return; - assert((ssize_t)n >= 1, "Control point invariant"); // Otherwise, split up that block. + assert((ssize_t)n >= 1, "Control point invariant"); + assert(fc->isFree(), "Error: should be a free block"); + _bt.verify_single_block((HeapWord*)fc, fc->size()); const size_t nn = fc->size() / word_sz; n = MIN2(nn, n); assert((ssize_t)n >= 1, "Control point invariant"); @@ -2773,6 +2830,7 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n // dictionary and return, leaving "fl" empty. if (n == 0) { returnChunkToDictionary(fc); + assert(fl->count() == 0, "We never allocated any blocks"); return; } @@ -2785,11 +2843,14 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n size_t prefix_size = n * word_sz; rem_fc = (FreeChunk*)((HeapWord*)fc + prefix_size); rem_fc->setSize(rem); - rem_fc->linkNext(NULL); rem_fc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads. + rem_fc->linkNext(NULL); // Above must occur before BOT is updated below. assert((ssize_t)n > 0 && prefix_size > 0 && rem_fc > fc, "Error"); + OrderAccess::storestore(); _bt.split_block((HeapWord*)fc, fc->size(), prefix_size); + assert(fc->isFree(), "Error"); + fc->setSize(prefix_size); if (rem >= IndexSetSize) { returnChunkToDictionary(rem_fc); dictionary()->dictCensusUpdate(rem, true /*split*/, true /*birth*/); @@ -2815,11 +2876,12 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n for (ssize_t i = n-1; i > 0; i--) { FreeChunk* ffc = (FreeChunk*)((HeapWord*)fc + i * word_sz); ffc->setSize(word_sz); - ffc->linkNext(NULL); ffc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads. + ffc->linkNext(NULL); // Above must occur before BOT is updated below. + OrderAccess::storestore(); // splitting from the right, fc_size == (n - i + 1) * wordsize - _bt.mark_block((HeapWord*)ffc, word_sz); + _bt.mark_block((HeapWord*)ffc, word_sz, true /* reducing */); fc_size -= word_sz; _bt.verify_not_unallocated((HeapWord*)ffc, ffc->size()); _bt.verify_single_block((HeapWord*)ffc, ffc->size()); @@ -2828,9 +2890,11 @@ void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n fl->returnChunkAtHead(ffc); } // First chunk + assert(fc->isFree() && fc->size() == n*word_sz, "Error: should still be a free block"); + // The blocks above should show their new sizes before the first block below fc->setSize(word_sz); + fc->linkPrev(NULL); // idempotent wrt free-ness, see assert above fc->linkNext(NULL); - fc->linkPrev(NULL); _bt.verify_not_unallocated((HeapWord*)fc, fc->size()); _bt.verify_single_block((HeapWord*)fc, fc->size()); fl->returnChunkAtHead(fc); diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp index 8bca5df52f5..6d67e941161 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2010, 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 @@ -48,6 +48,8 @@ class LinearAllocBlock VALUE_OBJ_CLASS_SPEC { size_t _word_size; size_t _refillSize; size_t _allocation_size_limit; // largest size that will be allocated + + void print_on(outputStream* st) const; }; // Concrete subclass of CompactibleSpace that implements @@ -249,10 +251,14 @@ class CompactibleFreeListSpace: public CompactibleSpace { size_t numFreeBlocksInIndexedFreeLists() const; // Accessor HeapWord* unallocated_block() const { - HeapWord* ub = _bt.unallocated_block(); - assert(ub >= bottom() && - ub <= end(), "space invariant"); - return ub; + if (BlockOffsetArrayUseUnallocatedBlock) { + HeapWord* ub = _bt.unallocated_block(); + assert(ub >= bottom() && + ub <= end(), "space invariant"); + return ub; + } else { + return end(); + } } void freed(HeapWord* start, size_t size) { _bt.freed(start, size); @@ -476,6 +482,7 @@ class CompactibleFreeListSpace: public CompactibleSpace { // Debugging support void print() const; + void print_on(outputStream* st) const; void prepare_for_verify(); void verify(bool allow_dirty) const; void verifyFreeLists() const PRODUCT_RETURN; diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp index 61dbb799186..d0cefc894c9 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp @@ -1019,7 +1019,7 @@ HeapWord* ConcurrentMarkSweepGeneration::allocate(size_t size, } HeapWord* ConcurrentMarkSweepGeneration::have_lock_and_allocate(size_t size, - bool tlab) { + bool tlab /* ignored */) { assert_lock_strong(freelistLock()); size_t adjustedSize = CompactibleFreeListSpace::adjustObjectSize(size); HeapWord* res = cmsSpace()->allocate(adjustedSize); @@ -1032,6 +1032,11 @@ HeapWord* ConcurrentMarkSweepGeneration::have_lock_and_allocate(size_t size, // allowing the object to be blackened (and its references scanned) // either during a preclean phase or at the final checkpoint. if (res != NULL) { + // We may block here with an uninitialized object with + // its mark-bit or P-bits not yet set. Such objects need + // to be safely navigable by block_start(). + assert(oop(res)->klass_or_null() == NULL, "Object should be uninitialized here."); + assert(!((FreeChunk*)res)->isFree(), "Error, block will look free but show wrong size"); collector()->direct_allocated(res, adjustedSize); _direct_allocated_words += adjustedSize; // allocation counters @@ -1061,8 +1066,14 @@ void CMSCollector::direct_allocated(HeapWord* start, size_t size) { // [see comments preceding SweepClosure::do_blk() below for details] // 1. need to mark the object as live so it isn't collected // 2. need to mark the 2nd bit to indicate the object may be uninitialized - // 3. need to mark the end of the object so sweeper can skip over it - // if it's uninitialized when the sweeper reaches it. + // 3. need to mark the end of the object so marking, precleaning or sweeping + // can skip over uninitialized or unparsable objects. An allocated + // object is considered uninitialized for our purposes as long as + // its klass word is NULL. (Unparsable objects are those which are + // initialized in the sense just described, but whose sizes can still + // not be correctly determined. Note that the class of unparsable objects + // can only occur in the perm gen. All old gen objects are parsable + // as soon as they are initialized.) _markBitMap.mark(start); // object is live _markBitMap.mark(start + 1); // object is potentially uninitialized? _markBitMap.mark(start + size - 1); @@ -1088,7 +1099,13 @@ void CMSCollector::promoted(bool par, HeapWord* start, // We don't need to mark the object as uninitialized (as // in direct_allocated above) because this is being done with the // world stopped and the object will be initialized by the - // time the sweeper gets to look at it. + // time the marking, precleaning or sweeping get to look at it. + // But see the code for copying objects into the CMS generation, + // where we need to ensure that concurrent readers of the + // block offset table are able to safely navigate a block that + // is in flux from being free to being allocated (and in + // transition while being copied into) and subsequently + // becoming a bona-fide object when the copy/promotion is complete. assert(SafepointSynchronize::is_at_safepoint(), "expect promotion only at safepoints"); @@ -1304,6 +1321,48 @@ ConcurrentMarkSweepGeneration::allocation_limit_reached(Space* space, return collector()->allocation_limit_reached(space, top, word_sz); } +// IMPORTANT: Notes on object size recognition in CMS. +// --------------------------------------------------- +// A block of storage in the CMS generation is always in +// one of three states. A free block (FREE), an allocated +// object (OBJECT) whose size() method reports the correct size, +// and an intermediate state (TRANSIENT) in which its size cannot +// be accurately determined. +// STATE IDENTIFICATION: (32 bit and 64 bit w/o COOPS) +// ----------------------------------------------------- +// FREE: klass_word & 1 == 1; mark_word holds block size +// +// OBJECT: klass_word installed; klass_word != 0 && klass_word & 0 == 0; +// obj->size() computes correct size +// [Perm Gen objects needs to be "parsable" before they can be navigated] +// +// TRANSIENT: klass_word == 0; size is indeterminate until we become an OBJECT +// +// STATE IDENTIFICATION: (64 bit+COOPS) +// ------------------------------------ +// FREE: mark_word & CMS_FREE_BIT == 1; mark_word & ~CMS_FREE_BIT gives block_size +// +// OBJECT: klass_word installed; klass_word != 0; +// obj->size() computes correct size +// [Perm Gen comment above continues to hold] +// +// TRANSIENT: klass_word == 0; size is indeterminate until we become an OBJECT +// +// +// STATE TRANSITION DIAGRAM +// +// mut / parnew mut / parnew +// FREE --------------------> TRANSIENT ---------------------> OBJECT --| +// ^ | +// |------------------------ DEAD <------------------------------------| +// sweep mut +// +// While a block is in TRANSIENT state its size cannot be determined +// so readers will either need to come back later or stall until +// the size can be determined. Note that for the case of direct +// allocation, P-bits, when available, may be used to determine the +// size of an object that may not yet have been initialized. + // Things to support parallel young-gen collection. oop ConcurrentMarkSweepGeneration::par_promote(int thread_num, @@ -1331,33 +1390,39 @@ ConcurrentMarkSweepGeneration::par_promote(int thread_num, } } assert(promoInfo->has_spooling_space(), "Control point invariant"); - HeapWord* obj_ptr = ps->lab.alloc(word_sz); + const size_t alloc_sz = CompactibleFreeListSpace::adjustObjectSize(word_sz); + HeapWord* obj_ptr = ps->lab.alloc(alloc_sz); if (obj_ptr == NULL) { - obj_ptr = expand_and_par_lab_allocate(ps, word_sz); + obj_ptr = expand_and_par_lab_allocate(ps, alloc_sz); if (obj_ptr == NULL) { return NULL; } } oop obj = oop(obj_ptr); + OrderAccess::storestore(); assert(obj->klass_or_null() == NULL, "Object should be uninitialized here."); + assert(!((FreeChunk*)obj_ptr)->isFree(), "Error, block will look free but show wrong size"); + // IMPORTANT: See note on object initialization for CMS above. // Otherwise, copy the object. Here we must be careful to insert the // klass pointer last, since this marks the block as an allocated object. // Except with compressed oops it's the mark word. HeapWord* old_ptr = (HeapWord*)old; + // Restore the mark word copied above. + obj->set_mark(m); + assert(obj->klass_or_null() == NULL, "Object should be uninitialized here."); + assert(!((FreeChunk*)obj_ptr)->isFree(), "Error, block will look free but show wrong size"); + OrderAccess::storestore(); + + if (UseCompressedOops) { + // Copy gap missed by (aligned) header size calculation below + obj->set_klass_gap(old->klass_gap()); + } if (word_sz > (size_t)oopDesc::header_size()) { Copy::aligned_disjoint_words(old_ptr + oopDesc::header_size(), obj_ptr + oopDesc::header_size(), word_sz - oopDesc::header_size()); } - if (UseCompressedOops) { - // Copy gap missed by (aligned) header size calculation above - obj->set_klass_gap(old->klass_gap()); - } - - // Restore the mark word copied above. - obj->set_mark(m); - // Now we can track the promoted object, if necessary. We take care // to delay the transition from uninitialized to full object // (i.e., insertion of klass pointer) until after, so that it @@ -1365,18 +1430,22 @@ ConcurrentMarkSweepGeneration::par_promote(int thread_num, if (promoInfo->tracking()) { promoInfo->track((PromotedObject*)obj, old->klass()); } + assert(obj->klass_or_null() == NULL, "Object should be uninitialized here."); + assert(!((FreeChunk*)obj_ptr)->isFree(), "Error, block will look free but show wrong size"); + assert(old->is_oop(), "Will use and dereference old klass ptr below"); // Finally, install the klass pointer (this should be volatile). + OrderAccess::storestore(); obj->set_klass(old->klass()); + // We should now be able to calculate the right size for this object + assert(obj->is_oop() && obj->size() == (int)word_sz, "Error, incorrect size computed for promoted object"); - assert(old->is_oop(), "Will dereference klass ptr below"); collector()->promoted(true, // parallel obj_ptr, old->is_objArray(), word_sz); NOT_PRODUCT( - Atomic::inc(&_numObjectsPromoted); - Atomic::add((jint)CompactibleFreeListSpace::adjustObjectSize(obj->size()), - &_numWordsPromoted); + Atomic::inc_ptr(&_numObjectsPromoted); + Atomic::add_ptr(alloc_sz, &_numWordsPromoted); ) return obj; diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp index 16ed981bbe8..8455ded4a19 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp @@ -1010,10 +1010,10 @@ class ConcurrentMarkSweepGeneration: public CardGeneration { // Non-product stat counters NOT_PRODUCT( - int _numObjectsPromoted; - int _numWordsPromoted; - int _numObjectsAllocated; - int _numWordsAllocated; + size_t _numObjectsPromoted; + size_t _numWordsPromoted; + size_t _numObjectsAllocated; + size_t _numWordsAllocated; ) // Used for sizing decisions diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.hpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.hpp index 19e3c1c0dd2..760fabf434e 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.hpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2010, 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 @@ -110,15 +110,21 @@ class FreeChunk VALUE_OBJ_CLASS_SPEC { } void linkNext(FreeChunk* ptr) { _next = ptr; } void linkPrev(FreeChunk* ptr) { - LP64_ONLY(if (UseCompressedOops) _prev = ptr; else) - _prev = (FreeChunk*)((intptr_t)ptr | 0x1); + LP64_ONLY(if (UseCompressedOops) _prev = ptr; else) + _prev = (FreeChunk*)((intptr_t)ptr | 0x1); } void clearPrev() { _prev = NULL; } void clearNext() { _next = NULL; } void markNotFree() { - LP64_ONLY(if (UseCompressedOops) set_mark(markOopDesc::prototype());) - // Also set _prev to null - _prev = NULL; + // Set _prev (klass) to null before (if) clearing the mark word below + _prev = NULL; +#ifdef _LP64 + if (UseCompressedOops) { + OrderAccess::storestore(); + set_mark(markOopDesc::prototype()); + } +#endif + assert(!isFree(), "Error"); } // Return the address past the end of this chunk diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/promotionInfo.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/promotionInfo.cpp index 9b1fdf07a42..09602b5b702 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/promotionInfo.cpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/promotionInfo.cpp @@ -330,7 +330,7 @@ void PromotionInfo::verify() const { void PromotionInfo::print_on(outputStream* st) const { SpoolBlock* curSpool = NULL; size_t i = 0; - st->print_cr("start & end indices: [" SIZE_FORMAT ", " SIZE_FORMAT ")", + st->print_cr(" start & end indices: [" SIZE_FORMAT ", " SIZE_FORMAT ")", _firstIndex, _nextIndex); for (curSpool = _spoolHead; curSpool != _spoolTail && curSpool != NULL; curSpool = curSpool->nextSpoolBlock) { @@ -350,7 +350,7 @@ void PromotionInfo::print_on(outputStream* st) const { st->print_cr(" free "); i++; } - st->print_cr(SIZE_FORMAT " header spooling blocks", i); + st->print_cr(" " SIZE_FORMAT " header spooling blocks", i); } void SpoolBlock::print_on(outputStream* st) const { diff --git a/hotspot/src/share/vm/gc_implementation/includeDB_gc_concurrentMarkSweep b/hotspot/src/share/vm/gc_implementation/includeDB_gc_concurrentMarkSweep index 99c5b3ae271..6efae46edd1 100644 --- a/hotspot/src/share/vm/gc_implementation/includeDB_gc_concurrentMarkSweep +++ b/hotspot/src/share/vm/gc_implementation/includeDB_gc_concurrentMarkSweep @@ -1,5 +1,5 @@ // -// Copyright (c) 2004, 2009, Oracle and/or its affiliates. All rights reserved. +// Copyright (c) 2004, 2010, 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 @@ -34,6 +34,8 @@ binaryTreeDictionary.cpp spaceDecorator.hpp binaryTreeDictionary.hpp freeBlockDictionary.hpp binaryTreeDictionary.hpp freeList.hpp +blockOffsetTable.inline.hpp concurrentMarkSweepGeneration.hpp + cmsAdaptiveSizePolicy.cpp cmsAdaptiveSizePolicy.hpp cmsAdaptiveSizePolicy.cpp defNewGeneration.hpp cmsAdaptiveSizePolicy.cpp gcStats.hpp @@ -85,7 +87,7 @@ cmsOopClosures.hpp genOopClosures.hpp cmsOopClosures.inline.hpp cmsOopClosures.hpp cmsOopClosures.inline.hpp concurrentMarkSweepGeneration.hpp -cmsPermGen.cpp blockOffsetTable.hpp +cmsPermGen.cpp blockOffsetTable.inline.hpp cmsPermGen.cpp cSpaceCounters.hpp cmsPermGen.cpp cmsPermGen.hpp cmsPermGen.cpp collectedHeap.inline.hpp @@ -121,6 +123,7 @@ compactibleFreeListSpace.cpp universe.inline.hpp compactibleFreeListSpace.cpp vmThread.hpp compactibleFreeListSpace.hpp binaryTreeDictionary.hpp +compactibleFreeListSpace.hpp blockOffsetTable.inline.hpp compactibleFreeListSpace.hpp freeList.hpp compactibleFreeListSpace.hpp promotionInfo.hpp compactibleFreeListSpace.hpp space.hpp diff --git a/hotspot/src/share/vm/includeDB_core b/hotspot/src/share/vm/includeDB_core index fe4252dfcfd..07c40dc394d 100644 --- a/hotspot/src/share/vm/includeDB_core +++ b/hotspot/src/share/vm/includeDB_core @@ -225,7 +225,6 @@ arrayOop.cpp oop.inline.hpp arrayOop.cpp symbolOop.hpp arrayOop.hpp oop.hpp -arrayOop.hpp universe.hpp arrayOop.hpp universe.inline.hpp assembler.cpp assembler.hpp @@ -236,7 +235,6 @@ assembler.cpp icache.hpp assembler.cpp os.hpp assembler.hpp allocation.hpp -assembler.hpp allocation.inline.hpp assembler.hpp debug.hpp assembler.hpp growableArray.hpp assembler.hpp oopRecorder.hpp @@ -330,7 +328,7 @@ blockOffsetTable.cpp collectedHeap.inline.hpp blockOffsetTable.cpp iterator.hpp blockOffsetTable.cpp java.hpp blockOffsetTable.cpp oop.inline.hpp -blockOffsetTable.cpp space.hpp +blockOffsetTable.cpp space.inline.hpp blockOffsetTable.cpp universe.hpp blockOffsetTable.hpp globalDefinitions.hpp @@ -338,6 +336,7 @@ blockOffsetTable.hpp memRegion.hpp blockOffsetTable.hpp virtualspace.hpp blockOffsetTable.inline.hpp blockOffsetTable.hpp +blockOffsetTable.inline.hpp safepoint.hpp blockOffsetTable.inline.hpp space.hpp bytecode.cpp bytecode.hpp @@ -1807,7 +1806,7 @@ generateOopMap.hpp signature.hpp generateOopMap.hpp universe.inline.hpp generation.cpp allocation.inline.hpp -generation.cpp blockOffsetTable.hpp +generation.cpp blockOffsetTable.inline.hpp generation.cpp cardTableRS.hpp generation.cpp collectedHeap.inline.hpp generation.cpp copy.hpp @@ -3436,7 +3435,7 @@ perfMemory_.cpp perfMemory.hpp perfMemory_.cpp resourceArea.hpp perfMemory_.cpp vmSymbols.hpp -permGen.cpp blockOffsetTable.hpp +permGen.cpp blockOffsetTable.inline.hpp permGen.cpp cSpaceCounters.hpp permGen.cpp collectedHeap.inline.hpp permGen.cpp compactPermGen.hpp @@ -3805,7 +3804,7 @@ sizes.cpp sizes.hpp sizes.hpp allocation.hpp sizes.hpp globalDefinitions.hpp -space.cpp blockOffsetTable.hpp +space.cpp blockOffsetTable.inline.hpp space.cpp copy.hpp space.cpp defNewGeneration.hpp space.cpp genCollectedHeap.hpp @@ -3835,7 +3834,6 @@ space.hpp prefetch.hpp space.hpp watermark.hpp space.hpp workgroup.hpp -space.inline.hpp blockOffsetTable.inline.hpp space.inline.hpp collectedHeap.hpp space.inline.hpp safepoint.hpp space.inline.hpp space.hpp diff --git a/hotspot/src/share/vm/memory/blockOffsetTable.cpp b/hotspot/src/share/vm/memory/blockOffsetTable.cpp index 03870202785..ee3ef125c52 100644 --- a/hotspot/src/share/vm/memory/blockOffsetTable.cpp +++ b/hotspot/src/share/vm/memory/blockOffsetTable.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2010, 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 @@ -103,13 +103,13 @@ void BlockOffsetSharedArray::serialize(SerializeOopClosure* soc, ////////////////////////////////////////////////////////////////////// BlockOffsetArray::BlockOffsetArray(BlockOffsetSharedArray* array, - MemRegion mr, bool init_to_zero) : + MemRegion mr, bool init_to_zero_) : BlockOffsetTable(mr.start(), mr.end()), - _array(array), - _init_to_zero(init_to_zero) + _array(array) { assert(_bottom <= _end, "arguments out of order"); - if (!_init_to_zero) { + set_init_to_zero(init_to_zero_); + if (!init_to_zero_) { // initialize cards to point back to mr.start() set_remainder_to_point_to_start(mr.start() + N_words, mr.end()); _array->set_offset_array(0, 0); // set first card to 0 @@ -121,8 +121,9 @@ BlockOffsetArray::BlockOffsetArray(BlockOffsetSharedArray* array, // a right-open interval: [start, end) void BlockOffsetArray:: -set_remainder_to_point_to_start(HeapWord* start, HeapWord* end) { +set_remainder_to_point_to_start(HeapWord* start, HeapWord* end, bool reducing) { + check_reducing_assertion(reducing); if (start >= end) { // The start address is equal to the end address (or to // the right of the end address) so there are not cards @@ -167,7 +168,7 @@ set_remainder_to_point_to_start(HeapWord* start, HeapWord* end) { size_t end_card = _array->index_for(end-1); assert(start ==_array->address_for_index(start_card), "Precondition"); assert(end ==_array->address_for_index(end_card)+N_words, "Precondition"); - set_remainder_to_point_to_start_incl(start_card, end_card); // closed interval + set_remainder_to_point_to_start_incl(start_card, end_card, reducing); // closed interval } @@ -175,7 +176,9 @@ set_remainder_to_point_to_start(HeapWord* start, HeapWord* end) { // a closed, inclusive interval: [start_card, end_card], cf set_remainder_to_point_to_start() // above. void -BlockOffsetArray::set_remainder_to_point_to_start_incl(size_t start_card, size_t end_card) { +BlockOffsetArray::set_remainder_to_point_to_start_incl(size_t start_card, size_t end_card, bool reducing) { + + check_reducing_assertion(reducing); if (start_card > end_card) { return; } @@ -191,11 +194,11 @@ BlockOffsetArray::set_remainder_to_point_to_start_incl(size_t start_card, size_t size_t reach = start_card - 1 + (power_to_cards_back(i+1) - 1); offset = N_words + i; if (reach >= end_card) { - _array->set_offset_array(start_card_for_region, end_card, offset); + _array->set_offset_array(start_card_for_region, end_card, offset, reducing); start_card_for_region = reach + 1; break; } - _array->set_offset_array(start_card_for_region, reach, offset); + _array->set_offset_array(start_card_for_region, reach, offset, reducing); start_card_for_region = reach + 1; } assert(start_card_for_region > end_card, "Sanity check"); @@ -211,8 +214,10 @@ void BlockOffsetArray::check_all_cards(size_t start_card, size_t end_card) const return; } guarantee(_array->offset_array(start_card) == N_words, "Wrong value in second card"); + u_char last_entry = N_words; for (size_t c = start_card + 1; c <= end_card; c++ /* yeah! */) { u_char entry = _array->offset_array(c); + guarantee(entry >= last_entry, "Monotonicity"); if (c - start_card > power_to_cards_back(1)) { guarantee(entry > N_words, "Should be in logarithmic region"); } @@ -220,11 +225,13 @@ void BlockOffsetArray::check_all_cards(size_t start_card, size_t end_card) const size_t landing_card = c - backskip; guarantee(landing_card >= (start_card - 1), "Inv"); if (landing_card >= start_card) { - guarantee(_array->offset_array(landing_card) <= entry, "monotonicity"); + guarantee(_array->offset_array(landing_card) <= entry, "Monotonicity"); } else { - guarantee(landing_card == start_card - 1, "Tautology"); + guarantee(landing_card == (start_card - 1), "Tautology"); + // Note that N_words is the maximum offset value guarantee(_array->offset_array(landing_card) <= N_words, "Offset value"); } + last_entry = entry; // remember for monotonicity test } } @@ -243,7 +250,7 @@ BlockOffsetArray::alloc_block(HeapWord* blk_start, HeapWord* blk_end) { void BlockOffsetArray::do_block_internal(HeapWord* blk_start, HeapWord* blk_end, - Action action) { + Action action, bool reducing) { assert(Universe::heap()->is_in_reserved(blk_start), "reference must be into the heap"); assert(Universe::heap()->is_in_reserved(blk_end-1), @@ -275,18 +282,18 @@ BlockOffsetArray::do_block_internal(HeapWord* blk_start, switch (action) { case Action_mark: { if (init_to_zero()) { - _array->set_offset_array(start_index, boundary, blk_start); + _array->set_offset_array(start_index, boundary, blk_start, reducing); break; } // Else fall through to the next case } case Action_single: { - _array->set_offset_array(start_index, boundary, blk_start); + _array->set_offset_array(start_index, boundary, blk_start, reducing); // We have finished marking the "offset card". We need to now // mark the subsequent cards that this blk spans. if (start_index < end_index) { HeapWord* rem_st = _array->address_for_index(start_index) + N_words; HeapWord* rem_end = _array->address_for_index(end_index) + N_words; - set_remainder_to_point_to_start(rem_st, rem_end); + set_remainder_to_point_to_start(rem_st, rem_end, reducing); } break; } @@ -395,7 +402,7 @@ void BlockOffsetArrayNonContigSpace::split_block(HeapWord* blk, // Indices for starts of prefix block and suffix block. size_t pref_index = _array->index_for(pref_addr); if (_array->address_for_index(pref_index) != pref_addr) { - // pref_addr deos not begin pref_index + // pref_addr does not begin pref_index pref_index++; } @@ -430,18 +437,18 @@ void BlockOffsetArrayNonContigSpace::split_block(HeapWord* blk, if (num_suff_cards > 0) { HeapWord* boundary = _array->address_for_index(suff_index); // Set the offset card for suffix block - _array->set_offset_array(suff_index, boundary, suff_addr); + _array->set_offset_array(suff_index, boundary, suff_addr, true /* reducing */); // Change any further cards that need changing in the suffix if (num_pref_cards > 0) { if (num_pref_cards >= num_suff_cards) { // Unilaterally fix all of the suffix cards: closed card // index interval in args below. - set_remainder_to_point_to_start_incl(suff_index + 1, end_index - 1); + set_remainder_to_point_to_start_incl(suff_index + 1, end_index - 1, true /* reducing */); } else { // Unilaterally fix the first (num_pref_cards - 1) following // the "offset card" in the suffix block. set_remainder_to_point_to_start_incl(suff_index + 1, - suff_index + num_pref_cards - 1); + suff_index + num_pref_cards - 1, true /* reducing */); // Fix the appropriate cards in the remainder of the // suffix block -- these are the last num_pref_cards // cards in each power block of the "new" range plumbed @@ -461,7 +468,7 @@ void BlockOffsetArrayNonContigSpace::split_block(HeapWord* blk, // is non-null. if (left_index <= right_index) { _array->set_offset_array(left_index, right_index, - N_words + i - 1); + N_words + i - 1, true /* reducing */); } else { more = false; // we are done } @@ -482,7 +489,7 @@ void BlockOffsetArrayNonContigSpace::split_block(HeapWord* blk, more = false; } assert(left_index <= right_index, "Error"); - _array->set_offset_array(left_index, right_index, N_words + i - 1); + _array->set_offset_array(left_index, right_index, N_words + i - 1, true /* reducing */); i++; } } @@ -501,14 +508,13 @@ void BlockOffsetArrayNonContigSpace::split_block(HeapWord* blk, // any cards subsequent to the first one. void BlockOffsetArrayNonContigSpace::mark_block(HeapWord* blk_start, - HeapWord* blk_end) { - do_block_internal(blk_start, blk_end, Action_mark); + HeapWord* blk_end, bool reducing) { + do_block_internal(blk_start, blk_end, Action_mark, reducing); } HeapWord* BlockOffsetArrayNonContigSpace::block_start_unsafe( const void* addr) const { assert(_array->offset_array(0) == 0, "objects can't cross covered areas"); - assert(_bottom <= addr && addr < _end, "addr must be covered by this Array"); // Must read this exactly once because it can be modified by parallel @@ -542,9 +548,10 @@ HeapWord* BlockOffsetArrayNonContigSpace::block_start_unsafe( debug_only(HeapWord* last = q); // for debugging q = n; n += _sp->block_size(n); + assert(n > q, err_msg("Looping at: " INTPTR_FORMAT, n)); } - assert(q <= addr, "wrong order for current and arg"); - assert(addr <= n, "wrong order for arg and next"); + assert(q <= addr, err_msg("wrong order for current (" INTPTR_FORMAT ") <= arg (" INTPTR_FORMAT ")", q, addr)); + assert(addr <= n, err_msg("wrong order for arg (" INTPTR_FORMAT ") <= next (" INTPTR_FORMAT ")", addr, n)); return q; } @@ -727,9 +734,8 @@ void BlockOffsetArrayContigSpace::alloc_block_work(HeapWord* blk_start, _next_offset_index = end_index + 1; // Calculate _next_offset_threshold this way because end_index // may be the last valid index in the covered region. - _next_offset_threshold = _array->address_for_index(end_index) + - N_words; - assert(_next_offset_threshold >= blk_end, "Incorrent offset threshold"); + _next_offset_threshold = _array->address_for_index(end_index) + N_words; + assert(_next_offset_threshold >= blk_end, "Incorrect offset threshold"); #ifdef ASSERT // The offset can be 0 if the block starts on a boundary. That diff --git a/hotspot/src/share/vm/memory/blockOffsetTable.hpp b/hotspot/src/share/vm/memory/blockOffsetTable.hpp index db9461c5ef1..e122ac54004 100644 --- a/hotspot/src/share/vm/memory/blockOffsetTable.hpp +++ b/hotspot/src/share/vm/memory/blockOffsetTable.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2010, 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 @@ -107,6 +107,8 @@ class BlockOffsetSharedArray: public CHeapObj { N_words = 1 << LogN_words }; + bool _init_to_zero; + // The reserved region covered by the shared array. MemRegion _reserved; @@ -125,17 +127,28 @@ class BlockOffsetSharedArray: public CHeapObj { assert(index < _vs.committed_size(), "index out of range"); return _offset_array[index]; } - void set_offset_array(size_t index, u_char offset) { + // An assertion-checking helper method for the set_offset_array() methods below. + void check_reducing_assertion(bool reducing); + + void set_offset_array(size_t index, u_char offset, bool reducing = false) { + check_reducing_assertion(reducing); assert(index < _vs.committed_size(), "index out of range"); + assert(!reducing || _offset_array[index] >= offset, "Not reducing"); _offset_array[index] = offset; } - void set_offset_array(size_t index, HeapWord* high, HeapWord* low) { + + void set_offset_array(size_t index, HeapWord* high, HeapWord* low, bool reducing = false) { + check_reducing_assertion(reducing); assert(index < _vs.committed_size(), "index out of range"); assert(high >= low, "addresses out of order"); assert(pointer_delta(high, low) <= N_words, "offset too large"); + assert(!reducing || _offset_array[index] >= (u_char)pointer_delta(high, low), + "Not reducing"); _offset_array[index] = (u_char)pointer_delta(high, low); } - void set_offset_array(HeapWord* left, HeapWord* right, u_char offset) { + + void set_offset_array(HeapWord* left, HeapWord* right, u_char offset, bool reducing = false) { + check_reducing_assertion(reducing); assert(index_for(right - 1) < _vs.committed_size(), "right address out of range"); assert(left < right, "Heap addresses out of order"); @@ -150,12 +163,14 @@ class BlockOffsetSharedArray: public CHeapObj { size_t i = index_for(left); const size_t end = i + num_cards; for (; i < end; i++) { + assert(!reducing || _offset_array[i] >= offset, "Not reducing"); _offset_array[i] = offset; } } } - void set_offset_array(size_t left, size_t right, u_char offset) { + void set_offset_array(size_t left, size_t right, u_char offset, bool reducing = false) { + check_reducing_assertion(reducing); assert(right < _vs.committed_size(), "right address out of range"); assert(left <= right, "indexes out of order"); size_t num_cards = right - left + 1; @@ -169,6 +184,7 @@ class BlockOffsetSharedArray: public CHeapObj { size_t i = left; const size_t end = i + num_cards; for (; i < end; i++) { + assert(!reducing || _offset_array[i] >= offset, "Not reducing"); _offset_array[i] = offset; } } @@ -212,6 +228,11 @@ public: void set_bottom(HeapWord* new_bottom); + // Whether entries should be initialized to zero. Used currently only for + // error checking. + void set_init_to_zero(bool val) { _init_to_zero = val; } + bool init_to_zero() { return _init_to_zero; } + // Updates all the BlockOffsetArray's sharing this shared array to // reflect the current "top"'s of their spaces. void update_offset_arrays(); // Not yet implemented! @@ -285,17 +306,23 @@ class BlockOffsetArray: public BlockOffsetTable { // initialized to point backwards to the beginning of the covered region. bool _init_to_zero; + // An assertion-checking helper method for the set_remainder*() methods below. + void check_reducing_assertion(bool reducing) { _array->check_reducing_assertion(reducing); } + // Sets the entries // corresponding to the cards starting at "start" and ending at "end" // to point back to the card before "start": the interval [start, end) - // is right-open. - void set_remainder_to_point_to_start(HeapWord* start, HeapWord* end); + // is right-open. The last parameter, reducing, indicates whether the + // updates to individual entries always reduce the entry from a higher + // to a lower value. (For example this would hold true during a temporal + // regime during which only block splits were updating the BOT. + void set_remainder_to_point_to_start(HeapWord* start, HeapWord* end, bool reducing = false); // Same as above, except that the args here are a card _index_ interval // that is closed: [start_index, end_index] - void set_remainder_to_point_to_start_incl(size_t start, size_t end); + void set_remainder_to_point_to_start_incl(size_t start, size_t end, bool reducing = false); // A helper function for BOT adjustment/verification work - void do_block_internal(HeapWord* blk_start, HeapWord* blk_end, Action action); + void do_block_internal(HeapWord* blk_start, HeapWord* blk_end, Action action, bool reducing = false); public: // The space may not have its bottom and top set yet, which is why the @@ -303,7 +330,7 @@ class BlockOffsetArray: public BlockOffsetTable { // elements of the array are initialized to zero. Otherwise, they are // initialized to point backwards to the beginning. BlockOffsetArray(BlockOffsetSharedArray* array, MemRegion mr, - bool init_to_zero); + bool init_to_zero_); // Note: this ought to be part of the constructor, but that would require // "this" to be passed as a parameter to a member constructor for @@ -358,6 +385,12 @@ class BlockOffsetArray: public BlockOffsetTable { // If true, initialize array slots with no allocated blocks to zero. // Otherwise, make them point back to the front. bool init_to_zero() { return _init_to_zero; } + // Corresponding setter + void set_init_to_zero(bool val) { + _init_to_zero = val; + assert(_array != NULL, "_array should be non-NULL"); + _array->set_init_to_zero(val); + } // Debugging // Return the index of the last entry in the "active" region. @@ -424,16 +457,16 @@ class BlockOffsetArrayNonContigSpace: public BlockOffsetArray { // of BOT is touched. It is assumed (and verified in the // non-product VM) that the remaining cards of the block // are correct. - void mark_block(HeapWord* blk_start, HeapWord* blk_end); - void mark_block(HeapWord* blk, size_t size) { - mark_block(blk, blk + size); + void mark_block(HeapWord* blk_start, HeapWord* blk_end, bool reducing = false); + void mark_block(HeapWord* blk, size_t size, bool reducing = false) { + mark_block(blk, blk + size, reducing); } // Adjust _unallocated_block to indicate that a particular // block has been newly allocated or freed. It is assumed (and // verified in the non-product VM) that the BOT is correct for // the given block. - void allocated(HeapWord* blk_start, HeapWord* blk_end) { + void allocated(HeapWord* blk_start, HeapWord* blk_end, bool reducing = false) { // Verify that the BOT shows [blk, blk + blk_size) to be one block. verify_single_block(blk_start, blk_end); if (BlockOffsetArrayUseUnallocatedBlock) { @@ -441,14 +474,12 @@ class BlockOffsetArrayNonContigSpace: public BlockOffsetArray { } } - void allocated(HeapWord* blk, size_t size) { - allocated(blk, blk + size); + void allocated(HeapWord* blk, size_t size, bool reducing = false) { + allocated(blk, blk + size, reducing); } void freed(HeapWord* blk_start, HeapWord* blk_end); - void freed(HeapWord* blk, size_t size) { - freed(blk, blk + size); - } + void freed(HeapWord* blk, size_t size); HeapWord* block_start_unsafe(const void* addr) const; @@ -456,7 +487,6 @@ class BlockOffsetArrayNonContigSpace: public BlockOffsetArray { // start of the block that contains the given address. HeapWord* block_start_careful(const void* addr) const; - // Verification & debugging: ensure that the offset table reflects // the fact that the block [blk_start, blk_end) or [blk, blk + size) // is a single block of storage. NOTE: can't const this because of diff --git a/hotspot/src/share/vm/memory/blockOffsetTable.inline.hpp b/hotspot/src/share/vm/memory/blockOffsetTable.inline.hpp index e9bee7907c3..39afcea87da 100644 --- a/hotspot/src/share/vm/memory/blockOffsetTable.inline.hpp +++ b/hotspot/src/share/vm/memory/blockOffsetTable.inline.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2010, 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 @@ -55,10 +55,22 @@ inline HeapWord* BlockOffsetSharedArray::address_for_index(size_t index) const { return result; } +inline void BlockOffsetSharedArray::check_reducing_assertion(bool reducing) { + assert(reducing || !SafepointSynchronize::is_at_safepoint() || init_to_zero() || + Thread::current()->is_VM_thread() || + Thread::current()->is_ConcurrentGC_thread() || + ((!Thread::current()->is_ConcurrentGC_thread()) && + ParGCRareEvent_lock->owned_by_self()), "Crack"); +} ////////////////////////////////////////////////////////////////////////// // BlockOffsetArrayNonContigSpace inlines ////////////////////////////////////////////////////////////////////////// +inline void BlockOffsetArrayNonContigSpace::freed(HeapWord* blk, + size_t size) { + freed(blk, blk + size); +} + inline void BlockOffsetArrayNonContigSpace::freed(HeapWord* blk_start, HeapWord* blk_end) { // Verify that the BOT shows [blk_start, blk_end) to be one block. diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp index 47d837dd000..92dde7768b1 100644 --- a/hotspot/src/share/vm/runtime/globals.hpp +++ b/hotspot/src/share/vm/runtime/globals.hpp @@ -1712,7 +1712,7 @@ class CommandLineFlags { develop(bool, VerifyBlockOffsetArray, false, \ "Do (expensive!) block offset array verification") \ \ - product(bool, BlockOffsetArrayUseUnallocatedBlock, trueInDebug, \ + product(bool, BlockOffsetArrayUseUnallocatedBlock, false, \ "Maintain _unallocated_block in BlockOffsetArray" \ " (currently applicable only to CMS collector)") \ \ From 8eca7db3071b0826a1198ea980265fe964e846f2 Mon Sep 17 00:00:00 2001 From: Michael McMahon Date: Tue, 17 Aug 2010 14:49:01 +0100 Subject: [PATCH 036/100] 6339649: URI.create should include a detail message when throwing IllegalArgumentException Create enclosing exception with message of enclosed Reviewed-by: alanb, chegar --- jdk/src/share/classes/java/net/URI.java | 4 +--- jdk/test/java/net/URI/Test.java | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/jdk/src/share/classes/java/net/URI.java b/jdk/src/share/classes/java/net/URI.java index c05b7b2abaa..9b891b4b4dd 100644 --- a/jdk/src/share/classes/java/net/URI.java +++ b/jdk/src/share/classes/java/net/URI.java @@ -856,9 +856,7 @@ public final class URI try { return new URI(str); } catch (URISyntaxException x) { - IllegalArgumentException y = new IllegalArgumentException(); - y.initCause(x); - throw y; + throw new IllegalArgumentException(x.getMessage(), x); } } diff --git a/jdk/test/java/net/URI/Test.java b/jdk/test/java/net/URI/Test.java index 449f7d1c19e..39b5b9487d1 100644 --- a/jdk/test/java/net/URI/Test.java +++ b/jdk/test/java/net/URI/Test.java @@ -1536,6 +1536,7 @@ public class Test { serial(); urls(); npes(); + bugs(); } @@ -1572,6 +1573,19 @@ public class Test { } + // miscellaneous bugs/rfes that don't fit in with the test framework + + static void bugs() { + // 6339649 - include detail message from nested exception + try { + URI uri = URI.create("http://nowhere.net/should not be permitted"); + } catch (IllegalArgumentException e) { + if ("".equals(e.getMessage()) || e.getMessage() == null) { + throw new RuntimeException ("No detail message"); + } + } + } + public static void main(String[] args) throws Exception { switch (args.length) { From 1f0201f90be4faa610513ddb3415dbb61383e4eb Mon Sep 17 00:00:00 2001 From: Antonios Printezis Date: Tue, 17 Aug 2010 14:40:00 -0400 Subject: [PATCH 037/100] 6959014: G1: assert(minimum_desired_capacity <= maximum_desired_capacity) failed: sanity check There are a few issues in the code that calculates whether to resize the heap and by how much: a) some calculations can overflow 32-bit size_t's, b) min_desired_capacity is not bounded by the max heap size, and c) the assrt that fires is in the wrong place. The fix also includes some tidying up of the related verbose code. Reviewed-by: ysr, jmasa --- .../gc_implementation/g1/g1CollectedHeap.cpp | 97 ++++++++++++------- 1 file changed, 63 insertions(+), 34 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp index d1dd28ca2f3..f717bd9226e 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp @@ -1044,29 +1044,56 @@ resize_if_necessary_after_full_collection(size_t word_size) { const size_t capacity_after_gc = capacity(); const size_t free_after_gc = capacity_after_gc - used_after_gc; + // This is enforced in arguments.cpp. + assert(MinHeapFreeRatio <= MaxHeapFreeRatio, + "otherwise the code below doesn't make sense"); + // We don't have floating point command-line arguments - const double minimum_free_percentage = (double) MinHeapFreeRatio / 100; + const double minimum_free_percentage = (double) MinHeapFreeRatio / 100.0; const double maximum_used_percentage = 1.0 - minimum_free_percentage; - const double maximum_free_percentage = (double) MaxHeapFreeRatio / 100; + const double maximum_free_percentage = (double) MaxHeapFreeRatio / 100.0; const double minimum_used_percentage = 1.0 - maximum_free_percentage; - size_t minimum_desired_capacity = (size_t) (used_after_gc / maximum_used_percentage); - size_t maximum_desired_capacity = (size_t) (used_after_gc / minimum_used_percentage); + const size_t min_heap_size = collector_policy()->min_heap_byte_size(); + const size_t max_heap_size = collector_policy()->max_heap_byte_size(); - // Don't shrink less than the initial size. - minimum_desired_capacity = - MAX2(minimum_desired_capacity, - collector_policy()->initial_heap_byte_size()); - maximum_desired_capacity = - MAX2(maximum_desired_capacity, - collector_policy()->initial_heap_byte_size()); + // We have to be careful here as these two calculations can overflow + // 32-bit size_t's. + double used_after_gc_d = (double) used_after_gc; + double minimum_desired_capacity_d = used_after_gc_d / maximum_used_percentage; + double maximum_desired_capacity_d = used_after_gc_d / minimum_used_percentage; - // We are failing here because minimum_desired_capacity is - assert(used_after_gc <= minimum_desired_capacity, "sanity check"); - assert(minimum_desired_capacity <= maximum_desired_capacity, "sanity check"); + // Let's make sure that they are both under the max heap size, which + // by default will make them fit into a size_t. + double desired_capacity_upper_bound = (double) max_heap_size; + minimum_desired_capacity_d = MIN2(minimum_desired_capacity_d, + desired_capacity_upper_bound); + maximum_desired_capacity_d = MIN2(maximum_desired_capacity_d, + desired_capacity_upper_bound); + + // We can now safely turn them into size_t's. + size_t minimum_desired_capacity = (size_t) minimum_desired_capacity_d; + size_t maximum_desired_capacity = (size_t) maximum_desired_capacity_d; + + // This assert only makes sense here, before we adjust them + // with respect to the min and max heap size. + assert(minimum_desired_capacity <= maximum_desired_capacity, + err_msg("minimum_desired_capacity = "SIZE_FORMAT", " + "maximum_desired_capacity = "SIZE_FORMAT, + minimum_desired_capacity, maximum_desired_capacity)); + + // Should not be greater than the heap max size. No need to adjust + // it with respect to the heap min size as it's a lower bound (i.e., + // we'll try to make the capacity larger than it, not smaller). + minimum_desired_capacity = MIN2(minimum_desired_capacity, max_heap_size); + // Should not be less than the heap min size. No need to adjust it + // with respect to the heap max size as it's an upper bound (i.e., + // we'll try to make the capacity smaller than it, not greater). + maximum_desired_capacity = MAX2(maximum_desired_capacity, min_heap_size); if (PrintGC && Verbose) { - const double free_percentage = ((double)free_after_gc) / capacity(); + const double free_percentage = + (double) free_after_gc / (double) capacity_after_gc; gclog_or_tty->print_cr("Computing new size after full GC "); gclog_or_tty->print_cr(" " " minimum_free_percentage: %6.2f", @@ -1078,45 +1105,47 @@ resize_if_necessary_after_full_collection(size_t word_size) { " capacity: %6.1fK" " minimum_desired_capacity: %6.1fK" " maximum_desired_capacity: %6.1fK", - capacity() / (double) K, - minimum_desired_capacity / (double) K, - maximum_desired_capacity / (double) K); + (double) capacity_after_gc / (double) K, + (double) minimum_desired_capacity / (double) K, + (double) maximum_desired_capacity / (double) K); gclog_or_tty->print_cr(" " - " free_after_gc : %6.1fK" - " used_after_gc : %6.1fK", - free_after_gc / (double) K, - used_after_gc / (double) K); + " free_after_gc: %6.1fK" + " used_after_gc: %6.1fK", + (double) free_after_gc / (double) K, + (double) used_after_gc / (double) K); gclog_or_tty->print_cr(" " " free_percentage: %6.2f", free_percentage); } - if (capacity() < minimum_desired_capacity) { + if (capacity_after_gc < minimum_desired_capacity) { // Don't expand unless it's significant size_t expand_bytes = minimum_desired_capacity - capacity_after_gc; expand(expand_bytes); if (PrintGC && Verbose) { - gclog_or_tty->print_cr(" expanding:" + gclog_or_tty->print_cr(" " + " expanding:" + " max_heap_size: %6.1fK" " minimum_desired_capacity: %6.1fK" " expand_bytes: %6.1fK", - minimum_desired_capacity / (double) K, - expand_bytes / (double) K); + (double) max_heap_size / (double) K, + (double) minimum_desired_capacity / (double) K, + (double) expand_bytes / (double) K); } // No expansion, now see if we want to shrink - } else if (capacity() > maximum_desired_capacity) { + } else if (capacity_after_gc > maximum_desired_capacity) { // Capacity too large, compute shrinking size size_t shrink_bytes = capacity_after_gc - maximum_desired_capacity; shrink(shrink_bytes); if (PrintGC && Verbose) { gclog_or_tty->print_cr(" " " shrinking:" - " initSize: %.1fK" - " maximum_desired_capacity: %.1fK", - collector_policy()->initial_heap_byte_size() / (double) K, - maximum_desired_capacity / (double) K); - gclog_or_tty->print_cr(" " - " shrink_bytes: %.1fK", - shrink_bytes / (double) K); + " min_heap_size: %6.1fK" + " maximum_desired_capacity: %6.1fK" + " shrink_bytes: %6.1fK", + (double) min_heap_size / (double) K, + (double) maximum_desired_capacity / (double) K, + (double) shrink_bytes / (double) K); } } } From 7125621712cca2f3b268fe8d5d1b084894f725c4 Mon Sep 17 00:00:00 2001 From: Antonios Printezis Date: Tue, 17 Aug 2010 14:40:00 -0400 Subject: [PATCH 038/100] 6974928: G1: sometimes humongous objects are allocated in young regions As the title says, sometimes we are allocating humongous objects in young regions and we shouldn't. Reviewed-by: ysr, johnc --- .../gc_implementation/g1/g1CollectedHeap.cpp | 15 +++++++++++++-- .../gc_implementation/g1/g1CollectedHeap.hpp | 2 +- .../g1/g1CollectedHeap.inline.hpp | 18 ++++++++++-------- .../vm/gc_implementation/g1/heapRegion.cpp | 17 +++++++++++++++++ 4 files changed, 41 insertions(+), 11 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp index f717bd9226e..0e48e05b431 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp @@ -2194,9 +2194,12 @@ size_t G1CollectedHeap::unsafe_max_tlab_alloc(Thread* ignored) const { } } -HeapWord* G1CollectedHeap::allocate_new_tlab(size_t size) { +HeapWord* G1CollectedHeap::allocate_new_tlab(size_t word_size) { + assert(!isHumongous(word_size), + err_msg("a TLAB should not be of humongous size, " + "word_size = "SIZE_FORMAT, word_size)); bool dummy; - return G1CollectedHeap::mem_allocate(size, false, true, &dummy); + return G1CollectedHeap::mem_allocate(word_size, false, true, &dummy); } bool G1CollectedHeap::allocs_are_zero_filled() { @@ -3639,6 +3642,10 @@ void G1CollectedHeap::preserve_mark_if_necessary(oop obj, markOop m) { HeapWord* G1CollectedHeap::par_allocate_during_gc(GCAllocPurpose purpose, size_t word_size) { + assert(!isHumongous(word_size), + err_msg("we should not be seeing humongous allocation requests " + "during GC, word_size = "SIZE_FORMAT, word_size)); + HeapRegion* alloc_region = _gc_alloc_regions[purpose]; // let the caller handle alloc failure if (alloc_region == NULL) return NULL; @@ -3671,6 +3678,10 @@ G1CollectedHeap::allocate_during_gc_slow(GCAllocPurpose purpose, HeapRegion* alloc_region, bool par, size_t word_size) { + assert(!isHumongous(word_size), + err_msg("we should not be seeing humongous allocation requests " + "during GC, word_size = "SIZE_FORMAT, word_size)); + HeapWord* block = NULL; // In the parallel case, a previous thread to obtain the lock may have // already assigned a new gc_alloc_region. diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp index d831e8d7d7e..a342d698d3b 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp @@ -1032,7 +1032,7 @@ public: virtual bool supports_tlab_allocation() const; virtual size_t tlab_capacity(Thread* thr) const; virtual size_t unsafe_max_tlab_alloc(Thread* thr) const; - virtual HeapWord* allocate_new_tlab(size_t size); + virtual HeapWord* allocate_new_tlab(size_t word_size); // Can a compiler initialize a new object without store barriers? // This permission only extends from the creation of a new object diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp index 207ee2ffa4c..c4eb0388417 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp @@ -57,8 +57,9 @@ inline HeapWord* G1CollectedHeap::attempt_allocation(size_t word_size, assert( SafepointSynchronize::is_at_safepoint() || Heap_lock->owned_by_self(), "pre-condition of the call" ); - if (_cur_alloc_region != NULL) { - + // All humongous allocation requests should go through the slow path in + // attempt_allocation_slow(). + if (!isHumongous(word_size) && _cur_alloc_region != NULL) { // If this allocation causes a region to become non empty, // then we need to update our free_regions count. @@ -69,13 +70,14 @@ inline HeapWord* G1CollectedHeap::attempt_allocation(size_t word_size, } else { res = _cur_alloc_region->allocate(word_size); } - } - if (res != NULL) { - if (!SafepointSynchronize::is_at_safepoint()) { - assert( Heap_lock->owned_by_self(), "invariant" ); - Heap_lock->unlock(); + + if (res != NULL) { + if (!SafepointSynchronize::is_at_safepoint()) { + assert( Heap_lock->owned_by_self(), "invariant" ); + Heap_lock->unlock(); + } + return res; } - return res; } // attempt_allocation_slow will also unlock the heap lock when appropriate. return attempt_allocation_slow(word_size, permit_collection_pause); diff --git a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp index 08d8902ce88..58b55123aae 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp @@ -790,8 +790,18 @@ void HeapRegion::verify(bool allow_dirty, int objs = 0; int blocks = 0; VerifyLiveClosure vl_cl(g1, use_prev_marking); + bool is_humongous = isHumongous(); + size_t object_num = 0; while (p < top()) { size_t size = oop(p)->size(); + if (is_humongous != g1->isHumongous(size)) { + gclog_or_tty->print_cr("obj "PTR_FORMAT" is of %shumongous size (" + SIZE_FORMAT" words) in a %shumongous region", + p, g1->isHumongous(size) ? "" : "non-", + size, is_humongous ? "" : "non-"); + *failures = true; + } + object_num += 1; if (blocks == BLOCK_SAMPLE_INTERVAL) { HeapWord* res = block_start_const(p + (size/2)); if (p != res) { @@ -857,6 +867,13 @@ void HeapRegion::verify(bool allow_dirty, } } + if (is_humongous && object_num > 1) { + gclog_or_tty->print_cr("region ["PTR_FORMAT","PTR_FORMAT"] is humongous " + "but has "SIZE_FORMAT", objects", + bottom(), end(), object_num); + *failures = true; + } + if (p != top()) { gclog_or_tty->print_cr("end of last object "PTR_FORMAT" " "does not match top "PTR_FORMAT, p, top()); From ce2883fbbd28a6786e7d019c984d838371743862 Mon Sep 17 00:00:00 2001 From: Antonios Printezis Date: Tue, 17 Aug 2010 14:40:00 -0400 Subject: [PATCH 039/100] 6975964: G1: print out a more descriptive message for evacuation failure when +PrintGCDetails is set We're renaming "evacuation failure" to "to-space overflow". I'm also piggy-backing a small additional change which removes the "Mark closure took..." output. Reviewed-by: ysr, johnc --- hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp | 3 --- hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp index ddbad890dbb..4dd197ebd27 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp @@ -2586,9 +2586,6 @@ void ConcurrentMark::complete_marking_in_collection_set() { double end_time = os::elapsedTime(); double elapsed_time_ms = (end_time - start) * 1000.0; g1h->g1_policy()->record_mark_closure_time(elapsed_time_ms); - if (PrintGCDetails) { - gclog_or_tty->print_cr("Mark closure took %5.2f ms.", elapsed_time_ms); - } ClearMarksInHRClosure clr(nextMarkBitMap()); g1h->collection_set_iterate(&clr); diff --git a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp index 0e48e05b431..e71c1097823 100644 --- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp @@ -3608,7 +3608,7 @@ void G1CollectedHeap::handle_evacuation_failure_common(oop old, markOop m) { if (!r->evacuation_failed()) { r->set_evacuation_failed(true); if (G1PrintHeapRegions) { - gclog_or_tty->print("evacuation failed in heap region "PTR_FORMAT" " + gclog_or_tty->print("overflow in heap region "PTR_FORMAT" " "["PTR_FORMAT","PTR_FORMAT")\n", r, r->bottom(), r->end()); } @@ -4321,7 +4321,7 @@ void G1CollectedHeap::evacuate_collection_set() { if (evacuation_failed()) { remove_self_forwarding_pointers(); if (PrintGCDetails) { - gclog_or_tty->print(" (evacuation failed)"); + gclog_or_tty->print(" (to-space overflow)"); } else if (PrintGC) { gclog_or_tty->print("--"); } From 51e553fe0e6c1ad739115d13cab6cdfc8ba9151c Mon Sep 17 00:00:00 2001 From: Xueming Shen Date: Tue, 17 Aug 2010 16:01:54 -0700 Subject: [PATCH 040/100] 6969651: TEST_BUG: tools/jar/JarEntryTime.java failed on JDK7 when run on NFS Changed to use more appropriate nfs file time Reviewed-by: martin --- jdk/test/tools/jar/JarEntryTime.java | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/jdk/test/tools/jar/JarEntryTime.java b/jdk/test/tools/jar/JarEntryTime.java index 525d6b9032d..2998b29bc8a 100644 --- a/jdk/test/tools/jar/JarEntryTime.java +++ b/jdk/test/tools/jar/JarEntryTime.java @@ -23,7 +23,7 @@ /** * @test - * @bug 4225317 + * @bug 4225317 6969651 * @summary Check extracted files have date as per those in the .jar file */ @@ -68,17 +68,9 @@ public class JarEntryTime { } public static void realMain(String[] args) throws Throwable { - final long now = System.currentTimeMillis(); - final long earlier = now - (60L * 60L * 6L * 1000L); - final long yesterday = now - (60L * 60L * 24L * 1000L); - - // ZipEntry's mod date has 2 seconds precision: give extra time to - // allow for e.g. rounding/truncation and networked/samba drives. - final long PRECISION = 10000L; File dirOuter = new File("outer"); File dirInner = new File(dirOuter, "inner"); - File jarFile = new File("JarEntryTime.jar"); // Remove any leftovers from prior run @@ -99,6 +91,17 @@ public class JarEntryTime { PrintWriter pw = new PrintWriter(fileInner); pw.println("hello, world"); pw.close(); + + // Get the "now" from the "last-modified-time" of the last file we + // just created, instead of the "System.currentTimeMillis()", to + // workaround the possible "time difference" due to nfs. + final long now = fileInner.lastModified(); + final long earlier = now - (60L * 60L * 6L * 1000L); + final long yesterday = now - (60L * 60L * 24L * 1000L); + // ZipEntry's mod date has 2 seconds precision: give extra time to + // allow for e.g. rounding/truncation and networked/samba drives. + final long PRECISION = 10000L; + dirOuter.setLastModified(now); dirInner.setLastModified(yesterday); fileInner.setLastModified(earlier); From a3319eba8ab8544c61e6e1e6881729fb8024eb04 Mon Sep 17 00:00:00 2001 From: Abhijit Saha Date: Tue, 17 Aug 2010 22:52:50 -0700 Subject: [PATCH 041/100] 6977952: Test: Sync missing tests from hs16.3 to hs17.x Reviewed-by: wrockett --- .../test/compiler/6894807/IsInstanceTest.java | 44 ++++++++ hotspot/test/compiler/6894807/Test6894807.sh | 68 ++++++++++++ hotspot/test/runtime/6626217/IFace.java | 6 ++ hotspot/test/runtime/6626217/Loader2.java | 52 +++++++++ hotspot/test/runtime/6626217/Test6626217.sh | 101 ++++++++++++++++++ .../runtime/6626217/You_Have_Been_P0wned.java | 11 ++ hotspot/test/runtime/6626217/bug_21227.java | 61 +++++++++++ .../test/runtime/6626217/from_loader2.java | 9 ++ .../runtime/6626217/many_loader1.java.foo | 23 ++++ .../runtime/6626217/many_loader2.java.foo | 19 ++++ 10 files changed, 394 insertions(+) create mode 100644 hotspot/test/compiler/6894807/IsInstanceTest.java create mode 100644 hotspot/test/compiler/6894807/Test6894807.sh create mode 100644 hotspot/test/runtime/6626217/IFace.java create mode 100644 hotspot/test/runtime/6626217/Loader2.java create mode 100644 hotspot/test/runtime/6626217/Test6626217.sh create mode 100644 hotspot/test/runtime/6626217/You_Have_Been_P0wned.java create mode 100644 hotspot/test/runtime/6626217/bug_21227.java create mode 100644 hotspot/test/runtime/6626217/from_loader2.java create mode 100644 hotspot/test/runtime/6626217/many_loader1.java.foo create mode 100644 hotspot/test/runtime/6626217/many_loader2.java.foo diff --git a/hotspot/test/compiler/6894807/IsInstanceTest.java b/hotspot/test/compiler/6894807/IsInstanceTest.java new file mode 100644 index 00000000000..ddc3fed365a --- /dev/null +++ b/hotspot/test/compiler/6894807/IsInstanceTest.java @@ -0,0 +1,44 @@ +/* + * @test + * @bug 6894807 + * @summary No ClassCastException for HashAttributeSet constructors if run with -Xcomp + * @compile IsInstanceTest.java + * @run shell Test6894807.sh +*/ + +public class IsInstanceTest { + + public static void main(String[] args) { + BaseInterface baseInterfaceImpl = new BaseInterfaceImpl(); + for (int i = 0; i < 100000; i++) { + if (isInstanceOf(baseInterfaceImpl, ExtendedInterface.class)) { + System.out.println("Failed at index:" + i); + System.out.println("Arch: "+System.getProperty("os.arch", "")+ + " OS: "+System.getProperty("os.name", "")+ + " OSV: "+System.getProperty("os.version", "")+ + " Cores: "+Runtime.getRuntime().availableProcessors()+ + " JVM: "+System.getProperty("java.version", "")+" "+System.getProperty("sun.arch.data.model", "")); + break; + } + } + System.out.println("Done!"); + } + + public static boolean isInstanceOf(BaseInterface baseInterfaceImpl, Class... baseInterfaceClasses) { + for (Class baseInterfaceClass : baseInterfaceClasses) { + if (baseInterfaceClass.isInstance(baseInterfaceImpl)) { + return true; + } + } + return false; + } + + private interface BaseInterface { + } + + private interface ExtendedInterface extends BaseInterface { + } + + private static class BaseInterfaceImpl implements BaseInterface { + } +} diff --git a/hotspot/test/compiler/6894807/Test6894807.sh b/hotspot/test/compiler/6894807/Test6894807.sh new file mode 100644 index 00000000000..da435cc380f --- /dev/null +++ b/hotspot/test/compiler/6894807/Test6894807.sh @@ -0,0 +1,68 @@ +#!/bin/sh + +if [ "${TESTSRC}" = "" ] +then TESTSRC=. +fi + +if [ "${TESTJAVA}" = "" ] +then + PARENT=`dirname \`which java\`` + TESTJAVA=`dirname ${PARENT}` + echo "TESTJAVA not set, selecting " ${TESTJAVA} + echo "If this is incorrect, try setting the variable manually." +fi + +if [ "${TESTCLASSES}" = "" ] +then + echo "TESTCLASSES not set. Test cannot execute. Failed." + exit 1 +fi + +BIT_FLAG="" + +# set platform-dependent variables +OS=`uname -s` +case "$OS" in + SunOS | Linux ) + NULL=/dev/null + PS=":" + FS="/" + ## for solaris, linux it's HOME + FILE_LOCATION=$HOME + if [ -f ${FILE_LOCATION}${FS}JDK64BIT -a ${OS} = "SunOS" ] + then + BIT_FLAG=`cat ${FILE_LOCATION}${FS}JDK64BIT | grep -v '^#'` + fi + ;; + Windows_* ) + NULL=NUL + PS=";" + FS="\\" + ;; + * ) + echo "Unrecognized system!" + exit 1; + ;; +esac + +JEMMYPATH=${CPAPPEND} +CLASSPATH=.${PS}${TESTCLASSES}${PS}${JEMMYPATH} ; export CLASSPATH + +THIS_DIR=`pwd` + +${TESTJAVA}${FS}bin${FS}java ${BIT_FLAG} -version + +${TESTJAVA}${FS}bin${FS}java ${BIT_FLAG} -server IsInstanceTest > test.out 2>&1 + +cat test.out + +grep "Failed at index" test.out + +if [ $? = 0 ] +then + echo "Test Failed" + exit 1 +else + echo "Test Passed" + exit 0 +fi diff --git a/hotspot/test/runtime/6626217/IFace.java b/hotspot/test/runtime/6626217/IFace.java new file mode 100644 index 00000000000..1c2e86f78bd --- /dev/null +++ b/hotspot/test/runtime/6626217/IFace.java @@ -0,0 +1,6 @@ +// A simple interface, to allow an unknown foreign call from a class +// loaded with LOADER1 to a class loaded with LOADER2. +public interface IFace { + public many_loader[] gen(); +} + diff --git a/hotspot/test/runtime/6626217/Loader2.java b/hotspot/test/runtime/6626217/Loader2.java new file mode 100644 index 00000000000..ea9e8ec1a97 --- /dev/null +++ b/hotspot/test/runtime/6626217/Loader2.java @@ -0,0 +1,52 @@ +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +public class Loader2 extends ClassLoader { + int _recur; + public void print( String msg ) { + for( int i=0; i<_recur; i++ ) + System.out.print(" "); + System.out.println(">>Loader2>> "+msg); + } + + protected Class findClass2(String name) throws ClassNotFoundException { + print("Fetching the implementation of "+name); + int old = _recur; + try { + FileInputStream fi = new FileInputStream(name+".impl2"); + byte result[] = new byte[fi.available()]; + fi.read(result); + + print("DefineClass1 on "+name); + _recur++; + Class clazz = defineClass(name, result, 0, result.length); + _recur = old; + print("Returning newly loaded class."); + return clazz; + } catch (Exception e) { + _recur = old; + print("Not found on disk."); + // If we caught an exception, either the class was not found or + // it was unreadable by our process. + return null; + //throw new ClassNotFoundException(e.toString()); + } + } + + protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + // Attempt a disk load first + Class c = findClass2(name); + if( c == null ) { + // check if the class has already been loaded + print("Checking for prior loaded class "+name); + c = findLoadedClass(name); + print("Letting super-loader load "+name); + int old = _recur; + _recur++; + c = super.loadClass(name, false); + _recur=old; + } + if (resolve) { print("Resolving class "+name); resolveClass(c); } + print("Returning clazz "+c.getClassLoader()+":"+name); + return c; + } +} diff --git a/hotspot/test/runtime/6626217/Test6626217.sh b/hotspot/test/runtime/6626217/Test6626217.sh new file mode 100644 index 00000000000..6bc57a7be50 --- /dev/null +++ b/hotspot/test/runtime/6626217/Test6626217.sh @@ -0,0 +1,101 @@ +# +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. +# SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. +# + +# +# @test @(#)Test6626217.sh +# @bug 6626217 +# @summary Loader-constraint table allows arrays instead of only the base-classes +# @run shell Test6626217.sh +# + +if [ "${TESTSRC}" = "" ] + then TESTSRC=. +fi + +if [ "${TESTJAVA}" = "" ] +then + PARENT=`dirname \`which java\`` + TESTJAVA=`dirname ${PARENT}` + echo "TESTJAVA not set, selecting " ${TESTJAVA} + echo "If this is incorrect, try setting the variable manually." +fi + +if [ "${TESTCLASSES}" = "" ] +then + echo "TESTCLASSES not set. Test cannot execute. Failed." + exit 1 +fi + +BIT_FLAG="" + +# set platform-dependent variables +OS=`uname -s` +case "$OS" in + SunOS | Linux ) + NULL=/dev/null + PS=":" + FS="/" + RM=/bin/rm + CP=/bin/cp + MV=/bin/mv + ## for solaris, linux it's HOME + FILE_LOCATION=$HOME + if [ -f ${FILE_LOCATION}${FS}JDK64BIT -a ${OS} = "SunOS" ] + then + BIT_FLAG=`cat ${FILE_LOCATION}${FS}JDK64BIT` + fi + ;; + Windows_* ) + NULL=NUL + PS=";" + FS="\\" + RM=rm + CP=cp + MV=mv + ;; + * ) + echo "Unrecognized system!" + exit 1; + ;; +esac + +JEMMYPATH=${CPAPPEND} +CLASSPATH=.${PS}${TESTCLASSES}${PS}${JEMMYPATH} ; export CLASSPATH + +THIS_DIR=`pwd` + +JAVA=${TESTJAVA}${FS}bin${FS}java +JAVAC=${TESTJAVA}${FS}bin${FS}javac + +${JAVA} ${BIT_FLAG} -version + +# Current directory is scratch directory, copy all the test source there +# (for the subsequent moves to work). +${CP} ${TESTSRC}${FS}* ${THIS_DIR} + +# A Clean Compile: this line will probably fail within jtreg as have a clean dir: +${RM} -f *.class *.impl many_loader.java + +# Compile all the usual suspects, including the default 'many_loader' +${CP} many_loader1.java.foo many_loader.java +${JAVAC} -source 1.4 -target 1.4 -Xlint *.java + +# Rename the class files, so the custom loader (and not the system loader) will find it +${MV} from_loader2.class from_loader2.impl2 + +# Compile the next version of 'many_loader' +${MV} many_loader.class many_loader.impl1 +${CP} many_loader2.java.foo many_loader.java +${JAVAC} -source 1.4 -target 1.4 -Xlint many_loader.java + +# Rename the class file, so the custom loader (and not the system loader) will find it +${MV} many_loader.class many_loader.impl2 +${MV} many_loader.impl1 many_loader.class +${RM} many_loader.java + +${JAVA} ${BIT_FLAG} -Xverify -Xint -cp . bug_21227 >test.out 2>&1 +grep "violates loader constraints" test.out +exit $? + diff --git a/hotspot/test/runtime/6626217/You_Have_Been_P0wned.java b/hotspot/test/runtime/6626217/You_Have_Been_P0wned.java new file mode 100644 index 00000000000..2bebbe0590c --- /dev/null +++ b/hotspot/test/runtime/6626217/You_Have_Been_P0wned.java @@ -0,0 +1,11 @@ + +// I can cast any old thing I want to this type object: +public class You_Have_Been_P0wned { + // Make a bunch of int-fields so I can peek & poke it + int _a; + int _b; + int _c; + int _d; + +} + diff --git a/hotspot/test/runtime/6626217/bug_21227.java b/hotspot/test/runtime/6626217/bug_21227.java new file mode 100644 index 00000000000..18d30c41fb0 --- /dev/null +++ b/hotspot/test/runtime/6626217/bug_21227.java @@ -0,0 +1,61 @@ + +import java.lang.reflect.*; +import java.security.*; + +abstract public class bug_21227 { + + // Jam anything you want in here, it will be cast to a You_Have_Been_P0wned + public static Object _p0wnee; + + public static void main(String argv[]) throws ClassNotFoundException, InstantiationException, IllegalAccessException { + System.out.println("Warmup"); + + // Make a Class 'many_loader' under the default loader + bug_21227 bug = new many_loader(); + + // Some classes under a new Loader, LOADER2, including another version of 'many_loader' + ClassLoader LOADER2 = new Loader2(); + Class clazz2 = LOADER2.loadClass("from_loader2"); + IFace iface = (IFace)clazz2.newInstance(); + + // Set the victim, a String of length 6 + String s = "victim"; + _p0wnee = s; + + // Go cast '_p0wnee' to type You_Have_Been_P0wned + many_loader[] x2 = bug.make(iface); + + many_loader b = x2[0]; + + // Make it clear that the runtime type many_loader (what we get from the + // array X2) varies from the static type of many_loader. + Class cl1 = b.getClass(); + ClassLoader ld1 = cl1.getClassLoader(); + Class cl2 = many_loader.class; + ClassLoader ld2 = cl2.getClassLoader(); + System.out.println("bug.make() "+ld1+":"+cl1); + System.out.println("many_loader "+ld2+":"+cl2); + + // Read the victims guts out + You_Have_Been_P0wned q = b._p0wnee; + System.out.println("q._a = 0x"+Integer.toHexString(q._a)); + System.out.println("q._b = 0x"+Integer.toHexString(q._b)); + System.out.println("q._c = 0x"+Integer.toHexString(q._c)); + System.out.println("q._d = 0x"+Integer.toHexString(q._d)); + + System.out.println("I will now crash the VM:"); + // On 32-bit HotSpot Java6 this sets the victim String length shorter, then crashes the VM + //q._c = 3; + q._a = -1; + + System.out.println(s); + + } + + // I need to compile (hence call in a loop) a function which returns a value + // loaded from classloader other than the system one. The point of this + // call is to give me an abstract 'hook' into a function loaded with a + // foreign loader. + public abstract many_loader[] make( IFace iface ); // abstract factory +} + diff --git a/hotspot/test/runtime/6626217/from_loader2.java b/hotspot/test/runtime/6626217/from_loader2.java new file mode 100644 index 00000000000..6421f2a3c65 --- /dev/null +++ b/hotspot/test/runtime/6626217/from_loader2.java @@ -0,0 +1,9 @@ +// A simple class to extend an abstract class and get loaded with different +// loaders. This class is loaded via LOADER2. +public class from_loader2 implements IFace { + public many_loader[] gen() { + many_loader[] x = new many_loader[1]; + x[0] = new many_loader(); + return x; + } +} diff --git a/hotspot/test/runtime/6626217/many_loader1.java.foo b/hotspot/test/runtime/6626217/many_loader1.java.foo new file mode 100644 index 00000000000..b5295a370c0 --- /dev/null +++ b/hotspot/test/runtime/6626217/many_loader1.java.foo @@ -0,0 +1,23 @@ +// A simple class to extend an abstract class and get loaded with different +// loaders. This class is loaded via LOADER1. A similar named class will +// be loaded via LOADER2. +public class many_loader extends bug_21227 { + public You_Have_Been_P0wned _p0wnee; + + // I need to compile (hence call in a loop) a function which returns a value + // loaded from classloader other than the system one. The point of this + // call is to give me an abstract 'hook' into a function loaded with a + // foreign loader. + + // The original 'make(boolean)' returns a bug_21227. The VM will inject a + // synthetic method to up-cast the returned 'from_loader1' into a + // 'bug_21227'. + public many_loader[] make( IFace iface ) { + // This function needs to return a value known to be loaded from LOADER2. + // Since I need to use a yet different loader, I need to make an unknown + // foreign call. In this case I'll be using an interface to make the + // unknown call, with but a single implementor so the compiler can do the + // upcast statically. + return iface==null ? null : iface.gen(); + } +} diff --git a/hotspot/test/runtime/6626217/many_loader2.java.foo b/hotspot/test/runtime/6626217/many_loader2.java.foo new file mode 100644 index 00000000000..1b72132a2a8 --- /dev/null +++ b/hotspot/test/runtime/6626217/many_loader2.java.foo @@ -0,0 +1,19 @@ +// A simple class to extend an abstract class and get loaded with different +// loaders. This class is loaded via LOADER2. A similar named class will +// be loaded via LOADER1. +public class many_loader extends bug_21227 { + final Object _ref_to_be_p0wned; + + many_loader() { + _ref_to_be_p0wned = bug_21227._p0wnee; + System.out.println("Gonna hack this thing: " + _ref_to_be_p0wned.toString() ); + } + + // I need to compile (hence call in a loop) a function which returns a value + // loaded from classloader other than the system one. The point of this + // call is to give me an abstract 'hook' into a function loaded with a + // foreign loader. + public many_loader[] make( IFace iface ) { + throw new Error("do not call me"); + } +} From a325f5589a57ddd04aea76c800ceb1ff02aa1324 Mon Sep 17 00:00:00 2001 From: Gary Benson Date: Wed, 18 Aug 2010 01:22:16 -0700 Subject: [PATCH 042/100] 6977640: Zero and Shark fixes A number of fixes for Zero and Shark. Reviewed-by: twisti --- .../src/cpu/zero/vm/bytecodeInterpreter_zero.inline.hpp | 4 ++-- hotspot/src/cpu/zero/vm/javaFrameAnchor_zero.hpp | 4 ++++ hotspot/src/os_cpu/linux_zero/vm/os_linux_zero.cpp | 8 ++++---- hotspot/src/os_cpu/linux_zero/vm/thread_linux_zero.cpp | 9 ++++++--- hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp | 4 +++- 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/hotspot/src/cpu/zero/vm/bytecodeInterpreter_zero.inline.hpp b/hotspot/src/cpu/zero/vm/bytecodeInterpreter_zero.inline.hpp index 702a165ef29..a35809f0a5b 100644 --- a/hotspot/src/cpu/zero/vm/bytecodeInterpreter_zero.inline.hpp +++ b/hotspot/src/cpu/zero/vm/bytecodeInterpreter_zero.inline.hpp @@ -1,6 +1,6 @@ /* * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved. - * Copyright 2007 Red Hat, Inc. + * Copyright 2007, 2010 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -268,7 +268,7 @@ inline jint BytecodeInterpreter::VMintSub(jint op1, jint op2) { return op1 - op2; } -inline jint BytecodeInterpreter::VMintUshr(jint op1, jint op2) { +inline juint BytecodeInterpreter::VMintUshr(jint op1, jint op2) { return ((juint) op1) >> (op2 & 0x1F); } diff --git a/hotspot/src/cpu/zero/vm/javaFrameAnchor_zero.hpp b/hotspot/src/cpu/zero/vm/javaFrameAnchor_zero.hpp index 5748301c096..0763790b64e 100644 --- a/hotspot/src/cpu/zero/vm/javaFrameAnchor_zero.hpp +++ b/hotspot/src/cpu/zero/vm/javaFrameAnchor_zero.hpp @@ -82,6 +82,10 @@ return _last_Java_fp; } + address last_Java_pc() const { + return _last_Java_pc; + } + static ByteSize last_Java_fp_offset() { return byte_offset_of(JavaFrameAnchor, _last_Java_fp); } diff --git a/hotspot/src/os_cpu/linux_zero/vm/os_linux_zero.cpp b/hotspot/src/os_cpu/linux_zero/vm/os_linux_zero.cpp index ce9d86fdd60..fd78628b3eb 100644 --- a/hotspot/src/os_cpu/linux_zero/vm/os_linux_zero.cpp +++ b/hotspot/src/os_cpu/linux_zero/vm/os_linux_zero.cpp @@ -435,22 +435,22 @@ extern "C" { void _Copy_arrayof_conjoint_bytes(HeapWord* from, HeapWord* to, size_t count) { - ShouldNotCallThis(); + memmove(to, from, count); } void _Copy_arrayof_conjoint_jshorts(HeapWord* from, HeapWord* to, size_t count) { - ShouldNotCallThis(); + memmove(to, from, count * 2); } void _Copy_arrayof_conjoint_jints(HeapWord* from, HeapWord* to, size_t count) { - ShouldNotCallThis(); + memmove(to, from, count * 4); } void _Copy_arrayof_conjoint_jlongs(HeapWord* from, HeapWord* to, size_t count) { - ShouldNotCallThis(); + memmove(to, from, count * 8); } }; diff --git a/hotspot/src/os_cpu/linux_zero/vm/thread_linux_zero.cpp b/hotspot/src/os_cpu/linux_zero/vm/thread_linux_zero.cpp index 15dba5fff97..8551eafce62 100644 --- a/hotspot/src/os_cpu/linux_zero/vm/thread_linux_zero.cpp +++ b/hotspot/src/os_cpu/linux_zero/vm/thread_linux_zero.cpp @@ -1,6 +1,6 @@ /* * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. - * Copyright 2009 Red Hat, Inc. + * Copyright 2009, 2010 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,6 +23,9 @@ * */ -// This file is intentionally empty +#include "incls/_precompiled.incl" +#include "incls/_thread_linux_zero.cpp.incl" -void JavaThread::cache_global_variables() { } +void JavaThread::cache_global_variables() { + // nothing to do +} diff --git a/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp b/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp index 29b4ede2bd9..9581f24bc30 100644 --- a/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp +++ b/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp @@ -421,7 +421,9 @@ BytecodeInterpreter::run(interpreterState istate) { #ifdef ASSERT if (istate->_msg != initialize) { assert(abs(istate->_stack_base - istate->_stack_limit) == (istate->_method->max_stack() + 1), "bad stack limit"); - IA32_ONLY(assert(istate->_stack_limit == istate->_thread->last_Java_sp() + 1, "wrong")); +#ifndef SHARK + IA32_ONLY(assert(istate->_stack_limit == istate->_thread->last_Java_sp() + 1, "wrong")); +#endif // !SHARK } // Verify linkages. interpreterState l = istate; From 3b69a96c58a9a491bf4e42fee6694e79fbe6a9d2 Mon Sep 17 00:00:00 2001 From: John Cuthbertson Date: Wed, 18 Aug 2010 10:59:06 -0700 Subject: [PATCH 043/100] 6977924: Changes for 6975078 produce build error with certain gcc versions The changes introduced for 6975078 assign badHeapOopVal to the _allocation field in the ResourceObj class. In 32 bit linux builds with certain versions of gcc this assignment will be flagged as an error while compiling allocation.cpp. In 32 bit builds the constant value badHeapOopVal (which is cast to an intptr_t) is negative. The _allocation field is typed as an unsigned intptr_t and gcc catches this as an error. Reviewed-by: jcoomes, ysr, phh --- hotspot/src/share/vm/memory/allocation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hotspot/src/share/vm/memory/allocation.cpp b/hotspot/src/share/vm/memory/allocation.cpp index 6331bcefc78..ada58593238 100644 --- a/hotspot/src/share/vm/memory/allocation.cpp +++ b/hotspot/src/share/vm/memory/allocation.cpp @@ -58,7 +58,7 @@ void* ResourceObj::operator new(size_t size, allocation_type type) { void ResourceObj::operator delete(void* p) { assert(((ResourceObj *)p)->allocated_on_C_heap(), "delete only allowed for C_HEAP objects"); - DEBUG_ONLY(((ResourceObj *)p)->_allocation = badHeapOopVal;) + DEBUG_ONLY(((ResourceObj *)p)->_allocation = (uintptr_t) badHeapOopVal;) FreeHeap(p); } @@ -104,7 +104,7 @@ ResourceObj& ResourceObj::operator=(const ResourceObj& r) { // default copy assi ResourceObj::~ResourceObj() { // allocated_on_C_heap() also checks that encoded (in _allocation) address == this. if (!allocated_on_C_heap()) { // ResourceObj::delete() zaps _allocation for C_heap. - _allocation = badHeapOopVal; // zap type + _allocation = (uintptr_t) badHeapOopVal; // zap type } } #endif // ASSERT From fc61f2721e9ff98c049b570d8f5e9cf93921f50b Mon Sep 17 00:00:00 2001 From: "Y. Srinivas Ramakrishna" Date: Wed, 18 Aug 2010 11:39:21 -0700 Subject: [PATCH 044/100] 6977970: CMS: concurrentMarkSweepGeneration.cpp:7947 assert(addr <= _limit) failed: sweep invariant Allow for the possibility (when the heap is expanding) that the sweep might skip over and past, rather than necessarily step on, the sweep limit determined at the beginning of a concurrent marking cycle. Reviewed-by: jmasa, tonyp --- .../concurrentMarkSweepGeneration.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp index d0cefc894c9..f5d19544e7c 100644 --- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp +++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp @@ -7937,14 +7937,20 @@ size_t SweepClosure::do_blk_careful(HeapWord* addr) { FreeChunk* fc = (FreeChunk*)addr; size_t res; - // check if we are done sweepinrg - if (addr == _limit) { // we have swept up to the limit, do nothing more + // Check if we are done sweeping. Below we check "addr >= _limit" rather + // than "addr == _limit" because although _limit was a block boundary when + // we started the sweep, it may no longer be one because heap expansion + // may have caused us to coalesce the block ending at the address _limit + // with a newly expanded chunk (this happens when _limit was set to the + // previous _end of the space), so we may have stepped past _limit; see CR 6977970. + if (addr >= _limit) { // we have swept up to or past the limit, do nothing more assert(_limit >= _sp->bottom() && _limit <= _sp->end(), "sweep _limit out of bounds"); + assert(addr < _sp->end(), "addr out of bounds"); // help the closure application finish - return pointer_delta(_sp->end(), _limit); + return pointer_delta(_sp->end(), addr); } - assert(addr <= _limit, "sweep invariant"); + assert(addr < _limit, "sweep invariant"); // check if we should yield do_yield_check(addr); From 737e6157fe0af6731d9329d8d85cb015e40e9d14 Mon Sep 17 00:00:00 2001 From: Kelly O'Hair Date: Wed, 18 Aug 2010 13:46:02 -0700 Subject: [PATCH 045/100] 6974005: Use of cygpath in Makefile logic needs to silence error messages Reviewed-by: mchung --- jdk/make/common/shared/Defs-windows.gmk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdk/make/common/shared/Defs-windows.gmk b/jdk/make/common/shared/Defs-windows.gmk index 121cd94ea49..ac9daf4cd6c 100644 --- a/jdk/make/common/shared/Defs-windows.gmk +++ b/jdk/make/common/shared/Defs-windows.gmk @@ -89,7 +89,7 @@ define FullPath $(shell $(CYGPATH_CMD) $1 2> $(DEV_NULL)) endef define OptFullPath -$(shell if [ "$1" != "" -a -d "$1" ]; then $(CYGPATH_CMD) "$1"; else echo "$1"; fi) +$(shell if [ "$1" != "" -a -d "$1" ]; then $(CYGPATH_CMD) "$1" 2> $(DEV_NULL); else echo "$1"; fi) endef else # Temporary until we upgrade to MKS 8.7, MKS pwd returns mixed mode path From fb2ceb5470e58bf186d65f0fa92975152acf2ecd Mon Sep 17 00:00:00 2001 From: Kelly O'Hair Date: Wed, 18 Aug 2010 13:46:39 -0700 Subject: [PATCH 046/100] 6932743: Makefiles not parsing version strings with - from uname -r Reviewed-by: mchung --- jdk/make/common/shared/Defs.gmk | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/jdk/make/common/shared/Defs.gmk b/jdk/make/common/shared/Defs.gmk index 3ccab90196c..ed32779c8c2 100644 --- a/jdk/make/common/shared/Defs.gmk +++ b/jdk/make/common/shared/Defs.gmk @@ -136,15 +136,20 @@ define GetVersion $(shell echo $1 | sed -e 's@[^0-9]*\([0-9][0-9]*\.[0-9][.0-9]*\).*@\1@' ) endef +# Return one part of the version numbers, watch out for non digits. +define VersionWord # Number Version +$(word $1,$(subst ., ,$(subst -, ,$2))) +endef + # Given a major.minor.micro version, return the major, minor, or micro number define MajorVersion -$(if $(word 1, $(subst ., ,$1)),$(word 1, $(subst ., ,$1)),0) +$(if $(call VersionWord,1,$1),$(call VersionWord,1,$1),0) endef define MinorVersion -$(if $(word 2, $(subst ., ,$1)),$(word 2, $(subst ., ,$1)),0) +$(if $(call VersionWord,2,$1),$(call VersionWord,2,$1),0) endef define MicroVersion -$(if $(word 3, $(subst ., ,$1)),$(word 3, $(subst ., ,$1)),0) +$(if $(call VersionWord,3,$1),$(call VersionWord,3,$1),0) endef # Macro that returns missing, same, newer, or older $1=version $2=required From 34198dbc4c7ebfe029baba07bfd696935095ac91 Mon Sep 17 00:00:00 2001 From: Weijun Wang Date: Thu, 19 Aug 2010 11:26:32 +0800 Subject: [PATCH 047/100] 6976536: Solaris JREs do not have the krb5.kdc.bad.policy configured by default Reviewed-by: valeriep --- .../share/lib/security/java.security-solaris | 27 +++++++++++++ .../share/lib/security/java.security-windows | 27 +++++++++++++ .../sun/security/krb5/BadKdcDefaultValue.java | 40 +++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 jdk/test/sun/security/krb5/BadKdcDefaultValue.java diff --git a/jdk/src/share/lib/security/java.security-solaris b/jdk/src/share/lib/security/java.security-solaris index 1e447b7c24d..43621a48511 100644 --- a/jdk/src/share/lib/security/java.security-solaris +++ b/jdk/src/share/lib/security/java.security-solaris @@ -260,3 +260,30 @@ networkaddress.cache.negative.ttl=10 # Example, # ocsp.responderCertSerialNumber=2A:FF:00 +# +# Policy for failed Kerberos KDC lookups: +# +# When a KDC is unavailable (network error, service failure, etc), it is +# put inside a blacklist and accessed less often for future requests. The +# value (case-insensitive) for this policy can be: +# +# tryLast +# KDCs in the blacklist are always tried after those not on the list. +# +# tryLess[:max_retries,timeout] +# KDCs in the blacklist are still tried by their order in the configuration, +# but with smaller max_retries and timeout values. max_retries and timeout +# are optional numerical parameters (default 1 and 5000, which means once +# and 5 seconds). Please notes that if any of the values defined here is +# more than what is defined in krb5.conf, it will be ignored. +# +# Whenever a KDC is detected as available, it is removed from the blacklist. +# The blacklist is reset when krb5.conf is reloaded. You can add +# refreshKrb5Config=true to a JAAS configuration file so that krb5.conf is +# reloaded whenever a JAAS authentication is attempted. +# +# Example, +# krb5.kdc.bad.policy = tryLast +# krb5.kdc.bad.policy = tryLess:2,2000 +krb5.kdc.bad.policy = tryLast + diff --git a/jdk/src/share/lib/security/java.security-windows b/jdk/src/share/lib/security/java.security-windows index c94c76b7ffb..21573b7d23b 100644 --- a/jdk/src/share/lib/security/java.security-windows +++ b/jdk/src/share/lib/security/java.security-windows @@ -260,3 +260,30 @@ networkaddress.cache.negative.ttl=10 # Example, # ocsp.responderCertSerialNumber=2A:FF:00 +# +# Policy for failed Kerberos KDC lookups: +# +# When a KDC is unavailable (network error, service failure, etc), it is +# put inside a blacklist and accessed less often for future requests. The +# value (case-insensitive) for this policy can be: +# +# tryLast +# KDCs in the blacklist are always tried after those not on the list. +# +# tryLess[:max_retries,timeout] +# KDCs in the blacklist are still tried by their order in the configuration, +# but with smaller max_retries and timeout values. max_retries and timeout +# are optional numerical parameters (default 1 and 5000, which means once +# and 5 seconds). Please notes that if any of the values defined here is +# more than what is defined in krb5.conf, it will be ignored. +# +# Whenever a KDC is detected as available, it is removed from the blacklist. +# The blacklist is reset when krb5.conf is reloaded. You can add +# refreshKrb5Config=true to a JAAS configuration file so that krb5.conf is +# reloaded whenever a JAAS authentication is attempted. +# +# Example, +# krb5.kdc.bad.policy = tryLast +# krb5.kdc.bad.policy = tryLess:2,2000 +krb5.kdc.bad.policy = tryLast + diff --git a/jdk/test/sun/security/krb5/BadKdcDefaultValue.java b/jdk/test/sun/security/krb5/BadKdcDefaultValue.java new file mode 100644 index 00000000000..1235cce4b9d --- /dev/null +++ b/jdk/test/sun/security/krb5/BadKdcDefaultValue.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010, 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 6976536 + * @summary Solaris JREs do not have the krb5.kdc.bad.policy configured by default. + * @run main/othervm BadKdcDefaultValue + */ + +import java.security.Security; + +public class BadKdcDefaultValue { + public static void main(String[] args) throws Exception { + if (!"tryLast".equalsIgnoreCase( + Security.getProperty("krb5.kdc.bad.policy"))) { + throw new Exception("Default value not correct"); + } + } +} + From c736a3d9c7bf0695e7fd2d95e0ad3c651ed6cc41 Mon Sep 17 00:00:00 2001 From: Weijun Wang Date: Thu, 19 Aug 2010 12:24:53 +0800 Subject: [PATCH 048/100] 6921610: 1.6 update 17 and 18 throw java.lang.IndexOutOfBoundsException Reviewed-by: vinnie, xuelei --- jdk/src/share/classes/com/sun/jndi/ldap/Connection.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/jdk/src/share/classes/com/sun/jndi/ldap/Connection.java b/jdk/src/share/classes/com/sun/jndi/ldap/Connection.java index 4503d261fff..dec38eb752d 100644 --- a/jdk/src/share/classes/com/sun/jndi/ldap/Connection.java +++ b/jdk/src/share/classes/com/sun/jndi/ldap/Connection.java @@ -813,7 +813,8 @@ public final class Connection implements Runnable { try { while (true) { try { - inbuf = new byte[10]; + // type and length (at most 128 octets for long form) + inbuf = new byte[129]; offset = 0; seqlen = 0; From 71962ebfb06da2e1ea1291d860d9fbe084c5fa5d Mon Sep 17 00:00:00 2001 From: Maurizio Cimadamore Date: Thu, 19 Aug 2010 11:50:50 +0100 Subject: [PATCH 049/100] 6886247: regression: javac crashes with an assertion error in Attr.java Capture conversion does not work on nested types Reviewed-by: jjg --- .../com/sun/tools/javac/code/Types.java | 7 ++++ .../wildcards/6886247/T6886247_1.java | 40 ++++++++++++++++++ .../wildcards/6886247/T6886247_2.java | 41 +++++++++++++++++++ .../generics/wildcards/6886247/T6886247_2.out | 2 + 4 files changed, 90 insertions(+) create mode 100644 langtools/test/tools/javac/generics/wildcards/6886247/T6886247_1.java create mode 100644 langtools/test/tools/javac/generics/wildcards/6886247/T6886247_2.java create mode 100644 langtools/test/tools/javac/generics/wildcards/6886247/T6886247_2.out diff --git a/langtools/src/share/classes/com/sun/tools/javac/code/Types.java b/langtools/src/share/classes/com/sun/tools/javac/code/Types.java index 6354e5be81d..5f6d2bd4495 100644 --- a/langtools/src/share/classes/com/sun/tools/javac/code/Types.java +++ b/langtools/src/share/classes/com/sun/tools/javac/code/Types.java @@ -2945,6 +2945,13 @@ public class Types { public Type capture(Type t) { if (t.tag != CLASS) return t; + if (t.getEnclosingType() != Type.noType) { + Type capturedEncl = capture(t.getEnclosingType()); + if (capturedEncl != t.getEnclosingType()) { + Type type1 = memberType(capturedEncl, t.tsym); + t = subst(type1, t.tsym.type.getTypeArguments(), t.getTypeArguments()); + } + } ClassType cls = (ClassType)t; if (cls.isRaw() || !cls.isParameterized()) return cls; diff --git a/langtools/test/tools/javac/generics/wildcards/6886247/T6886247_1.java b/langtools/test/tools/javac/generics/wildcards/6886247/T6886247_1.java new file mode 100644 index 00000000000..5133f8abe14 --- /dev/null +++ b/langtools/test/tools/javac/generics/wildcards/6886247/T6886247_1.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2010, 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 6886247 + * @author Maurizio Cimadamore + * @summary regression: javac crashes with an assertion error in Attr.java + * @compile T6886247_1.java + */ +class Outer { + + public void method(Outer.Inner inner) { + E entry = inner.getE(); + } + + class Inner { + E getE() {return null;} + } +} diff --git a/langtools/test/tools/javac/generics/wildcards/6886247/T6886247_2.java b/langtools/test/tools/javac/generics/wildcards/6886247/T6886247_2.java new file mode 100644 index 00000000000..395b77b56db --- /dev/null +++ b/langtools/test/tools/javac/generics/wildcards/6886247/T6886247_2.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2010, 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 6886247 + * @author Maurizio Cimadamore + * @summary regression: javac crashes with an assertion error in Attr.java + * @compile/fail/ref=T6886247_2.out -XDrawDiagnostics T6886247_2.java + */ + +class Outer { + + public void method(Outer.Inner inner) { + E entry = inner.getE(); + } + + class Inner { + E getE() {return null;} + } +} diff --git a/langtools/test/tools/javac/generics/wildcards/6886247/T6886247_2.out b/langtools/test/tools/javac/generics/wildcards/6886247/T6886247_2.out new file mode 100644 index 00000000000..f9997868be5 --- /dev/null +++ b/langtools/test/tools/javac/generics/wildcards/6886247/T6886247_2.out @@ -0,0 +1,2 @@ +T6886247_2.java:35:28: compiler.err.prob.found.req: (compiler.misc.incompatible.types), compiler.misc.type.captureof: 1, ?, E +1 error From d56e09153a819db0410f76d262ed90d0271fb2f9 Mon Sep 17 00:00:00 2001 From: Maurizio Cimadamore Date: Thu, 19 Aug 2010 11:52:58 +0100 Subject: [PATCH 050/100] 6885255: Improve usability of raw warnings Raw warnings should be disabled in (i) instanceof expressions and (ii) when java.lang.Class is not parameterized Reviewed-by: jjg --- .../com/sun/tools/javac/comp/Attr.java | 4 +- .../com/sun/tools/javac/comp/Check.java | 76 ++++++++++++------- .../javac/warnings/6747671/T6747671.java | 4 +- .../tools/javac/warnings/6747671/T6747671.out | 4 +- .../javac/warnings/6885255/T6885255.java | 31 ++++++++ .../tools/javac/warnings/6885255/T6885255.out | 16 ++++ 6 files changed, 100 insertions(+), 35 deletions(-) create mode 100644 langtools/test/tools/javac/warnings/6885255/T6885255.java create mode 100644 langtools/test/tools/javac/warnings/6885255/T6885255.out diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java index 4a7ab59fd16..aa58f1b0a9b 100644 --- a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java @@ -2008,7 +2008,7 @@ public class Attr extends JCTree.Visitor { public void visitTypeCast(JCTypeCast tree) { Type clazztype = attribType(tree.clazz, env); - chk.validate(tree.clazz, env); + chk.validate(tree.clazz, env, false); Type exprtype = attribExpr(tree.expr, env, Infer.anyPoly); Type owntype = chk.checkCastable(tree.expr.pos(), exprtype, clazztype); if (exprtype.constValue() != null) @@ -2021,7 +2021,7 @@ public class Attr extends JCTree.Visitor { tree.expr.pos(), attribExpr(tree.expr, env)); Type clazztype = chk.checkReifiableReferenceType( tree.clazz.pos(), attribType(tree.clazz, env)); - chk.validate(tree.clazz, env); + chk.validate(tree.clazz, env, false); chk.checkCastable(tree.expr.pos(), exprtype, clazztype); result = check(tree, syms.booleanType, VAL, pkind, pt); } diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java index 4edbf72443a..0d5784803a4 100644 --- a/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java +++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java @@ -906,33 +906,15 @@ public class Check { * * and we can't make sure that the bound is already attributed because * of possible cycles. - */ - private Validator validator = new Validator(); - - /** Visitor method: Validate a type expression, if it is not null, catching + * + * Visitor method: Validate a type expression, if it is not null, catching * and reporting any completion failures. */ void validate(JCTree tree, Env env) { - try { - if (tree != null) { - validator.env = env; - tree.accept(validator); - checkRaw(tree, env); - } - } catch (CompletionFailure ex) { - completionError(tree.pos(), ex); - } + validate(tree, env, true); } - //where - void checkRaw(JCTree tree, Env env) { - if (lint.isEnabled(Lint.LintCategory.RAW) && - tree.type.tag == CLASS && - !TreeInfo.isDiamond(tree) && - !env.enclClass.name.isEmpty() && //anonymous or intersection - tree.type.isRaw()) { - log.warning(Lint.LintCategory.RAW, - tree.pos(), "raw.class.use", tree.type, tree.type.tsym.type); - } + void validate(JCTree tree, Env env, boolean checkRaw) { + new Validator(env).validateTree(tree, checkRaw, true); } /** Visitor method: Validate a list of type expressions. @@ -946,9 +928,16 @@ public class Check { */ class Validator extends JCTree.Visitor { + boolean isOuter; + Env env; + + Validator(Env env) { + this.env = env; + } + @Override public void visitTypeArray(JCArrayTypeTree tree) { - validate(tree.elemtype, env); + tree.elemtype.accept(this); } @Override @@ -960,10 +949,14 @@ public class Check { List forms = tree.type.tsym.type.getTypeArguments(); ListBuffer tvars_buf = new ListBuffer(); + boolean is_java_lang_Class = tree.type.tsym.flatName() == names.java_lang_Class; + // For matching pairs of actual argument types `a' and // formal type parameters with declared bound `b' ... while (args.nonEmpty() && forms.nonEmpty()) { - validate(args.head, env); + validateTree(args.head, + !(isOuter && is_java_lang_Class), + false); // exact type arguments needs to know their // bounds (for upper and lower bound @@ -1015,14 +1008,14 @@ public class Check { @Override public void visitTypeParameter(JCTypeParameter tree) { - validate(tree.bounds, env); + validateTrees(tree.bounds, true, isOuter); checkClassBounds(tree.pos(), tree.type); } @Override public void visitWildcard(JCWildcard tree) { if (tree.inner != null) - validate(tree.inner, env); + validateTree(tree.inner, true, isOuter); } @Override @@ -1060,7 +1053,34 @@ public class Check { public void visitTree(JCTree tree) { } - Env env; + public void validateTree(JCTree tree, boolean checkRaw, boolean isOuter) { + try { + if (tree != null) { + this.isOuter = isOuter; + tree.accept(this); + if (checkRaw) + checkRaw(tree, env); + } + } catch (CompletionFailure ex) { + completionError(tree.pos(), ex); + } + } + + public void validateTrees(List trees, boolean checkRaw, boolean isOuter) { + for (List l = trees; l.nonEmpty(); l = l.tail) + validateTree(l.head, checkRaw, isOuter); + } + + void checkRaw(JCTree tree, Env env) { + if (lint.isEnabled(Lint.LintCategory.RAW) && + tree.type.tag == CLASS && + !TreeInfo.isDiamond(tree) && + !env.enclClass.name.isEmpty() && //anonymous or intersection + tree.type.isRaw()) { + log.warning(Lint.LintCategory.RAW, + tree.pos(), "raw.class.use", tree.type, tree.type.tsym.type); + } + } } /* ************************************************************************* diff --git a/langtools/test/tools/javac/warnings/6747671/T6747671.java b/langtools/test/tools/javac/warnings/6747671/T6747671.java index 4bb0855257a..c8060a06afd 100644 --- a/langtools/test/tools/javac/warnings/6747671/T6747671.java +++ b/langtools/test/tools/javac/warnings/6747671/T6747671.java @@ -27,8 +27,8 @@ class T6747671 { A.Z> z3;//raw warning (2) void test(Object arg1, B arg2) {//raw warning - boolean b = arg1 instanceof A;//raw warning - Object a = (A)arg1;//raw warning + boolean b = arg1 instanceof A;//ok + Object a = (A)arg1;//ok A a2 = new A() {};//raw warning (2) a2.new Z() {};//raw warning } diff --git a/langtools/test/tools/javac/warnings/6747671/T6747671.out b/langtools/test/tools/javac/warnings/6747671/T6747671.out index cc17bdb90ef..43069572c62 100644 --- a/langtools/test/tools/javac/warnings/6747671/T6747671.out +++ b/langtools/test/tools/javac/warnings/6747671/T6747671.out @@ -4,9 +4,7 @@ T6747671.java:23:13: compiler.warn.raw.class.use: T6747671.B, T6747671.B T6747671.java:27:14: compiler.warn.raw.class.use: T6747671.B, T6747671.B T6747671.java:27:7: compiler.warn.raw.class.use: T6747671.B, T6747671.B T6747671.java:29:28: compiler.warn.raw.class.use: T6747671.B, T6747671.B -T6747671.java:30:37: compiler.warn.raw.class.use: T6747671.A, T6747671.A -T6747671.java:31:21: compiler.warn.raw.class.use: T6747671.A, T6747671.A T6747671.java:32:9: compiler.warn.raw.class.use: T6747671.A, T6747671.A T6747671.java:32:20: compiler.warn.raw.class.use: T6747671.A, T6747671.A T6747671.java:33:16: compiler.warn.raw.class.use: T6747671.A.Z, T6747671.A.Z -11 warnings +9 warnings diff --git a/langtools/test/tools/javac/warnings/6885255/T6885255.java b/langtools/test/tools/javac/warnings/6885255/T6885255.java new file mode 100644 index 00000000000..d5b88a74f0d --- /dev/null +++ b/langtools/test/tools/javac/warnings/6885255/T6885255.java @@ -0,0 +1,31 @@ +/** + * @test /nodynamiccopyright/ + * @bug 6885255 + * @summary -Xlint:rawtypes + * @compile/ref=T6885255.out -XDrawDiagnostics -Xlint:rawtypes T6885255.java + */ + +class T6885255 { + + static class Test {} + + Class ct; //no warn - outer Class w/ raw param + Class> ctt; //warn - outer Class w/o raw param (2) + + Class> cct; //warn - outer Class w/o raw param + Class>> cctt; //warn - outer Class w/o raw param (2) + + Object o1 = (Test)null; //no warn - outer raw and cast + Object o2 = (Test)null; //warn - inner raw (2) + + Object o3 = (Class)null; //no warn - outer raw and cast + Object o4 = (Class)null; //no warn - outer Class w/ raw param + + Object o5 = (Class>)null; //warn - outer Class w/ non raw param (2) + Object o6 = (Class>>)null; //warn - outer Class w/ non raw param (2) + + Object o7 = (Test)null; //warn - inner raw (2) + Object o8 = (Test, Class>)null; //warn - inner Class (2) + + boolean b = null instanceof Test; //no warn - raw and instanceof +} diff --git a/langtools/test/tools/javac/warnings/6885255/T6885255.out b/langtools/test/tools/javac/warnings/6885255/T6885255.out new file mode 100644 index 00000000000..ab0a3ea9656 --- /dev/null +++ b/langtools/test/tools/javac/warnings/6885255/T6885255.out @@ -0,0 +1,16 @@ +T6885255.java:13:16: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test +T6885255.java:13:22: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test +T6885255.java:15:17: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test +T6885255.java:16:22: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test +T6885255.java:16:28: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test +T6885255.java:19:23: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test +T6885255.java:19:29: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test +T6885255.java:24:29: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test +T6885255.java:24:35: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test +T6885255.java:25:35: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test +T6885255.java:25:41: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test +T6885255.java:27:23: compiler.warn.raw.class.use: java.lang.Class, java.lang.Class +T6885255.java:27:30: compiler.warn.raw.class.use: java.lang.Class, java.lang.Class +T6885255.java:28:29: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test +T6885255.java:28:42: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test +15 warnings From 634d001c64306a9df12668e7fe891b906fa4f30b Mon Sep 17 00:00:00 2001 From: Maurizio Cimadamore Date: Thu, 19 Aug 2010 11:54:25 +0100 Subject: [PATCH 051/100] 6977800: Regression: invalid resolution of supertype for local class Resolution of superclass/superinterfaces in extends/implements clause skips local classes Reviewed-by: jjg --- .../com/sun/tools/javac/comp/Check.java | 2 +- .../com/sun/tools/javac/comp/MemberEnter.java | 13 ++++-- langtools/test/tools/javac/T6977800.java | 41 +++++++++++++++++++ .../typevars/5060485/Compatibility.java | 2 +- .../typevars/5060485/Compatibility.out | 2 + .../typevars/5060485/Compatibility02.java | 39 ++++++++++++++++++ .../typevars/5060485/Compatibility02.out | 2 + 7 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 langtools/test/tools/javac/T6977800.java create mode 100644 langtools/test/tools/javac/generics/typevars/5060485/Compatibility.out create mode 100644 langtools/test/tools/javac/generics/typevars/5060485/Compatibility02.java create mode 100644 langtools/test/tools/javac/generics/typevars/5060485/Compatibility02.out diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java b/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java index 0d5784803a4..9f46e32b648 100644 --- a/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java +++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java @@ -330,7 +330,7 @@ public class Check { for (Scope.Entry e = s.next.lookup(c.name); e.scope != null && e.sym.owner == c.owner; e = e.next()) { - if (e.sym.kind == TYP && + if (e.sym.kind == TYP && e.sym.type.tag != TYPEVAR && (e.sym.owner.kind & (VAR | MTH)) != 0 && c.name != names.error) { duplicateError(pos, e.sym); diff --git a/langtools/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java b/langtools/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java index 1630b5be84c..501e4e9a792 100644 --- a/langtools/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java +++ b/langtools/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java @@ -1079,14 +1079,21 @@ public class MemberEnter extends JCTree.Visitor implements Completer { private Env baseEnv(JCClassDecl tree, Env env) { - Scope typaramScope = new Scope(tree.sym); + Scope baseScope = new Scope(tree.sym); + //import already entered local classes into base scope + for (Scope.Entry e = env.outer.info.scope.elems ; e != null ; e = e.sibling) { + if (e.sym.isLocal()) { + baseScope.enter(e.sym); + } + } + //import current type-parameters into base scope if (tree.typarams != null) for (List typarams = tree.typarams; typarams.nonEmpty(); typarams = typarams.tail) - typaramScope.enter(typarams.head.type.tsym); + baseScope.enter(typarams.head.type.tsym); Env outer = env.outer; // the base clause can't see members of this class - Env localEnv = outer.dup(tree, outer.info.dup(typaramScope)); + Env localEnv = outer.dup(tree, outer.info.dup(baseScope)); localEnv.baseClause = true; localEnv.outer = outer; localEnv.info.isSelfCall = false; diff --git a/langtools/test/tools/javac/T6977800.java b/langtools/test/tools/javac/T6977800.java new file mode 100644 index 00000000000..edfbef49e4e --- /dev/null +++ b/langtools/test/tools/javac/T6977800.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2010, 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 6977800 + * @summary Regression: invalid resolution of supertype for local class + * @compile T6977800.java + */ + +class T6977800 { + public static void test() { + class A { + int x = 1; + } + class B extends A {} + System.out.println(new B().x); + } + + static class A {} +} diff --git a/langtools/test/tools/javac/generics/typevars/5060485/Compatibility.java b/langtools/test/tools/javac/generics/typevars/5060485/Compatibility.java index a2d2936cbf2..3817e207069 100644 --- a/langtools/test/tools/javac/generics/typevars/5060485/Compatibility.java +++ b/langtools/test/tools/javac/generics/typevars/5060485/Compatibility.java @@ -26,7 +26,7 @@ * @bug 5060485 * @summary The scope of a class type parameter is too wide * @author Peter von der Ah\u00e9 - * @compile/fail Compatibility.java + * @compile/fail/ref=Compatibility.out -XDrawDiagnostics Compatibility.java */ class NumberList {} diff --git a/langtools/test/tools/javac/generics/typevars/5060485/Compatibility.out b/langtools/test/tools/javac/generics/typevars/5060485/Compatibility.out new file mode 100644 index 00000000000..0e95db0532e --- /dev/null +++ b/langtools/test/tools/javac/generics/typevars/5060485/Compatibility.out @@ -0,0 +1,2 @@ +Compatibility.java:36:35: compiler.err.not.within.bounds: Test.Y +1 error diff --git a/langtools/test/tools/javac/generics/typevars/5060485/Compatibility02.java b/langtools/test/tools/javac/generics/typevars/5060485/Compatibility02.java new file mode 100644 index 00000000000..6a5a612c2ad --- /dev/null +++ b/langtools/test/tools/javac/generics/typevars/5060485/Compatibility02.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006, 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 5060485 6977800 + * @summary The scope of a class type parameter is too wide + * @author Maurizio Cimadamore + * @compile/fail/ref=Compatibility02.out -XDrawDiagnostics Compatibility.java + */ + +class NumberList {} + +class Test { + void m() { + static class Y {} + class Y1> {} + } +} diff --git a/langtools/test/tools/javac/generics/typevars/5060485/Compatibility02.out b/langtools/test/tools/javac/generics/typevars/5060485/Compatibility02.out new file mode 100644 index 00000000000..0e95db0532e --- /dev/null +++ b/langtools/test/tools/javac/generics/typevars/5060485/Compatibility02.out @@ -0,0 +1,2 @@ +Compatibility.java:36:35: compiler.err.not.within.bounds: Test.Y +1 error From 500e68f0b1ea26b3939a8cdbf2bfc571ca0e68a3 Mon Sep 17 00:00:00 2001 From: "Y. Srinivas Ramakrishna" Date: Thu, 19 Aug 2010 12:02:10 -0700 Subject: [PATCH 052/100] 6978533: CMS: Elide BOT update asserts until 6977974 is fixed correctly Reviewed-by: jcoomes, jmasa, tonyp --- hotspot/src/share/vm/memory/blockOffsetTable.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hotspot/src/share/vm/memory/blockOffsetTable.hpp b/hotspot/src/share/vm/memory/blockOffsetTable.hpp index e122ac54004..c0c78149a6b 100644 --- a/hotspot/src/share/vm/memory/blockOffsetTable.hpp +++ b/hotspot/src/share/vm/memory/blockOffsetTable.hpp @@ -163,7 +163,8 @@ class BlockOffsetSharedArray: public CHeapObj { size_t i = index_for(left); const size_t end = i + num_cards; for (; i < end; i++) { - assert(!reducing || _offset_array[i] >= offset, "Not reducing"); + // Elided until CR 6977974 is fixed properly. + // assert(!reducing || _offset_array[i] >= offset, "Not reducing"); _offset_array[i] = offset; } } @@ -184,7 +185,8 @@ class BlockOffsetSharedArray: public CHeapObj { size_t i = left; const size_t end = i + num_cards; for (; i < end; i++) { - assert(!reducing || _offset_array[i] >= offset, "Not reducing"); + // Elided until CR 6977974 is fixed properly. + // assert(!reducing || _offset_array[i] >= offset, "Not reducing"); _offset_array[i] = offset; } } From facee61c79411b148ee45d737d268e868e6af1b3 Mon Sep 17 00:00:00 2001 From: Kumar Srinivasan Date: Thu, 19 Aug 2010 14:08:04 -0700 Subject: [PATCH 053/100] 6888127: java.util.jar.Pack200.Packer Memory Leak Reviewed-by: jrose --- .../com/sun/java/util/jar/pack/Attribute.java | 100 ++++++------- .../sun/java/util/jar/pack/ConstantPool.java | 113 +++++++------- .../com/sun/java/util/jar/pack/Driver.java | 23 +-- .../sun/java/util/jar/pack/NativeUnpack.java | 11 +- .../com/sun/java/util/jar/pack/Package.java | 122 ++++++++------- .../sun/java/util/jar/pack/PackerImpl.java | 140 ++++++++---------- .../com/sun/java/util/jar/pack/TLGlobals.java | 97 ++++++++++++ .../sun/java/util/jar/pack/UnpackerImpl.java | 67 ++++----- .../com/sun/java/util/jar/pack/Utils.java | 48 +++++- 9 files changed, 420 insertions(+), 301 deletions(-) create mode 100644 jdk/src/share/classes/com/sun/java/util/jar/pack/TLGlobals.java diff --git a/jdk/src/share/classes/com/sun/java/util/jar/pack/Attribute.java b/jdk/src/share/classes/com/sun/java/util/jar/pack/Attribute.java index 515218729ed..0c1b0954300 100644 --- a/jdk/src/share/classes/com/sun/java/util/jar/pack/Attribute.java +++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/Attribute.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -27,7 +27,6 @@ package com.sun.java.util.jar.pack; import java.io.*; import java.util.*; -import com.sun.java.util.jar.pack.Package.Class; import com.sun.java.util.jar.pack.ConstantPool.*; /** @@ -96,20 +95,20 @@ class Attribute implements Comparable, Constants { return this.def.compareTo(that.def); } - static private final byte[] noBytes = {}; - static private final HashMap canonLists = new HashMap(); - static private final HashMap attributes = new HashMap(); - static private final HashMap standardDefs = new HashMap(); + private static final byte[] noBytes = {}; + private static final Map, List> canonLists = new HashMap<>(); + private static final Map attributes = new HashMap<>(); + private static final Map standardDefs = new HashMap<>(); // Canonicalized lists of trivial attrs (Deprecated, etc.) // are used by trimToSize, in order to reduce footprint // of some common cases. (Note that Code attributes are // always zero size.) - public static List getCanonList(List al) { + public static List getCanonList(List al) { synchronized (canonLists) { - List cl = (List) canonLists.get(al); + List cl = canonLists.get(al); if (cl == null) { - cl = new ArrayList(al.size()); + cl = new ArrayList<>(al.size()); cl.addAll(al); cl = Collections.unmodifiableList(cl); canonLists.put(al, cl); @@ -122,7 +121,7 @@ class Attribute implements Comparable, Constants { public static Attribute find(int ctype, String name, String layout) { Layout key = Layout.makeKey(ctype, name, layout); synchronized (attributes) { - Attribute a = (Attribute) attributes.get(key); + Attribute a = attributes.get(key); if (a == null) { a = new Layout(ctype, name, layout).canonicalInstance(); attributes.put(key, a); @@ -131,24 +130,29 @@ class Attribute implements Comparable, Constants { } } - public static Object keyForLookup(int ctype, String name) { + public static Layout keyForLookup(int ctype, String name) { return Layout.makeKey(ctype, name); } // Find canonical empty attribute with given ctype and name, // and with the standard layout. - public static Attribute lookup(Map defs, int ctype, String name) { - if (defs == null) defs = standardDefs; - return (Attribute) defs.get(Layout.makeKey(ctype, name)); + public static Attribute lookup(Map defs, int ctype, + String name) { + if (defs == null) { + defs = standardDefs; + } + return defs.get(Layout.makeKey(ctype, name)); } - public static Attribute define(Map defs, int ctype, String name, String layout) { + + public static Attribute define(Map defs, int ctype, + String name, String layout) { Attribute a = find(ctype, name, layout); defs.put(Layout.makeKey(ctype, name), a); return a; } static { - Map sd = standardDefs; + Map sd = standardDefs; define(sd, ATTR_CONTEXT_CLASS, "Signature", "RSH"); define(sd, ATTR_CONTEXT_CLASS, "Synthetic", ""); define(sd, ATTR_CONTEXT_CLASS, "Deprecated", ""); @@ -244,7 +248,7 @@ class Attribute implements Comparable, Constants { +"\n ()[] ]" ) }; - Map sd = standardDefs; + Map sd = standardDefs; String defaultLayout = mdLayouts[2]; String annotationsLayout = mdLayouts[1] + mdLayouts[2]; String paramsLayout = mdLayouts[0] + annotationsLayout; @@ -275,10 +279,6 @@ class Attribute implements Comparable, Constants { return null; } - public static Map getStandardDefs() { - return new HashMap(standardDefs); - } - /** Base class for any attributed object (Class, Field, Method, Code). * Flags are included because they are used to help transmit the * presence of attributes. That is, flags are a mix of modifier @@ -291,7 +291,7 @@ class Attribute implements Comparable, Constants { protected abstract Entry[] getCPMap(); protected int flags; // defined here for convenience - protected List attributes; + protected List attributes; public int attributeSize() { return (attributes == null) ? 0 : attributes.size(); @@ -301,16 +301,15 @@ class Attribute implements Comparable, Constants { if (attributes == null) { return; } - if (attributes.size() == 0) { + if (attributes.isEmpty()) { attributes = null; return; } if (attributes instanceof ArrayList) { - ArrayList al = (ArrayList) attributes; + ArrayList al = (ArrayList)attributes; al.trimToSize(); boolean allCanon = true; - for (Iterator i = al.iterator(); i.hasNext(); ) { - Attribute a = (Attribute) i.next(); + for (Attribute a : al) { if (!a.isCanonical()) { allCanon = false; } @@ -330,9 +329,9 @@ class Attribute implements Comparable, Constants { public void addAttribute(Attribute a) { if (attributes == null) - attributes = new ArrayList(3); + attributes = new ArrayList<>(3); else if (!(attributes instanceof ArrayList)) - attributes = new ArrayList(attributes); // unfreeze it + attributes = new ArrayList<>(attributes); // unfreeze it attributes.add(a); } @@ -340,32 +339,31 @@ class Attribute implements Comparable, Constants { if (attributes == null) return null; if (!attributes.contains(a)) return null; if (!(attributes instanceof ArrayList)) - attributes = new ArrayList(attributes); // unfreeze it + attributes = new ArrayList<>(attributes); // unfreeze it attributes.remove(a); return a; } public Attribute getAttribute(int n) { - return (Attribute) attributes.get(n); + return attributes.get(n); } - protected void visitRefs(int mode, Collection refs) { + protected void visitRefs(int mode, Collection refs) { if (attributes == null) return; - for (Iterator i = attributes.iterator(); i.hasNext(); ) { - Attribute a = (Attribute) i.next(); + for (Attribute a : attributes) { a.visitRefs(this, mode, refs); } } - static final List noAttributes = Arrays.asList(new Object[0]); + static final List noAttributes = Arrays.asList(new Attribute[0]); - public List getAttributes() { + public List getAttributes() { if (attributes == null) return noAttributes; return attributes; } - public void setAttributes(List attrList) { + public void setAttributes(List attrList) { if (attrList.isEmpty()) attributes = null; else @@ -374,8 +372,7 @@ class Attribute implements Comparable, Constants { public Attribute getAttribute(String attrName) { if (attributes == null) return null; - for (Iterator i = attributes.iterator(); i.hasNext(); ) { - Attribute a = (Attribute) i.next(); + for (Attribute a : attributes) { if (a.name().equals(attrName)) return a; } @@ -384,8 +381,7 @@ class Attribute implements Comparable, Constants { public Attribute getAttribute(Layout attrDef) { if (attributes == null) return null; - for (Iterator i = attributes.iterator(); i.hasNext(); ) { - Attribute a = (Attribute) i.next(); + for (Attribute a : attributes) { if (a.layout() == attrDef) return a; } @@ -457,14 +453,8 @@ class Attribute implements Comparable, Constants { public String layout() { return layout; } public Attribute canonicalInstance() { return canon; } - // Cache of name reference. - private Entry nameRef; // name, for use by visitRefs public Entry getNameRef() { - Entry nameRef = this.nameRef; - if (nameRef == null) { - this.nameRef = nameRef = ConstantPool.getUtf8Entry(name()); - } - return nameRef; + return ConstantPool.getUtf8Entry(name()); } public boolean isEmpty() { return layout == ""; } @@ -834,14 +824,14 @@ class Attribute implements Comparable, Constants { */ static //private Layout.Element[] tokenizeLayout(Layout self, int curCble, String layout) { - ArrayList col = new ArrayList(layout.length()); + ArrayList col = new ArrayList<>(layout.length()); tokenizeLayout(self, curCble, layout, col); Layout.Element[] res = new Layout.Element[col.size()]; col.toArray(res); return res; } static //private - void tokenizeLayout(Layout self, int curCble, String layout, ArrayList col) { + void tokenizeLayout(Layout self, int curCble, String layout, ArrayList col) { boolean prevBCI = false; for (int len = layout.length(), i = 0; i < len; ) { int start = i; @@ -899,7 +889,7 @@ class Attribute implements Comparable, Constants { case 'T': // union: 'T' any_int union_case* '(' ')' '[' body ']' kind = EK_UN; i = tokenizeSInt(e, layout, i); - ArrayList cases = new ArrayList(); + ArrayList cases = new ArrayList<>(); for (;;) { // Keep parsing cases until we hit the default case. if (layout.charAt(i++) != '(') @@ -1053,7 +1043,7 @@ class Attribute implements Comparable, Constants { } static //private String[] splitBodies(String layout) { - ArrayList bodies = new ArrayList(); + ArrayList bodies = new ArrayList<>(); // Parse several independent layout bodies: "[foo][bar]...[baz]" for (int i = 0; i < layout.length(); i++) { if (layout.charAt(i++) != '[') @@ -1132,7 +1122,9 @@ class Attribute implements Comparable, Constants { int parseIntBefore(String layout, int dash) { int end = dash; int beg = end; - while (beg > 0 && isDigit(layout.charAt(beg-1))) --beg; + while (beg > 0 && isDigit(layout.charAt(beg-1))) { + --beg; + } if (beg == end) return Integer.parseInt("empty"); // skip backward over a sign if (beg >= 1 && layout.charAt(beg-1) == '-') --beg; @@ -1145,7 +1137,9 @@ class Attribute implements Comparable, Constants { int end = beg; int limit = layout.length(); if (end < limit && layout.charAt(end) == '-') ++end; - while (end < limit && isDigit(layout.charAt(end))) ++end; + while (end < limit && isDigit(layout.charAt(end))) { + ++end; + } if (beg == end) return Integer.parseInt("empty"); return Integer.parseInt(layout.substring(beg, end)); } diff --git a/jdk/src/share/classes/com/sun/java/util/jar/pack/ConstantPool.java b/jdk/src/share/classes/com/sun/java/util/jar/pack/ConstantPool.java index ab138bc2d91..ac6199abe25 100644 --- a/jdk/src/share/classes/com/sun/java/util/jar/pack/ConstantPool.java +++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/ConstantPool.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,6 @@ package com.sun.java.util.jar.pack; -import java.io.*; import java.util.*; /** @@ -40,20 +39,13 @@ class ConstantPool implements Constants { return Utils.currentPropMap().getInteger(Utils.DEBUG_VERBOSE); } - // Uniquification tables for factory methods: - private static final HashMap utf8Entries = new HashMap(); - private static final HashMap classEntries = new HashMap(); - private static final HashMap literalEntries = new HashMap(); - private static final HashMap signatureEntries = new HashMap(); - private static final HashMap descriptorEntries = new HashMap(); - private static final HashMap memberEntries = new HashMap(); - /** Factory for Utf8 string constants. * Used for well-known strings like "SourceFile", "", etc. * Also used to back up more complex constant pool entries, like Class. */ public static synchronized Utf8Entry getUtf8Entry(String value) { - Utf8Entry e = (Utf8Entry) utf8Entries.get(value); + Map utf8Entries = Utils.getUtf8Entries(); + Utf8Entry e = utf8Entries.get(value); if (e == null) { e = new Utf8Entry(value); utf8Entries.put(e.stringValue(), e); @@ -62,9 +54,10 @@ class ConstantPool implements Constants { } /** Factory for Class constants. */ public static synchronized ClassEntry getClassEntry(String name) { - ClassEntry e = (ClassEntry) classEntries.get(name); + Map classEntries = Utils.getClassEntries(); + ClassEntry e = classEntries.get(name); if (e == null) { - e = (ClassEntry) new ClassEntry(getUtf8Entry(name)); + e = new ClassEntry(getUtf8Entry(name)); assert(name.equals(e.stringValue())); classEntries.put(e.stringValue(), e); } @@ -72,7 +65,8 @@ class ConstantPool implements Constants { } /** Factory for literal constants (String, Integer, etc.). */ public static synchronized LiteralEntry getLiteralEntry(Comparable value) { - LiteralEntry e = (LiteralEntry) literalEntries.get(value); + Map literalEntries = Utils.getLiteralEntries(); + LiteralEntry e = literalEntries.get(value); if (e == null) { if (value instanceof String) e = new StringEntry(getUtf8Entry((String)value)); @@ -89,7 +83,8 @@ class ConstantPool implements Constants { /** Factory for signature (type) constants. */ public static synchronized SignatureEntry getSignatureEntry(String type) { - SignatureEntry e = (SignatureEntry) signatureEntries.get(type); + Map signatureEntries = Utils.getSignatureEntries(); + SignatureEntry e = signatureEntries.get(type); if (e == null) { e = new SignatureEntry(type); assert(e.stringValue().equals(type)); @@ -104,8 +99,9 @@ class ConstantPool implements Constants { /** Factory for descriptor (name-and-type) constants. */ public static synchronized DescriptorEntry getDescriptorEntry(Utf8Entry nameRef, SignatureEntry typeRef) { + Map descriptorEntries = Utils.getDescriptorEntries(); String key = DescriptorEntry.stringValueOf(nameRef, typeRef); - DescriptorEntry e = (DescriptorEntry) descriptorEntries.get(key); + DescriptorEntry e = descriptorEntries.get(key); if (e == null) { e = new DescriptorEntry(nameRef, typeRef); assert(e.stringValue().equals(key)) @@ -121,8 +117,9 @@ class ConstantPool implements Constants { /** Factory for member reference constants. */ public static synchronized MemberEntry getMemberEntry(byte tag, ClassEntry classRef, DescriptorEntry descRef) { + Map memberEntries = Utils.getMemberEntries(); String key = MemberEntry.stringValueOf(tag, classRef, descRef); - MemberEntry e = (MemberEntry) memberEntries.get(key); + MemberEntry e = memberEntries.get(key); if (e == null) { e = new MemberEntry(tag, classRef, descRef); assert(e.stringValue().equals(key)) @@ -489,8 +486,9 @@ class ConstantPool implements Constants { String[] parts = structureSignature(value); formRef = getUtf8Entry(parts[0]); classRefs = new ClassEntry[parts.length-1]; - for (int i = 1; i < parts.length; i++) - classRefs[i-1] = getClassEntry(parts[i]); + for (int i = 1; i < parts.length; i++) { + classRefs[i - 1] = getClassEntry(parts[i]); + } hashCode(); // force computation of valueHash } protected int computeValueHash() { @@ -527,8 +525,9 @@ class ConstantPool implements Constants { String stringValueOf(Utf8Entry formRef, ClassEntry[] classRefs) { String[] parts = new String[1+classRefs.length]; parts[0] = formRef.stringValue(); - for (int i = 1; i < parts.length; i++) - parts[i] = classRefs[i-1].stringValue(); + for (int i = 1; i < parts.length; i++) { + parts[i] = classRefs[i - 1].stringValue(); + } return flattenSignature(parts).intern(); } @@ -543,19 +542,23 @@ class ConstantPool implements Constants { int size = 0; for (int i = min; i < max; i++) { switch (form.charAt(i)) { - case 'D': - case 'J': - if (countDoublesTwice) size++; - break; - case '[': - // Skip rest of array info. - while (form.charAt(i) == '[') ++i; - break; - case ';': - continue; - default: - assert(0 <= JAVA_SIGNATURE_CHARS.indexOf(form.charAt(i))); - break; + case 'D': + case 'J': + if (countDoublesTwice) { + size++; + } + break; + case '[': + // Skip rest of array info. + while (form.charAt(i) == '[') { + ++i; + } + break; + case ';': + continue; + default: + assert (0 <= JAVA_SIGNATURE_CHARS.indexOf(form.charAt(i))); + break; } size++; } @@ -586,8 +589,9 @@ class ConstantPool implements Constants { s = "/" + formRef.stringValue(); } int i; - while ((i = s.indexOf(';')) >= 0) - s = s.substring(0,i) + s.substring(i+1); + while ((i = s.indexOf(';')) >= 0) { + s = s.substring(0, i) + s.substring(i + 1); + } return s; } } @@ -732,11 +736,11 @@ class ConstantPool implements Constants { clearIndex(); this.cpMap = cpMap; } - protected Index(String debugName, Collection cpMapList) { + protected Index(String debugName, Collection cpMapList) { this(debugName); setMap(cpMapList); } - protected void setMap(Collection cpMapList) { + protected void setMap(Collection cpMapList) { cpMap = new Entry[cpMapList.size()]; cpMapList.toArray(cpMap); setMap(cpMap); @@ -756,11 +760,13 @@ class ConstantPool implements Constants { // // As a special hack, if flattenSigs, signatures are // treated as equivalent entries of cpMap. This is wrong - // fron a Collection point of view, because contains() + // from a Collection point of view, because contains() // reports true for signatures, but the iterator() // never produces them! private int findIndexOf(Entry e) { - if (indexKey == null) initializeIndex(); + if (indexKey == null) { + initializeIndex(); + } int probe = findIndexLocation(e); if (indexKey[probe] != e) { if (flattenSigs && e.tag == CONSTANT_Signature) { @@ -832,7 +838,9 @@ class ConstantPool implements Constants { System.out.println("initialize Index "+debugName+" ["+size()+"]"); int hsize0 = (int)((cpMap.length + 10) * 1.5); int hsize = 1; - while (hsize < hsize0) hsize <<= 1; + while (hsize < hsize0) { + hsize <<= 1; + } indexKey = new Entry[hsize]; indexValue = new int[hsize]; for (int i = 0; i < cpMap.length; i++) { @@ -855,7 +863,7 @@ class ConstantPool implements Constants { return toArray(new Entry[size()]); } public Object clone() { - return new Index(debugName, (Entry[]) cpMap.clone()); + return new Index(debugName, cpMap.clone()); } public String toString() { return "Index "+debugName+" ["+size()+"]"; @@ -901,22 +909,24 @@ class ConstantPool implements Constants { public static Index[] partition(Index ix, int[] keys) { // %%% Should move this into class Index. - ArrayList parts = new ArrayList(); + ArrayList> parts = new ArrayList<>(); Entry[] cpMap = ix.cpMap; assert(keys.length == cpMap.length); for (int i = 0; i < keys.length; i++) { int key = keys[i]; if (key < 0) continue; - while (key >= parts.size()) parts.add(null); - ArrayList part = (ArrayList) parts.get(key); + while (key >= parts.size()) { + parts.add(null); + } + List part = parts.get(key); if (part == null) { - parts.set(key, part = new ArrayList()); + parts.set(key, part = new ArrayList<>()); } part.add(cpMap[i]); } Index[] indexes = new Index[parts.size()]; for (int key = 0; key < indexes.length; key++) { - ArrayList part = (ArrayList) parts.get(key); + List part = parts.get(key); if (part == null) continue; indexes[key] = new Index(ix.debugName+"/part#"+key, part); assert(indexes[key].indexOf(part.get(0)) == 0); @@ -1048,9 +1058,10 @@ class ConstantPool implements Constants { whichClasses[i] = whichClass; } perClassIndexes = partition(allMembers, whichClasses); - for (int i = 0; i < perClassIndexes.length; i++) - assert(perClassIndexes[i]==null - || perClassIndexes[i].assertIsSorted()); + for (int i = 0; i < perClassIndexes.length; i++) { + assert (perClassIndexes[i] == null || + perClassIndexes[i].assertIsSorted()); + } indexByTagAndClass[tag] = perClassIndexes; } int whichClass = allClasses.indexOf(classRef); @@ -1113,7 +1124,7 @@ class ConstantPool implements Constants { * Also, discard null from cpRefs. */ public static - void completeReferencesIn(Set cpRefs, boolean flattenSigs) { + void completeReferencesIn(Set cpRefs, boolean flattenSigs) { cpRefs.remove(null); for (ListIterator work = new ArrayList(cpRefs).listIterator(cpRefs.size()); diff --git a/jdk/src/share/classes/com/sun/java/util/jar/pack/Driver.java b/jdk/src/share/classes/com/sun/java/util/jar/pack/Driver.java index 50d145db99f..2630febb7c3 100644 --- a/jdk/src/share/classes/com/sun/java/util/jar/pack/Driver.java +++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/Driver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,6 @@ package com.sun.java.util.jar.pack; -import java.lang.Error; import java.io.*; import java.text.MessageFormat; import java.util.*; @@ -35,10 +34,11 @@ import java.util.zip.*; /** Command line interface for Pack200. */ class Driver { - private static final ResourceBundle RESOURCE= ResourceBundle.getBundle("com.sun.java.util.jar.pack.DriverResource"); + private static final ResourceBundle RESOURCE = + ResourceBundle.getBundle("com.sun.java.util.jar.pack.DriverResource"); public static void main(String[] ava) throws IOException { - ArrayList av = new ArrayList(Arrays.asList(ava)); + ArrayList av = new ArrayList<>(Arrays.asList(ava)); boolean doPack = true; boolean doUnpack = false; @@ -61,7 +61,7 @@ class Driver { } // Collect engine properties here: - HashMap engProps = new HashMap(); + HashMap engProps = new HashMap<>(); engProps.put(verboseProp, System.getProperty(verboseProp)); String optionMap; @@ -75,7 +75,7 @@ class Driver { } // Collect argument properties here: - HashMap avProps = new HashMap(); + HashMap avProps = new HashMap<>(); try { for (;;) { String state = parseCommandOptions(av, optionMap, avProps); @@ -133,8 +133,9 @@ class Driver { if (engProps.get(verboseProp) != null) fileProps.list(System.out); propIn.close(); - for (Map.Entry me : fileProps.entrySet()) - engProps.put((String)me.getKey(), (String)me.getValue()); + for (Map.Entry me : fileProps.entrySet()) { + engProps.put((String) me.getKey(), (String) me.getValue()); + } } else if (state == "--version") { System.out.println(MessageFormat.format(RESOURCE.getString(DriverResource.VERSION), Driver.class.getName(), "1.31, 07/05/05")); return; @@ -493,7 +494,7 @@ class Driver { String resultString = null; // Convert options string into optLines dictionary. - TreeMap optmap = new TreeMap(); + TreeMap optmap = new TreeMap<>(); loadOptmap: for (String optline : options.split("\n")) { String[] words = optline.split("\\p{Space}+"); @@ -687,7 +688,9 @@ class Driver { // Report number of arguments consumed. args.subList(0, argp.nextIndex()).clear(); // Report any unconsumed partial argument. - while (pbp.hasPrevious()) args.add(0, pbp.previous()); + while (pbp.hasPrevious()) { + args.add(0, pbp.previous()); + } //System.out.println(args+" // "+properties+" -> "+resultString); return resultString; } diff --git a/jdk/src/share/classes/com/sun/java/util/jar/pack/NativeUnpack.java b/jdk/src/share/classes/com/sun/java/util/jar/pack/NativeUnpack.java index f8c55dc6656..8b54ce3a7e1 100644 --- a/jdk/src/share/classes/com/sun/java/util/jar/pack/NativeUnpack.java +++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/NativeUnpack.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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,13 +28,8 @@ package com.sun.java.util.jar.pack; import java.nio.*; import java.io.*; -import java.nio.channels.*; -import java.util.Date; import java.util.jar.*; import java.util.zip.*; -import java.util.*; -//import com.sun.java.util.jar.pack.Pack200; - class NativeUnpack { // Pointer to the native unpacker obj @@ -91,13 +86,13 @@ class NativeUnpack { NativeUnpack(UnpackerImpl p200) { super(); _p200 = p200; - _props = p200._props; + _props = p200.props; p200._nunp = this; } // for JNI callbacks static private Object currentInstance() { - UnpackerImpl p200 = (UnpackerImpl) Utils.currentInstance.get(); + UnpackerImpl p200 = (UnpackerImpl) Utils.getTLGlobals(); return (p200 == null)? null: p200._nunp; } diff --git a/jdk/src/share/classes/com/sun/java/util/jar/pack/Package.java b/jdk/src/share/classes/com/sun/java/util/jar/pack/Package.java index 1ea04690ed8..29d217687c4 100644 --- a/jdk/src/share/classes/com/sun/java/util/jar/pack/Package.java +++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/Package.java @@ -25,9 +25,9 @@ package com.sun.java.util.jar.pack; +import com.sun.java.util.jar.pack.Attribute.Layout; import java.lang.reflect.Modifier; import java.util.*; -import java.util.zip.*; import java.util.jar.*; import java.io.*; import com.sun.java.util.jar.pack.ConstantPool.*; @@ -77,10 +77,11 @@ class Package implements Constants { cp = new ConstantPool.IndexGroup(); classes.clear(); files.clear(); + BandStructure.nextSeqForDebug = 0; } int getPackageVersion() { - return (package_majver << 16) + (int)package_minver; + return (package_majver << 16) + package_minver; } // Special empty versions of Code and InnerClasses, used for markers. @@ -89,7 +90,7 @@ class Package implements Constants { public static final Attribute.Layout attrSourceFileSpecial; public static final Map attrDefs; static { - HashMap ad = new HashMap(2); + HashMap ad = new HashMap<>(3); attrCodeEmpty = Attribute.define(ad, ATTR_CONTEXT_METHOD, "Code", "").layout(); attrInnerClassesEmpty = Attribute.define(ad, ATTR_CONTEXT_CLASS, @@ -159,9 +160,9 @@ class Package implements Constants { } } - ArrayList classes = new ArrayList(); + ArrayList classes = new ArrayList<>(); - public List getClasses() { + public List getClasses() { return classes; } @@ -186,11 +187,11 @@ class Package implements Constants { ClassEntry[] interfaces; // Class parts - ArrayList fields; - ArrayList methods; + ArrayList fields; + ArrayList methods; //ArrayList attributes; // in Attribute.Holder.this.attributes // Note that InnerClasses may be collected at the package level. - ArrayList innerClasses; + ArrayList innerClasses; Class(int flags, ClassEntry thisClass, ClassEntry superClass, ClassEntry[] interfaces) { this.magic = JAVA_MAGIC; @@ -270,7 +271,7 @@ class Package implements Constants { if (a != olda) { if (verbose > 2) Utils.log.fine("recoding obvious SourceFile="+obvious); - List newAttrs = new ArrayList(getAttributes()); + List newAttrs = new ArrayList<>(getAttributes()); int where = newAttrs.indexOf(olda); newAttrs.set(where, a); setAttributes(newAttrs); @@ -295,12 +296,12 @@ class Package implements Constants { boolean hasInnerClasses() { return innerClasses != null; } - List getInnerClasses() { + List getInnerClasses() { return innerClasses; } - public void setInnerClasses(Collection ics) { - innerClasses = (ics == null) ? null : new ArrayList(ics); + public void setInnerClasses(Collection ics) { + innerClasses = (ics == null) ? null : new ArrayList(ics); // Edit the attribute list, if necessary. Attribute a = getAttribute(attrInnerClassesEmpty); if (innerClasses != null && a == null) @@ -318,19 +319,18 @@ class Package implements Constants { * The order of the resulting list is consistent * with that of Package.this.allInnerClasses. */ - public List computeGloballyImpliedICs() { - HashSet cpRefs = new HashSet(); + public List computeGloballyImpliedICs() { + HashSet cpRefs = new HashSet<>(); { // This block temporarily displaces this.innerClasses. - ArrayList innerClassesSaved = innerClasses; + ArrayList innerClassesSaved = innerClasses; innerClasses = null; // ignore for the moment visitRefs(VRM_CLASSIC, cpRefs); innerClasses = innerClassesSaved; } ConstantPool.completeReferencesIn(cpRefs, true); - HashSet icRefs = new HashSet(); - for (Iterator i = cpRefs.iterator(); i.hasNext(); ) { - Entry e = (Entry) i.next(); + HashSet icRefs = new HashSet<>(); + for (Entry e : cpRefs) { // Restrict cpRefs to InnerClasses entries only. if (!(e instanceof ClassEntry)) continue; // For every IC reference, add its outers also. @@ -345,9 +345,8 @@ class Package implements Constants { // This loop is structured this way so as to accumulate // entries into impliedICs in an order which reflects // the order of allInnerClasses. - ArrayList impliedICs = new ArrayList(); - for (Iterator i = allInnerClasses.iterator(); i.hasNext(); ) { - InnerClass ic = (InnerClass) i.next(); + ArrayList impliedICs = new ArrayList<>(); + for (InnerClass ic : allInnerClasses) { // This one is locally relevant if it describes // a member of the current class, or if the current // class uses it somehow. In the particular case @@ -366,10 +365,11 @@ class Package implements Constants { // Helper for both minimizing and expanding. // Computes a symmetric difference. - private List computeICdiff() { - List impliedICs = computeGloballyImpliedICs(); - List actualICs = getInnerClasses(); - if (actualICs == null) actualICs = Collections.EMPTY_LIST; + private List computeICdiff() { + List impliedICs = computeGloballyImpliedICs(); + List actualICs = getInnerClasses(); + if (actualICs == null) + actualICs = Collections.EMPTY_LIST; // Symmetric difference is calculated from I, A like this: // diff = (I+A) - (I*A) @@ -388,8 +388,8 @@ class Package implements Constants { // Diff is A since I is empty. } // (I*A) is non-trivial - HashSet center = new HashSet(actualICs); - center.retainAll(new HashSet(impliedICs)); + HashSet center = new HashSet<>(actualICs); + center.retainAll(new HashSet<>(impliedICs)); impliedICs.addAll(actualICs); impliedICs.removeAll(center); // Diff is now I^A = (I+A)-(I*A). @@ -407,9 +407,9 @@ class Package implements Constants { * to use the globally implied ICs changed. */ void minimizeLocalICs() { - List diff = computeICdiff(); - List actualICs = innerClasses; - List localICs; // will be the diff, modulo edge cases + List diff = computeICdiff(); + List actualICs = innerClasses; + List localICs; // will be the diff, modulo edge cases if (diff.isEmpty()) { // No diff, so transmit no attribute. localICs = null; @@ -439,12 +439,12 @@ class Package implements Constants { * Otherwise, return positive if any IC tuples were added. */ int expandLocalICs() { - List localICs = innerClasses; - List actualICs; + List localICs = innerClasses; + List actualICs; int changed; if (localICs == null) { // Diff was empty. (Common case.) - List impliedICs = computeGloballyImpliedICs(); + List impliedICs = computeGloballyImpliedICs(); if (impliedICs.isEmpty()) { actualICs = null; changed = 0; @@ -490,7 +490,7 @@ class Package implements Constants { protected Entry[] getCPMap() { return cpMap; } - protected void visitRefs(int mode, Collection refs) { + protected void visitRefs(int mode, Collection refs) { if (verbose > 2) Utils.log.fine("visitRefs "+this); // Careful: The descriptor is used by the package, // but the classfile breaks it into component refs. @@ -518,7 +518,7 @@ class Package implements Constants { super(flags, descriptor); assert(!descriptor.isMethod()); if (fields == null) - fields = new ArrayList(); + fields = new ArrayList<>(); boolean added = fields.add(this); assert(added); order = fields.size(); @@ -543,7 +543,7 @@ class Package implements Constants { super(flags, descriptor); assert(descriptor.isMethod()); if (methods == null) - methods = new ArrayList(); + methods = new ArrayList<>(); boolean added = methods.add(this); assert(added); } @@ -573,7 +573,7 @@ class Package implements Constants { code.strip(attrName); super.strip(attrName); } - protected void visitRefs(int mode, Collection refs) { + protected void visitRefs(int mode, Collection refs) { super.visitRefs(mode, refs); if (code != null) { if (mode == VRM_CLASSIC) { @@ -614,7 +614,7 @@ class Package implements Constants { super.strip(attrName); } - protected void visitRefs(int mode, Collection refs) { + protected void visitRefs(int mode, Collection refs) { if (verbose > 2) Utils.log.fine("visitRefs "+this); refs.add(thisClass); refs.add(superClass); @@ -641,7 +641,7 @@ class Package implements Constants { super.visitRefs(mode, refs); } - protected void visitInnerClassRefs(int mode, Collection refs) { + protected void visitInnerClassRefs(int mode, Collection refs) { Package.visitInnerClassRefs(innerClasses, mode, refs); } @@ -713,16 +713,15 @@ class Package implements Constants { } // What non-class files are in this unit? - ArrayList files = new ArrayList(); + ArrayList files = new ArrayList<>(); - public List getFiles() { + public List getFiles() { return files; } - public List getClassStubs() { - ArrayList classStubs = new ArrayList(classes.size()); - for (Iterator i = classes.iterator(); i.hasNext(); ) { - Class cls = (Class) i.next(); + public List getClassStubs() { + ArrayList classStubs = new ArrayList<>(classes.size()); + for (Class cls : classes) { assert(cls.file.isClassStub()); classStubs.add(cls.file); } @@ -840,7 +839,7 @@ class Package implements Constants { public InputStream getInputStream() { InputStream in = new ByteArrayInputStream(append.toByteArray()); if (prepend.size() == 0) return in; - ArrayList isa = new ArrayList(prepend.size()+1); + ArrayList isa = new ArrayList<>(prepend.size()+1); for (Iterator i = prepend.iterator(); i.hasNext(); ) { byte[] bytes = (byte[]) i.next(); isa.add(new ByteArrayInputStream(bytes)); @@ -849,7 +848,7 @@ class Package implements Constants { return new SequenceInputStream(Collections.enumeration(isa)); } - protected void visitRefs(int mode, Collection refs) { + protected void visitRefs(int mode, Collection refs) { assert(name != null); refs.add(name); } @@ -877,8 +876,8 @@ class Package implements Constants { } // Is there a globally declared table of inner classes? - ArrayList allInnerClasses = new ArrayList(); - HashMap allInnerClassesByThis; + ArrayList allInnerClasses = new ArrayList<>(); + HashMap allInnerClassesByThis; public List getAllInnerClasses() { @@ -886,15 +885,14 @@ class Package implements Constants { } public - void setAllInnerClasses(Collection ics) { + void setAllInnerClasses(Collection ics) { assert(ics != allInnerClasses); allInnerClasses.clear(); allInnerClasses.addAll(ics); // Make an index: - allInnerClassesByThis = new HashMap(allInnerClasses.size()); - for (Iterator i = allInnerClasses.iterator(); i.hasNext(); ) { - InnerClass ic = (InnerClass) i.next(); + allInnerClassesByThis = new HashMap<>(allInnerClasses.size()); + for (InnerClass ic : allInnerClasses) { Object pic = allInnerClassesByThis.put(ic.thisClass, ic); assert(pic == null); // caller must ensure key uniqueness! } @@ -904,7 +902,7 @@ class Package implements Constants { public InnerClass getGlobalInnerClass(Entry thisClass) { assert(thisClass instanceof ClassEntry); - return (InnerClass) allInnerClassesByThis.get(thisClass); + return allInnerClassesByThis.get(thisClass); } static @@ -963,7 +961,7 @@ class Package implements Constants { return this.thisClass.compareTo(that.thisClass); } - protected void visitRefs(int mode, Collection refs) { + protected void visitRefs(int mode, Collection refs) { refs.add(thisClass); if (mode == VRM_CLASSIC || !predictable) { // If the name can be demangled, the package omits @@ -980,7 +978,7 @@ class Package implements Constants { // Helper for building InnerClasses attributes. static private - void visitInnerClassRefs(Collection innerClasses, int mode, Collection refs) { + void visitInnerClassRefs(Collection innerClasses, int mode, Collection refs) { if (innerClasses == null) { return; // no attribute; nothing to do } @@ -1165,9 +1163,8 @@ class Package implements Constants { } } - protected void visitRefs(int mode, Collection refs) { - for (Iterator i = classes.iterator(); i.hasNext(); ) { - Class c = (Class)i.next(); + protected void visitRefs(int mode, Collection refs) { + for ( Class c : classes) { c.visitRefs(mode, refs); } if (mode != VRM_CLASSIC) { @@ -1259,7 +1256,7 @@ class Package implements Constants { } // Use this before writing the package file. - void buildGlobalConstantPool(Set requiredEntries) { + void buildGlobalConstantPool(Set requiredEntries) { if (verbose > 1) Utils.log.fine("Checking for unused CP entries"); requiredEntries.add(getRefString("")); // uconditionally present @@ -1291,9 +1288,8 @@ class Package implements Constants { // Use this before writing the class files. void ensureAllClassFiles() { - HashSet fileSet = new HashSet(files); - for (Iterator i = classes.iterator(); i.hasNext(); ) { - Class cls = (Class) i.next(); + HashSet fileSet = new HashSet<>(files); + for (Class cls : classes) { // Add to the end of ths list: if (!fileSet.contains(cls.file)) files.add(cls.file); diff --git a/jdk/src/share/classes/com/sun/java/util/jar/pack/PackerImpl.java b/jdk/src/share/classes/com/sun/java/util/jar/pack/PackerImpl.java index 9c285794303..449bbbeb5b1 100644 --- a/jdk/src/share/classes/com/sun/java/util/jar/pack/PackerImpl.java +++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/PackerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,12 +25,11 @@ package com.sun.java.util.jar.pack; +import com.sun.java.util.jar.pack.Attribute.Layout; import java.util.*; import java.util.jar.*; -import java.util.zip.*; import java.io.*; import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeEvent; /* @@ -41,31 +40,22 @@ import java.beans.PropertyChangeEvent; */ -public class PackerImpl implements Pack200.Packer { +public class PackerImpl extends TLGlobals implements Pack200.Packer { /** * Constructs a Packer object and sets the initial state of * the packer engines. */ - public PackerImpl() { - _props = new PropMap(); - //_props.getProperty() consults defaultProps invisibly. - //_props.putAll(defaultProps); - } - - - // Private stuff. - final PropMap _props; + public PackerImpl() {} /** * Get the set of options for the pack and unpack engines. * @return A sorted association of option key strings to option values. */ - public SortedMap properties() { - return _props; + public SortedMap properties() { + return props; } - //Driver routines /** @@ -78,21 +68,22 @@ public class PackerImpl implements Pack200.Packer { */ public void pack(JarFile in, OutputStream out) throws IOException { assert(Utils.currentInstance.get() == null); - TimeZone tz = (_props.getBoolean(Utils.PACK_DEFAULT_TIMEZONE)) ? null : - TimeZone.getDefault(); + TimeZone tz = (props.getBoolean(Utils.PACK_DEFAULT_TIMEZONE)) + ? null + : TimeZone.getDefault(); try { Utils.currentInstance.set(this); if (tz != null) TimeZone.setDefault(TimeZone.getTimeZone("UTC")); - if ("0".equals(_props.getProperty(Pack200.Packer.EFFORT))) { + if ("0".equals(props.getProperty(Pack200.Packer.EFFORT))) { Utils.copyJarFile(in, out); } else { (new DoPack()).run(in, out); - in.close(); } } finally { Utils.currentInstance.set(null); if (tz != null) TimeZone.setDefault(tz); + in.close(); } } @@ -112,21 +103,20 @@ public class PackerImpl implements Pack200.Packer { */ public void pack(JarInputStream in, OutputStream out) throws IOException { assert(Utils.currentInstance.get() == null); - TimeZone tz = (_props.getBoolean(Utils.PACK_DEFAULT_TIMEZONE)) ? null : + TimeZone tz = (props.getBoolean(Utils.PACK_DEFAULT_TIMEZONE)) ? null : TimeZone.getDefault(); try { Utils.currentInstance.set(this); if (tz != null) TimeZone.setDefault(TimeZone.getTimeZone("UTC")); - if ("0".equals(_props.getProperty(Pack200.Packer.EFFORT))) { + if ("0".equals(props.getProperty(Pack200.Packer.EFFORT))) { Utils.copyJarFile(in, out); } else { (new DoPack()).run(in, out); - in.close(); } } finally { Utils.currentInstance.set(null); if (tz != null) TimeZone.setDefault(tz); - + in.close(); } } /** @@ -134,7 +124,7 @@ public class PackerImpl implements Pack200.Packer { * @param listener An object to be invoked when a property is changed. */ public void addPropertyChangeListener(PropertyChangeListener listener) { - _props.addListener(listener); + props.addListener(listener); } /** @@ -142,7 +132,7 @@ public class PackerImpl implements Pack200.Packer { * @param listener The PropertyChange listener to be removed. */ public void removePropertyChangeListener(PropertyChangeListener listener) { - _props.removeListener(listener); + props.removeListener(listener); } @@ -151,11 +141,11 @@ public class PackerImpl implements Pack200.Packer { // The packer worker. private class DoPack { - final int verbose = _props.getInteger(Utils.DEBUG_VERBOSE); + final int verbose = props.getInteger(Utils.DEBUG_VERBOSE); { - _props.setInteger(Pack200.Packer.PROGRESS, 0); - if (verbose > 0) Utils.log.info(_props.toString()); + props.setInteger(Pack200.Packer.PROGRESS, 0); + if (verbose > 0) Utils.log.info(props.toString()); } // Here's where the bits are collected before getting packed: @@ -163,7 +153,7 @@ public class PackerImpl implements Pack200.Packer { final String unknownAttrCommand; { - String uaMode = _props.getProperty(Pack200.Packer.UNKNOWN_ATTRIBUTE, Pack200.Packer.PASS); + String uaMode = props.getProperty(Pack200.Packer.UNKNOWN_ATTRIBUTE, Pack200.Packer.PASS); if (!(Pack200.Packer.STRIP.equals(uaMode) || Pack200.Packer.PASS.equals(uaMode) || Pack200.Packer.ERROR.equals(uaMode))) { @@ -191,13 +181,12 @@ public class PackerImpl implements Pack200.Packer { }; for (int i = 0; i < ctypes.length; i++) { String pfx = keys[i]; - Map map = _props.prefixMap(pfx); - for (Iterator j = map.keySet().iterator(); j.hasNext(); ) { - String key = (String) j.next(); + Map map = props.prefixMap(pfx); + for (String key : map.keySet()) { assert(key.startsWith(pfx)); String name = key.substring(pfx.length()); - String layout = _props.getProperty(key); - Object lkey = Attribute.keyForLookup(ctypes[i], name); + String layout = props.getProperty(key); + Layout lkey = Attribute.keyForLookup(ctypes[i], name); if (Pack200.Packer.STRIP.equals(layout) || Pack200.Packer.PASS.equals(layout) || Pack200.Packer.ERROR.equals(layout)) { @@ -222,25 +211,25 @@ public class PackerImpl implements Pack200.Packer { } final boolean keepFileOrder - = _props.getBoolean(Pack200.Packer.KEEP_FILE_ORDER); + = props.getBoolean(Pack200.Packer.KEEP_FILE_ORDER); final boolean keepClassOrder - = _props.getBoolean(Utils.PACK_KEEP_CLASS_ORDER); + = props.getBoolean(Utils.PACK_KEEP_CLASS_ORDER); final boolean keepModtime - = Pack200.Packer.KEEP.equals(_props.getProperty(Pack200.Packer.MODIFICATION_TIME)); + = Pack200.Packer.KEEP.equals(props.getProperty(Pack200.Packer.MODIFICATION_TIME)); final boolean latestModtime - = Pack200.Packer.LATEST.equals(_props.getProperty(Pack200.Packer.MODIFICATION_TIME)); + = Pack200.Packer.LATEST.equals(props.getProperty(Pack200.Packer.MODIFICATION_TIME)); final boolean keepDeflateHint - = Pack200.Packer.KEEP.equals(_props.getProperty(Pack200.Packer.DEFLATE_HINT)); + = Pack200.Packer.KEEP.equals(props.getProperty(Pack200.Packer.DEFLATE_HINT)); { if (!keepModtime && !latestModtime) { - int modtime = _props.getTime(Pack200.Packer.MODIFICATION_TIME); + int modtime = props.getTime(Pack200.Packer.MODIFICATION_TIME); if (modtime != Constants.NO_MODTIME) { pkg.default_modtime = modtime; } } if (!keepDeflateHint) { - boolean deflate_hint = _props.getBoolean(Pack200.Packer.DEFLATE_HINT); + boolean deflate_hint = props.getBoolean(Pack200.Packer.DEFLATE_HINT); if (deflate_hint) { pkg.default_options |= Constants.AO_DEFLATE_HINT; } @@ -254,10 +243,10 @@ public class PackerImpl implements Pack200.Packer { final long segmentLimit; { long limit; - if (_props.getProperty(Pack200.Packer.SEGMENT_LIMIT, "").equals("")) + if (props.getProperty(Pack200.Packer.SEGMENT_LIMIT, "").equals("")) limit = -1; else - limit = _props.getLong(Pack200.Packer.SEGMENT_LIMIT); + limit = props.getLong(Pack200.Packer.SEGMENT_LIMIT); limit = Math.min(Integer.MAX_VALUE, limit); limit = Math.max(-1, limit); if (limit == -1) @@ -265,10 +254,10 @@ public class PackerImpl implements Pack200.Packer { segmentLimit = limit; } - final List passFiles; // parsed pack.pass.file options + final List passFiles; // parsed pack.pass.file options { // Which class files will be passed through? - passFiles = _props.getProperties(Pack200.Packer.PASS_FILE_PFX); + passFiles = props.getProperties(Pack200.Packer.PASS_FILE_PFX); for (ListIterator i = passFiles.listIterator(); i.hasNext(); ) { String file = (String) i.next(); if (file == null) { i.remove(); continue; } @@ -283,28 +272,28 @@ public class PackerImpl implements Pack200.Packer { { // Fill in permitted range of major/minor version numbers. int ver; - if ((ver = _props.getInteger(Utils.COM_PREFIX+"min.class.majver")) != 0) + if ((ver = props.getInteger(Utils.COM_PREFIX+"min.class.majver")) != 0) pkg.min_class_majver = (short) ver; - if ((ver = _props.getInteger(Utils.COM_PREFIX+"min.class.minver")) != 0) + if ((ver = props.getInteger(Utils.COM_PREFIX+"min.class.minver")) != 0) pkg.min_class_minver = (short) ver; - if ((ver = _props.getInteger(Utils.COM_PREFIX+"max.class.majver")) != 0) + if ((ver = props.getInteger(Utils.COM_PREFIX+"max.class.majver")) != 0) pkg.max_class_majver = (short) ver; - if ((ver = _props.getInteger(Utils.COM_PREFIX+"max.class.minver")) != 0) + if ((ver = props.getInteger(Utils.COM_PREFIX+"max.class.minver")) != 0) pkg.max_class_minver = (short) ver; - if ((ver = _props.getInteger(Utils.COM_PREFIX+"package.minver")) != 0) + if ((ver = props.getInteger(Utils.COM_PREFIX+"package.minver")) != 0) pkg.package_minver = (short) ver; - if ((ver = _props.getInteger(Utils.COM_PREFIX+"package.majver")) != 0) + if ((ver = props.getInteger(Utils.COM_PREFIX+"package.majver")) != 0) pkg.package_majver = (short) ver; } { // Hook for testing: Forces use of special archive modes. - int opt = _props.getInteger(Utils.COM_PREFIX+"archive.options"); + int opt = props.getInteger(Utils.COM_PREFIX+"archive.options"); if (opt != 0) pkg.default_options |= opt; } - // (Done collecting options from _props.) + // (Done collecting options from props.) boolean isClassFile(String name) { if (!name.endsWith(".class")) return false; @@ -423,16 +412,18 @@ public class PackerImpl implements Pack200.Packer { Package.File file = null; // (5078608) : discount the resource files in META-INF // from segment computation. - long inflen = (isMetaInfFile(name)) ? 0L : - inFile.getInputLength(); + long inflen = (isMetaInfFile(name)) + ? 0L + : inFile.getInputLength(); if ((segmentSize += inflen) > segmentLimit) { segmentSize -= inflen; int nextCount = -1; // don't know; it's a stream flushPartial(out, nextCount); } - if (verbose > 1) + if (verbose > 1) { Utils.log.fine("Reading " + name); + } assert(je.isDirectory() == name.endsWith("/")); @@ -450,18 +441,18 @@ public class PackerImpl implements Pack200.Packer { } void run(JarFile in, OutputStream out) throws IOException { - List inFiles = scanJar(in); + List inFiles = scanJar(in); if (verbose > 0) Utils.log.info("Reading " + inFiles.size() + " files..."); int numDone = 0; - for (Iterator i = inFiles.iterator(); i.hasNext(); ) { - InFile inFile = (InFile) i.next(); + for (InFile inFile : inFiles) { String name = inFile.name; // (5078608) : discount the resource files completely from segmenting - long inflen = (isMetaInfFile(name)) ? 0L : - inFile.getInputLength() ; + long inflen = (isMetaInfFile(name)) + ? 0L + : inFile.getInputLength() ; if ((segmentSize += inflen) > segmentLimit) { segmentSize -= inflen; // Estimate number of remaining segments: @@ -530,11 +521,11 @@ public class PackerImpl implements Pack200.Packer { } void flushPartial(OutputStream out, int nextCount) throws IOException { - if (pkg.files.size() == 0 && pkg.classes.size() == 0) { + if (pkg.files.isEmpty() && pkg.classes.isEmpty()) { return; // do not flush an empty segment } flushPackage(out, Math.max(1, nextCount)); - _props.setInteger(Pack200.Packer.PROGRESS, 25); + props.setInteger(Pack200.Packer.PROGRESS, 25); // In case there will be another segment: makeNextPackage(); segmentCount += 1; @@ -543,10 +534,10 @@ public class PackerImpl implements Pack200.Packer { } void flushAll(OutputStream out) throws IOException { - _props.setInteger(Pack200.Packer.PROGRESS, 50); + props.setInteger(Pack200.Packer.PROGRESS, 50); flushPackage(out, 0); out.flush(); - _props.setInteger(Pack200.Packer.PROGRESS, 100); + props.setInteger(Pack200.Packer.PROGRESS, 100); segmentCount += 1; segmentTotalSize += segmentSize; segmentSize = 0; @@ -582,11 +573,11 @@ public class PackerImpl implements Pack200.Packer { pkg.trimStubs(); // Do some stripping, maybe. - if (_props.getBoolean(Utils.COM_PREFIX+"strip.debug")) pkg.stripAttributeKind("Debug"); - if (_props.getBoolean(Utils.COM_PREFIX+"strip.compile")) pkg.stripAttributeKind("Compile"); - if (_props.getBoolean(Utils.COM_PREFIX+"strip.constants")) pkg.stripAttributeKind("Constant"); - if (_props.getBoolean(Utils.COM_PREFIX+"strip.exceptions")) pkg.stripAttributeKind("Exceptions"); - if (_props.getBoolean(Utils.COM_PREFIX+"strip.innerclasses")) pkg.stripAttributeKind("InnerClasses"); + if (props.getBoolean(Utils.COM_PREFIX+"strip.debug")) pkg.stripAttributeKind("Debug"); + if (props.getBoolean(Utils.COM_PREFIX+"strip.compile")) pkg.stripAttributeKind("Compile"); + if (props.getBoolean(Utils.COM_PREFIX+"strip.constants")) pkg.stripAttributeKind("Constant"); + if (props.getBoolean(Utils.COM_PREFIX+"strip.exceptions")) pkg.stripAttributeKind("Exceptions"); + if (props.getBoolean(Utils.COM_PREFIX+"strip.innerclasses")) pkg.stripAttributeKind("InnerClasses"); // Must choose an archive version; PackageWriter does not. if (pkg.package_majver <= 0) pkg.choosePackageVersion(); @@ -606,11 +597,10 @@ public class PackerImpl implements Pack200.Packer { } } - List scanJar(JarFile jf) throws IOException { + List scanJar(JarFile jf) throws IOException { // Collect jar entries, preserving order. - List inFiles = new ArrayList(); - for (Enumeration e = jf.entries(); e.hasMoreElements(); ) { - JarEntry je = (JarEntry) e.nextElement(); + List inFiles = new ArrayList<>(); + for (JarEntry je : Collections.list(jf.entries())) { InFile inFile = new InFile(jf, je); assert(je.isDirectory() == inFile.name.endsWith("/")); inFiles.add(inFile); diff --git a/jdk/src/share/classes/com/sun/java/util/jar/pack/TLGlobals.java b/jdk/src/share/classes/com/sun/java/util/jar/pack/TLGlobals.java new file mode 100644 index 00000000000..0e40bde8b63 --- /dev/null +++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/TLGlobals.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2010, 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.java.util.jar.pack; + +import com.sun.java.util.jar.pack.ConstantPool.ClassEntry; +import com.sun.java.util.jar.pack.ConstantPool.DescriptorEntry; +import com.sun.java.util.jar.pack.ConstantPool.LiteralEntry; +import com.sun.java.util.jar.pack.ConstantPool.MemberEntry; +import com.sun.java.util.jar.pack.ConstantPool.SignatureEntry; +import com.sun.java.util.jar.pack.ConstantPool.Utf8Entry; +import java.util.HashMap; +import java.util.Map; +import java.util.SortedMap; + +/* + * @author ksrini + */ + +/* + * This class provides a container to hold the global variables, for packer + * and unpacker instances. This is typically stashed away in a ThreadLocal, + * and the storage is destroyed upon completion. Therefore any local + * references to these members must be eliminated appropriately to prevent a + * memory leak. + */ +class TLGlobals { + // Global environment + final PropMap props; + + // Needed by ConstantPool.java + private final Map utf8Entries; + private final Map classEntries; + private final Map literalEntries; + private final Map signatureEntries; + private final Map descriptorEntries; + private final Map memberEntries; + + TLGlobals() { + utf8Entries = new HashMap<>(); + classEntries = new HashMap<>(); + literalEntries = new HashMap<>(); + signatureEntries = new HashMap<>(); + descriptorEntries = new HashMap<>(); + memberEntries = new HashMap<>(); + props = new PropMap(); + } + + SortedMap getPropMap() { + return props; + } + + Map getUtf8Entries() { + return utf8Entries; + } + + Map getClassEntries() { + return classEntries; + } + + Map getLiteralEntries() { + return literalEntries; + } + + Map getDescriptorEntries() { + return descriptorEntries; + } + + Map getSignatureEntries() { + return signatureEntries; + } + + Map getMemberEntries() { + return memberEntries; + } +} diff --git a/jdk/src/share/classes/com/sun/java/util/jar/pack/UnpackerImpl.java b/jdk/src/share/classes/com/sun/java/util/jar/pack/UnpackerImpl.java index d131a1255f4..11c8abf07f1 100644 --- a/jdk/src/share/classes/com/sun/java/util/jar/pack/UnpackerImpl.java +++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/UnpackerImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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,7 +30,6 @@ import java.util.jar.*; import java.util.zip.*; import java.io.*; import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeEvent; /* * Implementation of the Pack provider. @@ -40,7 +39,7 @@ import java.beans.PropertyChangeEvent; */ -public class UnpackerImpl implements Pack200.Unpacker { +public class UnpackerImpl extends TLGlobals implements Pack200.Unpacker { /** @@ -48,7 +47,7 @@ public class UnpackerImpl implements Pack200.Unpacker { * @param listener An object to be invoked when a property is changed. */ public void addPropertyChangeListener(PropertyChangeListener listener) { - _props.addListener(listener); + props.addListener(listener); } @@ -57,25 +56,19 @@ public class UnpackerImpl implements Pack200.Unpacker { * @param listener The PropertyChange listener to be removed. */ public void removePropertyChangeListener(PropertyChangeListener listener) { - _props.removeListener(listener); + props.removeListener(listener); } - public UnpackerImpl() { - _props = new PropMap(); - //_props.getProperty() consults defaultProps invisibly. - //_props.putAll(defaultProps); - } + public UnpackerImpl() {} - // Private stuff. - final PropMap _props; /** * Get the set of options for the pack and unpack engines. * @return A sorted association of option key strings to option values. */ - public SortedMap properties() { - return _props; + public SortedMap properties() { + return props; } // Back-pointer to NativeUnpacker, when active. @@ -101,19 +94,20 @@ public class UnpackerImpl implements Pack200.Unpacker { */ public void unpack(InputStream in0, JarOutputStream out) throws IOException { assert(Utils.currentInstance.get() == null); - TimeZone tz = (_props.getBoolean(Utils.PACK_DEFAULT_TIMEZONE)) ? null : - TimeZone.getDefault(); + TimeZone tz = (props.getBoolean(Utils.PACK_DEFAULT_TIMEZONE)) + ? null + : TimeZone.getDefault(); try { Utils.currentInstance.set(this); if (tz != null) TimeZone.setDefault(TimeZone.getTimeZone("UTC")); - final int verbose = _props.getInteger(Utils.DEBUG_VERBOSE); + final int verbose = props.getInteger(Utils.DEBUG_VERBOSE); BufferedInputStream in = new BufferedInputStream(in0); if (Utils.isJarMagic(Utils.readMagic(in))) { if (verbose > 0) Utils.log.info("Copying unpacked JAR file..."); Utils.copyJarFile(new JarInputStream(in), out); - } else if (_props.getBoolean(Utils.DEBUG_DISABLE_NATIVE)) { + } else if (props.getBoolean(Utils.DEBUG_DISABLE_NATIVE)) { (new DoUnpack()).run(in, out); in.close(); Utils.markJarFile(out); @@ -142,36 +136,38 @@ public class UnpackerImpl implements Pack200.Unpacker { // %%% Reconsider if native unpacker learns to memory-map the file. FileInputStream instr = new FileInputStream(in); unpack(instr, out); - if (_props.getBoolean(Utils.UNPACK_REMOVE_PACKFILE)) { + if (props.getBoolean(Utils.UNPACK_REMOVE_PACKFILE)) { in.delete(); } } private class DoUnpack { - final int verbose = _props.getInteger(Utils.DEBUG_VERBOSE); + final int verbose = props.getInteger(Utils.DEBUG_VERBOSE); { - _props.setInteger(Pack200.Unpacker.PROGRESS, 0); + props.setInteger(Pack200.Unpacker.PROGRESS, 0); } // Here's where the bits are read from disk: final Package pkg = new Package(); final boolean keepModtime - = Pack200.Packer.KEEP.equals(_props.getProperty(Utils.UNPACK_MODIFICATION_TIME, Pack200.Packer.KEEP)); + = Pack200.Packer.KEEP.equals( + props.getProperty(Utils.UNPACK_MODIFICATION_TIME, Pack200.Packer.KEEP)); final boolean keepDeflateHint - = Pack200.Packer.KEEP.equals(_props.getProperty(Pack200.Unpacker.DEFLATE_HINT, Pack200.Packer.KEEP)); + = Pack200.Packer.KEEP.equals( + props.getProperty(Pack200.Unpacker.DEFLATE_HINT, Pack200.Packer.KEEP)); final int modtime; final boolean deflateHint; { if (!keepModtime) { - modtime = _props.getTime(Utils.UNPACK_MODIFICATION_TIME); + modtime = props.getTime(Utils.UNPACK_MODIFICATION_TIME); } else { modtime = pkg.default_modtime; } deflateHint = (keepDeflateHint) ? false : - _props.getBoolean(java.util.jar.Pack200.Unpacker.DEFLATE_HINT); + props.getBoolean(java.util.jar.Pack200.Unpacker.DEFLATE_HINT); } // Checksum apparatus. @@ -181,7 +177,7 @@ public class UnpackerImpl implements Pack200.Unpacker { public void run(BufferedInputStream in, JarOutputStream out) throws IOException { if (verbose > 0) { - _props.list(System.out); + props.list(System.out); } for (int seg = 1; ; seg++) { unpackSegment(in, out); @@ -194,25 +190,26 @@ public class UnpackerImpl implements Pack200.Unpacker { } private void unpackSegment(InputStream in, JarOutputStream out) throws IOException { - _props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"0"); + props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"0"); // Process the output directory or jar output. new PackageReader(pkg, in).read(); - if (_props.getBoolean("unpack.strip.debug")) pkg.stripAttributeKind("Debug"); - if (_props.getBoolean("unpack.strip.compile")) pkg.stripAttributeKind("Compile"); - _props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"50"); + if (props.getBoolean("unpack.strip.debug")) pkg.stripAttributeKind("Debug"); + if (props.getBoolean("unpack.strip.compile")) pkg.stripAttributeKind("Compile"); + props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"50"); pkg.ensureAllClassFiles(); // Now write out the files. - HashSet classesToWrite = new HashSet(pkg.getClasses()); + HashSet classesToWrite = new HashSet<>(pkg.getClasses()); for (Iterator i = pkg.getFiles().iterator(); i.hasNext(); ) { Package.File file = (Package.File) i.next(); String name = file.nameString; JarEntry je = new JarEntry(Utils.getJarEntryName(name)); boolean deflate; - deflate = (keepDeflateHint) ? (((file.options & Constants.FO_DEFLATE_HINT) != 0) || - ((pkg.default_options & Constants.AO_DEFLATE_HINT) != 0)) : - deflateHint; + deflate = (keepDeflateHint) + ? (((file.options & Constants.FO_DEFLATE_HINT) != 0) || + ((pkg.default_options & Constants.AO_DEFLATE_HINT) != 0)) + : deflateHint; boolean needCRC = !deflate; // STORE mode requires CRC @@ -250,7 +247,7 @@ public class UnpackerImpl implements Pack200.Unpacker { Utils.log.info("Writing "+Utils.zeString((ZipEntry)je)); } assert(classesToWrite.isEmpty()); - _props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"100"); + props.setProperty(java.util.jar.Pack200.Unpacker.PROGRESS,"100"); pkg.reset(); // reset for the next segment, if any } } diff --git a/jdk/src/share/classes/com/sun/java/util/jar/pack/Utils.java b/jdk/src/share/classes/com/sun/java/util/jar/pack/Utils.java index 8edbed10c46..2dc47c41464 100644 --- a/jdk/src/share/classes/com/sun/java/util/jar/pack/Utils.java +++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/Utils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,13 @@ package com.sun.java.util.jar.pack; +import com.sun.java.util.jar.pack.Attribute.Layout; +import com.sun.java.util.jar.pack.ConstantPool.ClassEntry; +import com.sun.java.util.jar.pack.ConstantPool.DescriptorEntry; +import com.sun.java.util.jar.pack.ConstantPool.LiteralEntry; +import com.sun.java.util.jar.pack.ConstantPool.MemberEntry; +import com.sun.java.util.jar.pack.ConstantPool.SignatureEntry; +import com.sun.java.util.jar.pack.ConstantPool.Utf8Entry; import java.util.*; import java.util.jar.*; import java.util.zip.*; @@ -113,17 +120,46 @@ class Utils { */ static final String PACK_ZIP_ARCHIVE_MARKER_COMMENT = "PACK200"; - // Keep a TLS point to the current Packer or Unpacker. - // This makes it simpler to supply environmental options + // Keep a TLS point to the global data and environment. + // This makes it simpler to supply environmental options // to the engine code, especially the native code. - static final ThreadLocal currentInstance = new ThreadLocal(); + static final ThreadLocal currentInstance = new ThreadLocal<>(); + + // convenience methods to access the TL globals + static TLGlobals getTLGlobals() { + return currentInstance.get(); + } + + static Map getUtf8Entries() { + return getTLGlobals().getUtf8Entries(); + } + + static Map getClassEntries() { + return getTLGlobals().getClassEntries(); + } + + static Map getLiteralEntries() { + return getTLGlobals().getLiteralEntries(); + } + + static Map getDescriptorEntries() { + return getTLGlobals().getDescriptorEntries(); + } + + static Map getSignatureEntries() { + return getTLGlobals().getSignatureEntries(); + } + + static Map getMemberEntries() { + return getTLGlobals().getMemberEntries(); + } static PropMap currentPropMap() { Object obj = currentInstance.get(); if (obj instanceof PackerImpl) - return ((PackerImpl)obj)._props; + return ((PackerImpl)obj).props; if (obj instanceof UnpackerImpl) - return ((UnpackerImpl)obj)._props; + return ((UnpackerImpl)obj).props; return null; } From a54b1ff70e93f166d9d64a09a4f158b4abbdeaea Mon Sep 17 00:00:00 2001 From: Tom Rodriguez Date: Thu, 19 Aug 2010 14:51:47 -0700 Subject: [PATCH 054/100] 6978249: spill between cpu and fpu registers when those moves are fast Reviewed-by: kvn --- hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp | 5 ++ hotspot/src/cpu/x86/vm/vm_version_x86.cpp | 14 ++++++ hotspot/src/cpu/x86/vm/x86_32.ad | 46 +++++++++++++++++++ hotspot/src/cpu/x86/vm/x86_64.ad | 8 ++-- hotspot/src/share/vm/opto/c2_globals.hpp | 3 ++ hotspot/src/share/vm/opto/coalesce.cpp | 8 ++++ hotspot/src/share/vm/opto/matcher.cpp | 17 +++++++ hotspot/src/share/vm/opto/reg_split.cpp | 13 ++++++ hotspot/src/share/vm/runtime/arguments.cpp | 4 -- hotspot/src/share/vm/runtime/init.cpp | 6 +++ 10 files changed, 116 insertions(+), 8 deletions(-) diff --git a/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp b/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp index ba44fe6a52b..ca8a0ddafa8 100644 --- a/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp +++ b/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp @@ -112,6 +112,11 @@ void VM_Version::initialize() { } } +#ifdef COMPILER2 + // Currently not supported anywhere. + FLAG_SET_DEFAULT(UseFPUForSpilling, false); +#endif + char buf[512]; jio_snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s%s%s%s", (has_v8() ? ", has_v8" : ""), diff --git a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp index d6968a4f802..9fb37169a5b 100644 --- a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp +++ b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp @@ -482,6 +482,15 @@ void VM_Version::get_processor_features() { } } +#ifdef COMPILER2 + if (UseFPUForSpilling) { + if (UseSSE < 2) { + // Only supported with SSE2+ + FLAG_SET_DEFAULT(UseFPUForSpilling, false); + } + } +#endif + assert(0 <= ReadPrefetchInstr && ReadPrefetchInstr <= 3, "invalid value"); assert(0 <= AllocatePrefetchInstr && AllocatePrefetchInstr <= 3, "invalid value"); @@ -520,6 +529,11 @@ void VM_Version::get_processor_features() { if( supports_sse4_2() && supports_ht() ) { // Nehalem based cpus AllocatePrefetchDistance = 192; AllocatePrefetchLines = 4; +#ifdef COMPILER2 + if (AggressiveOpts && FLAG_IS_DEFAULT(UseFPUForSpilling)) { + FLAG_SET_DEFAULT(UseFPUForSpilling, true); + } +#endif } } assert(AllocatePrefetchDistance % AllocatePrefetchStepSize == 0, "invalid value"); diff --git a/hotspot/src/cpu/x86/vm/x86_32.ad b/hotspot/src/cpu/x86/vm/x86_32.ad index b4c57056f45..a9cdabf8fc7 100644 --- a/hotspot/src/cpu/x86/vm/x86_32.ad +++ b/hotspot/src/cpu/x86/vm/x86_32.ad @@ -852,6 +852,39 @@ static int impl_movx_helper( CodeBuffer *cbuf, bool do_size, int src_lo, int dst } } +static int impl_movgpr2x_helper( CodeBuffer *cbuf, bool do_size, int src_lo, int dst_lo, + int src_hi, int dst_hi, int size, outputStream* st ) { + // 32-bit + if (cbuf) { + emit_opcode(*cbuf, 0x66); + emit_opcode(*cbuf, 0x0F); + emit_opcode(*cbuf, 0x6E); + emit_rm(*cbuf, 0x3, Matcher::_regEncode[dst_lo] & 7, Matcher::_regEncode[src_lo] & 7); +#ifndef PRODUCT + } else if (!do_size) { + st->print("movdl %s, %s\t# spill", Matcher::regName[dst_lo], Matcher::regName[src_lo]); +#endif + } + return 4; +} + + +static int impl_movx2gpr_helper( CodeBuffer *cbuf, bool do_size, int src_lo, int dst_lo, + int src_hi, int dst_hi, int size, outputStream* st ) { + // 32-bit + if (cbuf) { + emit_opcode(*cbuf, 0x66); + emit_opcode(*cbuf, 0x0F); + emit_opcode(*cbuf, 0x7E); + emit_rm(*cbuf, 0x3, Matcher::_regEncode[src_lo] & 7, Matcher::_regEncode[dst_lo] & 7); +#ifndef PRODUCT + } else if (!do_size) { + st->print("movdl %s, %s\t# spill", Matcher::regName[dst_lo], Matcher::regName[src_lo]); +#endif + } + return 4; +} + static int impl_mov_helper( CodeBuffer *cbuf, bool do_size, int src, int dst, int size, outputStream* st ) { if( cbuf ) { emit_opcode(*cbuf, 0x8B ); @@ -947,6 +980,12 @@ uint MachSpillCopyNode::implementation( CodeBuffer *cbuf, PhaseRegAlloc *ra_, bo if( dst_first_rc == rc_int && src_first_rc == rc_stack ) size = impl_helper(cbuf,do_size,true ,ra_->reg2offset(src_first),dst_first,0x8B,"MOV ",size, st); + // Check for integer reg-xmm reg copy + if( src_first_rc == rc_int && dst_first_rc == rc_xmm ) { + assert( (src_second_rc == rc_bad && dst_second_rc == rc_bad), + "no 64 bit integer-float reg moves" ); + return impl_movgpr2x_helper(cbuf,do_size,src_first,dst_first,src_second, dst_second, size, st); + } // -------------------------------------- // Check for float reg-reg copy if( src_first_rc == rc_float && dst_first_rc == rc_float ) { @@ -1018,6 +1057,13 @@ uint MachSpillCopyNode::implementation( CodeBuffer *cbuf, PhaseRegAlloc *ra_, bo return impl_movx_helper(cbuf,do_size,src_first,dst_first,src_second, dst_second, size, st); } + // Check for xmm reg-integer reg copy + if( src_first_rc == rc_xmm && dst_first_rc == rc_int ) { + assert( (src_second_rc == rc_bad && dst_second_rc == rc_bad), + "no 64 bit float-integer reg moves" ); + return impl_movx2gpr_helper(cbuf,do_size,src_first,dst_first,src_second, dst_second, size, st); + } + // Check for xmm store if( src_first_rc == rc_xmm && dst_first_rc == rc_stack ) { return impl_x_helper(cbuf,do_size,false,ra_->reg2offset(dst_first),src_first, src_second, size, st); diff --git a/hotspot/src/cpu/x86/vm/x86_64.ad b/hotspot/src/cpu/x86/vm/x86_64.ad index ba91cb79915..ba24322a93c 100644 --- a/hotspot/src/cpu/x86/vm/x86_64.ad +++ b/hotspot/src/cpu/x86/vm/x86_64.ad @@ -1607,8 +1607,8 @@ uint MachSpillCopyNode::implementation(CodeBuffer* cbuf, emit_opcode(*cbuf, 0x0F); emit_opcode(*cbuf, 0x7E); emit_rm(*cbuf, 0x3, - Matcher::_regEncode[dst_first] & 7, - Matcher::_regEncode[src_first] & 7); + Matcher::_regEncode[src_first] & 7, + Matcher::_regEncode[dst_first] & 7); #ifndef PRODUCT } else if (!do_size) { st->print("movdq %s, %s\t# spill", @@ -1637,8 +1637,8 @@ uint MachSpillCopyNode::implementation(CodeBuffer* cbuf, emit_opcode(*cbuf, 0x0F); emit_opcode(*cbuf, 0x7E); emit_rm(*cbuf, 0x3, - Matcher::_regEncode[dst_first] & 7, - Matcher::_regEncode[src_first] & 7); + Matcher::_regEncode[src_first] & 7, + Matcher::_regEncode[dst_first] & 7); #ifndef PRODUCT } else if (!do_size) { st->print("movdl %s, %s\t# spill", diff --git a/hotspot/src/share/vm/opto/c2_globals.hpp b/hotspot/src/share/vm/opto/c2_globals.hpp index 3fdaf842740..e07e7fdec2e 100644 --- a/hotspot/src/share/vm/opto/c2_globals.hpp +++ b/hotspot/src/share/vm/opto/c2_globals.hpp @@ -178,6 +178,9 @@ product(bool, ReduceBulkZeroing, true, \ "When bulk-initializing, try to avoid needless zeroing") \ \ + product(bool, UseFPUForSpilling, false, \ + "Spill integer registers to FPU instead of stack when possible") \ + \ develop_pd(intx, RegisterCostAreaRatio, \ "Spill selection in reg allocator: scale area by (X/64K) before " \ "adding cost") \ diff --git a/hotspot/src/share/vm/opto/coalesce.cpp b/hotspot/src/share/vm/opto/coalesce.cpp index 311b55a717f..2144b59ec3b 100644 --- a/hotspot/src/share/vm/opto/coalesce.cpp +++ b/hotspot/src/share/vm/opto/coalesce.cpp @@ -780,6 +780,14 @@ bool PhaseConservativeCoalesce::copy_copy( Node *dst_copy, Node *src_copy, Block // Number of bits free uint rm_size = rm.Size(); + if (UseFPUForSpilling && rm.is_AllStack() ) { + // Don't coalesce when frequency difference is large + Block *dst_b = _phc._cfg._bbs[dst_copy->_idx]; + Block *src_def_b = _phc._cfg._bbs[src_def->_idx]; + if (src_def_b->_freq > 10*dst_b->_freq ) + return false; + } + // If we can use any stack slot, then effective size is infinite if( rm.is_AllStack() ) rm_size += 1000000; // Incompatible masks, no way to coalesce diff --git a/hotspot/src/share/vm/opto/matcher.cpp b/hotspot/src/share/vm/opto/matcher.cpp index 78931baa42a..94178813e5b 100644 --- a/hotspot/src/share/vm/opto/matcher.cpp +++ b/hotspot/src/share/vm/opto/matcher.cpp @@ -456,6 +456,23 @@ void Matcher::init_first_stack_mask() { *idealreg2spillmask[Op_RegP] = *idealreg2regmask[Op_RegP]; idealreg2spillmask[Op_RegP]->OR(C->FIRST_STACK_mask()); + if (UseFPUForSpilling) { + // This mask logic assumes that the spill operations are + // symmetric and that the registers involved are the same size. + // On sparc for instance we may have to use 64 bit moves will + // kill 2 registers when used with F0-F31. + idealreg2spillmask[Op_RegI]->OR(*idealreg2regmask[Op_RegF]); + idealreg2spillmask[Op_RegF]->OR(*idealreg2regmask[Op_RegI]); +#ifdef _LP64 + idealreg2spillmask[Op_RegN]->OR(*idealreg2regmask[Op_RegF]); + idealreg2spillmask[Op_RegL]->OR(*idealreg2regmask[Op_RegD]); + idealreg2spillmask[Op_RegD]->OR(*idealreg2regmask[Op_RegL]); + idealreg2spillmask[Op_RegP]->OR(*idealreg2regmask[Op_RegD]); +#else + idealreg2spillmask[Op_RegP]->OR(*idealreg2regmask[Op_RegF]); +#endif + } + // Make up debug masks. Any spill slot plus callee-save registers. // Caller-save registers are assumed to be trashable by the various // inline-cache fixup routines. diff --git a/hotspot/src/share/vm/opto/reg_split.cpp b/hotspot/src/share/vm/opto/reg_split.cpp index a4fee6f7cc2..9e3967e3656 100644 --- a/hotspot/src/share/vm/opto/reg_split.cpp +++ b/hotspot/src/share/vm/opto/reg_split.cpp @@ -975,6 +975,19 @@ uint PhaseChaitin::Split( uint maxlrg ) { insidx++; // Reset iterator to skip USE side split continue; } + + if (UseFPUForSpilling && n->is_Call() && !uup && !dup ) { + // The use at the call can force the def down so insert + // a split before the use to allow the def more freedom. + maxlrg = split_USE(def,b,n,inpidx,maxlrg,dup,false, splits,slidx); + // If it wasn't split bail + if (!maxlrg) { + return 0; + } + insidx++; // Reset iterator to skip USE side split + continue; + } + // Here is the logic chart which describes USE Splitting: // 0 = false or DOWN, 1 = true or UP // diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp index 48b61217130..9db182f4d33 100644 --- a/hotspot/src/share/vm/runtime/arguments.cpp +++ b/hotspot/src/share/vm/runtime/arguments.cpp @@ -3003,10 +3003,6 @@ jint Arguments::parse(const JavaVMInitArgs* args) { CommandLineFlags::printSetFlags(); } - if (PrintFlagsFinal) { - CommandLineFlags::printFlags(); - } - // Apply CPU specific policy for the BiasedLocking if (UseBiasedLocking) { if (!VM_Version::use_biased_locking() && diff --git a/hotspot/src/share/vm/runtime/init.cpp b/hotspot/src/share/vm/runtime/init.cpp index df3a8bd46dc..9eac44347c2 100644 --- a/hotspot/src/share/vm/runtime/init.cpp +++ b/hotspot/src/share/vm/runtime/init.cpp @@ -128,6 +128,12 @@ jint init_globals() { Universe::verify(); // make sure we're starting with a clean slate } + // All the flags that get adjusted by VM_Version_init and os::init_2 + // have been set so dump the flags now. + if (PrintFlagsFinal) { + CommandLineFlags::printFlags(); + } + return JNI_OK; } From 8a49894619c5cf779587832e0ef5e3c89e42ff04 Mon Sep 17 00:00:00 2001 From: Erik Trimble Date: Fri, 20 Aug 2010 03:47:09 -0700 Subject: [PATCH 055/100] 6978726: Bump the HS19 build number to 07 Update the HS19 build number to 07 Reviewed-by: jcoomes --- hotspot/make/hotspot_version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hotspot/make/hotspot_version b/hotspot/make/hotspot_version index dae71d920fb..aae9280aef5 100644 --- a/hotspot/make/hotspot_version +++ b/hotspot/make/hotspot_version @@ -35,7 +35,7 @@ HOTSPOT_VM_COPYRIGHT=Copyright 2010 HS_MAJOR_VER=19 HS_MINOR_VER=0 -HS_BUILD_NUMBER=06 +HS_BUILD_NUMBER=07 JDK_MAJOR_VER=1 JDK_MINOR_VER=7 From 28fff3ad36a419733dee57a28c6232c7e2856ec6 Mon Sep 17 00:00:00 2001 From: Kumar Srinivasan Date: Fri, 20 Aug 2010 08:18:54 -0700 Subject: [PATCH 056/100] 6966737: (pack200) the pack200 regression tests need to be more robust Reviewed-by: jrose, ohair --- jdk/test/tools/pack200/CommandLineTests.java | 179 + jdk/test/tools/pack200/Pack200Simple.sh | 197 - jdk/test/tools/pack200/Pack200Test.java | 157 +- .../tools/pack200/PackageVersionTest.java | 47 +- jdk/test/tools/pack200/SegmentLimit.java | 105 +- jdk/test/tools/pack200/Utils.java | 499 ++ .../pack200/pack200-verifier/data/README | 45 + .../pack200/pack200-verifier/data/golden.jar | Bin 0 -> 418156 bytes .../pack200/pack200-verifier/make/build.xml | 59 + .../sun/tools/pack/verify/ClassCompare.java | 160 + .../src/sun/tools/pack/verify/Globals.java | 310 ++ .../sun/tools/pack/verify/JarFileCompare.java | 199 + .../src/sun/tools/pack/verify/Main.java | 171 + .../sun/tools/pack/verify/VerifyTreeSet.java | 46 + .../src/xmlkit/ClassReader.java | 1003 ++++ .../src/xmlkit/ClassSyntax.java | 518 ++ .../src/xmlkit/ClassWriter.java | 818 ++++ .../src/xmlkit/CommandLineParser.java | 284 ++ .../src/xmlkit/InstructionAssembler.java | 464 ++ .../src/xmlkit/InstructionSyntax.java | 483 ++ .../src/xmlkit/TokenList.java | 449 ++ .../pack200-verifier/src/xmlkit/XMLKit.java | 4330 +++++++++++++++++ 22 files changed, 10126 insertions(+), 397 deletions(-) create mode 100644 jdk/test/tools/pack200/CommandLineTests.java delete mode 100644 jdk/test/tools/pack200/Pack200Simple.sh create mode 100644 jdk/test/tools/pack200/Utils.java create mode 100644 jdk/test/tools/pack200/pack200-verifier/data/README create mode 100644 jdk/test/tools/pack200/pack200-verifier/data/golden.jar create mode 100644 jdk/test/tools/pack200/pack200-verifier/make/build.xml create mode 100644 jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/ClassCompare.java create mode 100644 jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/Globals.java create mode 100644 jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/JarFileCompare.java create mode 100644 jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/Main.java create mode 100644 jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/VerifyTreeSet.java create mode 100644 jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java create mode 100644 jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassSyntax.java create mode 100644 jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassWriter.java create mode 100644 jdk/test/tools/pack200/pack200-verifier/src/xmlkit/CommandLineParser.java create mode 100644 jdk/test/tools/pack200/pack200-verifier/src/xmlkit/InstructionAssembler.java create mode 100644 jdk/test/tools/pack200/pack200-verifier/src/xmlkit/InstructionSyntax.java create mode 100644 jdk/test/tools/pack200/pack200-verifier/src/xmlkit/TokenList.java create mode 100644 jdk/test/tools/pack200/pack200-verifier/src/xmlkit/XMLKit.java diff --git a/jdk/test/tools/pack200/CommandLineTests.java b/jdk/test/tools/pack200/CommandLineTests.java new file mode 100644 index 00000000000..fefefd2a860 --- /dev/null +++ b/jdk/test/tools/pack200/CommandLineTests.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2007, 2010 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 CommandLineTests.sh + * @bug 6521334 6965836 6965836 + * @compile -XDignore.symbol.file CommandLineTests.java Pack200Test.java + * @run main/timeout=1200 CommandLineTests + * @summary An ad hoc test to verify the behavior of pack200/unpack200 CLIs, + * and a simulation of pack/unpacking in the install repo. + * @author ksrini + */ + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; +/* + * We try a potpouri of things ie. we have pack.conf to setup some + * options as well as a couple of command line options. We also test + * the packing and unpacking mechanism using the Java APIs. This also + * simulates pack200 the install workspace, noting that this is a simulation + * and can only test jars that are guaranteed to be available, also the + * configuration may not be in sync with the installer workspace. + */ + +public class CommandLineTests { + private static final File CWD = new File("."); + private static final File EXP_SDK = new File(CWD, "exp-sdk-image"); + private static final File EXP_SDK_LIB_DIR = new File(EXP_SDK, "lib"); + private static final File EXP_SDK_BIN_DIR = new File(EXP_SDK, "bin"); + private static final File EXP_JRE_DIR = new File(EXP_SDK, "jre"); + private static final File EXP_JRE_LIB_DIR = new File(EXP_JRE_DIR, "lib"); + private static final File RtJar = new File(EXP_JRE_LIB_DIR, "rt.jar"); + private static final File CharsetsJar = new File(EXP_JRE_LIB_DIR, "charsets.jar"); + private static final File JsseJar = new File(EXP_JRE_LIB_DIR, "jsse.jar"); + private static final File ToolsJar = new File(EXP_SDK_LIB_DIR, "tools.jar"); + private static final File javaCmd; + private static final File javacCmd; + private static final File ConfigFile = new File("pack.conf"); + private static final List jarList; + + static { + javaCmd = Utils.IsWindows + ? new File(EXP_SDK_BIN_DIR, "java.exe") + : new File(EXP_SDK_BIN_DIR, "java"); + + javacCmd = Utils.IsWindows + ? new File(EXP_SDK_BIN_DIR, "javac.exe") + : new File(EXP_SDK_BIN_DIR, "javac"); + + jarList = new ArrayList(); + jarList.add(RtJar); + jarList.add(CharsetsJar); + jarList.add(JsseJar); + jarList.add(ToolsJar); + } + + // init test area with a copy of the sdk + static void init() throws IOException { + Utils.recursiveCopy(Utils.JavaSDK, EXP_SDK); + creatConfigFile(); + } + + // Hopefully, this should be kept in sync with what the installer does. + static void creatConfigFile() throws IOException { + FileOutputStream fos = null; + PrintStream ps = null; + try { + fos = new FileOutputStream(ConfigFile); + ps = new PrintStream(fos); + ps.println("com.sun.java.util.jar.pack.debug.verbose=0"); + ps.println("pack.modification.time=keep"); + ps.println("pack.keep.class.order=true"); + ps.println("pack.deflate.hint=false"); + // Fail the build, if new or unknown attributes are introduced. + ps.println("pack.unknown.attribute=error"); + ps.println("pack.segment.limit=-1"); + // BugId: 6328502, These files will be passed-through as-is. + ps.println("pack.pass.file.0=java/lang/Error.class"); + ps.println("pack.pass.file.1=java/lang/LinkageError.class"); + ps.println("pack.pass.file.2=java/lang/Object.class"); + ps.println("pack.pass.file.3=java/lang/Throwable.class"); + ps.println("pack.pass.file.4=java/lang/VerifyError.class"); + ps.println("pack.pass.file.5=com/sun/demo/jvmti/hprof/Tracker.class"); + } finally { + Utils.close(ps); + Utils.close(fos); + } + } + + static void runPack200(boolean jre) throws IOException { + List cmdsList = new ArrayList(); + for (File f : jarList) { + if (jre && f.getName().equals("tools.jar")) { + continue; // need not worry about tools.jar for JRE + } + // make a backup copy for re-use + File bakFile = new File(f.getName() + ".bak"); + if (!bakFile.exists()) { // backup + Utils.copyFile(f.getAbsoluteFile(), bakFile.getAbsoluteFile()); + } else { // restore + Utils.copyFile(bakFile.getAbsoluteFile(), f.getAbsoluteFile()); + } + cmdsList.clear(); + cmdsList.add(Utils.getPack200Cmd()); + cmdsList.add("-J-esa"); + cmdsList.add("-J-ea"); + cmdsList.add(Utils.Is64Bit ? "-J-Xmx1g" : "-J-Xmx512m"); + cmdsList.add("--repack"); + cmdsList.add("--config-file=" + ConfigFile.getAbsolutePath()); + if (jre) { + cmdsList.add("--strip-debug"); + } + // NOTE: commented until 6965836 is fixed + // cmdsList.add("--code-attribute=StackMapTable=strip"); + cmdsList.add(f.getAbsolutePath()); + Utils.runExec(cmdsList); + } + } + + static void testJRE() throws IOException { + runPack200(true); + // the speciment JRE + List cmdsList = new ArrayList(); + cmdsList.add(javaCmd.getAbsolutePath()); + cmdsList.add("-verify"); + cmdsList.add("-version"); + Utils.runExec(cmdsList); + } + + static void testJDK() throws IOException { + runPack200(false); + // test the specimen JDK + List cmdsList = new ArrayList(); + cmdsList.add(javaCmd.getAbsolutePath()); + cmdsList.add("-verify"); + cmdsList.add("-version"); + Utils.runExec(cmdsList); + + // invoke javac to test the tools.jar + cmdsList.clear(); + cmdsList.add(javacCmd.getAbsolutePath()); + cmdsList.add("-J-verify"); + cmdsList.add("-help"); + Utils.runExec(cmdsList); + } + public static void main(String... args) { + try { + init(); + testJRE(); + testJDK(); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } +} diff --git a/jdk/test/tools/pack200/Pack200Simple.sh b/jdk/test/tools/pack200/Pack200Simple.sh deleted file mode 100644 index 71b9611c963..00000000000 --- a/jdk/test/tools/pack200/Pack200Simple.sh +++ /dev/null @@ -1,197 +0,0 @@ -# -# Copyright (c) 2003, 2007, 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 Pack200Simple.sh -# @bug 6521334 -# @build Pack200Test -# @run shell/timeout=1200 Pack200Simple.sh -# @summary An ad hoc test to verify class-file format. -# @author Kumar Srinivasan - -# The goal of this test is to assist javac or other developers -# who modify class file formats, to quickly test those modifications -# without having to build the install workspace. However it must -# be noted that building the install workspace is the only know -# way to prevent build breakages. - -# Pack200 developers could use this as a basic smoke-test, however -# please note, there are other more elaborate and thorough tests for -# this very purpose. - -# We try a potpouri of things ie. we have pack.conf to setup some -# options as well as a couple of command line options. We also test -# the packing and unpacking mechanism using the Java APIs. - -# print error and exit with a message -errorOut() { - if [ "x$1" = "x" ]; then - printf "Error: Unknown error\n" - else - printf "Error: %s\n" "$1" - fi - - exit 1 -} - -# Verify directory context variables are set -if [ "${TESTJAVA}" = "" ]; then - errorOut "TESTJAVA not set. Test cannot execute. Failed." -fi - -if [ "${TESTSRC}" = "" ]; then - errorOut "TESTSRC not set. Test cannot execute. Failed." -fi - - -if [ "${TESTCLASSES}" = "" ]; then - errorOut "TESTCLASSES not set. Test cannot execute. Failed." -fi - -# The common java utils we need -PACK200=${TESTJAVA}/bin/pack200 -UNPACK200=${TESTJAVA}/bin/unpack200 -JAR=${TESTJAVA}/bin/jar - -# For Windows and Linux needs the heap to be set, for others ergonomics -# will do the rest. It is important to use ea, which can expose class -# format errors much earlier than later. - -OS=`uname -s` - - -case "$OS" in - Windows*|CYGWIN* ) - PackOptions="-J-Xmx512m -J-ea" - break - ;; - - Linux ) - PackOptions="-J-Xmx512m -J-ea" - break - ;; - - * ) - PackOptions="-J-ea" - ;; -esac - -# Creates a packfile of choice expects 1 argument the filename -createConfigFile() { - # optimize for speed - printf "pack.effort=1\n" > $1 - # we DO want to know about new attributes - printf "pack.unknown.attribute=error\n" >> $1 - # optimize for speed - printf "pack.deflate.hint=false\n" >> $1 - # keep the ordering for easy compare - printf "pack.keep.class.order=true\n" >> $1 -} - - -# Tests a given jar, expects 1 argument the fully qualified -# name to a test jar, it writes all output to the current -# directory which is a scratch area. -testAJar() { - PackConf="pack.conf" - createConfigFile $PackConf - - # Try some command line options - CLIPackOptions="$PackOptions -v --no-gzip --segment-limit=10000 --config-file=$PackConf" - - jfName=`basename $1` - - ${PACK200} $CLIPackOptions ${jfName}.pack $1 > ${jfName}.pack.log 2>&1 - if [ $? != 0 ]; then - errorOut "$jfName packing failed" - fi - - # We want to test unpack200, therefore we dont use -r with pack - ${UNPACK200} -v ${jfName}.pack $jfName > ${jfName}.unpack.log 2>&1 - if [ $? != 0 ]; then - errorOut "$jfName unpacking failed" - fi - - # A quick crc compare test to ensure a well formed zip - # archive, this is a critical unpack200 behaviour. - - unzip -t $jfName > ${jfName}.unzip.log 2>&1 - if [ $? != 0 ]; then - errorOut "$jfName unzip -t test failed" - fi - - # The PACK200 signature should be at the top of the log - # this tag is critical for deployment related tools. - - head -5 ${jfName}.unzip.log | grep PACK200 > /dev/null 2>&1 - if [ $? != 0 ]; then - errorOut "$jfName PACK200 signature missing" - fi - - - # we know the size fields don't match, strip 'em out, its - # extremely important to ensure that the date stamps match up. - # Don't EVER sort the output we are checking for correct ordering. - - ${JAR} -tvf $1 | sed -e 's/^ *[0-9]* //g'> ${jfName}.ref.txt - ${JAR} -tvf $jfName | sed -e 's/^ *[0-9]* //g'> ${jfName}.cmp.txt - - diff ${jfName}.ref.txt ${jfName}.cmp.txt > ${jfName}.diff.log 2>&1 - if [ $? != 0 ]; then - errorOut "$jfName files missing" - fi -} - -# These JARs are the largest and also the most likely specimens to -# expose class format issues and stress the packer as well. - -JLIST="${TESTJAVA}/lib/tools.jar ${TESTJAVA}/jre/lib/rt.jar" - - -# Test the Command Line Interfaces (CLI). -mkdir cliTestDir -_pwd=`pwd` -cd cliTestDir - -for jarfile in $JLIST ; do - if [ -f $jarfile ]; then - testAJar $jarfile - else - errorOut "Error: '$jarFile' does not exist\nTest requires a j2sdk-image\n" - fi -done -cd $_pwd - -# Test the Java APIs. -mkdir apiTestDir -_pwd=`pwd` -cd apiTestDir - -# Strip out the -J prefixes. -JavaPackOptions=`printf %s "$PackOptions" | sed -e 's/-J//g'` - -# Test the Java APIs now. -$TESTJAVA/bin/java $JavaPackOptions -cp $TESTCLASSES Pack200Test $JLIST || exit 1 - -cd $_pwd - -exit 0 diff --git a/jdk/test/tools/pack200/Pack200Test.java b/jdk/test/tools/pack200/Pack200Test.java index 631b50709da..04dbf7327c2 100644 --- a/jdk/test/tools/pack200/Pack200Test.java +++ b/jdk/test/tools/pack200/Pack200Test.java @@ -24,111 +24,97 @@ import java.util.*; import java.io.*; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; import java.util.jar.*; -import java.util.zip.*; -/* - * Pack200Test.java - * - * @author ksrini - */ + /* + * @test + * @bug 6521334 6712743 + * @summary check for memory leaks, test general packer/unpacker functionality\ + * using native and java unpackers + * @compile -XDignore.symbol.file Utils.java Pack200Test.java + * @run main/othervm/timeout=1200 -Xmx512m Pack200Test + * @author ksrini + */ /** - * These tests are very rudimentary smoke tests to ensure that the packing - * unpacking process works on a select set of JARs. + * Tests the packing/unpacking via the APIs. */ public class Pack200Test { private static ArrayList jarList = new ArrayList(); - static final String PACKEXT = ".pack"; + static final MemoryMXBean mmxbean = ManagementFactory.getMemoryMXBean(); + static final long m0 = getUsedMemory(); + static final int LEAK_TOLERANCE = 20000; // OS and GC related variations. /** Creates a new instance of Pack200Test */ private Pack200Test() {} + static long getUsedMemory() { + mmxbean.gc(); + mmxbean.gc(); + mmxbean.gc(); + return mmxbean.getHeapMemoryUsage().getUsed()/1024; + } + + private static void leakCheck() throws Exception { + long diff = getUsedMemory() - m0; + System.out.println(" Info: memory diff = " + diff + "K"); + if ( diff > LEAK_TOLERANCE) { + throw new Exception("memory leak detected " + diff); + } + } + private static void doPackUnpack() { for (File in : jarList) { - Pack200.Packer packer = Pack200.newPacker(); - Map p = packer.properties(); - // Take the time optimization vs. space - p.put(packer.EFFORT, "1"); // CAUTION: do not use 0. - // Make the memory consumption as effective as possible - p.put(packer.SEGMENT_LIMIT,"10000"); - // throw an error if an attribute is unrecognized - p.put(packer.UNKNOWN_ATTRIBUTE, packer.ERROR); - // ignore all JAR deflation requests to save time - p.put(packer.DEFLATE_HINT, packer.FALSE); - // save the file ordering of the original JAR - p.put(packer.KEEP_FILE_ORDER, packer.TRUE); - + JarOutputStream javaUnpackerStream = null; + JarOutputStream nativeUnpackerStream = null; + JarFile jarFile = null; try { - JarFile jarFile = new JarFile(in); + jarFile = new JarFile(in); // Write out to a jtreg scratch area - FileOutputStream fos = new FileOutputStream(in.getName() + PACKEXT); + File packFile = new File(in.getName() + Utils.PACK_FILE_EXT); - System.out.print("Packing [" + in.toString() + "]..."); + System.out.println("Packing [" + in.toString() + "]"); // Call the packer - packer.pack(jarFile, fos); + Utils.pack(jarFile, packFile); jarFile.close(); - fos.close(); - - System.out.print("Unpacking..."); - File f = new File(in.getName() + PACKEXT); + leakCheck(); + System.out.println(" Unpacking using java unpacker"); + File javaUnpackedJar = new File("java-" + in.getName()); // Write out to current directory, jtreg will setup a scratch area - JarOutputStream jostream = new JarOutputStream(new FileOutputStream(in.getName())); - - // Unpack the files - Pack200.Unpacker unpacker = Pack200.newUnpacker(); - // Call the unpacker - unpacker.unpack(f, jostream); - // Must explicitly close the output. - jostream.close(); - System.out.print("Testing..."); + javaUnpackerStream = new JarOutputStream( + new FileOutputStream(javaUnpackedJar)); + Utils.unpackj(packFile, javaUnpackerStream); + javaUnpackerStream.close(); + System.out.println(" Testing...java unpacker"); + leakCheck(); // Ok we have unpacked the file, lets test it. - doTest(in); + Utils.doCompareVerify(in.getAbsoluteFile(), javaUnpackedJar); + + System.out.println(" Unpacking using native unpacker"); + // Write out to current directory + File nativeUnpackedJar = new File("native-" + in.getName()); + nativeUnpackerStream = new JarOutputStream( + new FileOutputStream(nativeUnpackedJar)); + Utils.unpackn(packFile, nativeUnpackerStream); + nativeUnpackerStream.close(); + System.out.println(" Testing...native unpacker"); + leakCheck(); + // the unpackers (native and java) should produce identical bits + // so we use use bit wise compare, the verification compare is + // very expensive wrt. time. + Utils.doCompareBitWise(javaUnpackedJar, nativeUnpackedJar); System.out.println("Done."); } catch (Exception e) { - System.out.println("ERROR: " + e.getMessage()); - System.exit(1); - } - } - } - - private static ArrayList getZipFileEntryNames(ZipFile z) { - ArrayList out = new ArrayList(); - for (ZipEntry ze : Collections.list(z.entries())) { - out.add(ze.getName()); - } - return out; - } - - private static void doTest(File in) throws Exception { - // make sure all the files in the original jar exists in the other - ArrayList refList = getZipFileEntryNames(new ZipFile(in)); - ArrayList cmpList = getZipFileEntryNames(new ZipFile(in.getName())); - - System.out.print(refList.size() + "/" + cmpList.size() + " entries..."); - - if (refList.size() != cmpList.size()) { - throw new Exception("Missing: files ?, entries don't match"); - } - - for (String ename: refList) { - if (!cmpList.contains(ename)) { - throw new Exception("Does not contain : " + ename); - } - } - } - - private static void doSanity(String[] args) { - for (String s: args) { - File f = new File(s); - if (f.exists()) { - jarList.add(f); - } else { - System.out.println("Warning: The JAR file " + f.toString() + " does not exist,"); - System.out.println(" this test requires a JDK image, this file will be skipped."); + throw new RuntimeException(e); + } finally { + Utils.close(nativeUnpackerStream); + Utils.close(javaUnpackerStream); + Utils.close((Closeable) jarFile); } } } @@ -137,11 +123,12 @@ public class Pack200Test { * @param args the command line arguments */ public static void main(String[] args) { - if (args.length < 1) { - System.out.println("Usage: jar1 jar2 jar3 ....."); - System.exit(1); - } - doSanity(args); + // select the jars carefully, adding more jars will increase the + // testing time, especially for jprt. + jarList.add(Utils.locateJar("tools.jar")); + jarList.add(Utils.locateJar("rt.jar")); + jarList.add(Utils.locateJar("golden.jar")); + System.out.println(jarList); doPackUnpack(); } } diff --git a/jdk/test/tools/pack200/PackageVersionTest.java b/jdk/test/tools/pack200/PackageVersionTest.java index 0cd9ca26453..344aadd6b61 100644 --- a/jdk/test/tools/pack200/PackageVersionTest.java +++ b/jdk/test/tools/pack200/PackageVersionTest.java @@ -22,13 +22,14 @@ * questions. */ -/** - * @test - * @bug 6712743 - * @summary verify package versioning - * @compile -XDignore.symbol.file PackageVersionTest.java - * @run main PackageVersionTest - */ +/* + * @test + * @bug 6712743 + * @summary verify package versions + * @compile -XDignore.symbol.file Utils.java PackageVersionTest.java + * @run main PackageVersionTest + * @author ksrini + */ import java.io.ByteArrayOutputStream; import java.io.Closeable; @@ -74,14 +75,6 @@ public class PackageVersionTest { JAVA5_PACKAGE_MINOR_VERSION); } - static void close(Closeable c) { - if (c == null) { - return; - } - try { - c.close(); - } catch (IOException ignore) {} - } static void createClassFile(String name) { createJavaFile(name); @@ -93,7 +86,7 @@ public class PackageVersionTest { name.substring(name.length() - 1), name + ".java" }; - compileJava(javacCmds); + Utils.compiler(javacCmds); } static void createJavaFile(String name) { @@ -108,22 +101,8 @@ public class PackageVersionTest { } catch (IOException ioe) { throw new RuntimeException("creation of test file failed"); } finally { - close(ps); - close(fos); - } - } - - static void compileJava(String... javacCmds) { - if (com.sun.tools.javac.Main.compile(javacCmds) != 0) { - throw new RuntimeException("compilation failed"); - } - } - - static void makeJar(String... jargs) { - sun.tools.jar.Main jarTool = - new sun.tools.jar.Main(System.out, System.err, "jartool"); - if (!jarTool.run(jargs)) { - throw new RuntimeException("jar command failed"); + Utils.close(ps); + Utils.close(fos); } } @@ -136,7 +115,7 @@ public class PackageVersionTest { jarFileName.getName(), filename }; - makeJar(jargs); + Utils.jar(jargs); JarFile jfin = null; try { @@ -163,7 +142,7 @@ public class PackageVersionTest { } catch (IOException ioe) { throw new RuntimeException(ioe.getMessage()); } finally { - close(jfin); + Utils.close((Closeable) jfin); } } } diff --git a/jdk/test/tools/pack200/SegmentLimit.java b/jdk/test/tools/pack200/SegmentLimit.java index 249e47b032f..f47207115b5 100644 --- a/jdk/test/tools/pack200/SegmentLimit.java +++ b/jdk/test/tools/pack200/SegmentLimit.java @@ -21,22 +21,18 @@ * questions. */ -/** +/* * @test * @bug 6575373 * @summary verify default segment limit - * @compile SegmentLimit.java + * @compile -XDignore.symbol.file Utils.java SegmentLimit.java * @run main SegmentLimit + * @author ksrini */ -import java.io.BufferedReader; -import java.io.Closeable; import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; /* * Run this against a large jar file, by default the packer should generate only @@ -45,89 +41,36 @@ import java.io.PrintStream; public class SegmentLimit { - private static final File javaHome = new File(System.getProperty("java.home")); - public static void main(String... args) { - if (!javaHome.getName().endsWith("jre")) { - throw new RuntimeException("Error: requires an SDK to run"); - } - - File out = new File("test" + Pack200Test.PACKEXT); + File out = new File("test" + Utils.PACK_FILE_EXT); out.delete(); runPack200(out); } - static void close(Closeable c) { - if (c == null) { - return; - } - try { - c.close(); - } catch (IOException ignore) {} - } - static void runPack200(File outFile) { - File binDir = new File(javaHome, "bin"); - File pack200Exe = System.getProperty("os.name").startsWith("Windows") - ? new File(binDir, "pack200.exe") - : new File(binDir, "pack200"); - File sdkHome = javaHome.getParentFile(); + File sdkHome = Utils.JavaSDK; File testJar = new File(new File(sdkHome, "lib"), "tools.jar"); - System.out.println("using pack200: " + pack200Exe.getAbsolutePath()); + System.out.println("using pack200: " + Utils.getPack200Cmd()); - String[] cmds = { pack200Exe.getAbsolutePath(), - "--effort=1", - "--verbose", - "--no-gzip", - outFile.getName(), - testJar.getAbsolutePath() - }; - InputStream is = null; - BufferedReader br = null; - InputStreamReader ir = null; + List cmdsList = new ArrayList(); + cmdsList.add(Utils.getPack200Cmd()); + cmdsList.add("--effort=1"); + cmdsList.add("--verbose"); + cmdsList.add("--no-gzip"); + cmdsList.add(outFile.getName()); + cmdsList.add(testJar.getAbsolutePath()); + List outList = Utils.runExec(cmdsList); - FileOutputStream fos = null; - PrintStream ps = null; - - try { - ProcessBuilder pb = new ProcessBuilder(cmds); - pb.redirectErrorStream(true); - Process p = pb.start(); - is = p.getInputStream(); - ir = new InputStreamReader(is); - br = new BufferedReader(ir); - - File logFile = new File("pack200.log"); - fos = new FileOutputStream(logFile); - ps = new PrintStream(fos); - - String line = br.readLine(); - int count = 0; - while (line != null) { - line = line.trim(); - if (line.matches(".*Transmitted.*files of.*input bytes in a segment of.*bytes")) { - count++; - } - ps.println(line); - line=br.readLine(); + int count = 0; + for (String line : outList) { + System.out.println(line); + if (line.matches(".*Transmitted.*files of.*input bytes in a segment of.*bytes")) { + count++; } - p.waitFor(); - if (p.exitValue() != 0) { - throw new RuntimeException("pack200 failed"); - } - p.destroy(); - if (count > 1) { - throw new Error("test fails: check for multiple segments(" + - count + ") in: " + logFile.getAbsolutePath()); - } - } catch (IOException ex) { - throw new RuntimeException(ex.getMessage()); - } catch (InterruptedException ignore){ - } finally { - close(is); - close(ps); - close(fos); + } + if (count != 1) { + throw new Error("test fails: check for 0 or multiple segments"); } } } diff --git a/jdk/test/tools/pack200/Utils.java b/jdk/test/tools/pack200/Utils.java new file mode 100644 index 00000000000..e80b2aa85c2 --- /dev/null +++ b/jdk/test/tools/pack200/Utils.java @@ -0,0 +1,499 @@ +/* + * Copyright (c) 2007, 2010 Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.jar.Pack200; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/** + * + * @author ksrini + */ + +/* + * This class contains all the commonly used utilities used by various tests + * in this directory. + */ +class Utils { + static final String JavaHome = System.getProperty("test.java", + System.getProperty("java.home")); + static final boolean IsWindows = + System.getProperty("os.name").startsWith("Windows"); + static final boolean Is64Bit = + System.getProperty("sun.arch.data.model", "32").equals("64"); + static final File JavaSDK = new File(JavaHome).getParentFile(); + + static final String PACK_FILE_EXT = ".pack"; + static final String JAVA_FILE_EXT = ".java"; + static final String CLASS_FILE_EXT = ".class"; + static final String JAR_FILE_EXT = ".jar"; + + static final File TEST_SRC_DIR = new File(System.getProperty("test.src")); + static final String VERIFIER_DIR_NAME = "pack200-verifier"; + static final File VerifierJar = new File(VERIFIER_DIR_NAME + JAR_FILE_EXT); + + private Utils() {} // all static + + static { + if (!JavaHome.endsWith("jre")) { + throw new RuntimeException("Error: requires an SDK to run"); + } + } + + private static void init() throws IOException { + if (VerifierJar.exists()) { + return; + } + File srcDir = new File(TEST_SRC_DIR, VERIFIER_DIR_NAME); + List javaFileList = findFiles(srcDir, createFilter(JAVA_FILE_EXT)); + File tmpFile = File.createTempFile("javac", ".tmp"); + File classesDir = new File("xclasses"); + classesDir.mkdirs(); + FileOutputStream fos = null; + PrintStream ps = null; + try { + fos = new FileOutputStream(tmpFile); + ps = new PrintStream(fos); + for (File f : javaFileList) { + ps.println(f.getAbsolutePath()); + } + } finally { + close(ps); + close(fos); + } + + compiler("-d", + "xclasses", + "@" + tmpFile.getAbsolutePath()); + + jar("cvfe", + VerifierJar.getName(), + "sun.tools.pack.verify.Main", + "-C", + "xclasses", + "."); + } + + static void dirlist(File dir) { + File[] files = dir.listFiles(); + System.out.println("--listing " + dir.getAbsolutePath() + "---"); + for (File f : files) { + StringBuffer sb = new StringBuffer(); + sb.append(f.isDirectory() ? "d " : "- "); + sb.append(f.getName()); + System.out.println(sb); + } + } + static void doCompareVerify(File reference, File specimen) throws IOException { + init(); + List cmds = new ArrayList(); + cmds.add(getJavaCmd()); + cmds.add("-jar"); + cmds.add(VerifierJar.getName()); + cmds.add(reference.getAbsolutePath()); + cmds.add(specimen.getAbsolutePath()); + cmds.add("-O"); + runExec(cmds); + } + + static void doCompareBitWise(File reference, File specimen) + throws IOException { + init(); + List cmds = new ArrayList(); + cmds.add(getJavaCmd()); + cmds.add("-jar"); + cmds.add(VerifierJar.getName()); + cmds.add(reference.getName()); + cmds.add(specimen.getName()); + cmds.add("-O"); + cmds.add("-b"); + runExec(cmds); + } + + static FileFilter createFilter(final String extension) { + return new FileFilter() { + @Override + public boolean accept(File pathname) { + String name = pathname.getName(); + if (name.endsWith(extension)) { + return true; + } + return false; + } + }; + } + + static final FileFilter DIR_FILTER = new FileFilter() { + public boolean accept(File pathname) { + if (pathname.isDirectory()) { + return true; + } + return false; + } + }; + + static final FileFilter FILE_FILTER = new FileFilter() { + public boolean accept(File pathname) { + if (pathname.isFile()) { + return true; + } + return false; + } + }; + + private static void setFileAttributes(File src, File dst) throws IOException { + dst.setExecutable(src.canExecute()); + dst.setReadable(src.canRead()); + dst.setWritable(src.canWrite()); + dst.setLastModified(src.lastModified()); + } + + static void copyFile(File src, File dst) throws IOException { + if (src.isDirectory()) { + dst.mkdirs(); + setFileAttributes(src, dst); + return; + } else { + File baseDirFile = dst.getParentFile(); + if (!baseDirFile.exists()) { + baseDirFile.mkdirs(); + } + } + FileInputStream in = null; + FileOutputStream out = null; + FileChannel srcChannel = null; + FileChannel dstChannel = null; + try { + in = new FileInputStream(src); + out = new FileOutputStream(dst); + srcChannel = in.getChannel(); + dstChannel = out.getChannel(); + + long retval = srcChannel.transferTo(0, src.length(), dstChannel); + if (src.length() != dst.length()) { + throw new IOException("file copy failed for " + src); + } + } finally { + close(srcChannel); + close(dstChannel); + close(in); + close(out); + } + setFileAttributes(src, dst); + } + + /* + * Suppose a path is provided which consists of a full path + * this method returns the sub path for a full path ex: /foo/bar/baz/foobar.z + * and the base path is /foo/bar it will will return baz/foobar.z. + */ + private static String getEntryPath(String basePath, String fullPath) { + if (!fullPath.startsWith(basePath)) { + return null; + } + return fullPath.substring(basePath.length()); + } + + static String getEntryPath(File basePathFile, File fullPathFile) { + return getEntryPath(basePathFile.toString(), fullPathFile.toString()); + } + + public static void recursiveCopy(File src, File dest) throws IOException { + if (!src.exists() || !src.canRead()) { + throw new IOException("file not found or readable: " + src); + } + if (dest.exists() && !dest.isDirectory() && !dest.canWrite()) { + throw new IOException("file not found or writeable: " + dest); + } + if (!dest.exists()) { + dest.mkdirs(); + } + List a = directoryList(src); + for (File f : a) { + copyFile(f, new File(dest, getEntryPath(src, f))); + } + } + + static List directoryList(File dirname) { + List dirList = new ArrayList(); + return directoryList(dirname, dirList, null); + } + + private static List directoryList(File dirname, List dirList, + File[] dirs) { + dirList.addAll(Arrays.asList(dirname.listFiles(FILE_FILTER))); + dirs = dirname.listFiles(DIR_FILTER); + for (File f : dirs) { + if (f.isDirectory() && !f.equals(dirname)) { + dirList.add(f); + directoryList(f, dirList, dirs); + } + } + return dirList; + } + + static void recursiveDelete(File dir) throws IOException { + if (dir.isFile()) { + dir.delete(); + } else if (dir.isDirectory()) { + File[] entries = dir.listFiles(); + for (int i = 0; i < entries.length; i++) { + if (entries[i].isDirectory()) { + recursiveDelete(entries[i]); + } + entries[i].delete(); + } + dir.delete(); + } + } + + static List findFiles(File startDir, FileFilter filter) + throws IOException { + List list = new ArrayList(); + findFiles0(startDir, list, filter); + return list; + } + /* + * finds files in the start directory using the the filter, appends + * the files to the dirList. + */ + private static void findFiles0(File startDir, List list, + FileFilter filter) throws IOException { + File[] foundFiles = startDir.listFiles(filter); + list.addAll(Arrays.asList(foundFiles)); + File[] dirs = startDir.listFiles(DIR_FILTER); + for (File dir : dirs) { + findFiles0(dir, list, filter); + } + } + + static void close(Closeable c) { + if (c == null) { + return; + } + try { + c.close(); + } catch (IOException ignore) { + } + } + + static void compiler(String... javacCmds) { + if (com.sun.tools.javac.Main.compile(javacCmds) != 0) { + throw new RuntimeException("compilation failed"); + } + } + + static void jar(String... jargs) { + sun.tools.jar.Main jarTool = + new sun.tools.jar.Main(System.out, System.err, "jartool"); + if (!jarTool.run(jargs)) { + throw new RuntimeException("jar command failed"); + } + } + + // given a jar file foo.jar will write to foo.pack + static void pack(JarFile jarFile, File packFile) throws IOException { + Pack200.Packer packer = Pack200.newPacker(); + Map p = packer.properties(); + // Take the time optimization vs. space + p.put(packer.EFFORT, "1"); // CAUTION: do not use 0. + // Make the memory consumption as effective as possible + p.put(packer.SEGMENT_LIMIT, "10000"); + // ignore all JAR deflation requests to save time + p.put(packer.DEFLATE_HINT, packer.FALSE); + // save the file ordering of the original JAR + p.put(packer.KEEP_FILE_ORDER, packer.TRUE); + FileOutputStream fos = null; + try { + // Write out to a jtreg scratch area + fos = new FileOutputStream(packFile); + // Call the packer + packer.pack(jarFile, fos); + } finally { + close(fos); + } + } + + // uses java unpacker, slow but useful to discover issues with the packer + static void unpackj(File inFile, JarOutputStream jarStream) + throws IOException { + unpack0(inFile, jarStream, true); + + } + + // uses native unpacker using the java APIs + static void unpackn(File inFile, JarOutputStream jarStream) + throws IOException { + unpack0(inFile, jarStream, false); + } + + // given a packed file, create the jar file in the current directory. + private static void unpack0(File inFile, JarOutputStream jarStream, + boolean useJavaUnpack) throws IOException { + // Unpack the files + Pack200.Unpacker unpacker = Pack200.newUnpacker(); + Map props = unpacker.properties(); + if (useJavaUnpack) { + props.put("com.sun.java.util.jar.pack.disable.native", "true"); + } + // Call the unpacker + unpacker.unpack(inFile, jarStream); + } + + static byte[] getBuffer(ZipFile zf, ZipEntry ze) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte buf[] = new byte[8192]; + InputStream is = null; + try { + is = zf.getInputStream(ze); + int n = is.read(buf); + while (n > 0) { + baos.write(buf, 0, n); + n = is.read(buf); + } + return baos.toByteArray(); + } finally { + close(is); + } + } + + static ArrayList getZipFileEntryNames(ZipFile z) { + ArrayList out = new ArrayList(); + for (ZipEntry ze : Collections.list(z.entries())) { + out.add(ze.getName()); + } + return out; + } + + static List runExec(List cmdsList) { + ArrayList alist = new ArrayList(); + ProcessBuilder pb = + new ProcessBuilder(cmdsList); + Map env = pb.environment(); + pb.directory(new File(".")); + dirlist(new File(".")); + for (String x : cmdsList) { + System.out.print(x + " "); + } + System.out.println(""); + int retval = 0; + Process p = null; + InputStreamReader ir = null; + BufferedReader rd = null; + InputStream is = null; + try { + pb.redirectErrorStream(true); + p = pb.start(); + is = p.getInputStream(); + ir = new InputStreamReader(is); + rd = new BufferedReader(ir, 8192); + + String in = rd.readLine(); + while (in != null) { + alist.add(in); + System.out.println(in); + in = rd.readLine(); + } + retval = p.waitFor(); + if (retval != 0) { + throw new RuntimeException("process failed with non-zero exit"); + } + } catch (Exception ex) { + throw new RuntimeException(ex.getMessage()); + } finally { + close(rd); + close(ir); + close(is); + if (p != null) { + p.destroy(); + } + } + return alist; + } + + static String getUnpack200Cmd() { + return getAjavaCmd("unpack200"); + } + + static String getPack200Cmd() { + return getAjavaCmd("pack200"); + } + + static String getJavaCmd() { + return getAjavaCmd("java"); + } + + static String getAjavaCmd(String cmdStr) { + File binDir = new File(JavaHome, "bin"); + File unpack200File = IsWindows + ? new File(binDir, cmdStr + ".exe") + : new File(binDir, cmdStr); + + String cmd = unpack200File.getAbsolutePath(); + if (!unpack200File.canExecute()) { + throw new RuntimeException("please check" + + cmd + " exists and is executable"); + } + return cmd; + } + + private static List locaterCache = null; + // search the source dir and jdk dir for requested file and returns + // the first location it finds. + static File locateJar(String name) { + try { + if (locaterCache == null) { + locaterCache = new ArrayList(); + locaterCache.addAll(findFiles(TEST_SRC_DIR, createFilter(JAR_FILE_EXT))); + locaterCache.addAll(findFiles(JavaSDK, createFilter(JAR_FILE_EXT))); + } + for (File f : locaterCache) { + if (f.getName().equals(name)) { + return f; + } + } + throw new IOException("file not found: " + name); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/jdk/test/tools/pack200/pack200-verifier/data/README b/jdk/test/tools/pack200/pack200-verifier/data/README new file mode 100644 index 00000000000..64278ab13d6 --- /dev/null +++ b/jdk/test/tools/pack200/pack200-verifier/data/README @@ -0,0 +1,45 @@ +The files contained in the golden.jar have been harvested from many +different sources, some are hand-crafted invalid class files (odds directory), +or from random JDK builds. + +Generally these files serve to ensure the integrity of the packer and unpacker +by, + 1. maximizing the test coverage. + 2. exercising all the Bands in the pack200 specification. + 2. testing the behavior of the packer with invalid classes. + 3. testing the archive integrity, ordering and description (date, sizes, + CRC etc.) + +Build: +To rebuild this JAR follow these steps: + 1. unzip the golden.jar to some directory lets call it "example" + 2. now we can add any directories with files into example. + 2. run the script BUILDME.sh as + % sh BUILDME.sh example + +Note: the BUILDME.sh is known to work on all Unix platforms as well as Windows + using Cygwin. + +The above will create two JAR files in the current directory, +example.jar and example-cls.jar, now the example.jar can be used as the +golden.jar. + +To ensure the JAR has been built correctly use jar -tvf and compare the +results of the old jar and the newly built one, note that the compressed sizes +may differ, however the timestamps etc. should be consistent. + +Test: + Basic: + % pack200 --repack test.jar golden.jar + + Advanced: + Create a pack.conf as follows: + % cat pack.conf + com.sun.java.util.jar.pack.dump.bands=true + + % pack200 --no-gzip --config-file=pack.conf \ + --verbose golden.jar.pack golden.jar + + This command will dump the Bands in a unique directory BD_XXXXXX, + one can inspect the directory to ensure all of the bands are being + generated. Familiarity of the Pack200 specification is suggested. \ No newline at end of file diff --git a/jdk/test/tools/pack200/pack200-verifier/data/golden.jar b/jdk/test/tools/pack200/pack200-verifier/data/golden.jar new file mode 100644 index 0000000000000000000000000000000000000000..53b77c41dac4f10e09634c63f3264b693c177dd9 GIT binary patch literal 418156 zcmaI7b8u!s`z9PdvF&7HXJT{0C$??d=ESyb+qRQQCbn(kn|FV;U%k73?CCn)eY(4D zRGn&E_to-1a0qk|=zpo!@RAqgzaDfD2oM=jWg&V=SusW#Az4W=Q6*&t8L_Vk5RiMB zsYz)dJ;OW#ke+5{YPM04=?}}!p(BHo44tIRyh{ZHOxh{V5S}KLIYjCa^@}M5)*+S} zW@^z9CawDs`MJfNtA-kWi2;0( zi}jK+%aKq_jgdhj56F&+&)+FciYZb-5cG}p4gEpH0H(y_Lw#WX%OdV~a~<`6`b__y z7D4~(HFUACHfFFeo}8kUq@Msz&C4`29sG|FpgSrq(!U`H|A&wViLw8~ExLcbMs_xg zPA;~LmIkf{j4sX=*8ggbjP?dbR*WM5aaGOH!r8>}f85U2fYDD{YW|XC9?wnxFn~dB zAqGVm3`Sl>0h^Fzf(Qm_C?7OVfG1;dFa`Tz+iY8Aqf;rbRxzYAZ!Dz5j!2I zx0-I?kQ{GP5VxN6p|<9?I;FOp_IJOn)@^!^K0HD#%p)cQhd^9HbDcsdc zeN`Q7JAd{b?=sTt3T$kA3Ant$U4Et}e5>9GR7`(++6#PH?tb&8{T}3g(ISi~d-ylB zY~odsvUg$YCvZoo*w}+iOIym3H-R?g>8?$1W9{CZOwXEG5|krZH|1d_k)Mz*sb@n4 zHu*tmL2n!+kW^F(a5PDbQ)MjC1D|Y2K1jzU6aqh+h{|DEJSW0DKu>!3i@b1ylZsM< zc`fMCu^~Z`+{hgMmWI0U*0^dW`Pd+pe18LajGD%wD*?uX`KKJkX+ggSv&K4ghkIQ@ zxo3d|A(x7*YofFovxsEz%=DivI`UF#E}d~}z7c%jQAfQ_Y@5yztwvtaTtWz;2AN6< zUCRM{Kr=b=Ds~J2LXmvTc6EaoTjdgoL+zKA2u7y2mN!Oqy*`>(d<15$pVKfis-B2G z0P8)oAVnF@@reDkKp{W}0~Yd!De}(TI>OJwDN77Y+h~|j<;>!K`(a+asn#a88gX>G$-tqNh6fO~q40X`Wu`S~RZr@?&lDXa*3xUfty9Kfc9kyW?8yJacYx?#X- z{BOH;hxJ3pO+}T8Z7M!_GR&eTkNz?{Bc;3;`|87l34JQ;O!73Xwa>;9_L&vCDVhK) z+qRg{6CDD0NK4I34#1D*j`Tcz(WoCOFPc+n6l51Ef7Bb2@~y_1M+QIcKLaoee?V!G z_N(iQ8gU9=>J+Qc@XSuBgcy!r-3fM+o1u=q=BpcL>IO>xsz(c_z#Yr=2G$v`Z`sZLFfd*^aGte zC1TYR{v*NIG9-M84;tnfzVIR64Xx3kl%LW7IUnsnHHx3UkUT+ItVV+-$L}a5Np{=8 z!9W8YKtOb+eWU(d>o8f!m?d88&S*;(xs|ncc%`*=^f{e|)xM)P36-qPoTeVpZ-_JI zSHGgFbK`D$yaQ|CO#PvDzO6uibc^*+b;^rPIU1pUUi{(#9Gh|+2exI}BCfObfi3L2 zj|{m}>U2}FF0Qpj(jz>)R$#;UNO#-p2qEe!oy6i$~~DOP+3x1b>)THwz*iPg-nPA zc2?FDiF7ihdY~&=OHnxcZ#gPF|KHGrbaHk=MNvk1&X!4v{-G;C5t>=J;LJ5^!=Z5d zSVE*t`!9y(Lny~|a)G8p$NV$336&OYdLHX`v6|)sZY^K3rV+UVPdpp@E zr>R)yX|+Kwr{U0o9O?x7E~@G}J)?faK6_kgt^QDh%Y0w7%Xr_UYv4xOM*GJYjmBtT zLx0LKww$9xPATmeTuPVuBoOvwQVMaLwxz4$rkkR6-BEoUPim{^c%0D~Yf+Tr50Da@ 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

64|5sdA~Q=i z+1@s>OlZ-{^KW+`K;nsk+v5ncaeE#=S0TzA_k=%)15p=m0iP(@mw5@~PB zCT?l}IUQb2Tw&6@bf{2qao&ff9$3QeNfN?Q6U;FVE{v0^EHQHY4qRcQ80d-edU?pC zAS;6T7K#0iY6+R;95s|yDK10Krg-MdX;{Rgu)huoAHP%ByTH8>%=e$%_mu8A5PWRwH6mAaXsFVS|WcGhe?c-o<$TOSjei1mzA{{~^+4CF^Vi;qNspVjnZMcY5& zWIFsffMOn={mUq!+P>6SL9((etaSu>WpQQ~u_7jm%@v_Xi37Nac>g9(OI!$NG}bChFVsaPQp9dhJQ|xE7jWZwd0j zY9(h$9oZkY_G@L&dBcI6x?$I!%lOMJM@})|w-T;Oq{R z9DyxD{n;U&G%oE0lB0AXgq5+~Q|SK8&cCr8zhCZ^ zO9{p{AV-5~l3ms4%eIdi`T(lPaI=2Q%M6Bs_P@njS5`AE-lL2zj%kXP>@Wi(ykMrt zEMaJ{ME$P~Z%xriqm`C#Jm|#JMsw;Ni#?-ZN%;5~{$RU)1G;}t zNT?*R1IhB_sWAUi0fKYZ>mNbgGM$F12EQkhW-XEAi%}b=okXcVL40tiu}??ZeI9d> zhnL5+?m?by1gi{@jIv&B59Jkr4xj)~R>y0NP%!e>kuYb-b}yrR6|(WE*pAVT_>QPeAlduL=$dj{sD(iX}(HZR1U*SK8&G-<1^hRL((l{LeReh!F|V;F$qXm22u&- zM`$UCyw2GXktb0YZZ19?5ord~)F<-0+yxv09#-^sZ z2ChaK7*08Fy}*@XSWK8!JebV)Df6+7|B=-tk~RA9P-ryDb9#E z!o*$;0C{SDg$^SOzT8c7qj5CS8ig-E)3bej~Z z+AY2u>3r`Ta|QHq4ve$}7F=xW0s}NT9terGC1kTS4E2HpBexlHQI~d{i`>Z4uKxy- z$(CU1i5;r51W~%+=-(Bd5$n^hUQX3(UIZS^E|p#nt!GOnZHVLRM-2#K=#`19Vhro+ ze)&9%@Y<*h%|yu7XRODc$T2K1Ye+OK@m_PPLt_=XfmMQL2srS{Vw2n)8!JUP+L{>n zCrM{b9vaEl8~8D)-BP~q@y(DF{A!2|y$`*&I%wMi2*Lfd6?h0faL zSVAW7dE{%MM=|0%U}7N^qGqB*-A4G^vzTD%dAG6i;8Fg*@2SFyRMvIp^0)XuC54Vc zkc*-cXBJRyCrWS^)qlcLa-HBIk1yrJV`yy z>pYGs=hM&}c)H2mm3)qm$PZ)trhcH}Km8uJJS&e6{n5y$6`YwZ@hTpDa1THwqG93d zY~%B*@qcWU7(D?DwsDS?YwM1h1}H{ zOSDA`dLlq|eqF_*tSPe_M8bI)FeyI2&*{=82Res+H>Lb^!rJ(D?(hM5?z4(*2WR$C zO!#jfg4o}pO;{Az#_CSFOtmKn-TDGxy@H~|8EXcDt{F~??B*hwCx~`T+*{LDq zU{Hxt5`iFk1DOFuMGHh@O)Fh_`)uAZb< z1);PML&5URK50aJ%JV`s)#0c{+#IYl4`uD-oobT!9GtzxkfZgGUrQTrz6@e|qz(6k z>f&n`%H_o&B!ZYeAkMkcYcS9^S^Y?7s7O>L=;F-v%&w9El4^rgd#bryGM%$j_Z$aD z0JF^|eVe&S_Nuzw8yGpRfQmFw-{??a8@7!1ION}&7!x8w-bz?BN^|_KϢs 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

!s{z0dqO=J#Z~0mBsWKS9U8g?dYnJo<0KcN zG)6Qj9+#`ZUoD^RCCx3k0O67DQijL7Qh$RkteRnbFu?&mtN8BXl=SaMGW4vr z=&zW>7N^heGHj^KTO_VgI9n=|hR@G{e03(Y7|+k77P_=TQG=8g{R#_v$eOHaqSsrC z&ns^jQth%(NWc~fUgFaH5ig7+zx5$ul$v#= z%l7x+mL>Wvu0GDM$canBd|#OlI8mM+KU#B_&+-ILCt~#ZN~KDxpGk9&4XFW2_2Gw)t&W^4p2i5O z;d#=sEY;E{vJi8v#6UOBS%D_cEgS2>|d>}p)$%82Lv zAS;bNRV|graUq&XxBiq!x3(yE>h6C(Yh|?zepsw#PBhhW*s>yNp4G;2NsFwP5tQB$ zb<>@GGk_IEkuL=OsT@hsLzp^ca3m!vc&WF^iQ6V_&oaiTQ?er|;Wu&LNJ7ZpkIO}$ zADr@BM@=H)H61w#-7M+d4T+@0kC+aRYUFJ({1V!PdAh-(VR9qdNs~NdnY&4kvg%z8 zjBOGMxW<#0bVrT7PLHJMmHlHYtwu3YojAl$-CuT+x zLl!qXUiGlUdm@bQi7=rj!Vx_Yj_ipru_wZ$XpIjMsw2M;q(n5LKwCy+O|H^|Tc`mKKXZ8=T<;gtRv-cXoyy{<_A z(h*r%<+Wbrj!>oE#t{hpzCixQLBo^Z(h@*|(3>Zb8!^*mSj$B7CpYK=nfZk+4Ne(r zOSbqN@(YL~5b-V47Vw7N+`R&ku{3+DyTJ@XCbfDRea<>*Bhj&KHN>dNpK(v4YYniX|D}a9pn0m8#}_rCfe9p zHg=AUoo{0o*x02ucC(G$YGb$ASeK0jZEUBFYZ^OBV@GRjvc{%p>==z5tFfsXo2Iej zG*+mw=^C4%u_BEXYiy>*W@&7;#!56cM`Lp}R;sZwjg@PxLSyqZHeX{4G*+pxDvd4F z*dmQB)>yU1YBaV)W5;Xk1dW}jv6D1btFeS~Pa5#=153u*M$I*rOWTrLo-_+oQ3)8r!F_$27KIV~=a>fW{7L z>0*) z#@^A`yBd2>WAAJ11C4#Cv5z$NvBo~p*ryu%Ok)1+(b%sV`%PoNYwQn={i(4-XlzU41y!fuH5Def zo6%7i7k;uMHLEmEuoUalN}~dySeI_8(*tu+mHHWus?-lMR`xCJDFTJWjMA7`P6fM#_#RmN&8$>urNFG-D_gAdcjUF$;o~Zg&h}7J8RLf-;DU#5GOsZDYJ&1Lg zqrzIMzpkdx4_dP=)uG3cBn*s02sx;-L1sh)dOFGK-qz~S^T-%#w3Q@(sUl@$?MZ(E z$o75cm%vsEu9-f>(4Jd3!Vd?@m0=~(>p<2Hv5p~-jzj9Ru8pG6ulj1zzEfhvmSM;x<-GmnnJ(e9&PQ$-8|0U;vN_G zC5W93h-@2U(g#`T6lxq75eSIeBHDgrk6JR?Vy=x3W>dmDZ+;`1LO)Z_9A#~7-VIr! zVr#A2A5p&~Ed7Y@JQ2GbNtvT}Hae0@jeFWXW{sWyDRF8(3I$9nYUZ^s2MYQ zAec5Gk`S?Aqn6|rw|-6!_0fV0)h`L_v$eiNHwSShq<_UC13zlcUr-}gZ(idOb1-US zw#fW`WspW&F4gcCAi4xc#ivA=OWX^+ z?5)o(502_PDv~DK#PnhD zJV+_~NB3bN%gZX7J<2%=T|+$tPV`NW=blK;k@0C!u8V#NMUAnz4Er7)?}%7#b{W^4 zLKZiU4GOml{|Exx&as$p`e}kCW@X4Fk}geZ+=wW4HYSd%6J?FV@#U!YdU!deeK_ij z?F;|XHHzkp6P3n34v*`1^}ri%o`%O2!x4&MN3{=mNKx&>5m#(~WDj0d6i;NN)-q0{ zZH(sfnwMjKtcw=iI26{!)+K-IE=4i-6uONX+3>2On9X>uMS;WKR&-+{#*1yo#q+nK zU4U@tH%bF~)Sf7<86CwFw|$(o$^dRmV>7IattZ5H|Drjp@!lwnvATv)Y>|=MsL?Eb zV_denxDpHdlu=C9P;b;o7I!lalNke!IyoWRAH@<5gZ{NijCkK$_rzlVV(p~-w}&Ho zHlUGS;K(=)}UGlIdFeTHaeLauamI*`WHl_gRz#?Si&Tcj8=xq zILp_UHzi(AB|Qp@E-jbG@bHQ(y~JL8_@>P{DOy*JL;bt#^Yn!&+%1P*Lri6&vyJK#zF9|uq7|8?*_ z{1*o-;J-SclHd*gn}e-J;Jpaa`0ox_$Nz9Z8~@Y6HWS>z4>`D90SE7|Fb6DBaGsA7 z+^Z-KcuEltwn|YQe1MYR;2DbMfR7=^0pAdOOYjrHZv=l3{E5JN69lNZ4Ej3YAi)O& zpA&pV@B;$NB*-S{Ly$v+Y0%FB=MZcpxRKyig4+n5CwLiw4J61V7)&sPU>Ly)f@=w= z19m&X4uTE@o=%WW(3`qC8~Qug0D^pi0)nvw;|We9SdYK~0jD;=!L}iA1_K@JOc>-~ zKFD>zOc?Bd)dW`&OoJf~SV6FoU@d_UhB_dCz*1qD16p7>trFpM7~z03V59?P!6*mJ zCMY3jAXo#V9ncDS4mgwGG{|?r3@C8Gg)qher@~koZ5*u<;XZ;^INZT5M1X0EjUbW0 zPLM?4aKIs@7v+*2@R5>2xm1EQ2YjKVQ!c{+Un`jo_^*;h#cYD!1bql{2xy~}egq^J zWdOlIfR?+Hp95Z0`~(5&rIlcX1I|)b zI^Z(|=Ahg(WfiSsHNj~PHb^<$!PY2eIN%p$jf1PonKaN^2XrWBQRDLnE^x37%7qR# zO1X%N7dv2;vW{|>5?t>+rT;7#Qkg7pN~5?n`cJ;4nG8wfTM+(>W}!7T*05^N&4jo@~II|%M1 zxQpO!f;NJC3APg4Pq2+(J3%`^2f+gb4-#||bP?<%c!;2z;8B8I1iK0L5bPz`NAMWI zeuBpd4iFq9c!J1f2urd=Uy?I;5dR=cHSaZ*||@QwR6AnJAyde&Q~hW z5xhw7lAW&-C)oLFF^Hhh&QBAk+4&jD_jbO9;7sL11W{w>YYEO4%+Ajhi3HOLs_pzd zA?*BoYInZ4)Xp!Ub{A3cBIRcUQEun!#5{rqJHJHv%+4Ic7CPu1;LL9f)flTs3$nz&aa_fuAy~YBNo{C zwUoO~Tx;joi*pFhweuUOZUZgrMrwSca1)$Pu-?vZqT(GyxI^ZMaECa90I7Ep;ZD(L z=XZ-G1jiGcLU6gAw~6I;eh+cnOYQCzClah7SW9r0oo`m&Lm+Y9Cx+Ph7Lj7-TdDE= z1lyDk5Qw^s1hHLt6G2>Q=R1_25QNvx+r=csqYWOtAAO#SwP?l$dDePm4)*{){-%j%V;yg7*=qzvsl&cK$rE zy+D(EQ5eQA@C6d z2v!rENpLp7g#;H7TtTqW&flV?zAaYR`8x#f(ph+q>fRUU+4%?J8aw}pCi#&70)=3u zoqsG^?fesQtQ`;W>jeKr5D5etft?_Uz(LT9AekVQAdMiMAcG*2Ad4WIpf^Duf*gXr z1pNs56AU02NN@qc^>+T5m}=*rlZZbjyYYoM%+9}5{zMRq?fffYBbY-l*UrCI4k3se z2sR|~ifsl3Dzg^ zO`@1!b`rmhn%quJZWl8MmJ*y!a0UT3-Yc4u_=kdiQa3~XOQc?*Uyg_V8MN`ws_bT9 zjQ#^3B_5CQ4=d=`cqyfBk4yj8epUKE=(6ii1B%wm|1gl{;|>hwZ}NMA^NaaTI123K z1OtGbJYr1yl0S@T4x9`Z0aM?`Jm&+kywp-2gzeGsmcOP$hnW)cnK6+LKD8nDUbaQ| z7*iX)At)tBoKo`CWjRNtiS%}mWpu?Q9er!bhmN+K4|?5_ZqwV*b40egSB_(+SBOmS z*6Zm(LS=H^Wj#I*WP5b$WqPO454!X+U8JY7ba>jNH4L;ShOkIyDx;Ff!2qo}cs{Q~ zyc|@$L3Lw?Xx|}!pZPvww)`!o9|ZdFHsnu2|2_@fdmH$ba-*NY*bDWeVjs_kS?xd~ zUyZD%VRI++Gn$W=^#I8@T)Suws+(~9m&1JNKq|2Y6mOkg!1rLZjgX*>|CU=6I%DKo<1tSfP0QRV1dRRB#U6b3TmQEJ!;^oI7194!E*r~oL zV4|9(ZoxuOJx^vQC*&mT!g1^{0P>MH7e>i6AI8X3AFVHt`d^H#E`e0I6x&{jEicEm zS28=j(y*b8UR-iB@oi7kYz)qjUPVp^ebUN1PG`n5wIshr@=|S~^nhvq64HB?sd(M& zm`h}7O0m($7(*_-+QricAR(x(z)YA`7wJpb#mlIfuL7e-r@@+s!_x%pdHJ2{+Weq8 zK6)$O#6`S?d~f5BLrhsz8GQZQ#f@{j`PP~)^@vjJVswXix(#e|^E$=nJLI%7Ae*0I z^f*xJ^Czg+p8-V|GT<8&(wER1zQ&%vWr@5W>LwZ9A21DVIuC97_N`Ik_z7EnX|`O3 z{n8mMzE)^y=noYtj?D#Miq9_gXI?$BFea5KA)2O95>Wz!jSx{|107>oQ$#h4j zI-;9zt_iBmUFzXw1)b_)9by|!Obx2Hb%=ZN_o2AT^KmBFx?SAbsgB$LX+iPdZvJ*n zUYB}#r}$-uoa9k31&5geQ)Q}8ax7Xl%t8W7K&!8zO|+pEPlDmh0R=1>CbCpGj-^8> z%YY>;2h9sUJB^8t!|f#@rr?QmH9n&fP+@2SuH>^JhHUN6DLCE}`GCuYw?uk-T zHVko^DW4xuI&=&!#>Jq11vfxqdat0mb8%3-w-H)dX*a(HwI{VxO)o2WR5VN-a3k0Y z28{0%zm?|W%<|J&r}$$7B&3T{?8)m?{XunodRkB|=;l|}e9nN7Da1oo2%fC-q zY%KpU6e1m?)NbBhli#HtTb7Udd0|1cn5*!3ErevY2)VnI+F0)W#67(l%sSGcJp&<26l;WQ4NPI9)qf9pDf1g%MrVD73(7{em@u_ zQ_H=^oTy@TD4cp2!Wz&h-((K5$QWcu%pjfW;5cLSmM6R~+Je4OV>IFz%OH!nVK6%d z5_w-`jxk0Bjv#c)d$6V~a*W*Fd`H2)?d1ib!<=IbFu*c^E~O?Mpc&7H2c>kmQekwy z-neE<+r@rdK&WR@m;E`YM*Vj4jg!?J^#Jtl=69m14xOw@QjXfG_FtT%7VK2_a4?pd zfJ;pgP z7W;7)WD?zY3XfrB;klsMpBO!zvI3k8iL0PX9ag4KM#agfyO1y%=eUnS`Iw#Reui#9 z4rU%_u$%9lEOJEtPW25AZHGRlX*+PblW@>UX!ItT)1HhQ3V0C;b}>$U9Z=vuhh2(h z?=l$8E{E~#N|?&7g6ZsPC}G#Y0=6DbWY@wnb_43}4a!twNmC6IIIvyZ+|4h?z2)gz zr>dcWK{G;^JLb}&ZbWt?j&&1?>So*|P|h{_x(;VcIyy7&0H|y`UVv|vBD{_IiUziK z{vN)6BYHla;?22|)^_Nt0%`k6anaDFP>T&<%iqoSZ)e-Fw{$2#BTa^SyiBboe-p0# zHeCB1kc5whbaofc=Wg7(Hps)r#}RBZD$;#W#J1vXf4gamM;oqKBDx59N~d~e^wQcv zVI3%<2PE4#osI1^O!%YSd|OSs=;%@>l?Bz7eqs}3<4uOn>>WXM>qeLmR8MV#jX~9i zwdj)GzERRunRCeM)7lPwYO>siwyb0$TKk|nYXeQ{)V4#L+r<;@Vw1cE(Vuw&E!9zY zg%Sfc*b-`XZXOcOmG}ECa@4`)L3Ik|c2GJdsGcKhlP2RdPDR_(c4+<%NWoR+!cF`t zJcTF8v;oeS=_R;*loI_;evPD4N_0wzPASpb{a@JRnnD z#;4(V0~W+BdkAgy!;r!DKrZ_NMzH;;wokyZ>?yowo`#w1S(wLOfRm8cz+QzG_8OeV z-h%V7dsYC;J5MVxPi2>@(PcTKpvT z9cb)(T-Xng#{Px6_;2)+e!@Y&i8tuhxP!Lgpj)kjZvDGKf5Ab2#X*0=L4U_V|G+^H z;lW5S2kmdX@lXrgQ7;CrU`r9N<+A9O8st!b-|`F~mvj&-#enk31GN ze|ug~9TUEyt9j7}5s(7KL|dX3^sOyzxWD!Y~Q z&DW&v7@&=Pxu&2~yxXPLMfqXqOEY$V=n%{2E{&k}>kysM1B?K{N1{CPBHmg|cknu+ z<)M2_p_YS;mReA~zEiz6x-q^q*l4ry0Q1q^sh%F)^hm@Xm6J@72)RUiH-D@qzkioH zFM5HK5j&Jcz|F4Z0#Smqqnl1u-=)O><14V-u3T*lQi^oC)C$_gBlP@A(Vt(B$miE1 z`JLiF=#6*t=V}VZWZ{M1rJhK(Il^f2nb4olf;?V=J>Rd?#JX+|kn1)rzU#)z(9d#y1NR!_J`=guBKJ1s z_*m{v;x6og`&{Hc54q6}z!s%Cmb-(vr}n^oF>v~d$coECdJ?w-8{t%?{hmqq! z<($~@chUHBdl-KYj=vYj--k61C}+oVKT6zldf+~Q+y{~S2^>49oNO5Ckp_2CH$O<+ zRb>`N{XTjI8J{&xb^zJ8E0-DULygBEvOiRlUofUiEsnPKubQl9VfiB^#hCX9W8`Gf zFDQNx{Sks{Q8f2Ecw?v}B#(%(R^!i8Bc+Q-LJ>4d!@m$0-p8LCL{Djl^8K?0doW?IWjbCvZzu`3g zz-j!6GP%WkY?Kr>gMlKfagjX;JGYna}*m&g)3K>5lcNzF$x{F4WvA0)^v$4cXjixbtQU~ z-kzQ6BOCEmMcHs~(%DqfaXJ~0tYkx9r4I~Ia$va9PmXYv841ySiM+TYXDF0zudC)%tJlT z4fUKyJ(uR~%m3S+kHVgh#-1l*&r?t?s_9yvjC>?{G!dTTsQ2={g9hpEjCGm(h~-A% z-gVx~$L!l;1dDJqJ9@dpk#9J@TZc!DHWh?24eZKsxC=$h2H#Pz2h*?gl%1gvhmC&C zWXxiM5wXl@7u^TwE6gjM>H{638;en9ODV^tR6vSCk-SgM4lFN(v!Ydv+^?T;>vRj1 zYnW$SH$Q-exiY%-38s1S;F9 zWqGJ&a(+h39WpF53+C_(Wm?5A!1P4ib=vuQnd`v-;a9 zqu&OwQBXn^TktK?uT#A)T6hbG`1zFT%N_ue*4{m@l zdVV(#cB(UWb8lI=j|@H$Sqn_AwnJCQiImHU+}^1Uh@8k#|L+rNJG5VRSs}Z8tW)j# zf7WH0>~jDAQJ3N8>QX!`fHvKsTnSldc?aW5E>F1zj!@Ra3CgvwOt}tLDc8fb%8hV~ zauaM;ZpJ6#E%>s!6+TyPhaZ$X;1A_amZ03lQkA>e6y;u4q-1nXvW3+vTiNBx zcC?J2hrTPZbD@{CU!Un`t6g+$g#JPGhEBDvYT}p>|NXDgf z`ZJyC(V2WmYOb1y_H;)mVs*v_DDCEZaP?)SX6sBoKK}u6S5V#F&0ngaY?tapm3W~* zhD7nKX61LPt0<^(qeh#K@0DaAW8{U_xzA1Ik2RG#z zJlN0S#yk&wloxShUP9CS3XE1>g&E3QP@=p870SD?Sa}~#Q9gzh$|tZ^`5ewwzJPU@ zUV-Ti%D1>_-$9%56K>qU;6CL?cun~k9k~C1U_(t~*M>7BOSuvqmd7*r>~4N`P3rhA z@y$;4IRzH$wIy+DXHl)qtdzl<@jB^vA_dh`qBxdNH98r~&HAu5%J8}JD`YFbL4oo+ zTChLSf*pdB1cMbqpjGERu6og+n)#&rzMojJ2{0u&)Q)L@qR_I7kh4>vkzu!B>A>8T=K26{7vVx#^XM4HT z(43$f8cjcMY&$a7$Zu}wV6Cp9NKKdcWIKu@xDgIV;)yuaM(BmPnR3tpdE!A4l(J06 z)~iTxc!;0nussj``*GWGw+isla5&y~mH@s;1G`9v0+9uWi)MKP z5QAZ@7y{>sp|DO2gUiKm*dRuuTvnKxFv{=*9q7~EkBd3-LGgN2Y)g!VBry)Fu2iNQ z2b!J%^gU%SeWbv%^)y)~u>*6@Fp&1j{4~)N`ZTeLd_rlb(9@i(nzBP#Xynt8@FuHy zIV!c1=IbKsxpRe@W$>dYE37t79o}~6Ds(#YY9_0Db*(!|if=bR8;4c*Q6M`3)0~7| zIfA~{X7IV>tX@P;*?PDK?gRP|yAKtf~0jSP*O%emM#rh3; zq#lQ;rNLPhlM`?+Gk7WP6fJ+Uwzr!!vBP(%PRV%2OS;^?&wSm7~Y~Bccvy%JX3W-_C{Wd{z zPC}MrY=_v~28-H7np;x3jW?q>bP6>NyOjExfavBSg`HdSn8bHx2@so2KO5!=}< zVh4Ljbl^eRsEjd;%Ou0Nbmbj@!(_FG8Dm88_!O z=oGJ`5bvVLt`s7LPKNe z9OPHY^cjAQA?lN$H-MT54%H4vsL4>Qroc=!9p%auQg>TMLiM<)T3asdJN1}r{YBatz2RZKhJP4`?Ioc{(cSla#sh{?A?5Q8HEhT zbgI1zw#)EnI%M%1tf4G*CO*z*L%KR2veXL5R_8%qbpiJND?Z%x-YboO+#puo%|F22 z3%b;6WcSyV%LfTJ_Yl+GItE-2Fl#An9Jl;t1DhEHg;jf`dgqE_8U~2TpePE zrT4+s-raid1Dz(nEQNPn236ISAk2yUeLPh!!=C;{fhhAw(xheC=n!3^M}XSJZJlb~Zhoru3#P6DSAnWt4TlI9@F!J;`^*Ir+JAS?B+LOE~i60 zX6E*{i#Ia)uuk=%4$;9o#QpepYfx>K@nk!8@e2BVLGk`>{zZp)tzEp&SbtTAcp)lE zgHOIh7=s!%79}+S2Eh~<3ME)G2PVN>D1`Y?1`F|jC!o7=B3j^+zy&9R2kPK7sD}%n z0e8g(*Fq!Q0L$P;+}U@~tbYcb@D+RyKPg>k0Y0F!0Ozak0mT;+`6hm=`c6V3e@og* z8-H6jn*1Fjeb=ae&q&`l(hrREL;j}vVS?>{08mQ@2yMh<<)b zdQSo*35p_G3u?Wude>X)U9H;MD*xx1ncbZY8?>MQk2c?VuJ?Z4=Y8IpZJ+w( z!Gi!$C|)W696hSQQU#VNaEb!U6*yIa6$-3Wz@+SwbSU6aV6_5k z6zEi-O98I}J_WiJSgU|vfq()*1$q=%r@(pz&QM^h0%s|3wgTG}I7fkV6*y0U^A)&D zfj=m4j{<*G;9dppQ(%t*_baehfqe?}DbTOLegz&-;GhDB6nIpD#}#;5fiD#%Doj$C ztT3y>;uWSUEKgy@3aeDuJcU&&>`aASsIWgO>|TZ4r?7_<_K3pXRM_7X_OZe~QP`&n z`@6#ap|GD6E-HMu!pAcJ2F%G|lGFTelGEdDb8m1KR8?0v-D{njyM5hG{M+K(T(QOJ zS>^5wxEYL!vh8$x+k+j*26|eYwM(6Co^{Sb1|u}1$<89D-`CmcbOkzm>z!Wr`fgXS zV~ew_g26b=qRQ#s(Awc_cL#MM(CX@Rukv|=&VV~e&rnsZJ$}F28*KBmqNTUZ+2&s5 z@w#=3GP8wNc-bUpA^vBA_E_HKTBFw)y9vEN1zII-J>5>MxJ}a&6CR(d4I8D_Tiop) zuX8hlT&+ZxZ=E~n3(rR&==XTr*L&Kq5KSLW3C;|IJk5BEbCu8U!U-v;^tSri(9&7m zSXbi;dc2du66}mb#sv%(IkR0YtvJ^94$tZ}on2mE_ga4-*t2f^hK-v_%gQS%tLDzD zK52f2y{Qq*_KT!a!xKR)b|GdCz@T}>hrd`f>iySIpmYw zu6B2ud!471CWn(dDCFGaW(;I!C;Gik>8?j=oysN>D`@w-HZo9&^tZRP`Z|4n8m#UJ z&H_(YW6-bn7CnnLSkN5^HtO1#-Xf26R^f(NhD$qkWPRg=(l%do}8ZOds zDFZP(XIVK`nO#*y!Z>`=KcdC#YO<>Cu!d%DW?Zcr_aS|Jatu#`(A*G`ewKbyX8? zsq&h-#>)EArg^%dwY;vbp<+dSLnV5RrM#}by0Q_wTE4JhQDsw|Zlgv=b4z*mq@t-s z*wqR&O|0;{*6U-eb#>uH$d$EoYpNUPVJ)~jSCrQ+SWsDuEhkqls;pg6S5dK|yr#Nd zuQ?*Cs;;TgWel50(Aba?d!X@)t7|K&Yv!~!(t6NOHq8i*m zIxKWgi)-BI4R+9hjE8E|;yQhRD{AU$=dP%%Ap5k46f0?HGQ{G-rmE?KA z@yE2q=-L(d7oe8QJ6wL;d)SpFlO~b0#T5*oH(;D+80$F2cTOh>TaH_JIUTTa_z*tf z|1;mhxp1}k{J{zzj@asIZFL6%V+sq4BFrR;9`O;z%uOQ0Fjr(QHkk}V%oH;-)yx!| znd#AtskRwr%?#2AnBbzry1KhDy~c%e`7w*Y0|zsS1XrNd8Y%RN@=gzh)K-0Qv^B!!T033X0Zfl|g8X;G zm~L|DG2uqW1cEMao6FxeMn8BM6#gdb3XJR?FXq-hJX!O9QxV)ly5El0%>zAN!??oZ z!qlZ{W4F7}-HB;~R;k#k4N7Y5^wG^o)t-$#E#2B|#A&Qf%jjS0+FmU{XbXG3 z+UCO;u?|mMJdKFOK+XY3@i> zr5=x%_twq!26IE94CX5nH=*>S>A z7WU$JWNI+g?a@|;RN;nXWOO4MVJ$|#0i#Jmah-NTH5vz0DoS)`sHUrKsb9NhC?v&| z^Xql$i=|QSu*~gU5~i9l3n!{$V*qbyo#Z&7@uUrq8$-%8i^-SFbKr=jGz!{d>6Q#4 zZ`Oyb@r`ajZ6tjH@zR2$3mPle=-K3^m9r5YboucLQ{$sco?cwfvdVlt-nM`~H;uuK zq)ZHvx&>|q6fA70){PdqI(ytYY4kl&BfaEp4Lwu=P2Z9Rx3P518l|-RF$UlsqW&0U z=q(HGly=H{J>1oq;Iyqj4dqah%-9zyr(a7e*mCM?@ zjXKPi<0!u^clFSg)o7X>EMoNpLfb)ODm(#fj~ELwKCfGo&cg$t6HgorCY}H~m)jrq zbqdTAJACAj3ECCSwLVy&2cEPjKJ3BCVR0l#4m(Cx>0RgX`@DL{%nYkTJ*nK#>h9Kq zc~)52?)G&Rl&+!|Os0638LcNruf`F$v>4-vRyN=Xf%~pgbCpP^w0lTFSN=CnP|e2Ui*(3q+U>&zYNLa} zh>)6sW4U&u1+Z3A^=ilh&m$aWhMA3OX^2@fAmoHpQ5}y+y|HZdEo?}WBKQ4rI?@?9 z2S{pt7=m%X9@BtkVv>%ENUJ;XEOm93`rCWRvrIy<@qFtF^&sHJ9KsXaSWv3veYk2tdOJ&H z^bz+2J)H$wT(9v2f}xr;X(-60MMOqm)J}`BT4FU(bGC`$#A;$Fv6>h{Mw;(@2@Z#Mp%fFEBQC zfzVJ2qUUFDyBPB`xN&%X#{7z5qo<7;w{&VRqKVL6zhGX}>1iRBA|WRABE4F{%M-mE zv360$Sl8W1nP+@gd5=Gm#B@_1&tF#?b6Qzgw{1wM-Ap? zdQxiDX+0;^75d4LsL`ev9p6>!!%;VOxM|Xrt~zb|XkEhVxe*eiER%uDA z>7w0-q5w`Gj@I89x?Uy*v>O(!y`D|R(Jjh2Xj23F#&voE&bDxpAqPSi zOeNqBdrBaZkH;fTp0_}2fy{LevIb1)vWkppxf22%c-%JOs*#IQ)g8X|-P)NQ7oZ)Z z-@3#Ari3Kw_W3&XvrY++3u%|5xPb0WS^}=ofyEwuP(3XH!&@A9&(uPSHgy#3x;@=) z+I5(A9HY~^71W$5iK>a{F@+_TA7wh{N&#;z70dXwF(GI_2eB-dW0_`It; z?LB^D_okb5<||fONKua=gl12l-IS2HvB%qlv$MuS_f`^;qZEX0L1|Hv1um~kJ2q+L zp$tRsyPjugzCufa7l-s=~GKKbNML6j6I(MapDJE#=t1BU?kkAM+y0+B!=NFuNi zd_!;)f#uWh3b4dcG(7P{015Pe3oR%54{4MF8x+{6z$OJYD{z_uTNF55fio4@rNB)J z+^oQE1#VH`Rt0WT;C2Oi75KdZcPMbD0(UF$paKUJcu0YV6?jB}XB2o=fe#h<==#M`n9ucE8N_%50y^`efEGv;8uAK<1Ln zEi#v7uE;!2=2n@<%UqRtg3J?To+NXd%#&rFBJ)(4r^(zd^K_YK$b5**9Wu|9`B0e; z!}PQ!e9O^7{!9!m5fQ}_l%z*X3|E~QRl1QPDvCx#M6vFGXfZNk5&P;jlRgueFEo)Z zpul{)L4`f6Fx^>Do^1L`sofV)mQph?-;Y!fowrAfO__V78v}B8R20_yZ|N)+88=9g z=#&x-ht$MAGf*|GX7B*7Eyi&}JX(yd`PI;iR;k$o-Gsi^reqKB6#5K2Bs*s?Z%H@V zhvJ}CQHHrpNK;fXpkdv^$UcS?TOO7DE3QXHM+`JFzk|>$GR>XocDw!BDCkYx%mJe_ zy-z@uBRjT+KF23#NBc9iGbAE2-OP_sMXu+DDrc~r;K+`xp<-#-$7~0hGJAG)&aq$f z$vh|4R!Pq;FOR7hP>-oVb!NF3I2^Eb_ZqiiBM5$eXq3LOKZ4|Z~j_7jp=VP?R# zH@#iN>Sx5*yP-FTGmlY)UU$#be#kO5mTSg1d~##_Zl*ehs9GOKP4w%g$j2u=#hV#( z-{uZJP3G=ZRnd}-`<0G|H*0nb!)37pL8wl%zsEGpAKaS;G5-yH4R6mV^QI2JZ@so& zIWeUS#>c2V#yD_YCT7RpxP$KJDKP@?uWn%mc>|BuW#Jqk>aIC6XBqCU2{HP=G-t3P z(I=(8b!rA&DA8!@per=kPM)Mq(TwA)?5~A@lK;6)qbM~zeb80>RS%ytc)P!?f|dVs z$4&Xwpz{)ygPnTZ%w=GH_A9AStgLEvjNpNuFi5r@nVeXW@i^hMdMP!wHM=JE#rXfe zy}@$J6~D<}mL4mrj^Px`VtK{-|G};PFI!gGv+%OYo+Egk;01yM@UF@pBY2%)0kf#A zmf$Z0uMzy4U@KEpHkQSy>?HoM3ZL_XDl6rWsH_<-P}x#~Wd!R9R>6fTYbWR?XoT%5Ya$2`tRvV! zaDd>y2=FJkNM$1l@(CsoR1wT2*i5j6;I9N<6O4w7RaQ@MI>8wP=MbDra2~<=1Q#Ii zk#LF19)?R*_6or$xJ+ed!R6HM6)O8D!S@6+;Yw0mMT)CcJ`S!`*$DWZ%DzJ2S#TXG zu2)$B+@P|_aHGmf_#u^jN3fVbrm}bWpHw!9Kd!PF{Ld=;mS7A&tg>1Bh|2y&P{^N9 znVUbUvR3{S*2kYFc!uCvl^rB-@#lzrUS*&27gScqUnB})7+j;WGJ=!&ODbDPa0~AVt%h7?i@%L2b08HvfUD`jO_~-zsb2KdJ0x z{vVYs;{PSZ&jd%YtN=&}qRKuLEOC5;kR8V_6msMEmBIvqi3A04{AxiZNF=ZmOp4>z z3Po}J24Qj>-z6vnaRgR^c!C6iBmx^jGC?Xq8bLZi2Eh;l2f;9c;RGWHoCG5YMiGo5 z7)vmYU_3z%K^{RqK_S7EIDU(e8OLuEh7x4O@!N^IQ%E5gZRH)pOoBNC0fI9LwpqDH zxYf#63#+YsjnHJ}okF>lcj<73mHUM0R^BbFB`q&KB;m@^gezf<**N304re3CIUoc}RjHAVEPfg9&q1 z3~OXv69$5!qA2E^)~uiyc6E*G;u^Sbbx#6u_xry8zt8>eb1x55-CbSbRMn|Er>fpM zt+CUpblJnPf=PvM-{lOM>b2u@cGaJKCr;@Z*fTVx*-@jZS9d%M89wminBIxT?RD-3 zT^e3vtcg=;J3i}O8Palf=gHA!gKkP|`l&zF`+WJ#E?w7sZy#uk-<~}qWzEu8n=;Bm zOCOxsW!zUcvBi--cWQf{_R(fMx1$4iosLEd`sob9_jHfCPR-XyvMHSg6woL24+B4jwEdi)o4@^8 zae-dCPP%Tb`T>o$FI@zmwm-Wze#f(G>gC2GG{y}4Xzpk3d&qEt*>7b%O53g-pR2F& zM8B=xzLW3raz?%R+*{h_=v={{?mB0Sw4*d?eB2Hl(EOrvdc@aEX_NAY>ywYK4>KCk z?#nCveFLX_NZZ})+~xNcZf$$@otB|ho4K&nL#Kf1BNugV1P+})^wY-;rh#t@N&#%!F`#k2*Jt{;Eu*YdM>1>gUxXz@h%x5+DNtsiUW*ZMu4)b@@3!bw+; zKed^3{P=L2x>>WWzOuICsZFZ|&x30(*sja@+)4C$*PFww%Ecdl2{~x` zv3yl+`^RViZvE2YRHxz9`3okUKJIoh%P_dcsaM^G zph@Ci_XiD~_t(YXzamdfN~|7kU1zL6bn54UEx$%Qe$f7n)55xn<0Y1LIkSdN|6CsO zS>sgQ+2a?d1p8DRcN03bDC#!QVb|tYflDqens;T$%UKCOefXSbHR<-l)~BzpUGZi_ z*txMqc3TeK)=vxlOQ*7V&7bjM=Q4}zo*Y^Ab@axy{_y$J67~G?ZmsRM<1tt z*w?#y+aLOSWABbFe>SwteoFh%k2aPj>i3)7ZkSO{o5Ch{4)SdO8gpWLLBuQLv?ouS z$2oke)c@;v#VUTb|2t-4Qd+r(dr6pCdDl$RI@UKQ(eKo;a-Y!=&?tzYdj8fn%)=E*LjAF_t&3Jb=4IwZ=}UUui^oIM-ASidsE}f}0 z+HYO|oJrm8m340O3-SKJ&fRw@8A=_~lcC!>^p#nG-Xn$&x*5 zTFoDs~J(R2-Jsp~L3Xb*}@Hx_|VkD!R-6 z%ro=<>QlAj)2WsF?$71re%9`?Z04c8zItb;wmm3`*`t@TN8eI=!s^}H52GGlYi5ym z*fM%n+6T>{FDKXX5ANMRwDzxscMnx~b+y#ZFBRmkKn^o(MYXZ#J>I93#%cE!yMzXt6pH@;RF^-QZaZ)f&W$E}y0vie|abYqdz2s^XswSxvdS~;P* z`@O$n_xJ>rOpM$+@mAsCIoZ{%+E?j(I{th@wcnv5P7%Wn4|!;~l(Ri-XQVex zO|6_zp}+s}My;)3&d-fa4w&v7;`Vq+|J3kb+opQ#sL}Tr>e9k!Mop{G-N)WeE?vru zm~nq@kMI-AswX%+PJCL?ZP04tOSgvPbUE1k_N{HDb9Nn#IJo-a@Z<{K2cz1XUWM}x ziTdsRvnud|)!k!0(v6{nPZ=J+E#ie()}SZ4#~#-? zZ{5qSHa?`2b=L<@+1X7K|4P^M_ZqhAbxx-K4gOQ$K%+`n~$|p^8s4eAl0}QMb4@%zI!f+wCJS{AN7y zcjKiKDth$4*u!DtUtP8Z&e75h_v?~0^OUx((5j-D^>Ll`)74vaX**+Pg-&a$F2`r6 zJ9O#qcf5=32lZqNru@Wh|F`dTTHAJs$(bp&s?fEbK7HmnorMP0iB{Gvy4c!W*3{g6 zdC7ii=D-tnjO+=nCWJ?1ChC~?i_XEYt!rt=ej%avY7j<%6{Scs!Pwz7YCOe(fu zuI|@&{MfYrXXX@4&D;<0moW}jQ;cJkaGp67l}t_Ut$ z9yX;ctM%O(x2`@~(aU>U zxzpq8iE5o@Eq(Jb(Px)5B%4uNJ>bTf-}>9_8t(l$W{hX@u%~YV$CxBXdDZ$A=etFX zeN&m}HngJolk+P|oMYYIhTkm9{^3*3x-&E0)nrSOtMoHX-JT>(fj#7Ezbg`T|J_T3DtT;8;On)`B}>2KzLeiGNQ!!MV%%r~3uV6eIN(SqZ3lINe* z=Ty55+q~BwpTPF_KKV5D(3M`df1cNFSxMMFNqo-DH9fnuo>y()?Qiv&ln>7H@@|)P zE%)G|iWyDEK6}$`lUL<}$@j9%y&p9xeAB^dTDj1s_D-vL<(m4f!tyH~%;^2q>fBQc zU+=o6M(@HN8hnksHpi@HXwx;X0!-FE)H(H={^3{p6IWQp&2q|f+46#X`gDVts2OtZ zw$JWiyR%NlJepWIFn+kYmE%G0A>AF?s~;Puexjtc!?9xn<2&CzT0X<$E8q5Q-G;q& zk{>?XUlULMNciPPHXi6?clXO_k9Vb>3x00b-(o~ouZTZpz378@@r=PbR z)3Vt+LAb-dgO|z4`?(3{I!vfe9WW=>%W`@+f!RV zblJMsWA~rW#%|etTR)-q>L)3>Yulb%c5?W`L32V*Z>V*j+kaO7RmWOeMLFxn?shEP zR;3Z9-m87C{m+rVOdok8WTxRvkA;uJj}?h4gT`FH@@7HrsIv=7?(8tH#8UVk?^|mN zP2=t?c6Ywh&&aOk)?%~bFHvcud+$7U!_Czp=yb-W_j!3e_63a{wt8{VtSW=;i(B{a zG=2A8+sMV|+f2Qqy}~VG`c%{HCwdsEM_UbPDr#?K`od2uMx51Zfc<2%^*Z9*pjQ4X zcp(+(r2{+|ALAz#{VF=v`u>zVG-uovb+v?+4v0Jru;UnvvCkGdq zEXuiRIrt{eCTB-a>%?j2E5>97mFf*K+&Oh@&d93psrRgZSOWK^2!sOo_Y6|VKZl*9IiP;_{;KB<8$oBPEDKtdP%8f zZ}Szx#Jd`A-rp_cSNET_**MPZ;l1LR?wP*RwA=ZzczjIidwc_EL(xU#mH(tE3rBcu5 zj!nm#cAh=9Yi&E9Fz~Wn+_IjBpB64U8|pA?{^JFY&pirH)j8Ef;u!j%`JBW)GsZQY z9#WVW(R|wDnBb7#T17es4J&F|*#BjF-3~`yYy4T=?6Ab*$jR)15v70WmcCBznX}H) zx7K9(Y)Rm3v$w}RRyYq>-(}8>maUm(vw{i(gLCIbc_tdpI2sUTG{{M7#=zs3U$6FP zKE&u<6@PbAAESaJ1=SI?lIL!fk+0j`_;@JO`u+i@+D>zpe$sXo9P&NT{+H>Z9cv!s ztlaF4KJheGhnOPoH^xfx}wCqsos9oNjbp-(NEz%6zru4gZyy?&tJPrBiMSG%Irp z`wtShj&3bD-XdjHslf&RuTgIdboMWvv@uMuR^To)J>124*}lX{IE~o++?Jl6?b8!S zh(8JP^7g`cYyXJVXc2eu4~gd_tvE?fw7ImL7dc^2e-XRAJk3~89+Y~s`Ty@pHGtcA1xs%$?aYJPrve4E#MZf#vN;Md)D zyNllz|8&jag+%wai2FI$Zb}CBFf<=i9{i&6Qt0@HNq65KdTrbB{4b`>zce-2(6wZ; zWwGz-XVbgvHa}C@?%DpqJ)Oo$b4!hGA1B-OI!qhdvB{0Rf@6`@CMi=&4R;z`ocdek z-u3-D8m~2Qn_95@#PG_Oucu~eWVaZ?bej3gk&D$ulH=yVx?}ekyzzXZlRPZ+O-ltl>H@+POhi5p=>A9!UL*O{${^mp1)@(hLD0*4iY{aFH`?qHQ zaL6LKrr?GCkl%8H-+ihWGVajWmKTag9LSq9&13bJlhPHLg2246hjEkiTFwlM?vkw* zH}hhJ^Bdt$IzvzF%s#1I=~=$kJZ0jn_v++OAw(vVxtGle3iEE#@ zC1*+g#q~ zz2*5j(W+$|p9hv6X=NVgF?q#_f-j>d*JSUz`##^<^wOTib(MwZ+7|P|%;HP1pEPmW zV5i$VH15QjjL}@4cZd%v)%4Eox7a|K96SyPXZ4Qb(^2aVWl>Soy|px1*t5 zb)OEeTFp7N@wo2ZEAJ0388Ek7;kGeZBX=KgSy*E+{e8B7SX6-V)lZ)#Gva$6e=aVw zz5nx@zH2VtUO)Pc=khZH|IGVgYxeGCwO_BVt!>pw6h2Kb{z|cJ#ElNUj}^R{JbL=f zw?Dr=9hva;+0uY5qvJh&mL&++9FJPE!*G;c}3XZBv%_M`2q+*b)&YLaa`TL67}%uQPP})+O7lwlI;tNLt%v zR?H^7W5=bwj*IFJtA}5*xXAbQv~a#6)Ug|^u5o2dw`HzV+dkJjwZJtu_<5@RlAV*W z?b*|MaFevnBY*6Bz{uvNR)2?=Ib)uC_IJ5Q?p>K%IOMi4e^Y){`oUxCZ0_-%_9!`8 zbme8r#%r2myOq!Qw8`sX>~q)59tZNLq-%$7>a-{7xljA9w~zh0`B37lee-uL{Lp6C zd-B`D<}m{|^ApS7>y-5#x8jeW`GwQ2SbaGBzUimnzw|AvzpV7yeC%MiaZ{If3wv90 zEe7z?>P&2 zi|(wrH2(eb^$X8?t)8dxT=?)x-U91Gg2*dp7q}dnT5w}qEB|p#I{eY&^T3T&{wIDr z^!vex`#&Xj+GLdA)jhOk=lF#qZ>bv-TyVcec_^zAoY<@Lt?tAZfPX>Rri*8%;NH^&6=yp$v?Ox@Wbszoa zVbwf zmv>2AyR2>A*Hi0P=B?D*EPmu{9bDL7dZ1FxuK%L-cNg)BmPN!DOLA+LNgwW>bHvB3 zYF)DluI_&YKWKgbSN(U-2L3wGzp~h8bcNN#9x-e8bv(C-Kf8tT*<_8UAxocmMohHL z*w*s1tI4k9DVKnBKkV+f`RawSXJZyPhg;rrH(b3eYJ2KlhoY-xZ9lJY4QczUSG&Lo zX@w*0I{#eL<W-Lb2=(&J#jPhM-%cRi`;WV!aJG}P!z)UUlR)h(Pf z|9JVVzyTATzIw0DcQbgBmpj3F`mK=_!TxT8CSUIxvRvnBXh}@BpH466+hTiYP542V zt2GkM+p+yCcrAuHZmJXKPLIF-zBIds)t7#AX12PxsLhf&r4pxG0}9t~8Isa$$FCc* z_qQ5)+0eb%+hWE(;j7@TC%bNm9N!}<%W(P2QQh_(f}Yjx}`BPq%j*to3gyt5|U zeowYq$TGKw5oeb_+>^~8>0C#4)riywOw0*cHtFb?v*lIe&W642dUn#o)@LVG#h&d| zyX{<6kI1RXF~YA7c1JFz>a|o47ar|@_BQ8^Ye7Eqe8P!Q-e?LssQ^eY`ev znzZD%*Si9jZ=ac}`?9%>SF42=ax$~*Z$6d|Ti&%oJCdx{wS0Q3@^ptO2STn4y?Ag}js8Y6Bk|hPlB;>eXY3|N|%zI)WjaHBDU$MhG> zaE^Ityw^N6X40->ceNhtJ#>$|^wxPn&8=ZepLSlA&5Jo>R~@{xqW^x$o*GBk+19Li z+wrGmPyN>n;-9S%Ub6b7qS=0}gQ@zf?c8!SYXVYM9u(|co?jK3wF8;rd3YG_@P+KkuN?+ZxN@rQN4@p~4=*{t)NOdXw}#t?9A30z(XOb1)BW~3Jsx@U z<=djf`3I+&FB!Y1;PuCY2lEcOJ*f0q`YP_hnMowK#hXdeVOJ*l736NMI^hs~deq^e znV;@I+bI0CFlTPLai6FaTYTfnpUsZ#G;fkg?9iA&hkK{~nfK<;8)grOM~n04__nIi z{o*6m>vU|8=6KVQL67R@{9KvcVxo87y&oPKWVY!$CH?E0ABA62$IjuIOf~B>%q(us z`w>>HJGgbqJAB>4`O5~ozTy7!u9kF&G6b$*$}drdO&tq=;@QlJ>2l%xzyoe$bWe`X zJN}}tdtK~;B&m_%w$8JU%)fe~Z1?OPBNg?WuKQ_S^>@502RDy{WBc zamALn%k8&rjof3{uHC@I#rdmZ?=0C8SJ}R3>#exmhBjx46GGnmCV57!9;LS7df(Yi zlb!q{M%B#JC>9^@GO$I|q8&d!pIBv5HL)t_^LTOH+PFWjK3$uCX-B`SuWZi!%>V3q z@WPH?jXP|7qSI;kMAt8RHcO-XdSr|G2ef@zDREm`)^5t2sRi%dUsk!dTj8^~`gymg zo|;={-g_8jl+wneWAGH$VuKrdmZmiOZSszD{5v;Cc&yuzYrb8SH0-w@m+zP{{$}9K z`3}|oFC>}M*EDe}9Q9i4Wn7x6ap}&DkaQ;Yn>|Y*sj>Q`cUWC>t1FUU)f%mISrawBAeq zcY#g5L9CL*|cyfiJ|KRE`ph{KaJQln!76B6;DPMe0-9V)9{ zdoe_(VU;pOUnPh%#g=V1HHI(*Esj4e0SF`_LU2_l8thd}G})^}c5Onm*k^5`!(Me| z&w7+)5YK=ZvR5Nw%wC%k6ZYCn_G~Ik$BgEKe9c+6IT7M21|@v_1Zo;Rd+-HH&aBS8 zkog_XO!)tt*%)Ugr2L=fOf=26oSBgFkDQr6IWsOb>;DGMd^pTC`#YQ|{r{ZV7-vfV z2XH2u=3CB`{xfI(-!x_h>hu^35lZL}L$InemlvafOg9h_Z4uFf41kzr<+6cXNT~{D z$OgQZNo4gj#qGZ=mK8$ndtzTj*m2rJ1Cb|dJ`vGxT%>(hC22$?s!B~MOzkR!p%B81 zPXw&+63VWP3P%Vr2`j{!5M7xNV;~fD8Va#YO`}H@mW0rs1p+E%aZn5-enNUyH^}&q zhQkJ9&9|tltK%aU;dh3kPIS~sD^}ZpVucEV&cP{F0{bAUYtakUO6-H!M@{>P9$pcB zy&Ujkh=%+LA=EoYCnh8%r2C-BJIZeK{iP|=Xb@u=n;9?7NK226am9$;B_PnZPjJLg zf8U9GBf>T%(TG7K*y0Qo=W@v0C6juRNHc>>S&Z(22!lIlSDxeIbs@SW8vQ%k$G8AD> z&rC>m3knO!jE+s=>cNENc=&Sk2PHwN4o^>wm5yRav-cIy_?(y0Z!q7T*L4PnV|0s)R6B2|*13BuNtkQd4P7HZo!v zDFdVSz(IZ)adENr!-i*}Oz5`~uFYNFoUjhpw$=aA~7q-i5VGw9S)eN=LcKbV(x&?L5> zO6Y|aIVmPK6J48~?kc9L55jZ|Mk&cV$$sAWApNu3W^@@qF`dB)Iv4m){ zw;UyWNHzg=EQO;t#4>H(KA)`C>CH;|w@O4zM8r!(I*N$5i1>&|ClTo^BEBNxCnEkL z(nUl9L?lo|x{63Q5eX8J?jq7dM1n;mL_|VGBuqqlibyXJ=`AARA`&4YeMAHTP_HpD zW5ju~O;m4QuC4L{tps7}THmLm%A=xjJ95(3$IAy1@;dhLD^GKw}EsA(=+%xG*#W~Yx94Fjl-%WR16Xo!t!c2F3M8(E9t z^pZwnpotpwhfZtMNJ^}_Q=cM^7g5%F>BxMkmShTLNu_$YH{6JdSbwuhV=mW?8!$>o zs9ChB<(1pm_~S*@G^2V6$VVE)tiQ%2n=hHk$}1l^Iy!-`oY=f*F&bNIYE>@-aO^+x zXMLJz6y!on^Dis+c8#=)O8s)3on@VWF(lf$m1@}=hy^c)Ms-DJRWjtwtwKkGcJ}(a z9JpwWwWDoowj}?B6-K{*&RBJ%P$_}DCI?7JB+-+QSn3R?jxkvvA#ub+LPn5<65>i0 zNk~8H3?*tb92{*Tk`QO=xKO7pQJ0VwM1#H%ONc#nJg5^*osrZTNHitHjx3iDcj~mE z&H(BRqfQ61LP8SBDhY8T`4ZBOtd@{?vWBL&RzhsaItlS48zp2g*+dE2A|d_BRtZU< z&JgmmggBCI5)wrnTe5>j+bJP~$Sw^sjqIk*9_s9+&OYiCQKy(XCDbVulMJ$-ItQt9 zMoh+#L)1A=opWN6Ny@||n_Lo;3DlWLA186n33_*4OeWLE$>bt+E>q{Wm`ovO#bhdd zF_j?PG;)AC<xb*@t98g;Hy=LU6tq0UX}+@j81F_}*8h{-JK zR7a-c9_IlhV?qN%C2^dnT)faY^3r$3eScr&pgBhhqGb9 z__$WkUV!UV+6Qo@>&*+;XYfDLZH{{$Sq%RetWnGJNe1JIDKEl@Zh6>Z`j7PzB5^!P zH1?2(1w`Ojh-ZeO>(pxOyPb#u#+AirfmCUXCXNWcg@~BOq7)$rx|}RZTUitx#}e|V zBU3~kIqF!JkT1Ix8R^mW267MzQ8Jd{4+={xB^j?~BR167r(l(b2So{8cZHW+)CJr)7DZAx+3xbo}$AHMxMaF&N15 z>We2zO-nV?cwwTqnI3N=5t*$f8hSjR0-`JOF*n;l#O8ubpMttS&9@^YS6R*31^(N} znX^;0Eh?wypTFei`i18w3Oh;nPcoPT4 zhqyAGuvFC<)HslQSrO7u07^xNf22Yv*v=Bb->yPHbprxIK|mM?=m`RPfq-xj5CH=E z5L>1%i0DV0nEoKZNkKq&5I{M)Ijw;ZfseWK;SEI3+1%Y}3qh7Sd|pA_?<#G`_7o-# z`3xsbn0R8qBp{#R$Y&%{g5;7FEEmHA5k-Yqf&9D-DV|79?a6O(u)zFyly5CYx9=6G#hYB5BV| zB3{g7;>}D!Q6V@LEiYkP{vt@jejXYBx1>dSicSy*mONt_d7kRrjdQq(dZ8+5j*{`UpL1{9I|sDTITiH4)0u!MYNl*6bW-8y8JqA<%)nB^$U3KS-v z*f6WnUTcUu18XqMIuvF-31T*paAq@!WVR3~#@00ECz8d0v8igp)jMN>!qL6LjTk(< z6J0ZBxdz1T7>A{tCF7^?(~RZ7g6{ltzrKwOwI^qxbcEpwQ( zV~(J8j}kxT7zt*Mlio}v>BpQPLz$B#hB-w>GN(~7XUKTwESb)nBXgPaWEsM)W-gHp z%w=!{^ezQQa0BHWR2toLjo^k!Z?jdjE@*9Sg7kAVW5vZ+ywqmMeg=bNsLOsv!jP53 z7B@{8a)iwD6gZj)_A@$IPr^l)A;pY(zk<43%0adzIs*P$>Oov^pT`PI3P@PPA1rBgcL*;682y^AxdH+HOK7oMpw&CZa~hwY`eI z#sC652Y*#r27&) zJtE}k6HT50(cu}Aro5)af@eZn@S2fUJX7M%Gb0^&<|L5UoCNbMNCeLk1+gN7cs4}J zvnA0ys_0;1AS=`^Fb^HXN7DWl;(XZ0)SML%&X6IIo`Mr3!bD)s4tf~6)iw}qLp9S) z#L(1GxQV^rNbGUHkytyMnh3(3O$Eac6TK`P7lt~tlNky-8Vb2|3+jGSa+N#XaN)V5 zKy8RR&y$$&{D>9LpLFJRApyJqv_>H5!|Mv-yOE(hFEWDH102>81amUGA6kS8U^|ui zMKOZ;-eyh^1x=jHnD87PQ&9KLkq=H%X3Xj|yk11i>rJ$I;l!91K}>PA;`Jddd3})y zray{i7qS{wvXdh#R-BHKX)8%Lrz2!qh;uRdh>E+vjyAG0vJU097Z?g$ka-IiLxH=e z&_sBG7+}o66KbV|QCmfW<_Iv9+!ky{T60$tBcwGq#Icy+IkVlgn7rApBDT0@AAwpL zNkqIvqQx6U^m$37IWL)5@4E27ys_ZpOf-EKYHl1E#mh#a z+A9>J(P(z6HvYix$TmEm6P`{Go>bPIAZX1}M&KzlHGvTO;A{@!ah*D>BP&&}n<}}I z9U6E`36Hl7it{?`ym1xXWATOV^hF=HXI%JPor>sr~fCs7e(-L}bEh;kDiE7VI zl$2uf`UpwG5bIPJTt+5B3I4qtaS1Vl#yJl0sRGYRa^BBinr);BZ#&v<2QlaEM3wA9 z*nVV-Vvzia6`MXNF3qnQa(Eof3X|De!&1z+ud2E`RNVB?On)i|B1El#7X;vdm%%2r$7JQTDBW0S?*)%nf z=A#B_3T&Y1*b)sv3!*8YI%OhRhBWCEC=+;{;PdOD;-2lNbr|h`C@6=$K0!1oKF1Ja-T*VChf;4e2BcE|w<bOftOQ^7h= z`6H-Y4=OhhZ^0(wFW5|a2)3xynmYJ<11P5TNLWh$ZXQF@PzW=!{ksvWlsv;td}#!W zzscdAvU4jVST=}Yi^`+jQX(6B!JLi8GyNtWWer)p`vU}2#5b0o6{=6Q3Yz1Ic44(< zrVvn##jwW2tkD>brVwC_#jv5eN~6_`v9GaOGbe@hB2bN$3LufAlQYKl#`0+Xw^2lm zMbT0SI2*Regn;oTf$?7LB@dMh#f~l84fu?IC`V2Hu(j6LG4%TKU&LEdeV~p@- zIGgpSffPQf1VRM< zYk&x`eR@3h%dw&208FH_CAPB~fk-gk*i8!vjp!W|5RP?%K%hO8MWB0t@B~46vK?Vd zIiiAYziY`ogOjB()PrIr1OPxFB~=83T3_GbJ~Vkb6oG{{$RFy%7gP}t^$-i~R8bD? zT6w6z7vzvtIc7m!j!I|q6-IMgkpBiqAu*P2h-2ANJC;I#1Cp>oE&~cQVSSX# zkBuQ3IK+Y$a0~L79I65y%836iph1a87AB;HN<-1o35q@2|1t0dGj>N=208$&+FzQc z3M}~Fjc(9ZW*?ZCEXCF_x2Mk%^nJX;_t6Ym$^=8C>42CtS9;J*?w-l)cMudy)&Cy; zK?Urf+_weWaqtm#|E5a<3$m6{xVj1$TG4&$Atn^Q6!&1mQpzTx^k*UMa;~Is4Xf{l zQnIQF_Ch5k4nog0c*9&2aB!}=zIm+!xeWOKSm*bVy_1}tO;E$x>egz(0 zfJZck7_1Z%2qX%vF#afMW@FHX23UsgfERRAV=-=}0Xv5xCnxo!aDU(y;#v5T0@?6= z0E1ClR#J3)YH|{QQovToG58p%R9~?BC;xzT5RTzaDEt%50R#Sc{3h@rU|AJ_p?5r? zewlU)tL~3W|0{R~5$Pu){Y4~FLdokzpdNpZ%MF2UC@uPi5`M zH@J;e-pcy`8UdTds_`1IINqAcHf$SkDZ5pL*9I!d$~z6b{~N#;swt~%ZMW+f(HQiR4HJ`hzNE|-2GpPCoh3K((L~T0HPeW8UQ_5+P=kq$Rvgw>A}@#Lr{nkqVV63 z|M>62dHmaOj=;ZxbNp`udzjRNTBtA!IJen%ISDVDe=njOc=0y@IGjmSRRn>ex3LJ5 zW{tlJ8bGz-`cMx!pMoUg`t+*2`F{kMQV%;Khgooai0xPnup{+?|1;FYt?@=t8TAy6 zD1(=@X*`A;`0|}`R8Cg?*Ish86YH(gw*LhTOJ`O3lybsLrON8pBAd5LK!*6+2Ynm* zdp`9`#6c)o9FDsxAsFHRfdU$W;$ciBIM8S=AwJX@PMr+uM3MCpfD_BX#1blSrL$WDf=C?4^(yI31`nhU}wpizsBKm^vjA>_(SSzj6uj zBS$FG<|u{!R8abkQy|Yt2^mFB(ZHvvbA|>!OVKwMBqWtwq>q;*SP#0aL6(p!)VWHX z>(seHom@47!ViSEg%o6^MX3>#bhD%TS)$(&Y#q&g~Kq^;fcv2Mo1lz z7|0HUTSBU-^MpFTQs*soK2qm1b-qyND;#K{aB#wK8I87_IxA?FD;PB~S;_FlWEJ^K zO!Dd7YKE^#){v*vd8J9#l4|O_qs~|A)M=7+ujn?c zKl%%<%}5Hd#3Fll_T6NlanLL99Kk;SjAwKe#q(A?qfaTGHzK|j(vioXfM*+(=ZSc3 zrt&-qt%vT2v?;8j09T54K!-vKL3lccC1a{qXFHZKF4)3h#6a0Fy42C3j)X(Y%mG?v zVPpx@LQho8IGPqS_Qg!g5~g{%t>&_|L~L8in6IR$8f7?jgtIMX>}26gu_+_y=xAHS zs5_b-A}fj*El1Powj=V27)@@yEgV$%0KP-8EhGS^Yw+4*AEpELVLU+ZYOKGmLAPE< zx`1(pqDG?0Mhr-s$Oy6-J#!0?Q3Y(tbYvx?tEY4|6Zz@!N|;v4Tv)&e*@Gp(y=;so zvKTE8gRYxuIO=FN8sz{M*UH!^6C+V{4 zeDIA`x#K=Yi*A0JM+#zm))ITg&Iq=__Av&iBU`$VznwlAdI}2ao)pyGkZsXX;<)W` zEr0;Du{Ewobg^o01_)eB#t_@%M%er}Aih|ZrTdC&F3!77vpO z34ni$Kp7HAXJ!=X!6cC|CK(47Mw2Kel_WB0z`%?_HA8|cs#%{_Goy*-p?gyW&JEZ! zl(s(IWCnFEXnzIT>jY^-iPtj}{75Y5os0qW>Ae9hnR5|iz+xuYA{NwLP^qMSj0S2+ zb3HM_KC~OH&d`Qg0C`bHRzw;KoSaS6N*J$j8XeG>B1S`{A$tRrNtirzg*oU73s4yg zQ5lO+8H<6qSOVpSx&(t}#O`~noE1Y=SkX^eblUVfCJ2gJK^qqk32=1mldOO- zC?I{Nbtr_?Ve3#x6PNMvYL()1yif!_E(RZ$fR9U2^!)&u8~_iL0cdg%#Xd|10tyrh zC{Q9(K~e$n$w0VifC5=TWHcPV2cf6}6tyiz?=BR@(~Th^fC_Y`C>|@^_)-+lx==>| zda#KBw=0G4bO40su98cQ=&;9JMWb9JhRk(9&TfD+Zz4x0MX&0OoT$KUNh>v2Sw~KX zy>obVX8A-+Ar%F6e^6npp-h`Y)?t4eupn*bAu(a9h$T}ENZTW%54~8{Yq!Y+dNc&O z%@MLB82i%bzA}+ZZ@taXF}>H47EX8qj0p~4S$f<}G2?lJtN>1u(r^oy0Y;ZPI@FOk zQT>1g4a~)uZoz>DB7tq6h0N(7&2GptzY}%l6)|950|WGibOA0X2)Lk7<^%9cf0F*d z2Mqu|XdtjbR#Zpl2nSYFz;fFm^bS`+Oh=i*K|ves*!Bo@*3*De(T~M{m~JF_Xa!%k zSttgCZ5PThY}0s(oMrJ%)PN~b*8|qnSgykGk&200G2;N;1h;y)buL)Qn52o!yqL`fNk>(XFV}}a7GB_XzK|{O?aBXYXG9pYXYo>Hn18x#DS+v+5%zZ z4TMo35Jqcx#$*!^M%(aiHxNdpKp64pG(|C5ZDWN3z@zXd7(6f{K;G|RniQ6iRgsRS z5aQm-d|CB^XNi2Vy$eJVkIr!vX>I>bTH3O-Al$!6%N}VtAT1}z)@X8Ek=6+|EpO08 zl?k8+8Qqfr;*^nm7E9sdSju2H)TRCn$qPV|fdE!@MN~|@6iHTolcWxpY%x7H4&O_cck}Y=o|r~Qiu#O)mot{TCuvKKBP0NGvW%g zltN*Am_tD<7Zqv90915bVo`8>5#jOGi5_2r7+{)e&X*8teiP!z*CK8C+N2w7YfcJf%~(ZQ!;FNBP5q#U zqAj3yK|%c+S5(z(n=<-LPYmP*NC8E5eh#x-*WlTi*Qc=D%GLoN8 zGWZ#aMw^au*y8925B!Bcwrt92ifIYcMmY*`)4l}6Xn{j5sxkg{o=79uVPgNi2wIKh z;LsokTb9K*3=!aateW3o0!3&>E&nc_2E`tIJCUXF>>I>WEs?mfjEof;RBM!s{u!2$ zWncr3y$k^B%W?O^UPfElQtT{WU`fORD+Mp@z)=UfGGm764~O4bid7tnnOTN4t{zw; zS&nNkt}Ad2!F46Bp@^{x*Dzf3aqW%kYFzu`x{j=6LK@iKfR9o| zBu$I~JY7UGL}ZK@%sW;@GR0_kT(dOMkOOH-@syfzH0SXmk}V<=L}Vh=f^SaAiD=1# z(&=_K>#Cg8r$8WuZ50lpBs;_R?HM}0G$y9OLFF)|Ej$heW2o4iVYeb=?~!pDwK!+j z*fP56ewZS>QKKQTEu`2r7bQ@u0%39Ap$O&Dgr&c9*7g7mB}Wvgp%kpCJnVa^Y-*@1 z_fu$=3fkto8&Q%(wp!&_p=R~A%D5`$-eJ>9xwFUa4m5ZxO3R3%Uf5lxMsZZgqP~c( zFiE6h^8~F$3(1*9=ncz+INHeC)IlxPJL0HYuLID2L08^8&@F>@5*#k=MVI8ECvnCm z*mmoalqR<^5|%;>fsJzo>O}$&9;_EkmXIlAssw5c638LBA~IbF9?gFLxG6}B=|$+HE6QtIZ&5iKk^Tq9nLdoQm{?P z_VaJ{y?}sYBY9d%u&g&oB0rb$y! zkjxGSS4ZD49^P|yI_mFXEBauQl+@TU3CS60Y)zs@fm;b>4SR6roEowHqArgDv>;7e z7E=>3d&&~4hj&sfN2BIR+No}t$xF|63YAWx>Mz@qRM zD9kOW7!RpmXR>Y824j(YvlSh(o_PdSda>b&*XG|XCWeE)5T-7WQw+v^20abk7V=FO z$^v>Zd_3^v2(d_KgkqdLVCbn39xIeFr-j)}cJ0H12ylu(13sAZU^|bCXNPxGvTHgz z785@3=>kk?s0g7Exi$L;MCv18u=(fBzVX4tf{Sd4sB~FHy^II}<5eqH#N@On9BZH> z%fA>Jr=Zahyj4O|!oHU_-FNUK{<6^1WR!G8DMTSy0&ibH^qu!(O#~AW+~l*UEO zkX3cR77$A(?qxC2T=~uWCpdW_uLAab0MsTSDMvLNArj2%4Y87ChI@NbiIzBliaAaK zpgc3!CXo{~Lq<>!TCWlupHm?Ur+zu2aCD6lh4X1jqVQ5dlG zS+)-`ld;M;1;-z!A>j}bDoeftDu{Li3&*3xW+l-nBesZ+6N)Vg=&{b0+=HDmF}6Is zc8CsfDNE!KC{#lt8Ojw7dv>{j<7jZ(ejGj}p#{X5V_i>y3(MbFhZA5e-9&hj=oAob zdbR`%#70FxFg#}a2+4B(Db8mV(UJMc&-Nl?uFJDiEu0t=;^deJr^d8!YD|aMAFC3P zIFB~~D-#2WBX1DS;|<24tRYyH7(fQ|h9NctnXD*7P!u|Gcg5agF>wqjAZExHoD!;I zVH)ppl8E>PMC4LToQ8G83zluFxP=2H+wex=s}~1o;z$ckErqHBl!hbb6SXl zR`ZEHZ!r?(=D~dclAycCd~9&J;Eq5mcfraA56Zj?cI zmMIwx<{|?%aC)Vk7`?bXz3ZTK#OR4Xh~ej{@biMTDb@!jr^FbQd=zikczE4O3=!d@ zh~-5S>8t_Amj?9%kW-AUP-TD)G(Z>n&Px^`fVEcD9oi>7bzs{Hs>XMOE;Q77hXdrs z8vjA+eRrl&Uti#~4Hea*zU)=#E2>ZR`2Qw_u8L6o_-4K!-lMt5>-B6Kt$H8?O$o;#zFcd!-!+tZdRDpA= z>NHe|m(Ml7qkCeG4N1h1|5(36xzZ>=n@(U=@=zrMB(TOLzc(i}ijI$s9!X8mCBt-` zqIwXGwOJ~n)5qTb-n3O^X04+zbg3|l_uWbDcU5={1{U}+7uA>u-xi~sOfbty8iU^+@@9go46JbZOm0JOo;}{}3KyBors{k8_ zU}Zh2K%;U3_yhuDbyOB57UM9@(FJEOh1fd6g~1rChP@-48*?>nxAY#ses`C&N7oZ%5?q$$FglrCogvo$5*cRkLbkQZbwovn6=LL5b82kfqwZt{3p;+^8G5?qS zPy*@C5)qUGjB0nIy}p4+3lalyOcVL6Auyj zK}0;MNNWX~iehkWEBca}R-^(Ap0Kaj8O9YMmQ=?8Wp8byE591f zl>dI7tyt3H#+~mBOE4x0hW=&<{!a(ce-v}(q`3r|55TA9q@@J(Uzc@%yW%7lR*kG^G`cGB-SPTwWRZ$!sQ2ZO zzmrkuZTjdpSXN*aYWABI=znMixP-O9Q25fO1h!V&>lD*?=O%$H}wu*gD8_*Nh(D@YzLL1!cfHpC2Y zACf@=#YBBwJ$XS>;p9a=LfSxDaWSJX!ckIOX)GGMm}pSZAaJvL%w-3;#Af)JcUq(xtaL7nE;8+i%2L0t=jwKg5p z#WET&Q_|^2-&1;ESc9d69^L+?hA`g3Yy&41xY0fUlUF%S90X$l!Rg!LcSohK4CADZHgbE)6t)moG^BJAx%n3hN*5W&bviOQ=#nE zBeFHo7ZDv1Y~WENazfb&z!vuMgBcBzkWD}$vQnt=-FmI3DNCL!mq~9}c}^G=+czOC z0hW4w6;$B#q!l+sY?yYJirj(#9qFa_1#1i4N#9yw3M0rKgcR7r^QuTUQs3gQ@BNP)Gl(aJ0bLE zRBt@tHZ1D#M=NG51|@vo1KbCm)Tyh($34_Z8+8(Z&1-d%s!nFA1Jz0=&6tXFDy#rw z4762x@uwGm2iXOaexbd;zx*p8tgq@fL8wIxHcSv3Q~rq%b?k}`^ka8QMD*65YTvA< zzwA>Y1O2F%n!O?vZrE4q)Qd}vyOl>^(?A3+1rfcGN1%6YI)?^hRcPUz6Trr#)0U3MAnP>l@sT zDSg7%*;oSxG?upF$uBl8nOemaMJJC+k`5MOAD%PNp%xQMX+TM0leilg*N92$mq6_`4od4nQ5WJQAMaikf-_$5WIj11Nzww&b$N{ zoNZ#xBC?993@j7IBv8RCO$^7!S?~!HG&B3@7Zr`GOL7MLRg-UxZgBhDYzFvjLhNvw zsfs0y;AHv{85NKJ+_Z)YIb3g&n}c98=e+2?wlhRPoF|kPe=lXaUwZzz@;k2*u648A{qvY{rcj!v| zQ3;48NMgV4p_UvM$6|Q!9gg8(6)r!7g6WHtL@8xIe7A$WW6?Dd*+XDLD#f)JaUbdE zNk7toProFkvjZEVH)8xkn3j@AKiwmS3Gvi)*@&PCh>VicHSm8TUz?Tk? zAcuQG%G6)k9r1*&i_d?u1DJuzlSh*ihK;)FlG-sCaPYcDordaHi?;~$M>h)9%(M2kp_h{TFWoQMn; zk$4eF0Mtwbi>svmXF}xc4pHLB>A@`TD7j4b?FALqWsOLTP*Iv?TP2)NgTYJnD6=e4 zj24Y1s*vdmf)Zf$==>Dhg zwkY~Yy?}ClY%Ilk#L5FUnzy|wv9gf=K&EP?H_9%Jrpb|6FD~lJp|R{62})-NauXXY zQ`Dca*R!!9tB`N2xRG*G`w|WkU~}N4Q74bMP_q=S^jj@k zsdWcU2^KtIc%nNsGBJf3naHJ17_~IflRA^Bxe0foPc2u#nL?cz)M-f!=nlIfMXDM} zaD)a<2;F)}q|RvSw4~pX=}MZ>I~c}jOP$Vi`=TYWppGSVtSQVAJNF^Pmd1q>M0U|V zd^kCr18WvJ)P}_bYQ7?eb4sa!iv2VioR;JOy*oq`fHR&PrWwJmg)g;hkxorov?Rx< zrH&Ky4t6cNQ9~9fdm6rD!tr9r+BnvG$oT=mn zEias8@(ac2!hFRVYUW}MxkDY;x>!SPU96$DF4m9-G9wdNKa01q@G68y3r{4T}}j4#i4p$YK?>SCLO` zXXG>LFg!u;Rzpui<`1a@0~%|nJ&pfG+V-1_qSi{ya*08~gHEh0O4O_if z!&WcW&=CN(X0e7XT&!V>8Eb@(fuBKuK{E!;8MI&!VbG32il5ermVT;dGV0mJMmR}J4I)?tsv^~Rh=lk6f2jPVQKPmEDfP$*ePr5eV*YmC9b<>>=YSzbl!(+ zS{a)t6Z=jgLn7o*{AD7bv(BNrMcEb{Mdk2>{FJYj8cK)4I{`-)-D@V>ImRGz$@)9wwbs4yQ=Et?(R&+ly*e(%c>q{cO42TUB zzJL+K!Ax*3ZJd$1OO)928BH#f_#hC;0MjXC1cZ%i`I~ z{Ue+rHE^QT!imzBI87RcsfzaW3^>a^Z=Q!1dI5)HUxY?^Ns@&V4&n@uLz1}d*mE=S zql$^kN@I^Hqr=8S9(axj8_WW@cMzT7M4@ZgU>_1}CcwLvTEh#3H!JK-o87l19XaQ^ zscZs*ve@Cv;Dm^6Ibv!&=6M|T!*~hgWk31`CHNK+@jXiL12!jr#1<8ssrUuj?^o;< z{}=lI51djuLJI`1fOo;hl)DsNz+&D4@kg^#^{jon#F%&v@_;JL`ubqfm(8o-Fo-NU z`x@djJrb$}7F1;Y;Br^#{*>sxRHsT(Qe~CI@u~wTxCtmSaX_^*F7|h!cA_h;QFnut zgYJ^bgP@XI#VpXrJ4Zd4%Y5t-$2(WgNP;o%!TYmPQKzF}i9x=UfZHr~omu`GXO@r5 zMxh|1vAsG5m&yxp(q$~XX^eyM1a-~uTA3~1(dBGiYynqbk$Gwyqhd=bu$47R?J#H* z7{qehUW$gIQqnl9X&Tjq`emRIey_xGY-XMfe;DUTim=ZtDe4T2yuId&X32UQ)tI2G zu@`!cOoDUKG6`Sk;(E%EWHg9VilW)7o%55G1V1x(ip)4Jwbu*JA26oigE57c@V*ie zYq8(EPU3#*7`RXUpK;#@+}nWre&F5?+z$Zv4$1O}je8L2X7;sWx}!PFvlI}C!&UBz zRZQCrY`uF>4rD#r*B4PAr2z z>s|89a+Ly*IlEsRjoKc58~ft#;7ILzK!<^pjqYNgW4SFp7P`!o+3-vQ1p&7b{f0o0 zqpK)`HC4t2K8}PB{rDCuu5IJEywon)Th&2tSp;P#=2ztX8}j}y&Kv!K{rDqr-9Wf= zk0a=CJyNLgo%P%pO)#Y)X&Mj5QhbXyAhUB9lLInsy4ZSxBn$+!N9+WmK&&5EY;pD7 zO_V&k4_6_EmX1?h9bpWh6Q#pdNETd$2TF4+@?j~#7h^#EBcsBD~} zGm{`yHup_&!IZ5|>x*;4{ZJ?jJ?*rMp@x~!!xQw6EE(L^#5#-=T>VuYL`s?LAncq{ zX2YKb4lpO=C6AKfD7K+ZgHs4~6uUi+VRynY>>k>wG)$Wf8gR2eL6x#e3f@30{d<%^ z>Ekej$V{D`i<1Zfa*>N8bfR;f6*k#VN^4x$UEvWC{9Iv*e%DUfa^%xETh!@sj0?2O zKg6SA*1RaccZJ%ER&M;o1!}27Wch!-vKk_|#zXAF`F*@wRG7Q-+LI zUPM#-zItUbG-HnDd_Y{9^VV|E;eY=PQi zh&2LZ0M!ETL@M5hoe@=Dx8Txb%__K5I6!OQnT!n;vDT`mJjR-5uqs1e3y$S_S&ikI z(&o}CYRi5UR(M4}bfA)D>yCWFb_4Wy%ayiO8d(BJ*51H@^fw`}Z^3iR+c@|B4!HL& z9jCoVL$&we1my#`kol0NYkb9HFvbds?1LbitNo;`8AXpWow!AQ5)SC-*$1~WrF?Vy zWS9|R5`5 zHuAHG#>?juqyU53iS&_;#ZhA_>H=1zf+F(Uzj-f1D6So zP?0WZhRy?nkub%f3Y3o)#hNgO>w!Iyb%C3cju;_kFzNL$&0*+Sa`4z7rX-wPjVo83 zk3(S=r%(zWFW^Es-NG0KGpZrnD-Og}d|8V;{LGWmpKIbhGnZxGs;A?_X08v{2+Y~J z{;)6X-$1ZT754SZ4Me>B2!;dU;5zd0qipL69}FYBxrKU%4Q4J*)pm760B*UwVQYmY z`YA-52KdUhZaMNwYFb%bGG_ArtX@POy_o#^cwBs+fQ#=FVMbvRbrpE#X61@wfUGH4@;k!{41T4}&b883Q)hHI z9aAo1Mqd;*E-Jra4JD;rgb4P}wJ6$Fn?Y;QRxrqvZ3X;h_N+VdZ)sx#gIrmgQl;z9 zl2`u+3?4iOlM>HUs{SI%{W2^yyb2=+ufdGOKdDrIQ%xsNEMaov4>xWA8q0S)kk9NaCsg+l=H_k9TkmDZ+=B=Q=`)XZo_t&A{S z!L*@#BSNE%6q;zH(n&@dl^gA;(&#|dMn|eOy3jJCE1hq2qf3qMbcNBAZZUe%YNIc$ zG5EF-)+_A#y9yP~+6`U+p%b|Jzz;(TzDJ6@7Z8d8+>q9aGQuVX(Cp#KJQ6lnQycVC zxKqPQO;*^tBcCSFFDEp{SR4!=MT4y+dnL+yHJsvHLp|~AZ=mBdZU7ZUu>nxLoGB>h@W$obCL_18CD;VXJioh{DP=$?KlINBbTofOm)mj#y!Q|#rNxPtW^If&VFBVT zxCVL$lKU<=@g8+FJ^(j9qAcUza1rz=92tE^LygaA4Bm^4FKL4D4NWt?qmzyAX^!y& zRT)3hV&f-TYWzZ%7{Aih2*1%d0;W{PIGAZyelW_T;S2*cKO#=8?@o$u3Thv8O@`mmQ*@%}w`J{`NQVV#7lE*WQ0-h0+>B*<4XAJf76wvXWu`~?tqdi4b;F&;EJQHcA zXEM$9Org1+sZ`^cMrV1Z(>b0Qw9+$^Zb100o>|}qRx)iZ5SKu1X%ic>u0}?F*pA#E z^2ftswmH?=DK%>h))(CF;BazWV-zdEZ7jIj-1h?kcf7Y_DPcW)QhH!gD;3Pv;8gTB zD0M7MAPB1yTcKY1<(es}VNV%4l9Vuv=x`TlZG=NVbhTlekc~jus-Wgh5LE6LD(D3E zZHdm+ANI^f=Nn)zbDf$!JXy1>KDx}a zjjr=-r&XREv=;F;d3H;wbm}1kd|{1;We*mm1~I8IAD@x&|4vmDHb(IdsshX2c2xv0 zO=*rSS2K$-00jS`IpF`x+(Z}-#%qGFO)?ANUDWEA>w{B7{>7X{odht{@vp~BDIDk* z_F)*aA^;Kofl*4vtH@J7Jfv!z8mo-M@dLh6(TQQT;r z;O!~nOi||#xs^{RNewI zykjZkEu^rwh}wI{QztLFQ|}}Yv>XFAMG!Y?CdkAUq{IeGxj8C=b(u zq3mPT6Wzulm^%9*PjOTZt8<_l^0V|*-1B15%->6EX2=hzvvZmt@(S{JSCY?rB?Y`! zQ!DQ^)WLf#MZMP{-$hteRr!92e0ltMRZ8kQx|}f&(VXEoeqj3gZScORr} zKMnF8pi$mOXfmENya#EP_c5yQK2FQLhv+=-6SUI%G|Ij(He}(m`rO*RSulfF5tnk= zUwJ=8+(EIpUG2CS-ZY2_8rmjiK#u=zCR8Q*5s8-2>o#-Q6DmtU2>$;nVGM6Rj3myB z3zJM@qDRCY)So3et#Oj$kOZ4mb}rmdI2XUn1)_F(u&>EqkgQ z9eq<7I9T2wir8*tl5aB`6HYQVlOGS$@o}9Cdu-5YDwl_$)sPK$JU|YW=Y_+=u&<@Q zv7XT71W@k2xfUkI{JjUuViJ}m17T~o=(URG^Uqks{&^-x#HZfuUwdC}FlS#lI2&_5 z2y!S#kA!q6O=C95)S)4vBR5ba5|UxzX0su@%^=v#c*q7ZMn{Hj(TId#4Iq@GB3ai> zWwi^PyyeJHDb+dq65A)1VW1VhB*XlQg62=u%>0>Jo4-=3`5R@Lzf)K97wT;u!O>or zue5X;Y?(C9^3fD*^)0ko&~mFKt+0~l3M-jzv0Bk4t2NziwWSBGcJzpqLeE<5>1C@U zy>4aDM^-2L*6K{ZTU|uZ>MWA1ZX(s{E;6hvk!`gQxfYu_F-=g$t-Ono-M_8d>2tKFpR7BCVcjK~S$B)}cxGDnh#uCx zqMx-*46&{iW2^_HG#o>AW;B)^zH@w8Owz7~(&80bT>BCkgLIzsITL|Y7@39J`?S>A z{aU1N#9)S9EPuhFtr41nagQA?Fv$5lbq(7Rk@_eYO@(!SG)BYKg~&B=>tE!tJ|xrn z2rT?JC0U<vJ@*|IkqDOAv?C2X-^y$qF|E&4ov*YwIV3pMy0F)m-|a2;7C> z79MY<7OI|_2l4q?@#4!J0UvIZ_;gD0@pAA=>?0}+UF@8iMGwPJFSu@^qc|a+R_5w! zDbqY|r|AgRF?Bu`7wm=+auMn7an?*t;bgTQTSM29^TF}87nUL}c=V?N*`4evAT=yp2=Lk|{9tmSek~Hk*^NkvcLvk`ERa3)vsfLy2h7aIJKT;lkU4d< zhacGH!@75a7~g2LLUS;w=o~vV&{NhoR)zJ4>FNhWm%X%Z1{_h(d_Z))pHa%?63H(* z4@=TmhOti{+zo5-e1ahhmgCjM#I-o(0PRETaJ-|6{E63{%75F?YZtoO=v_GP%9|M}%3t{|Q(XlNC@dd4QRO^A+l65mPlVN;&3 z!&)%WN};?$4t^k*AHd?_v&{0R9cLzoTG^E)ORE>v!I^PW7q6PdOUV2O6Gs&k zPH`?=1q+Ht6iywPKWlP+At>kjSIvv7(`#X7C%tqYPSfV0RLPU`hfNwWX4aVeVbf+z z&o3A~X3FG5Lq>+W0@{BHdtIKps7g9#b@2#yF)GW;(w6{ZdHTHSO1=b=j(c0>h1I1v zf-g{vusV9$1C`Q)55~jiVHI2Sw8ML<%8G|~vX82Ac==+)jaMaG;VDOzBDJ>!hA0=} zj)byV)Cx9DWBhQU2?`+?EvSOepH=eR2>dQsu&@#e0&j(qy)l~jj#*h*adlnATttoF zeL_t&E?V;Gog@tb*pIk+2Br5FqL%U`(oR#7Hzk2NnXsT!`b7{3Jdh#LwBvFlCdYzM20 zd&4A`QERx@Q#yY%`ihWzn-Y^RZz)U^)nQzl6%DyhPyInbEeD3&T+>~m)0&$uyHx1^lm$coy9&aI*@jiO1>1{-ZU{##c?LuCAQVm&<%mIu4ou-s8HaxvuDtQuEFsJ;KrD@O;W;6tO*fY`7IEhI8p$9g zE*1z1xa9HQvx=)nRB|_>hMi&gv5{IsqU3luIxnZP+j3}Ax(+%a{;6rj%oy#P&M|6K zGCOosf`~ZM;FHM~SMZHZ&VD3%d1cK)wj0N=MFgKDf|$?^!yV&V=@gwABEQ-F6_+_N zGwwN|sGAH2!wN=@;64qV`cW|)GEMoV3YEu1j;<+LIIp6#c0_g6+=|l{DTCxH`Iz#O z#YV$IE28;)$y60jZU0$ZBW>J_@jv*8J zlcj~|c3tAeT#OG$V7IfX%vQiqaj^RBuASI`%{xzy51>qW%#17bEDG%^FqS^8q!PWc zJvvK=zPF2#a_o;7$os(3x)CKPJPOV41tU=xsr;ro)24*ei!M|O0#ZNZY`y`pEgBg$^IMf`^&O3mUy%q7T6MXl40 z*cd{nqIM+uwaSvEYKm%B)Jm7DT&CJspVOj3b@s8QDDk*kV0$}8tdmjAD6ZrqO-GQU z+kpMeo+ZQT0Xle|{z>`@;{!TKM@6kmpk-kRo@*X5(wz>;%&$&#T6)1S1o$QugVR zmA0iKP{U(ndomVl3GqPJ=vbQgmob}GztRSP{H`iHHX@c*VAO{=>$sMkv2c2f2050% zw6IZk;kXXqIpDDAQ)F9*JaaNRUv$EX7FE_^a)((VL{S;4OPEf=99k?*tj%UBMNm*B zD|S@HnT-?2*rKcvD2#R>8&^;^4ibZ16u;+HnZ<(Vp9%aKxPA$g}j9*me!q6!Z zgZpFVghdXDMl`7GZS<;CjDDfApTo0u7yYWj@ZFi$t3I}y%vfILmS#%|q${H){oA;V z>kP--6^rJMbgH}HYEq;sFCKz!brgHbagpM(_fw!oTEl8ilQ#KWk^aiqxTDZyP6dv! zAD7)To(y4{22HE-hv_69RL0AzDl#ER-89E|F&;|=DVqt2acxUEE{x8CgrcIh9P1Pn zbxV7yE{Xf1nrf66RJi={@MeP?h$ww6Z_mr$63Ll8d3m)K#tkcAh{i$1vte5wA3H*< zP1t?!pT)H2zFv&}u+EKYRWGjY)2Bj;PGcTodhD#M$!?-i5GzwGsaIlA2@@lJlviOO zq|ATC7fV~lzQn^qmDMF>g%xu!zOP_&THZ>Y!th{vLCKkF4$u;BJZ(`rOof@S59EAR zBS81p0KUd2I!RS>`jI&52vo(!`aIUGDqkXX5f*}ZwYoVw5R$#@nDR<2L}Sof1p=Ic zVUD+udrL%=&lu-{s+|mJvTMP_4+G4S1&+{p)o>G>W4(mg=E777y?LyK`F z<|B2swoK-C46j*C&>^@(YsXiX#m6F`CJ9%-kBZ9f4CX5CCG)GCZ@Kx$z6;#5Fwa6& z!_DP0MwTq?F*{PMoXF^MM66QcRkawT3@=nAh>4tCj-VZG|7?k$XD(IK5gCM}I`k;B z#8#6Cf%&JBGRLfw{Nc3qn1O3wja(eX00$zf-eXy-isdS3p}x3xF2X8E@h4VxNQ4S^ zb}cLzowBzOV5UmF+RIfys(#y3B%X`au;sK`C1Viw8a2s)B-Sj#LYCdRp=3ra=6}#n zf5V1VQmt!^$XQ5gi9jjPu8CaO35!kLAWq1$LzCeNYbQs=a0a1Ar)@kPG-XY`!(U4&_4x#dp*h-kV;oh~Uey)(Dbo7gfoD#mMqGi%xSIv&*(t2ovn8S_r#j z4#YCy_izl1mtb206_5}gHt$bha+|L zrbTaA^tMIsSU4dix>z^|CHk2ZH7U!a_f7i1q<@+8p-CT^^ly_sHt7?SJ~inxlRh`; zKPG))(w8QEWzyFsePhzMCVgkp_a^;d(vK$nWYW(j{bJIuCjDm8?(Ka=)Lk!#Z4de?qvIFcMB$5_EbWFYwq8=);#8RDZrBx$a~oB<9(TG$)RuAp#X6D<@` z9gAK}khA6>jZiiH0lVEXUzwaFMm|uriCLXK`io=8tnn9^MLX7~2tGNc!5SxX%yK1$ z^Ra_te8JI!V|+o0+S*o{60yni3Qd2KhZi!MO6A&Zwz+7VQc}3%!kkEr6W)DPV>)AB zjw(UyOXE6m%e*_AcsQTLNOqAg8L=$MM;QXHf0xY^NU6wb6|A|iQ8BFRMv zFQ^0WaICsLYqt$M7&cY2v7l%&onRE4GH> zT7;1ix;W%C6(hD3gW`8+jLE&=B2jfc3Y9xbY)uBZB5_dVZWi(DNDoL@!|HP8!F;d7gb4{U= z9g(Bs-FeKEN&ei^ZDLW>gb_tain;f84Ih#n^QEycE?p-l>amwVfY8yv$ByJ4MRd32 zUrO8(9RFi}C{<6TDJSOMT)Rob+)Cu3Jiu zt!qJ((?}&vb|%M|Ob&73k1hHQj0<91j*p=b2H6PQ?D`wYPe{l=QBahiIqlw!)CuEp z<2)CJV-4TpX^$1dRk~wE`AbW@j_j%2Btz^VSoJw3o80k^PRR{#l%2K-0dXbxAB1VF zjVLxGbdTfgNXU{J?k2>?x`Xi)SO?*+vm@qQ4lGt33IKDeFwoq<;1Gku44z={B!j0IJk8))2C%{u zq~{ns&)@|HFEMz9!K)1Z$>4PcZvco~eoJH1&$$c+0EkKamInLJ;tVzdJ%f_?Crm$& zXK*U)KZ^lu|9L)4KZ_!^|Ga>$J|9PIgRo)Mmdggy&t>AvAeA%N2_OpjtxS9sq#6dd zh_8e60D~*VH$l3J!6o8b7;P5c1?e&Xk;89w;`<;iVz5&D05ZgnppU^c1~UNQ!u6*h zd~pK2$Y6x{IY_UGUxGAH{0h6y;k!?;`bno2?L~w{|0G4gM$E~ zGlO~j^GWeX5JrcO1VtYP`2durQ4ogmL{OZdX+gR{(}T2{!EKr+C{EM7LD5w+gQ6RQ z;~3;I7{y==0Bz7L20jLU1`xds8pLdahB{xPp;B(tPz(DsnBu-pLoIC4TA?a5u>Beh z%$}@)*;4?-IDYG)wF`=&T1rq12cY}4)Sx&~OAAt8t$h%N3eXzIYZ*b&UF*aE#XYp)+I2D<^oBz~*cx(DfA1_uDdKn4r=XPOp;3sMH> zXjwr~tYtIE;h#NVbzJKSTiIH#AkEf#2kBO=Pf(O;ePLl->j#_KT7Q_>)^dYlzBYhS z9Ur9YwY(tRtqly)R&5ZBa%)3^beDEQP@Jv}4N`AySdeCE!(qW&8xa&!8O+p121QRT zKPY-L7|GxiZB$SU(MB^5#spzmvH*s}8Kh}r8Ou0$wABh>8(b@5a*Km>x;CCEoe-qG z+QcB;uEC4=DcY1EU8+qD(&Y?Z)}}LuPGpvx$OW9i1)Rx|PYQ}twUd2ftyadMoWW@f z7BHw{u#mwS43_ysy><$NQyI)+Fq=UMgEHmF<8W4F@q%x&SbEZfh6NBemk4NISkHaa2|v68C<~NLIx`sT*TmF zpV+9?_{3%gcQBE6Fp+!tZ7;t)rOoq+XSL-%@uIfeFK*MWWN^J-tkF+la4Lfm26O!2 z#w83cWpFuzs~B9(U=@Qk4AwHJXRw~ZMh3St*veoVg9ZkB7#v{m7=x!6Jj38044!B3 zPX-?`_=Lgd48CLVJ%b+@{K(*E2LEO72ZJL3y2hXxgBA?hFo-ZnWst@oi$MT$3$`Afs#$Y9b>loa~;1&ibZ@qRagVhW+FxbRk3xhit+{NH-2KO+ymjQ}j zuiejJJA(%pfE)GNE(W_9>}Bu}gNGS_L-pE028S3t!Qe>-PcwLl!OIL@Vel%0*BJbh z!Rrj(VDKh`w;8<4;C%)kF!&dP4;lQM0r*_6eahf72H)QyK9vegTXkz zxJUOez}vmLmjT}H(@h3=+ooF#@OHoMV}Q5qdcZFp(7XA?4(&d_*vZ1*pgjVhckqi{ z+6xRmX7DY8Ujg(GgJBGY`^AIW4GbP<@EijWzE|7l7Z0(VKCIow;ILop(_RG72l~Z+ zZjJl(L4NVL-i|>>zc{4#WYCAfEWbFcZ3fU&7-aax6M8QOy%|jLizoF7e({XKCta*sFRrgMJLA`NeDcWWV^Q-i1Mb zzj$4r>KAWtymz!K0QBP+9PbzJ>N5gjllERf?AG34@C|@IfrA1`wxRJ7<>(& zk6|z|AP(p)86*Y7BiarI&jRRe1L9Hb8V1)gxQW5d4AwE&37`i9;!W*N0KF4~Oa@&8 z;w?QdAl}v*0Q6=Inq%qkSO;zmbqnQB{Sc)5=?UDHa$@LSXu{Es*by=8eOvLi?uuT<#1H4FJ`bbYV6eK%Pa8) z8-JY6HrxEWYUQXWsRLtoe^fxw{p(D@PZ$(! zFj~Plvu%o9U$~yy?-kF?*ehP0vCEj3q%|1xQyPrZ8;puw#;pC(9s2T=KG_Gi6=xsJ z*^eTbm^lpqo&j(O!%rbeRQBhCIJOaj-T7v4CeVTgh?bxL^PMJ2_l-0L5-**Iz$rOq zMRwFvaax6m?IMXSge7TxA*uzIul416Hq5{23wIlp*;ybf%ONWr(nk2plutKV=^(2k zH5VC}^>jk}U`s6PwH8N{WrP}&)fZ7QDM?`ZG#E@^>o~&L*hIM;90=4% zx-zRH`6Y!%OhK8Z$|TcVN%#zqFPfdjNwVX)sU#&xGDjwXB{eBNoQq15B~qKpd(~|^V zIJG0ONcM=RUq!jmJR@QpqBJ2|*rCFYl)u2BH zBW*^+v)TUW<&Qa7kI2etFph69@*4D~;zgZC@YW7z6Nb1Bt`}~A1A-f|l5;b46}M1N zv5Ka_X~Zee$mOukHeb|ZcE6U+6`SZ{v6-$Bcfixpo$z9G7i|=G(>>xIx?kK&55Xyf z#&bQJWsgBsfMo)W7c2vXKf_vbG<&y^0h8nSd3$zrGc5%lE;vLXen0{jYZPrIF37@P zgMNQqgZ>46uE5U~&QB!VfbdVU#7=M;O^eNxswd#Wuu*1|p_y?=bOr*9vY9*eg<>)S zmaU=*pyib&zV_KKMILB z4f@lttHq5njK>97JOYjX28;0cXS~K1QyO@=7d7@U1;joG=zeM|9)WgvlrqIZD6hxS z_Z-I3*^@L?JWVsjGgz2;jxG`}U|Hrxx?a2jhg7dpy?Bjwi#M>0@+Q_|-l5mUd$3~i zKK)mGC<5YR(NcUSGQ<~RfcR1jgl>ah2Jw^Kj#!nlhARbWZ?WoV(BFi0S4FQMk9T1V zQ{Rf0w^yNKYB0>A>_^1ytEffx0dad)gVEXkE|ob-dp6{gAE?ydY?Jwx)Y-;XG}(L@ z#y=U^O~>*UKToDv@~PCM^qwPNiVwas+QV-~XQ;O*{AToq-;6<;jx_@lJ~S-&(D1?H ze}GD`2&C~Uon4rV?ZOm;+!qa4H3Px=3ma$v43PJQIXwN_;*^{Q{ho6MAs{tJHqdZN z&H+)sOMe4Kc%@Ova|TI+XNO^$R0YKQGil3AYYj`iZOE&&rB+&m+G*{mqm}~C1gW5} zEf!=tpj=q*vS}I$HKjUrZZc9Rn)BlkaZs5VZ7{UBhLrjk&NvLMJG@3jsg0HmgF;yT za8(x{psa?;KdzP?`swl|8yN9oa7Ep-3I@?+*#c2jG*u}l%FTS zl?6W)@yZp-N;2V#BLrU@sT$0(!WTzp?Rv=B4e%^+GZvz5!J^eFs6DK1*<4=^%*^$X zELS>QUP!4rE;|hp#RN;9!^4GSdJ5CVd^ggzP&5AcIJDpj4PFOeNAFO)`73E!4_0oZ zX0T}*);432><&#a5=RiK3X{5+-vGGN>fHle~(Rc`}dOASUc zYA?$nAQ|5uw*w^=@%)uT75M7W_K>ALNIvZ$IEdK?nL9w;wS#oL_BiswYM;tmAE=bS zHlFilMZXC$v=D#G_rb0=zkB8xvoSnkzZw`HMGR+v1aD9n$);&A3(8t|VLgmbBZgGw zGmY6$=2?@sPt&_*Wz;D567e1-u|wfsDDnn$CtKiSfzfP$L21UEWp@~? zg-RkYGTSTaphlxl8|5)q`Kv zLwY*khclgDlp#7YK;inL6S1FT1X6*>xGHm!`Td(6<$R03LVW3cI!q z`W;(ngeuse-NuLp;{-LPiOBT7tKxB7i}4|HYtgU$jp92#eJp({}9>!jc5m z%D+@UQw+3 z#8e$i<$4QIsfWcGdK+=3-d0?oN5u7d3KkXHi@kbBEXHJt=kzY(Jw01|spp6v^d91O zy{BgAy|i|EZ!JUbul3jSw6Xd?tymw7*%>xc>_)s9&5G4Yeil@=29+)QPS$>?Y*Rz8 z2_1uKgyhP0lGcZPzN8^ss*sPB8yW*{XpH?DyP*kTN;?P$Bh*Vidt*O{e-EOe@~O0+ zbVdO$Dh9l$c;P`M1n((fcu$GIdrBvGPs!1zQ*XQv&}Y&J{bV^pA7_)yE(UmnZbh@Q zc~BqMB63WxFNPh%B{D^!D+LcMI7M0$DXvF~8-Nv?0V;*|6!Z(MyNS;vL-LGA@eJm9 zBqugf5Z(ooScSMp=2GEei0zGdd5G<8G{pAu5F6I;j~-%2Jcp=x7DjH)IGVW+6#Rg| z{~|NpH4875pABlP++twRxF_wFU zhkJ;D@!TtaSA%$?5pPo(1o)aB{6-9P*a6snHV#Hi^w=8wHu#y-Qne?m;M4B2TwOc^p|K1-Y3D+%?#=3<}I41zfE=e`*fE6 z0bQv73m$$xq#N~5X}$g#-K&3259|M-gZh{By#5t^sDDGh=-=V~*7w4%|0tU4KZ`W| zSCOUv4ps1n$kUIAA%+n7h9(LOub6<#A2SV0%r|_Z2FuRp86mO8Xd&)4lEwW-Yw@TN z5w98T#G6K{_|(9#o;T*~mcSnWxFuw$R=s>5Y+8ds%hUQ4WgmilKh)GyvSEK1Z&h+} z2aaq4MpsILBb(N6WRrs59gS?*55-Qg!YwXWeX{V{O2oCA66DpClwHQD_Sy+tx^ShK z7y#eS^=;44a$+;IoWvO#ey(t4XhGC!M7&SA3~E)B~NqMEToZ(q3!@Ur8$mLjXbzv z8AwrM5FGIgfn%Bz=mcXpT(FF!Q;bnG*BC?djRLyBD5UG*u;*4|Jgqe*(q3ZQ?TzQ&+Bcu84)X@Edf5JuS6@j}Peg zBj3S6X@mYL9P1oiqW;oc=yA~HY&KMtNRHm%1;{i`r{+c#9L!WxhOrPOI)nNei%^=y zplGtQV1mN|mh5!il@wtq3$+bGs+|cr-mBz0UMhJu&Qz`?j^mzn_mOK~{7ufkS)ee0 zaSIf53#R90@uKY)b;SVa}z{h72RvBrGqe`e%M$~|1dVti^e8;2V+3ulNok%KOK#on|q&V zhh6&pLpp%ZSUqUAUB8Cc1gg_!Z$;1wS5Os#_-k6DiKcNU{9fG+KT2Dn0(QWA&`vb} z2I_3=qio{PZz6Y`Td{K=Y)?$Wm^K^t zbAFX6z+D+lY0y88qn7Kl#=AiM9%ic_Kyv>D$^8(ha1O>s)eCKh=Qwaw$g2r(L!h4x zET6=&$W=Y#6IA7=z`$DsI67pf?u=0g&$oRx!z<%ll|vQdD`fr?BH%28o#Y8L4L?Gb z>+rR67hdqM5AG+PiN9rdPsLwn{^R<~HDR0H#$ClfO#Z>v>wWlK$xZ}wEgjwxthLl3 zY$0rU*w-f|JEt`qAYx$%E@ZHyzvajWVR$A{p}X{_XUD0`!*T{0e~=C@Pw5`qQ1HO3 zi^oGb9*g>Ve3a`6P@bnHo#07=mz^+8^t7ha@Lb?Y0gX5&WS8nQyHpp;2A}776n=&N zx^(a&5icEnsM107Rq5cAC|5chG2wj{CE(IoeNy1!S3f(Q6&T8AO3x$jMLe>r_u(1w zsjePwGtLH$XJf~QpQ>FB$BH|7pw2vMZ-P0 z@bWZ(iadEV!!r;bh4N^L=LEV4o}mP7)ROFSyZ0h4h_9^3%?r;M=)?*w*^b)Zmb8}9 zterY*I!B|#MNKZqcqYO3)MN_6cT@}blM;O7%tq1C7B>$jYp=zVNm296A;VLO9B~FM zVdXxaFZ$s^ICAu7MfVs>G>Rio`NA)aXFmBnmB_aWIpJ8Foo@k1=K(nnRDnVc@0w%Q zzo#f0sLsZk>n?qB9Gx6bc$Na)G8E%1xNlkxw?XGns^?tLhqG@s+E;*F-oV%r;>}Y@U2OnYsmCm3l3Zd4qOjUQ#YVZ+z9VqH&GwYEocj? z;B9I(4fWiHwy*{sz}C`u&pLPk+d#8D8>!5*3ErpRkU-#MpIyW!kt6G8UZzG7tC@aT z61gmglI$@qk_5kmFUt$1w-ug>^~y@^Q>@kuQ}Eb#E4uC-`dn;0@B$)=jK+dq>@K-_ z_>U&AUIMJlm3Xbl#Wc@06cK)x;TS8;vx7Q%cB08Qz`5Bj>gU;wBJHKgo`-2V!cOrV zKvRE&YCVt9nV!c{h{s~Zy4|k1Np}ApqdS|65hL9u_EoGIF!iVf(7ajF%&U`5130=% z9Sxu5gui0>R8=I`Ydz0^{%1k|KOj-hQ3ucSP**R3{+B`jvrtw4goC!%Q9W-$RlNmi z!#O>>DrVX=N8x{>!LW*>SV-aB4~y3qayEm(hbV~U$_66~tCSFzSL3{szMwoG;fDFg z$o~^a^{15N`HVVy{)7C#L}S^G6=b!)6juoi2H5Rr9;Fg~1xYwvn9xA$(Y9DdP6SKN z;|9tL;{UppH!by#ZpHS3nzFOn!#J8dsteHSmT6duG12fI-l?A_Q?kUX)o?Me)up{J&egY!ojC#p{9M^+fUd zpm=>zy#6R&E{bqo|m@c$HY(s;H@|DWVC;5$*)#(`qV5avhjYhhug8+f>`jMUP zMe>&LUDO4Hb-_6bek#J0TeLaw&{aw;y=CyuRZbnfb3xcV>hC=rMBt3|-y&jplSHil zYa$kdh$SH6Oc1dYL@Wal%R$6BkU|{DwTZY6jfMNh_26ZKw-m9pY z_i9S=UIW^$Ma^ADS>78#+gcjtT@U(dV)Vh&Fqq8)K1p9XvQzstEwgl9=Q#s^3mS}U ztTY?GxJr_PTkn0yY8zN^KRmuYK$-BGCin!hD!Moo1!s?M4=U64{v z-7DU}@rQ*w^{Y4pbEzve_~=2mu~cn!7})AC_OqgV)JcX&S92szu*X;#-?fP)Qu`xY zXdv7<&&V@nW`C9{V#=W#oW-%H>xg&%Zlgc2OP1%}k8x#luo!E#_{)>eQ8lRs1ovzluBt}*_4FPDzaE2^JS@n04^wOJ6A+uHDBJrq zJj*>pgT2qvaPM@miCU3Ge%$;sf}$`v}D047n|+SxCy$wd6#u$D=);}Zb-$DH7Kf%E3c#`;jjWC!VGEFZfn-DBM z``#4sk1+9p#tDkWJED(QOV+<+CIu%)`N?Fy(407pZljnEGI+$@Qxs)2iR5-URb5fE1#=~c%d^|EQRcOAq^hjw1iIUf4q8Jk;B+=@)&m!=K-joW2QDT%kICXy&AyPf33nreC>wu}1;J0uP(==& zowKphd>)S{{QP#ld*&m1Gjk{CZy=w!i@KS6sHgcL^)Vl!y72Cl|W%vSgy%0gdMz&JNnfDB~M1b8K3alCT=XhOG`U*qfD;(NQmnJ6&#oN)D=nzmiP4#%C^#wAP+ zXiZ++xan9auEhzX#woDrqVdHKJC9Sz#D6S-2zG_dy93B8V!#3JE+eA0T6_^NHtFpT zdhA1-5-Egmj;LEIU3`BYO805RCqlLM0Jpn!!rITZG z1Kq^VH6k|D=6s}}(iVA`+H#X#~)*3=y z>jb#M8wSP&9(|@o>fHERxw>>O{S}?DRhH1o$9Tbv=Q;{v`T58RYr%bd9a67 z0c%*5K#5ymcBP$&_Qx%dXM0Sa8be|Ej28&b?$DtB2L-|_S_#kCFYM4@jE;+&s;(R1 z6X_Oe3BQD?)_OQc+CW*>X6QiNxJsBl%(C<98^w0n_4vD?LI0jxeS6-mj%495N@*QH zSzUwCmA5zcNtM`Oc&9hcaw~ z(b?7qbUmCf-h!~T*2i=g958~(+F+Z>oxx&OLm{rw8}WA&YP2z{v3*VVXsyBO8xeIi zlJE}4xZTFURd8-7c4FK*X#ZZZ9;XD(Z<%*elCB#4S>SIP&MQK`7b)3i z-73%IJLn2zwD7%`=y><}>zi24-%!0iB!vNEQO9kRBK zn28@6^ah2f9Y=(cPD1_xelN%5En;q4;l+yg<$s>{D*AsjZ{RK=37KZd`m=I-!hTns}vo5nCaqh%AYl??;15M-z`XjE6%n^Jcc^uhK1ul5HxJm z?UEb$s?%wX3F3p?zzTNi7qT9cT8y1MhmF(?)Pgq?vGFg})YtgVE3>?qW*=wr^BWE_ zspUNncFdej(^F_R@|=y45I>dR%AE+fh&Fw9QNVXMg?(G8qwiko;=2zmZX5N(+;?=wPC`s4zB3G9ebnL z2nGG&%#{Zz4u*K@e{*W-Z$a(+EvchFiMsfcsV9E-_hUrDSDl;8`-%TG?-!X(im+15_0)`y?k9_DFeKc9U53#gU<5+J`6vam|)Vt5g)1huW! zqQ}~JqD|u2pr+^o?f0FR;1{ziD$n>0Eo2N<6w_IpUN@*H8=@aA%0}1eM+7i$ znCoVT5{)m&^nU}~-=ccHr`G--z{MXa)&DE1={M@;{{vi@89S|Ahy1wjJI+=M>S!U$ ze;XW2!v0DN9!>^uykHMn*yr5f6hpEOZjE2J>4T*xlFL#2RBc}lX9MVu13EPec&L5A zq%Hv;^$qYgFU%F#C9be3=q)KIdKAsPO&0P@!ju^K2e`=>B5US^${J_z;unyzioGm|!&?@1biG*Tp%voDU&h+ohcgVN_l~9 zG(3<+#ep1}9OywO1$xu0Kp!d#^rgx`f4Vr3ODh8dXm#K?S|7-x`vL>$;Q-I)iyS`F zCEzm;*-n6D6+)K|b(kH^-b}3(x0{1e4#z)tbCbo*$^sXLsV&MVR+kBXz2#FCL`r>N zC<-y0e1VbFB9Kq%fl<^gFoyaC#?tYDaWo`Q1lfSS4_oGVH-kIF(^Weyf#N>`F+c@( z1Bo=F_z-n~++i@t2XNozmW-Pjd=9rL2eSAEB4#0?$-R0OM5Q~=Uk=NY@eM{dHJR~8 zJbHt^o!5qT>G#E3zua#OOn`7sq+nnY+QwAs6_`nV0w>Xcz$r8^a4HQ8%%ZV@*)%>- zLem4KbaJ4KN&}}+dEj&?SJ+Fj%drjRVCK$N<(M8XM@CcRhq8*$kawd5@1X=;xCF&H zOuxJzbr?NAg1V`5#qt>O7PXajXIw)o>Z?KhLQsDOsIQ@}fhE*Ea3=K*ETjH`vmiFh zAvR~z=)gG;o%0|%=Ri*gN<0y%q`4b8*UXWEPC=NIu2fb-n~5KR_WWAbWr z;`%qBxsnk-ncB#wThHJGe_#XZAE)>Oo2Xr2Gj$5w4&4mPBzA7|@QXETFYxLe9+^W$ zJ0tV+xovy0c!CNY%jW^Xnr%`S4v+UJO44>Ahux5}y%Y{SM3KP5loB`qiF<@H1CN3a zk0GCDVv^>ZgyHqC^N;RKNk4e}PN!XG`gkAH$5Eg{e33eb~ zFda5{I>Ka526YN{!h8+(e3~F^Nn^r}1IHG|nn59igS*{y)eKTlaj3~*_``m+(TyvY zkK%5Li?QsSf@8=G7Jz_pFyT`~DZygu5S$1_0Xs4_5q*J|g@(t!XDi8oG$zFn$q`F% z3VDK4QJQH$b|MhK8qJ>~XxSLSNkDKi5S#)8vw#4Wk8A{3&BuQ%4jPSlz}Gin=Esi5 z_m-K`bRKN%iXA*i<}qe!AC!KVJYVp6JZITU2G2mwwJ>^AM-eWcGi=Vv zrBT8uRolenkj&dF7Wl)$&+W@KY4?Gink193t`` z>Jt2ddI!IxLBX$Rbnt7M6#Rxx34Ti@!SAUu_ye60{E5yA{!AAKf2AvezsHtAxZD$Q zFHM$PG@$=M4W{_s$!PX$QoueES?BH-lvS+|5U%@bgOb z^FcKMJ?Ai%6%l%i3f;mR?D%;VFiz!*>v&&@^BcfEpWm-h;Y(2LLyhP0(4~PQ| zMhoUKeoWtTDN#$3^WW7R_HjtuJ#@f@>OjU`4zJbR35F%zQW`RzJj^X?UUXYfK{ z1Q#rBEaT-ue!Hn`moYXE=lvqSJ;wFCYHsG>!oEBWcr81b~rm)^Sy3$%D1#4?jt=8Ux^J7>Jzq!#&c3#3@i8~5UT z7FgiN!7R>H;4jaQ6XO2;V3kQX3O>~pQ$2UhN}v{oH9`1>&Xml zpk|?slpNYbDWT1j9=e0NhVG=^p}Xk#(A_jRw3YHh_t4nTy)-d&AI-)U{)*83baChb zx-PVXZbIDKLVIar=wS%LKH3r5PkTa-(EiY)^knEDy%2hg-U&TUAB7GH5jrf4(4)d1 zdIo~Ug)e7c6Tp|?=px5*cJ2W5Vj+J@evjL`ek8Sh!452pTI2WBJ@fqLkZppU7z2YUTUY9{FleAgt-2?hHEv$#dlQZzBxsm#LJO$ds4JC>|B%=I#0wGRloA$`vxo6*3CiAY;99yS`FJxk*J?DWj~EQA`!( zmhJjwGRpNT%4IUjWipDTqFl9IzeWCTVWVB?5Zl5=a*ZA10sZ3LMj@8`p&)nbFUPwp zIjRAZQZWGimR_eH=nY)NcoSDJ-bS~>b3&YWGj7APhMI|WVl7V5w-D=@hp5#JqTaXx z3u7Dcyah^O6P~NQme`EvTKwK>+-cO~Zu~uXuE+CUJU8HZAD$cW+=k~SJnzSIGoFXU zZ+%7F-kq8sa^xN#u3xQ4nq)CKJnYcZxv$6H~G=qtpJ9dCBm_s zq0w!u^Gl)_D`Q#R#yV+@Zj)66JC(Gn?&$_A>WDA01^Kd3RJO%ihAgw^Rg}R&FpLLf z+rEEc7pZUm0mtX1Po7_~a3OOB9|x6IvU}no@K-d*erZ0eR5_iiET3E_of5SeK7PuS z@kO(S6&8#xF3K;S0@Fq#@W`L!4(c1ToZF+MOnDxgKCisI(y{H^t$SX2f@`((!Jwd7 zJQd3UEF!@LdSzL9Rdro@DLi@Am8X}a&sj7VUYXKs;a;h({4{t@GV_av4KK_e39_Y` zC?FE3I`61q1%>JG4Xa-II4^yjmwwJme=vMhL2*7Ri64_V9M8!pns3zj5mP5KdgLSD zIiHApn>A(9uxa^|OF&K}!;%sB= zG^Z6zE|66wxHX&vyZBFN`aOW2~ALl)^IxF1Jf+O6M(A zJ`G)QkIpj|*A=VtQF%&N7DeTC^Qz(gkR2vUH%n5ywBKwFpb_X$ByeW|7}F9C)sRI5=U0|5fERS=Bm79HIXH1aP>lVFRi$voKE`pj zMw%un1I-j4T-;X~Ca_x&CvJkj&=1as1cpMC5$?DQjNrX#?4>ngl=69eZ$&58^f@Wv4?QsDc7j+~;P8V|*E3HNo~^7*H{o�ti*ROa* zg-WW>XVg{K6jd)~ebn6f4TtF!%Q(be;_7a__)%Vk&vNkT_d5R_h|gJLX5 zAORvFK|&&6!`^!hHmq1s0U>~>fMT$Vy z=FH5QGqd;BEUSVrc^V58ne`CyGgH6umydqkyL@rUg0kM_m6&jt&60}Jr6nh^lK9V- zmT?qMKVXGFk-cCkf7cdT#sU5;VB@M{yq(KL{H{WIGi3=hNls>XslGdx#pab%j*y&A zISD-<IbJM$Z=E8mQpzt0U^9|`#K-wWA zY06-bnU%ERA#;%#2aN6+iwsmbs9cy*d?2GsQ-cWDC@Wc-@#C6PD#w+VqVe+tiTny@ zSn={jeC7{swr}#8muvRCDootf^U8#!hR%@37J&E|lKfbJHv|sn_^L`CzdYlmdSTT` zJo34Qdo?;}8Ep7WwgGE91|B~Tuq5o?s2_vTD-i_T!3*CKY7~c=2bFf~;*MxZKO$w2 ziu=+2NY~Df@;{sQ-Y@ysJ-n*LfFl`ThX8jIS6C8ST?f?p9jWpKCf!Y3jRz81|zQI z_{wGYkULtargE9zZ6=kmaf!@KX_50FBTRGoO^{$HYbB$_?Bb{Pnmy(|^w{EJL@tde zvt+zGB<0G`kyZRDq@QF1qF6i7dtA@LisVo{_ct)dVmsSlA&0c8Z(kkzBxK1f%JMT*` zXq?ij8Qg0;^@G^BYqT!)K2$4I0rQ}oHc)vm87;-9*W|l#p^#!H6P2RyCgsII*Ur0*Re6{vzpU zQ5>P4IzKbpB>NtypRKEJ%8Vtgf*sPh(qmwBjRY<{_00uLfw`Q{vR1ioqy z!=$DfHiMkmu=v9Rq%imbB{KQSA1u$AUoJk99r_9#_C)4a&8w;IkTTI?9V2Js#U)F4 zKstPM@$oZ@r;Z-gK~xp&WKQLcY&DhTP&}~&E9u~Ob$roRlrwLTE7?VK&=Xl9z2zbd`%nl? zIKO;B4b~}Q{$NRtE937t9U7j`3+=HJFJJ$5jcNu&Q6I5 zfR@m*z+MXVZmf+92BN|hh%y7X1uVYRsSzf-l zX7LOxmt|v4Lhtb-xHjn7?CxVjjF}GXaiPOA%h@($VY*Y@{3`y)s_YdaD#Q(oQ^cjZ zf{?SHfwzM63d8i7;=TYaE1w+Oc_5K*(s%nk7tA7`ieXSJEScT#fGqFRbdxibm1YK~ z!uwakGkLg=v9zaRHD1Oub}7aaFhgF>G~6US-D2$&9L+rL0wgss_e_Sg=W!{h^0@q#KaN9_vk@0G#%S zC8d~jrei{qk{1%5UWJ)RbUBs>qM%MZDaWvxqIncmzdm!?EQh73h17$7vX*)8tMaxomX^$$3TxFFK zp^#^DpiVpm3Kn8~@6CH2zxaX&dI;NEMtSx4O5U_6ub#-0+(~W+fKVn@Em+{Q9U7-a zCSWZzQi~|Nyn4!#5}5bsp5@g%1hRax`F26sm2+r(#_s8k;GqmeeenxwKr) z3!J(N`vFJ|`J@bRqM&4XHO3CK5>~VoD~>F$E*D}0ExZxDFml~}ZU!@8)=ydL*)*~{ zRI?94lx~$*ABip?TQZ!N6lSoBVp%OKTghKZkotvV!!-l#h>AvQxvm+= znBVj@UnaUtUnc6FA-@^uz1}z+tRmYsrc7D0h2`uz5Mc%diCn`fh#7_Xk-ZB+LjP0H z7|Q&Fu6pIBMz{;Cpi-h+dt-ftx0-3@r{YmN+>$9Pn?Rp~g-fBI)Qj`Jh17*D%Cyfn z{U_?wk2D3EY7W>|JN_bWfCtg0upZV3F?(?? z;YhY_VScjFxz=@bX`uOs2l}?QLLYisMgAqIUWR@fm zdgvw(+A8!cNGhhDQ11SWlD-L*UQM6SEhoL2{_~)$^lFN!$xYtg7ox0YXxefq#Jm%# zX^0{yA`r98##;|#OEay2lqoO0W@1ir%S{(xrs*(Lt&G!Q(AQ+O1d*5y-9p5CnVw79 ze3@QJ(fi%>>D)=Rb03O{R%BkjxQ`HLWo~uZQdq0- zmiPi)f}to-Te;M2^wb@W<=!3Kj^|~W8Y?z+ zAX{kzATNKnLzuvF>=Fl6My%i^piC_yumrX6?hxA(amLXzdITxE0pV4KgH$pkQ^@`%32>&s9oRz^2yyFetcB|2`*&ow z68TJrYZ}g|P#z&MW4W;u;tA!7_7&?7IS+{(WRx_LQfXJfJcWA6`CHI>CyvHr$tzuQ zau+L9yzJE})N(QJ$)}nZQRT~4^pS1EeV%Gr^zmv3IzL54%{vQDP5W#~oIrS$y#qZ%!zL z>ogWOJQCR)BUe+Ju&5(AXXE+sti>hErJFJ8-U%^eArLr&t;L>?-@@@ok6hllk0H^+jW>7SB^%(-`S9#3HS%HlNZ!1G^;R44TZ;Z*N(Npv5gg z%MC4SElmg*t|C6oppZ36iA2${fYd!DqAwQp&@07}jRaptFJFQUyYgifD@4=eRHE%` zu@fz3@-gMhS)|QU&P79)Duoj^!WUtoqAdS@Tv-Ji&KMtX7OZ^!3U5j8X+3n#sjOKn zgJNLSgch6VCS|2)4M*Havj_xq9Wu{ag4uJiH)xv( zVZwq|9?597uG72{#yCfV6hw`~`trS-2^@ zjQyu>dK$jW!0pw6PmKw4g@fn1?SE|rLroz6|)Jp4o`zx#l*S-wJUV2UWTjs_Pw zL;>^9KY(N6IHl+hE#!?a5>Hp5*7Avaj+ghOb7hQ>OF-9F#`?Ucuesb3{O3W+e3>#N zo#0tQK7)LMIJbNujUd*|B_yNZSxcFVc$P9mps&eC-U+uhGB_1SZEysOHzPyaQc!UD zSZ*$Pa}#r0@Oj!8x)e^$l$mOGO5NkFMi{~cMD5+M+>b}EjAlDiuGDl zU>Q!(y1UhGW5<@(R8FZZ^BWHxyc$P`FbQIP1E<5)09xE8W4xcNea6S(t$OB0WGsz8W>b!VESg~dK~`_<$T3T+7R!ZP zJ3N<1FI^r*E};z*u9_ssxG{>>NZ&?hj=>hMcVtsw=UM!QA`1`rURoj92!(bvu!`V_ zum!^-t$pqb88lOeZfy9=Q{T|bI@rqY_JtQ+&z0_@Np)#jl|V8 zjW8YC%c{J&wHY|Ati~KBB#fCQCCuH7Pf(#IDe*@v5jukg7txq<(N*iF4?-)s+>{jFup(RbWX{wF4TGDYgmD^Nd(`hzcXwyYDU1HzGJJO+Un=F`lYRYW6^Ajj<;xzMI{!^ zwP>D2r52T0G~c3yma@fCwpz+IOL^E*wp+?0mhz~jJZ337EM=#q?6Q={E#(PI*=;FL zT1tbZ?6H)mEahoSdB#$nwUp;9<#|ha!BSqdl)aYnlBK+CDX&<{tCsSbrMzw_Z&=Ek zmhzURylpA(Sjs+2dDl|jvy}HO7s$r?7rCOG1TWZ8oqn4Uwsg9*Kv(#)$jah2kQWKV%W2w28 znzYnBOKoncEiARArRG~|D@$!{sckH^t);fJ)b^Iz!BRU~Y9~wWY^hx=wVS1Ox6~e% z+S5`CEVa;5pRv?uE%iA|ecn=Eu+$eVb+4tqq)@>`;mzO;$Hu#XDf@l+fml2s7A{pp zbq`0m#|pbN-VOt0`wWyJn*kKbFoo+`MRXz*Ew;K+N&T%YM8^VB9^w$wA&BwjAWTfb zSvioj3`kOTRq!Z3c$B)qfoGk9XJvdaG4K_8(-w%@r4z}4&mE^g&?B9|H;Yrv=1c|p zq@e=4r018xm_}Z0(#b-0b*5^knAjj_R7?hI8)Ti+i)n0lgDRvM(kGv~o@T}3ucQ;InmbjgXHaTXYgh|cq)L#80&n}_;@=P?<= zKc~<90KXyb*kiopDDyBT66f^j8PIs6S8k{%87Iq0A$8RVHEYo${0n1$A%rI_`85LJ z{DVaKcyx3V&sz4FkSP&YqD4y8yzKtw3FOc+V;b>tf{vLVi8mG;?Y{9Kb8_*{f>s%m zAAR%$exX6fRMEP1O&!}iWy&B-l0opn6p$(mFTSxb{3obYZc45Ei36{F2#Gw1ZWP(5 zLg9%fT%jPIlceaGSe!sT!g?m-m^4Ey>YWTpO45oKRpd2}7V*%@pz4BN9N<8qDb13) zXpo0EK$8fkcBjmoP)Q<+>zWrP^k!0I5bl$HG$-hjDM8ccJdo9Yk%Gr$FAt>Xl(CGo z>AiI(B6;QmS)??V@HZkdncADZ=y3m#as0jy(F!TD#$4Ly>;`ge9g=Ix1l4$a5?gyn z64nqIm##N%Ec7OR6i6w zODrG-aiFknB#$B~?bOKHY7-J(+C#2ycz7vGwZ?+})-`4LP9qE1P+dc1HATgRwrfNu zdRb5)w4%}58qy8G@W3n!OQqm~3Nz&68-I=L#KK0^YKk_qMZ#``=~g({a&ZMUwJHcMXbcpP|T9+qiwBqQcL)h{k#$%-*1o zOnQ-SVK>%`R0=LAlY9hK>V6#hTx0oUqT@U}H)2mv23rbZNck_Q(51U&5N42@-z-cR z3<S76C8$Eba*?gc#32z$`b;M99WFID}#XF=txbWDxV;l5v@y24fv0S2m*8xvL>r zDan{m5I!{|G{l=M)0R@4INxPD?UArk)04@wLn6oYWbPx8%s2K@R%rR2{Datu;@6oF zS~BICzkbW}?Zeb{oo9Z9WBFBJ8RNlY#&~oI&t3}WBVH2b8(89rdse4WJ%cr)D?>r; z#x|;t7mxaQ6MC3_gm_6>rY9rCd($#K86{q&mg&hD@piRLPvra5GCi3f-lLZ3$qexV zwM|{qePsPN_82#NnLzHSo ziHF|tpl}O`2mV6c(|~X+*F49sxp*R%@VVX!){kRSZ>|!Nsf&2Fm^|~h=@mF?*VEPW zrip?DJ?sq8t(&IxPY)sI9#RGWhM0e)J%npo@Sihwf6_H>B7Ai;xh-d@e{HbabE;aZ0lu|a|$GSz~RtHOYe!YENNP_0d(BdJ&CYDrTv%irjWdzmY0ufdYS!}vZmAPk!BreRTw6oVxv4dO&LleRfZ*}&!_E6@el83)d$Rl`o z$VzW@Q4{G*W>Rm}nNmSnOo%d|<6bV86d@wlj^Q!av#CX5FTOFULf)9??}WqkU5mH( z;5eb&0rwguoKv9sP1QFrVFrhEORBl-6|(kj(}`>{c+9=t8!kMYByHwTA@}T`5Z8Z4 z9#{h50)p7oUXrmny%WxI%^{|gUSY*^n>*~o7g7ir>E4ncumoiF{jfs4!&#;>nQAI{ zK7CIOg%I63_#w%p~F%#@Z9U;GvyGD^Gf164FA7nAxz(ep*&>Q@GI%7L{NTH z6d@j)B2nnrMn$DMn$n9&ecGsq)W-^~Y?Qt4`bqOpr5Dr`U85qKB2=ic7=79%#n_ml z=>q*s6sl~*XC{wSI**xX8Wop`NTDMe6&Ut6r56-_)~JB+!%PYx91A|L=~<^fZIpTH z<4l4Q6lt!hjD@7pWG*U=NTEfI1SJYgIp!G}B4kg=K$5w*)NIm>0(W|q%rrGq`96k& z-HL_@-5DVq<<=t{rBF&*cj#|iY~vUnu*SIMiaBC5d&$$fuj52$FLWQSP zFhj9it0|#o^9R3BI|b&p%$R2(f2S#kl3NjQ203&YjdSQBfKtw|l%a-!AE-qPV;DYS z_=e$I0R4l;JM=ys>(ECG|6=%!;dg*i&2SCF4Ggyc)WaA?G0b6@%dmohb5TD8P_g!* zLlY>_ajyjTIIe`AqK6lim0Hsv>g6Y0= z=xyyQhaS+rcIXG~8%L?ozIBu(+INm}vi7~B_R;?7s8h8c9Ce2FqoW?9{p8T)+Ru)9 zqV_L_UmUtd`_)kwYQH(OTKnCh8yIe6xQXFrhFcicFx<*;8^i4kcQ72T{o&Ag0QC%p zPqaTB^?2!sV&(!5Pw1VMIhPxQR87^nIg5he0YZ*8{rH)}W!;K6#G2G0shT&EQd>O@2?qI+t z03GEnfLg{-%kUlmqHDp>(or7;;4Yt`l|wu65hnEthWi*E2hgX~#!=1&fTXRX&Stos zVFv>~zwW4S0`UFAc8+oy0Cepgd+Gl(1Ly8Q7Il*1V&F-!)i59_TR zrIp^+QKsn-;t0KiqmE`+$?!A)S#^Zi0rZI8iMi~;3<7%T5ZFM5ae6mL9nWw$!z6|y z7>WT(2fe$abk}=0N)NrKqZ9ztEqZ~Y6zPSIx=1f_)KdV;fqE~7-j32s@8c-#^uCT_ z>-`+st@n47e0`v!UZx-5C`0u@jxvnlP=I;}fG*VsJIZkYdRRZiQR4bgM`^1MbCjbQ zj$vr2AL=M;^}`%mr;l`KJ;O$ZO#t;wzN^wlJM_Ii#-X19>e+mEqCU={rTPSiR_TX3 zbOu1F1gPgQoXc_5`Xq<0)+am4sQ~pn04>v}I%+?Cnxpn-7{G86!}$QJ(Pud7Sbe6W zPGFeGFqz>B7(rpl;SLaFpxx3mrw(FLH1~{$fYb^h+G7)Gu`uOTXMv@6@m0 zT&`re%28VDS3C5Deyu}K=+`+)PrZ({ZZ&J&4USr^-^g?~u@2qBEx5*^>-1Y4^<@1v zhaT7OVwtXGncmGZy_<>eai~te*P%Zd7U}mn$~=HN3_$z!bq>9u-_H_yz)?TeA9U1z z0KnjSh7FE#lD?61dB{d!dpUHY>TtxSKB;cbQwBH9W1=Mim@{v5;e3@?VEB~bGlnl1zGV1{;Twi;8NOrqp5dPiKQR2r z@Dsz&48Jh^%J3V*?+kx1{K@ba!@n8!0}NtN7*qzG!DO%)Y=#I!lp%}3VQ9vX%@AXV zGb9*t8IlZn49yu@FtlXIXK2OHj-fq62Zo*u1re=A|2m@8vLI?%5Vg#7Emv&?i+u$P zdnGr|O8sSqR~R7sl`Q*}MovV#kb@U;@M8U607HvtmvGukIPE3;^b&r$UVkg1?Jp$$XZi1xbCk)abqXNE2eT^YJDbZ6)h)yj;148s{p817(r6<{71)#jU*GF--R zIl~nUS2A41a5cj<4A(MT$8bGE9m8sd8yIe6xQXFrhFchJXFzESjl!r_Zd3ux3!;!; zZ-%}MgBVbaMaDr4!x#=@7|k$_VLZd(3=q5+F2g*AQik~q3m6tMEMll&Sj@1Lp_-wFp_bt!hUE+^7*;Wy$#6Eqc?{<>2v#rT zyNekvVSscO8IbNG12SD?Kz@r1$ZwGWsVy=fwME7nhFckKV}KAB8Fw<=#c(gf{R|H< zJjk$~p`Kw2!)}HKhCK{VF+9!i48scyFEZ?9c!}W^hSwNgXLy6*O@_A^-e!1*VIRZ0 z4DT^~$nX)vKNvn`_@3dP3_mdZ$nYz}Zw$W!Okz+NbOwXLWQa0kF*pn{hB!kWLvx09 z4DA^@Fmz<-#L$JID?>Mi?hFMCP`yQFABO%60~iis7|k%2fi=vW&cOO&vc8xL7+51r zRtB?@VF|;D46Fhsx4yZO;bew$7|>)D#=}u}CSGQiMKO3B$bhQVvU=CDKG(9I)*9j|>Qc+vS;IuG-_EICmGL2wdKa-sJ6m5m*GN&m!lZwiWr77lrUTr)lN1BGu*>)AHW-M?|&L4YbVZoZ@ujDu$04{sl10quMItGk|$#R6E1?9Kd&H8ec}Wv&=D3 z?QG+OsCEvE{akZmR6EbWuynq0D8o92hZr_7Y-8BY@Cd^WhMf%0GJFFtiy6=g=bMWd zs-oJ3%;kmLyDu`ni(=p&$}lskU1D6ya3#afQSDM=8pHJrcQd@t@C(4~!O)ul%v@@s zx|gyzFJ*CFYA%gxml=3)nXwhX`gghU7{G)$FE@KdwJUf;zruJts$Io*SMlAoe0QyR zF~cQMt&W2?NH~@6&WdU`aquRie^gsz^kG17jSQk!tT6^M9KZlgT*I2U#u&nIBEu6= z?Ka~lfVnuT-C;~%fY|Ra9*t^unmwc1I__ob%)SiBd7TNltTU%EOlN@Ft}~Z0KzG-1 zPkF#VOFv*jBOWwH0?fmr+6EI1wZWVk)i(0oM!wr*po48TPKj#uChAvj4q`w*s^|V& zZ!V8&TaDAA+BW0#sJ5Nk{1Nl;sP-t|J!V`R)poEl>@XpNoqV@TfC+wgv93LCBJC5# z41jq=RNHOb7}cKS2Tz*gqFMtJH<;kJ!30TzIg{ZGhBKqu(_Dk6&0C||GbZHxtO?zC z&P3OJo|8Rqp2Dyus_iunWz&u}=yREDD%j%Jw6a5ckPh7AlG8J=W#kzp^x z#|)n^e8KQF!}koo0?Zx^1q|g3Con8wI5A7R+nB|G;N8ZuEbU(7k1Xv4V-CPPI7@rc z9L@leSIpzHwAYMtvb492%d)h6=IIQp@LCRRm<6x0Y#zMz7xBIa6le1@yrfchUkr~u zFT4k!av9zN%Rd&5jw-_|Gw>sE{zY!o{}C>~h$GW0ES=Q%csk=Nv3n-`zWf@T*hFlm z$&VDyP(A)#)l`1nfp;R`iwmF9$3eTyFTIS)S0{|YMl;S``9DJRJiA_= zmi?#RQCp<#B2BwXyFh!0=20v5BjV??t=N@_>niqfiYxr7d}W6ruEfq=afN?ZT;cKM zEBl4{%05|fg@aaH;dbRKdtOBtu2~U=R}tF-@Kl6nhQiYjJ~kAdj_@m?@JxhH2!&@L{6#2y z6v{s(6h0K;6GP!)2#5I{iu7NFo);thStvXU62cY)uoe5dakY8BL0qwWfond^qQRd0 z)E^NZ7=#JE2%mrsp8tgKgix62r-i~lq5s^C@;tr!8Tx}A0+d6%#evJ2?B>T+TEpDri8dEHV-b@oJ2hpKjc1*vf;lcc5J&DRv;A%ph#pm>G9U~8p1={ql9K9q)J8p@ zI;s~^p?VQY!iI%g@`tDu4=ptXTkO*cl+EU|46hexo6Y(HeY3fP;UNw_#;~1(oB8em zzS~r=(fnbv`24%MkLCQNcRQZBN zC_m9ywIxkf%V~yMNwcxFFt1HFTG(zjEmp6gW$LwPIc%l4>~@9FnB7Q$`Kb2dX7j6{ z3Q;^FwGK+U8U*lCx=C6gff#(1^@x`e7GEh>ZzY9&m@Zv4(z8+=i1zwy5B0`n`)cZ5 zAoT+=IWi`z0FAtHv-w#eH>J=SPO%=zbEtrNA&XwrS6pRb;%ndP{ZN_*C`WyeI;iWZ zhq{6Ks}GT)?$(#P6_q=>o;B|lEN>J!vn-A&!qC&4y04BS%Ny5cdw)hX!` zdLEx0$F@Qv(oN>>bkf(5E1c7$+B_qW=x0IRo6I-T6TAm~-k^_=c`MwznE5LU!TEF8 zhT}^E#rRTKZ?CE&eFNVYPiml^xOt<2vN_DR_4ZJ{oy@BFTra$hV#r!D2I~cdM%%k1{kb$TYRw^cY_GVar@KnrCm7kDZJ;PPf41<6^@u#VftvB9 z-ueO&3jXA%r(?`XLVXDe)m2=1OyV%BG+#rirS%|7>q*&K0p)3h)CN;|H?21nXni4v zTm8;@xLcVPUS-fesD{^TVfilP@%ZW@ZBb- z3EqZro2-r70ridMC+Uq_g2u&$mrEIRr+Vv}bkfD>s@N8DNsEyw4`!mPOe}1mMBy&& zp4AjB+-AS2(0bH-_atb(T~u#;}E8=6RRH6!slz ztL>xq+WXW~`+x?+BvX`e`XCI&Z0yv53m#d=Z?zM3l(k(u2XlJPj_q1SalsaA_g4F| zo!UhcQKb#VlO8X?#IkjAK`*`DI%zdoN)ZMPQ7!9X+@fYY0U^UKG>AqZ(*tR^xQ@UI zg~PHxb)yL0cBGPt)mb)HVr}(o>Y~S}K#$V^JwXTRxi}7zw0X^-lcJr2gxK45<<;5k z-C2bNcV$)#9d?(3a|v#O321HZmfd7)cfbY8n9r@vsviYUZ~AQ>(o4}Z^&Y5VPs-H` zP(|o7pQLc}4fnT+X$q$gMt{S3i$ZlMH4qeAm=nR(L#t`5XfIfcO z6OrFF2wU{K997w9IraAKJOF1?AL@^M`_lp9DjTprIcS+<;SSk$>kN}UI~?GkTYhbgGu8oZXcRtE(<%NAHhx?V$B z`biYiS0D{e*req1h|Bx2%zGWpg7w+T;~#eFL%5#=yRSg2w-$oLm&NY?Q5HFLsH@DQ zL&`jiVvxlu$l?st@l49m&!QIk+0;(IfI90JQ4jrMD$*~Z{`#eKuznd0)vu(n`c*Vp zzZ!>^>Yz~<_-f+Yq9H95qoaNY_d#P#{yZ(# zUxd!@@%c&7`9(oFQs>`>&c6eC9DPb@_g^v6@yK;CB(P&Oo#(YVbe{EDv~zFh{L^UX zgt45b|J_OTkE9mcYWJ$Qw#_QSoQIB&bi?sC8qc5KBP`2}QAiWBVHAXQl(${GqPQSF ze~W$Sgvc9N621!rper5ewtUOR~=_qmxjtljvk|m3?Lc%zx59f}VVgKJy9s z%s^`cN`P=gU-|cq>J?Z zLKc7DmqyCIG$fFv=u3u)chp&wWke`uWTB2YHP&c|f(E4xQ3fw}apKJ-?FeP$C6mED zEhs^>mVt7NTs(x0?vm#t#N8RceC>2hoxmlLFLuwIcISKH_WL|9U>eV2eBFn_Q*$c+viz*f5*zS5W zWqvp2po9{N8grp)^C;ISrItn+wK3*XXJZi+8WmvTDqsBqgAXM(VelDP3iD~Y41Dj* z!1w!^_&yu8ItP583%<_>-xq*y9LvkV_s)Rtoi5+iKHo1e-$q8hZ$t?J79O<#9amvJ)J>BpJfu! z9x(6}MD#R7^eja597Kfkdv2ENp&IOboLNvjSZ6=P(?U*z4Y8hndUC$OwpG1#F&7ul zH=rc1Kv>Y8XYzFnn^}WRT=UU^b1Y0q))UBr7X?T%j7Lucmc~KI{~#JFuChN3W>1^( z4r;a!qJ0;&dXHKd??bE~P)FlK>S}yMy^N2kzws#@W_&^8jIU{m@eR!|zJu(p%qZ@s zn-cdQNP=@_F2gg>xww1rs_x4a8Sdc~-MPg%+7>%^2RhpKL1Tzjk{Kn#%%X_tP?niZ zaWhUyGePZ4I2zT*^z+;TCP1QG0Ix>A6i%Hl3vwbtw^C%|buY0TH8HlB?I~(@rW~^y z<(u89t%+t=a3Ijlm+eHhBr`4Y-oheJ82r{&`xstuTwQPX3#u=RH~>WqLbVU17Un_7 z{$T284nYw(km(i?R>doWRIRtJ@ZAf4>r^%8k!6-rwpj+<#t~3A z!*viE8$n|9Z=j>W#1VBg10s7v%~u z;3YAyhU=~4(`&jB$>HEsX6YT=WMO4mm99*WqXZmJPGKq}!4)h)!O0fuU+HO|L0X(1 z&nUs!>GgU85_}U0aPT~(utpLzSb`{3v>P|Z|0KcpA;AwIf)63Vk03!T%GCxPmX%nP z2L>^W$}`fd_#OJ?_gqELhyAH3a{m>w|E1oVlb-k&6#6UV9>o$>78aKKRcW%b4Cp8h zTc?!M7yb&?+?Lz5OMK1s)&!ZXYdM|WH7T3*H#VFLTZ65S+URo8cjuf#vAq|f6 zhs)Jwqg)u#>Rh43gN?@vOl|`-eA_kT;JPkPzr}S43^KWCTJt^&x>XxL+Cq@Whe5VB zD8eLz*!#I*5R*MPNMtgo8E?g`FKDyb+)&VVGt?>%ONw=Q=vY`pV&fyvTupYKRR>QE zGHam6@@(ludP8u8+V$IJKWEZfn$~(ycAG)$rQSU_o*!?ZqWriwK6T8?=D_+ngR=8t ze79lFAaMoh+WoI_)_HgW5&0SBas3_yuX*gNTIVx`V`>Pm=Ldpa|AEDo7E>#p!^Opp zxZRt~A9=xzJ1kh3fR~UNS3a%e%BKvr@ZfALee$!h_E}2_ET>jRl3GHnqskzO|Cj|_ zVU4m_5y}DroQGP#8ON*m-Gj69vl}Rn%X__!n&oG2GJhVNy>|cC9G%a>f9=xlt|O4) zcBghX+cAS<`7!tDPj!^Yk8QI*P~bb0{>DL(ya;~N!nx-~^Rn_F!+Ps2h<12^4K2zy zgl&?*H9sLX-)!|rwHVq~tj^{uwD(`Yx8d0$t0&o30X4Gk{pe^5M!YouFRva*Cs+qil{JWJtb=H!bugW74WSFHL+EmAC|zR>qkF9pbiXx{ z)?1^f!5T+HHJR14yRwO$x6bSq7+zDl|I%qWq>t98D-5>4!340Q>~+va_bnS z#yU<}WtAwGS__n`tcA)tYY|?Qy%_JpJ`r!ZUXHg|uf%(+PsVGfPr(bKSE+-nv(+Ki zx#|S#e08#Qp*q95NG-80=3c5}eitK9KlD-_!}BnPTL%72{OS0E7o{}4!`?(m>t>88 z_ytv?F$H>=wwkooffT>9%0Rjyopd`$@#Cxvqz|T(Vw6|#E3XWs>(WWz0%=r7jRmVm zVpw5T`xow^)?!Gjw+2En4#3|4{Po|0A*~QMDUtPdL18e^iPg3BIqB9H)W-Un`dZ(h zj9L2iZW&`R*>f44P)6={4bzpEH>NAjTNG4p{n9AUKauBOl!Q~agN-}uV9`- zN$Hh35v0>Inj)tMtt{(*3hIxaONFNniV zctU`I`Lcp3f=B05H=9q1gu(Vd>qy}i>sQ`4ZbygVOah-v7$dH7B$BM3PlkN~wXiRw zcJ@Vh=l`X6*FR2U*jJ#EyD|#-VJYN2fsprvh5T?qPF6-C-vJ@t2_fGNA>Ruj-v=SD zgOKlstE zo{9gvke`Q;Ux1Kbgpl__$ge=iuR_SLqmnx_3VE9pazh~GhOm&g734%S3i(S2`6~$d zTL}4k2>G87@(&R5kB}UGmf1*ho6K$LLjE^MhxC+luI#k0Ej>E^WKKe!&_@5X!plf!?bYyhvdGTa~pTP-=eEm*pn-9OWldr>FuCJz8Q7`Gj=mi%B9E?xleXGL#Yry?$ zhyyf_Nw@Dbnu)?@(oy0nWitqeg(xx{!W==_5&ZTzGK$(oMpNg=80r}rOG6{$X;fqa zjgL&EDUr!^Ok@hpiA;km@v~r8mTSQVI|ycY^@kDq816B?h)wLLaF1>v?)Tt+ntR^> zo-!JJ>6`2mXcDrYL{r68R$T1yM2EM$k`D8ximF$9?gziK=UIP(!$8av^a7JxWVrNU2c9v1F$BYx6%9p z8=hC~)K25=6{84Vl>6$4*MS(<*+s;>t`@tCZ=fRYs5FU_U_<5VS;Db^2I&Vf=6^EW zid}mlyJ(08I#_QT`TF|7hPZ)-!8-$@0U;uu6{g{&>j|jR2~;7jvf0EoLj=p7$gLEM z+(wDWozx<77Z|;p21M?mLn8Omu*m&1I-L0X(I1XB(jgp3J%8@gzoaODX9j$ zVGh5{9-P?0;`TchV+7sm8E&#%DMw!BV17hYP>9$xj(m(3`~-}BO3BFQlppz$+D5*j zZjo=OSL9n76v48Xf8OjCKG`jtJ!?;#)=zpiO4?W)O+beD!LwT(9<^A8=^G)@ULf1&lcg;FFAXFh zJUr29WJITfzQU&;gDn%zwi^Ts4_L>ob{3B2T~ZwM6Bw|C(4dG}Z#zMLqS>M)WJTvv z*XTmjZIGYm-`C+p)L|*;7yI;0vbG#m#VZV42^7I~EPP)09b?dotp!1)B$Jm=EPAO( z**TPw$G~_&`@c=O1}Se9DLu{Yjxr6T%qobu4u?j?ZN*mKHj${CTillvCEg#q*H_yG zxI^E)0hje{Z^ZEm3@~ly3}S!6hCJBh9AV_?a|YRY2I3yPLoBf$0TBzKv|{_a9O()dVp0F&XNwY+Qj>wR)^y;gDgxgZQ?cp z%{Q7`>L|X^ynm3DXWd999_Cp$z-5wWos*(+Z4$KRw8l#!A@;0kq-9N~Xx0phXU(J* zSw~RYtRtyY)-39tbrcn39ZP+(j-$h}jz>ShFV5Y%jYbD%?d}Mj5c}3Syf?j?V2N+D z_o+CLCFg*F!B{0$oLS|N#tA5D5w*-(EV9H}Osr~;LWb-f@5@2#xPr#tm?3c(KE;o{ceEeMU3t)?{u zkI|~Uc)NC9F-|%rz3WEvo-J12Nd>LZ21_?g&P0OmyEP`j-kQjRuW1C27Uyd1w%9B9 zz*z^JkMFK6z;QGDR0h|%w2;bil@b@bht3!>oN?5_nTU73Ou{Q)CgbfcGibPTB#m`u z;RR1e;r&j>(p=|wTIkH7a;Jn&aON|cIz}F$Ob>xgR;H@f`fZfWr#W-A?tGlHXpa}{ z#zE-G>$|k2@LclUPHkzu^-ODh4a}SE+Opz;qAk`7IE25JdO{d|>M+D?*OnGTG+V6a zM0^4fC(4MY5fKc5!i;rRfsr#P?wpO66P<$>-JFj%)LcLVoQvon=VCB&8BK96r#a4* zROwt3>KCLqIH)E2ML&!iyj0i;$RY z4mRsvmliEJJ`n?`*)aJA#~73G94AdPZ$Eph{+*)DjC_lb{gQDXAqdaO=tuW^Ow1Lgb5=4yCL^v7;xFv}=6>wQdb=AH0on+uT4g-dJ0Ao9nqs&j|}9 zygKC|x7e=8fkheKEh|Y+0-uwDmjq?3X>A})$<{TH7+xlnQq%4(b1|`RSi-vksVgLz z^d?abG7$QnscJRprQtjSJ)?I{DGedPlf?%{&zhCqOzX*nEs)@MDY+%3%=uO6^bez1 zw?jmD?UJO|4+lG#%ry=9C;52)_q>?u)eAp7!LI>UQ|FE5x7)P_9A!N(S8HEyUkvm2 zqFk*7?&|IH>+Kr~w_5wL@O&438Z12Dt+(&pfDN;?tFaEaZ@`E(spTvbOsH=5sUv?BHP&2`ik_kKZnZb7n$HVNgAHCP&AqCIvsbwVX!)UlFa zY4a>^OvDOM^qP7LGqb({=Y0q3`8rDK4W62>#-ewZb_3>Y6wh=w)Z3l;fqTerOdXYh zppJKPc4_ZnzXPG``2@G9;&{wW_woGIK*KO8;oP50+fP2097G#)&?d~mjT_qB2dOW{ zANLDBwtP7H?G?Co#Pv$PqVQU&Ol%47-l^5fQU{?_uC(>FEV$c-e0`U;4+1O5*LQ0B z@bLC+_E#$LkBaK8v4X8l<|B0&B#{Cm$$flXvX*X_a?EyxwRSCC0VZyeXGV+b=q!-6 zl!Y$OH#V8S4>I}k2Tb#P?9rYzi}4TC5p;R{)B>3=7A1vPBL=ha||tPEoJP4;8xEW0S0 z{W!JEeu56lewq%+-b*91U!n=wFVo@KuhI1EH>o)LEt;MEHqFcafEH(eOqJQ6(iz#G z(GA(((o@+#($m?$(F@tX)2lIs-ifL7QB0$6Vg~&bGwJ78w$d`zT4@(+qjZk7ReHwS zDScz@m65Ry%5kwy%IsKyvMSbJxjHsLsf!I%Zi^kD+!Gt5+#5SY*%%wD?2a9(JR3Vq zc`i0lc_lVYc_%hq`8YOH`7(Bt@^h?M)ndn~k=SfCJ9fO97n`HDiOp3z$L6Vpu~M~9 ztX!QLt5j#ls?-x=C#uV0OVzVtr>j@QR;hKdGt~QHXR8}y=co_GE>|CpU7_xbU8%kr zyGDICcCGqR>^k*}*!AkSu{!nV*iGv1v70qDhKY^evE>d#O)Vy3cY_S?-jZZWE1XB- z#l%K*t!dETfpdo9ZaVG`m@{mn`OjIn+fd9~)HqZk8?tx4iH3a-D$<6V^u;+t+wyhq zoMHLqF6}b>JcNUv3^v#9e}!}4#vdco)X~uG+L~ex*qA}>8TgyjrY-Pr{LRGQ6#Pxb z-_$Mk5jdC17HW%p9JH#p-gS32E#BF*cIfLcFn7S|Oy=iqx?f!7R6K@%ViVg)(bz+j zjBTZsv7OW*wu`#O9;bn^-E>IoNg5T~Lla|9(UGwi>FC%?v@rG>T@(9&ZiszIx5YlD z^|4QoZ{VpeMQI9Rcop(#0|vug+Hy9Fc52J(t)JIY3y!b1&zdE#XNn&g>g%*%{u2g5 z>~peWUr=`JOVsLX)ao1R9{ZMxV&7Bm*bm4bZ#r}v@KD#`-5ojlC!2ZH-I!GWlE-=x zc)1CWP3$*hhFABwnc;i|{u8T*P3Bz~$Afx_`7*{o`^FhwNv>9-Y>+FTwdQe?@FPb4 zu`W?5{`kJky|Puw;RW)FmoO51>|$gUSB$LUiXTZUt?`E+lZz`hpvCnUT>IhgS2v7G zi}>H&_&;zRi#2jFuFv86D6aU^&c+pPd;XC+UQkNf*C-!rEAiuPT=j{FKMq$6BYeeq zOa2Kv87pwT0N0g#Wl_54qjCFu1N~E9C&l}RFWwg#@a8x-Hbv-9da?U`+E*IrK{r-Y z{Mc6;@IEz}gO=;ZzSe+q&~9v&ANx`RPL#W`F+X;11I_Yc6MpQA_`#nSt0A@y2lL&e z5fSX{1uYRwdO<@34Sikfg!SSbIFcS#(v`3a@oOmBhmUc83y)LO8*4K;Dc08-OxL`K zor-3`Mh8ZnGvVyO<#)l%?#VE5{v3qOeDkkC2F#|OfqUKh6aHxHET6oPkG}j_m}hJ@ z{|a?V@uP=$1sU;5Y8J1eM0^S5#;d7id>OTi*HD*uE%k_>L<8f?>5%veIxN1DM#N90 zaq-h=a(orc2fTXG@37MTZ6RF3>`8wf##yV)=JWjPl}7?wQ1~O_SCJmS8U3nHA)ov4d&)e0lv^RrgU76P`1SMK zdnlf(jofNIR{W6p_F6I#Rc}4MQ#&Tj430knrtt1aH;-f7dKCKgcmYh|M9~X^DQ+0y zS>OdC|0to}dON6~a6`wRM+IL%1^1$YFM-k5s6+g9Du~0Or(Wq7)R1Fnnw}!+$|YcoU|Z$6FZsSm#cZ49lM= z@7*I#9AOxi-BNDk59K!Ai&}QrZ0>D?Lp&Vx#^a$vKH;;7plBy%Q=D2vMwrs(%f|+O z?rh+wj*g#`jm4`B2!E5s^;nuu5PDUb^B8en*PPCvtiI#L& zBA-SlTG6ybYdRv)hK^0NrP4%uT9W994tBNA=3bZ0iWD~G!K$X%>=sy znw1z%$0tV8qQqz@LSKyga=jPU{C9;SjFF1)u26(`g(A#OQ-s7!C<0!X`ge-(qOS;Y zBn~OUi%^7io6Q$pMZoy%J>=fsw3zU_qyL*C%!im3Kujkqp!q&W4`19{Sc*%Dd8=~#e>+owRHr3FfizI`x zL$}AX1fKDaDbdF4<#Cy>OPe9Z7?ijjHM*M)NjyNq60Am#`RBD-L5XMTsG zL_NZIORHP6r(kxo;eB>%xr#egnkj6Wz#AzRsA6e_n({jW{61;(LC2}vwQJd})~h*O zlcNS>fr#}~rM^xW?1BR~jch)Vrj3_oi#;+mMQA>h9q$oplX#2{NbI6fiQP0I@g(H@ z4CMSgl_&O6ZQ^A*CGiTKmUx5COzcC)>!Xj)(DC+(jyGC%JZuVK%GoD6-f`(2kIlDT zn092FUfR47$W0rD$Ee!Y-iZ-{lUj{Q-g#^LeoeIeFyQmpuC5XAbv5bK;YvE~c~ z!+8C2lMKItdUV@tzLJsQ{TUdZj0#Rc1*f5c)4}kOV0ac7E(XKb`2{s*m<#HTg3=jY zg#2K8R!0`28cr_Tl-Y=*z!(32L6qL^JI^=gh@_P{Ztp~$n^;sDh z{!lPHK{EWIVE9A9@Z5BUcYtBMLfg&b6xUW8=i6$pp&kXB&DR9O7#w^DUt74x&|w~_ zw=WbvzPk|!8**B1iZsO9HRmOH~KdxELs)0x5wgI}jD^H81yQ<&3~Vr5ccX2bq2 zar*T6No$q9+#ks$4L6K=JmlQ98;yF-c8kQu*%b>Uy6q)+5 zq<94s=Yt}X;!BEGK`|c`nG``%yatL`0T+*9a|+*4^? z?rGGJdpf<4yNW)~J%hf_JyVJ1o~7jEo~;z+o}&!RJy)5Wd!8~Q_X0HOAbqy07QWFC z+E5ZkLoUv3@%!;zx`8k)cvZ;u3EugzN1QsVx4sE#EEVu>DBnF4$-S4da@XONGwU9sE{(J;m?m)s zGDEud{9Am{vs_;t9N6L)lHTCLE2Q0A0!EjP?6crARMNfJk{4L7K-Z%I+M;p_#C=WTth3w+)p8o&mfP_A&;*hk8dE4?;(#L z=(yY;X;JP^bYkw$v?BLkbb9VDbV=@SkoP@0&Nhqj_XlVi8>rX9kCGIw;I_gUcp>6o zcPa4Q@%xe#`0Dtob-;Bvt{wS`orRlt7pUHzP=s4t*3e}6dqnrtxHuMqV~3!;4ewiF z#9_gD`$#yM4Zc{?cMQwcE`$+YxIW*4Us>E2g*RTsLBV7TGLkJRl59%@lO1SqvLlU8 zcA>+Q1$1n(h-N4IQfaauosjHLCng8b^5g+@N^%IDn>>WhPY$CilZVpPN&F5$#m7(F zo-z*nb6?vhcQ(Yf%~kbQ9S?V6+h!IEMjG$zXJj8I{FQjmNbol}?S3@x{oGV<-M$u` zNZ#IoUlO}JLU*r)*Fo%+iNdhwbn99=9x+$2Zvo_2)LYlBrAdgvcA1E&tG8}hOG6NI zZ5<8tE7U8rm}rGMbmc$J;o5rZCbvM`-WW_=Vx&vXCM$V7am0dL?-V zy^}l#QpHC~T&=nmGG(is&!s%iM<&_vKWtcAmg5Ngu@8W~$=&hS7JvQlH-rxltyggn zr5)cr$ahJ;+rW1?f4bM3PaVsb`I2|j(BwUI zSn@s^n_Nc|k`K_df2dL&<_KFL?8fATdtI0=Une_O>ZdzD-ED3tv&m(ANT zW2AfrLc=b{AMaQ4BB?X}dU4s?eTi)IYw>Wn76mv3NO=m5>!}pU!^?{Dbn203QlC7F z`sYRH;Jhdul7~-J<;77Oe7wc24Ij_t)+$A9Ug6pthuVynwVCPa{BL-EF#cG_SoaYx zPG67Yn(g5F^x(Uld?(uP5x=&N`L%sCTw8IePI}EkQS7$P`nm$Ns<8t^_=)B3qxE?xZ^b5eT{6xBE6+AZ(GOAu@pMAp}{HunSRq zq)D34l61%J4zj8!?hE1$uEU7TJONRl6GRqS)EPGv6@1Te0Yqm2M`zq-80Gz^?oIdY z(3#=A@AdcHuDev7I(6!ts#8^`P5~!)3(ENqP_Pmd^nrp^pkNL1#=$m5-qQ`r^#%kaIsU!C5{jsPJwx6-tt*fO#{)QM_J4f(lJ>Dm0pL)2Xl-xIGWtwg9&mK!vTK z!ZuJL1}eOSym7Wr3Mw3Gro!kXD)8D22`a?QIq6jR06Be#oQ@!;qoBfnfeN1>2OMFP zqIB;xm(Iw+sAFFBVR;c#E24DU;-%Ye&QmWPY(sPYfjrM5&wrwHzo2yIs8Gg{gmOBx zF`Of1UZjx5K|;_XU^4|)*`oCWH(R(@DZ9^T$T(bJA#izRA0UmqBe6ccl;I9`u>qoBk~4(RXrR`b93lv?R{sGSe1ojyMDfWlw9#W^1w6gPJ^OWZKG?|CkIM(a}V+TQ7 zobPmjv9CkyxO-Zx55wXae911!49CL4+BZx0MD+s92s^O**azWyYfwem_KYKF1~Ez3 z2z?qo2?k&Yu#U}(r$RYSJBJ~}wv6|izla4?U2T@cmk;7!co9|qk}bZx-TWnumu+=u zXT_KI;$L{_R{!$ziqS&R=IxmWx?&iDF(Kcw)s+GV8rY5vE2fKKw-{l!fp2ZkV9SRQ zh;mxDH$EM+KLmp_%LvOrYmU{kj@Ux(G`9cSAKh!Yn@w6`_FgNk(uQ*<@Zrr@#ZdfB zY?~Rz1i2equQ)rlAV7fpUM2S9$9LHM%_{Q_`{xpz8D{a_rxJU)!?29!rQ^f0wiuNO zhhD%E)4X9{3%*1|C%<->u8(&480_;r-Sly^apt(9hEoH33&^BCBH~#<*kxc-X?XFw@VJ( zd*mjmul$lUK;9*l$h)N}@*e3bd9O5AJ|Nv7za~8`zb!p0ACg{_-vx8y97(;|m)-&c zVwFzn!=AffSQfK8;Y1N`&bq)Yt_ng3t`sih+T&@~TFMl$cv`$gMGm)&@TlvAJ=`~qZR7asfe zg45O`{PzcJe1JMHf%(5UvCNJF*%k*qST zxaH3&NB)9T`6T7ZU!vMiQL%iQCdg-Kru;R{#{FFRZxoWhqp18nTKFGmx%>~hNj{5~ z{%5*R{)N^$B--e((pE=0?Qqy>wxR{PQI@0%!97%SllIrLz^>*;~ z1{^YXVJ(rPCl~>z(OqC{^eTKlXi{vm$?#_0Vu%I0&8V*J=wc0`h?N6J3E3QDsjUNx z;ZVGr6|aOPZp>DmNFsDE2XxQ0Slq~WHF~df04*|HU2h-G9yzv~7X$NPk+3;0+cH#j z$86iNc<69r?*RBrxO#;2&exsOu_E3ct4#3b4wq~k>QXCqYdEUN>Zqnnho9Ox=8?-$ zLtPvJ>g~YRM#lmgn~4FM?VPUK1r zEM|)(X14Sy`4q4|1FYlNoQLBQbvRNfsEiQ~9cuRFbqdRppyBYouhK2plXp@gZkc!3 zk8{%+;ky%f1-rz!y?cMgU&+{Ona?JQFQ*1lzU!(!HL5;zjV5J@H$2%L&v8B-|cVW2*fKi8?mNzTeVjB>yED}-SHPN=r`2f@i$QA zJL>89p1h79X@KJ=8tM3%#yZZ?6bv|KJ1rD&S}EjAr?AsbH#xKD9=vzUp*g^SH83wDudlM9D2z2L+nB$>d(nAj=7WE~#V*n=?wDOmRGY8? zak{Ca6Eo^gjhxPI4pAGT#|OCTuP2PaJC({Ko~!T-4^Fbg2u@j%k+TH2R8?<1QNZ3t5I zF;cK?KT8a3`#D8R@DQ)Ggrf+K%;0T#7I-^3FY+lsemWK86p3T_vV!`*w~9@u_touSW^dw7ptHdN`%(Yg z07ryE<0$K!x1KJ7EZX}*0O!8|@=ahTPNG6sUzw;Im`{QVl;>`=1vti17Ty6+)?uy7 z4GpJ2fGX^OVB%V8j{x3Mj1+iSTDIYw$r4N0%_)<}rc8l@0r=firoqe63>bM|MTN?2 z8l+rJgOy4euFR!zN)=Treh^?DU8Bqg5w1ZIa5%M5pd#=nv$O&yz})dcS!=H=Ya2u( zJ|@whh-EsZmaIx0tjvSdNePoviGt}rh|3hr2n5r!Iy@?oRP3u!mLuu)kX9TiZRC!j zp~&6E4H<8(?uc2`_pxIU1M2TfH=O&XrPCQ@6{Rcpp~S0^^#g!)x3#(b>T6UDtDK9= z`d;#^VFHwpQXPxm00Q>)rQwA1B*g?)D*|;et8G@x#z5JKRGTpF^T%77vtR_aRolS% zmFATJ`)D z3&F?|g3%fG@g?IP5DcE6*Cpd|#fnyVUkLzUe$O4$P$dWA}redx9K(^BOCty2!tdgXPHFX4M* zEeOGS7Tc{~I3ao_2w^#&5Oy9qjZQF-nAK3;1I~wm^ZUU0BjEgB!1-g~{72yYDRBM_ zIDZbDj|1luG)4Imcpi!K=Eq0ipc{30W&UiRzYkumb;QU2xwSULsAU z(`)1>lP ztafKxcz0K}#mOBVd;rAkcf=oFFh4vm91v?Wu7&ihYY~JBr?4BP z9c8duZv!dx-Z#YT5rG=+(VN@0i3ij*z_k5yC zMC0wci|nquIf*r3Ai%Cnb)D>zR0$rU4A;XV!8JyL(FVJ?^r?sTmt6%`hD_Tsj=m*#!+N*6t!vc8Y`4@*Vklq!KtY0 zTWah2E2Q^3h|Bko)W_nWF9To(oda>mVlb~^%c23!0|IR6gp78>T&a#h zsxm^rOkPDrA>E_N>Mo{qcL`vQC8v8Fb#-%d^q937HV>;={$;fN$%OUmm+ zr(!D4*Am_%fcGfzF&!N+!c^Rk3wX;f6kcUZc>f7_&jB8`Ubr^{-t*vzGkmm%kjl%U zn{j8)&XkTqKi6@~yJV+jTZP(a4|3>1D8~8CBMZGsc7cw&=_2=D>gIk$0A2ELfo^A@ zp`DiP3^bGi=sN&<2teNh(8B`g4gVJC0S3Bqr{w?xU6}&t=Ky*PKu-YZ7Xm2MYf=0O zSRr8LlzmR1_*t54g@H9#9R%22p*7lNJut&cQ=ZK~+q zXm8kQX{vAn+lCZyoq(xev%DL7B2@^@(dO<%lEDIOMe}5~1!r!oDCN$H2VX8oVi<0` zJ0iUHHP+Vb_0ZP7!lwrPRZzrumj0t}`A$n+MP7cR?VvcssG7xSx3zj89P&IyBNcej zDdefFnEjq?OCb*ET0^-zEy0RBC8N>yDw1FaW*(-+@14nKePDGUeTeOamFed;oZDyQ zdJj)}ETfflo8=`D-fdy|Lj{Xol2|JQS^{{1wEn8!V$5jah{IujpB&W zf8LA*m&?$!%twECDQfqvCR;vkFT?HI`t2m#zNg<##_eJKb_#Ca*KbGS_FcY3c>}x~ zY(IzD7GA(Pq(|XR_V2`_WLwx0<%8zugoF!)SVfDYqQh73fj5MEsk6F@`l$EO0ChEu zRPUz=>Vq^%eTZhNkI-E8QL0ndQdoVQma9+DP3lu|j}@Cx+uELYr4GjIx0ZuY`OjjljX&(Q z+#j<&3L7Gj%=GfXm~HI_nw@Q#WP15{%(iX=U7l^3Y6copYVJ;oJg=ckI@bZ=m+fn%iu)wS+CZdw8&sm%rE2fRi`Ddd3-? z@j{F;3Ngfr`cbTjVmuPFJ&BpsY|C)-+lOMd#}awI+%mH`*gO;1sb&Dbj@fWbX*1Fo zKqX@fx}S!xDh{BK542iXzH;Cng_WAqJd_nnLl86`MXXH6-0m?xGw}^NM29U~=m%-I zG|I9?DwD34R^VBepRZ{sJI9wzC``i-859)L&fSV zV5xmHOFcky)PrEB*C?vK4p#aDLJ>yzDuvD@6+$qkLV5cV>+aM zMTgZt(g*4n^hfn1eWw1IPN=8oFX|ckLH$-D^{-N<`kmB4{a)&#{wVcQf0BNs{wxjm zw2?+(*frW?mnL{Jr7}-DX{P65snXLyn(yf-E%eCJ^`2a5g-4NY_H>o*@^q8#_4Je; z^7N9P^z@c~>**uKJbBW~xZmf=7c8HFwdqF7CTN_St%}jUPDDG%DoGa*bHDJ2yB~`l z1K8QXe!SX^bxY{!BNFl_bREw)O81PXEKeC_dnS|1Gle>Pu!BRAtr%{KZYD8g1mG5` zy~l?(eq|g7c5=eHAMdHe`EYwNI$XYoca<(mWGnh#Pd!|sT}y2}5$fQHV)kuye1v)z zAaFT~fh}g%c%BWDXFSh_$upk2#5|ksY}i;A$5JmZuN=UqV>o*^;jt3?gDfzb8c4$c zZV*hUI2?wfxj59BN6ejjZYI@(W4JuGQxDG_)Yo$-4f5PgMV@;Q&1oHZ9vzx&J6q79 z^I#mY?8E|(PO`zE3M;S9Jaz@8&lUi^r4un*fXReaiPS=~>pY6ubS|Ro&SRu3X%Y7a zr~txgVCPrBUlK{h*s61#h?{Lh-GLIL6tP-f+gDY6{pUvXU_@c`r}10yLbeP@YihP5k3z2v7_;b1wVdl zCmuzSuSRVg-eLbzBBs$KsK~$8Rp>En9DvvQ964N+#O&*I=L~s((4HY6^3%HGH~3G; z!Sd8sygbDjbTBf&yi*P`z>wux{R0m8TtBlf&N3NdhpB*l4~l7{BT!}t*T*8%iFiGn zn2~k}M0}Sn*4`soJ4~Im52%OsA?0gFX@K?-4b%RcinWhvqV@?0_$ghfeNH~@7%k9_ zQ%E~O4cZs9L_0}0YhTjs+E=tn`x8B={h1!uPSHB;G;Pq%&{pkh+Nu3O`?MeFP3QZL;^pDK+R8n{2~&T0Voj zt%64TMxo9foCqC>=k!X{~Olxw-h60qZBbxSRt@v}gZMHn=~kY$1WGzPx)2+vrpmTM7W_-zRz#HJog z1mTZxB@jjU4|E7O+JCnHgo9yMApAQyCr`A%8yWxB%sn$~*V|RcuEsEtFMd0E#`w}P zWhDiX`Q5vE=LUk_$b2n-QC3#O9~EJDZJa;s*8&l3e&{+aIv+Q_aMk?4Lh){HL!dTV zkkwu5Jt`tgE#A zYAqL^wX3yd%e19gnl_?mq2{leAJV#&hqOpT)qJfw5cXF^L*d0kMAUAG76{_ArWIFQK689|p4LY^;d$nm;g$8*Racsys}|SP`=Tgl zotBFT&EFW?O7l0oX#O^#XlnoA9NAkr7deY;8UMW z*J=6lv|L6*{P#m`=(U-Zl@$&9>a|`*6r+^dP`%>%_tM7An5>O2pRP@rF@Cx>y<}=x z7Px1Oe_pLG>eoO@ZC;?(AL*+_!2?lWII3M2h|Vw2z+iQ#`DTs?_#d0KvbI1r3#%e2zsImMGoON*wL3@_Fq;6`nPmTRDr1^Q}L3!Bj&mAlmV0G8@L z)n69^W1DhG&ZGKY<*`hgowX!TFBn^pCVv!{&43vRflo!*3UrtW!OPRG@h$WvK(_)W z0!i>g0z=??0Z8*j@#xq5!D=SZ_+V9SLv<4N%`{5n)gr6&DU}}v0cU6VYr=k1qmghn znulex3#w{;kw~wW#O&A53N^Jd-vZQWQ1i{58}=^@poXh8P)4f?1*5(|Fc7TKLc!X_ zq7dC`LZRwzn#dxFPC9KasEQL+X|Cjc+J#kefx$@sn@aAdaV2NfMzs9eWPwN)fULid;^V@nDe53z1CW$oVchXmX(C*G%zy%x}bCBX1%%~ zP;E~2-MH5a)uVHsS<#;8j_yQ#&55WQsj5PC-bh0bE!aYzw;>v+<+jXQ@2gtiEoRP{ z84g7K;q&qN29Tq>z9A(6d}POe33*HyT~P|4OrB4cTz~&Km1xlj!V@iR_l$o5(C03e`qL zjaPY5|MlLYXtb5uxN(?&;G3x4`lgmu{P6MA0P6)^4Duwf%o2(V4k-jl5?Em!$^1Z# z`4ai->Z6MbqKl$tM&@KS-nARA%$DdzTs+E3{60alK%K7!6*d=~TN7r!D5wd{YYAfb zjz7aU9FD`q7?mX{@mpGy)(oP$VSztY;v)s_HO+$CA(0Yylg6Y`sJc4h9qp?wD6dPD zy1DgW^s7&GF6PZYEceODPnwdWDpDuE@Ao5~W`+LzJDE}w zRgN>VFY^T#Gb0wJhG1Q|=7*`!e}}ed3E|dBEg(cvLwM$aEtpx=_s}Jiz%|2wQ0|XJ zbBj|aUp%%zPF?*kG$)@_UEOL0EZJH9{sH7Z2vkgFy>V9KVa6bUF&3I{WuKTzUM^MZg=a zr#>oa1~KS^Gz$BD)ph<<5^L`2l52hB@idAw%!Sg{-}|q+yc;dQ`CIv9qOAe=>1xhY zgdfEr6naN5j{2vEis$>n#r6F!9eio(Qv7sj?y=j!zda!QymvD3iwv+B*d)U$95gsJ zsNap-S8)|6aTsutJE^gQ5};5(y^Muk#?4oSzGRFq>g(4(HPo16+HF;1!6HYoER?_w zcRv=TggVAwUmIFn=MP5bT+>STbN_-5k9~_tr97BK4^D!)`Sxa*mqG(DFg5OUw3D$e z^+f~0Rxp1j_Sp2d`oSS2S|=?jm-N+~OVI1PF6y1tzrS}@F*=-35GYUfx7JzL9jN}| zH$#Yy+^D-%OaRpOwSYDqUD<*_D{Q-K=7@f?(A)G!M+=R@^lS684o3_=ypyL*=0S)* zJRvkUQ8VTqt31?gZUC&q7u%L6ew(~jO6X2yq59MSj@|P1qSOE`FH8auXa&G$l?BUE z1NeME5&&N-0LHeTc-@s~KKN1+_~UNrpcVjfOM=nx;?xCM)J8tOPb17Zv8#UbukB@K zFypIH#ewKz^a}IKTEpqy^xvIiwUn=9oZ4Jz2F5_5zPYs~z1Unk_bCs5IH4`v!r@A; z=Qoy{!4%DnV4zzSEe)6(Iy0oIgS(DBgl>v2D7L2Gq&r)Kl*{18n-wTC#Nr;Sj_g3U z#g}G+2MJ%@D#TWyo!b4vy;}i=ujJ;?^GK`EOfqZ6j2mZ&t57JC)v30R8nD83f%95t zGl(;x?lEi`AilaL6>fbqh+1C|I(0A@ilX@l1-;XK;Tpe5Ud`Y-?b&wrG(ho{O#Ro^ z8W^<%H`O0yJ`0)jVKcINEs6%9CN;rZsd2Tx>0j~V&FC+1CF8pHJtG%xh>P{Hyt(}~Q>|zFmB+?mqLZ&=C}*xn*IPRs niqVpvk4vga%LM5blxCQwFegt;hqMt^dIr)wXhz>m;m7|0()8*F literal 0 HcmV?d00001 diff --git a/jdk/test/tools/pack200/pack200-verifier/make/build.xml b/jdk/test/tools/pack200/pack200-verifier/make/build.xml new file mode 100644 index 00000000000..76e5f72cf89 --- /dev/null +++ b/jdk/test/tools/pack200/pack200-verifier/make/build.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/ClassCompare.java b/jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/ClassCompare.java new file mode 100644 index 00000000000..63a66f765d6 --- /dev/null +++ b/jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/ClassCompare.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.tools.pack.verify; + +import java.io.*; +import java.util.*; +import java.util.jar.*; +import xmlkit.*; + +public class ClassCompare { + + /* + * @author ksrini + */ + private static XMLKit.Element getXMLelement(InputStream is, + boolean ignoreUnkAttrs, + List ignoreElements) throws IOException { + + ClassReader cr = new ClassReader(); + cr.keepOrder = false; + XMLKit.Element e = cr.readFrom(is); + + if (ignoreElements != null) { + XMLKit.Filter filter = XMLKit.elementFilter(ignoreElements); + e.removeAllInTree(filter); + } + + if (ignoreUnkAttrs == true) { + // This removes any unknown attributes + e.removeAllInTree(XMLKit.elementFilter("Attribute")); + } + return e; + } + + private static String getXMLPrettyString(XMLKit.Element e) throws IOException { + StringWriter out = new StringWriter(); + e.writePrettyTo(out); + return out.toString(); + } + + private static boolean compareClass0(JarFile jf1, JarFile jf2, + JarEntry je, boolean ignoreUnkAttrs, + List ignoreElements) + throws IOException { + + InputStream is1 = jf1.getInputStream(je); + InputStream is2 = jf2.getInputStream(je); + + // First we try to compare the bits if they are the same + boolean bCompare = JarFileCompare.compareStreams(is1, is2); + + // If they are the same there is nothing more to do. + if (bCompare) { + Globals.println("+++" + je.getName() + "+++\t" + + "b/b:PASS"); + return bCompare; + } + is1.close(); + is2.close(); + + is1 = jf1.getInputStream(je); + is2 = jf2.getInputStream(je); + + + XMLKit.Element e1 = getXMLelement(is1, ignoreUnkAttrs, ignoreElements); + XMLKit.Element e2 = getXMLelement(is2, ignoreUnkAttrs, ignoreElements); + + Globals.print("+++" + je.getName() + "+++\t" + + e1.size() + "/" + e1.size() + ":"); + + boolean result = true; + + if (e1.equals(e2)) { + Globals.println("PASS"); + } else { + Globals.println("FAIL"); + Globals.log("Strings differs"); + Globals.log(getXMLPrettyString(e1)); + Globals.log("----------"); + Globals.log(getXMLPrettyString(e2)); + result = false; + } + return result; + } + + /* + * Given two Class Paths could be jars the first being a reference + * will execute a series of comparisons on the classname specified + * The className could be null in which case it will iterate through + * all the classes, otherwise it will compare one class and exit. + */ + public static boolean compareClass(String jar1, String jar2, + String className, boolean ignoreUnkAttrs, + List ignoreElements) + throws IOException { + + Globals.println("Unknown attributes ignored:" + ignoreUnkAttrs); + if (ignoreElements != null) { + Globals.println(ignoreElements.toString()); + } + + JarFile jf1 = new JarFile(jar1); + JarFile jf2 = new JarFile(jar2); + + boolean result = true; + + if (className == null) { + for (JarEntry je1 : Collections.list((Enumeration) jf1.entries())) { + if (je1.getName().endsWith(".class")) { + JarEntry je2 = jf2.getJarEntry(je1.getName()); + boolean pf = compareClass0(jf1, jf2, je1, ignoreUnkAttrs, ignoreElements); + if (result == true) { + result = pf; + } + } + } + } else { + JarEntry je1 = jf1.getJarEntry(className); + result = compareClass0(jf1, jf2, je1, ignoreUnkAttrs, ignoreElements); + } + if (result == false) { + throw new RuntimeException("Class structural comparison failure"); + } + return result; + } + + public static boolean compareClass(String jar1, String jar2, + String className) throws IOException { + + Stack s = new Stack(); + if (Globals.ignoreDebugAttributes()) { + s = new Stack(); + s.push("LocalVariable"); + s.push("LocalVariableType"); + s.push("LineNumber"); + s.push("SourceFile"); + } + return compareClass(jar1, jar2, className, Globals.ignoreUnknownAttributes(), s); + } +} diff --git a/jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/Globals.java b/jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/Globals.java new file mode 100644 index 00000000000..2a51474bfde --- /dev/null +++ b/jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/Globals.java @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2010, 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. + */ + +/* + * A collection of useful global utilities commonly used. + */ +package sun.tools.pack.verify; + +import java.io.*; +import java.util.*; + +/* + * @author ksrini + */ + +class Globals { + + private static int errors = 0; + private static PrintWriter _pw = null; + private static String _logFileName = null; + private static final String DEFAULT_LOG_FILE = "verifier.log"; + private static boolean _verbose = true; + private static boolean _ignoreJarDirectories = false; + private static boolean _checkJarClassOrdering = true; + private static boolean _bitWiseClassCompare = false; + // Ignore Deprecated, SourceFile and Synthetic + private static boolean _ignoreCompileAttributes = false; + // Ignore Debug Attributes LocalVariableTable, LocalVariableType,LineNumberTable + private static boolean _ignoreDebugAttributes = false; + private static boolean _ignoreUnknownAttributes = false; + private static boolean _validateClass = true; + private static Globals _instance = null; + + static Globals getInstance() { + if (_instance == null) { + _instance = new Globals(); + _verbose = (System.getProperty("sun.tools.pack.verify.verbose") == null) + ? false : true; + _ignoreJarDirectories = (System.getProperty("ignoreJarDirectories") == null) + ? false : true; + } + return _instance; + } + + static boolean ignoreCompileAttributes() { + return _ignoreCompileAttributes; + } + + static boolean ignoreDebugAttributes() { + return _ignoreDebugAttributes; + } + + static boolean ignoreUnknownAttributes() { + return _ignoreUnknownAttributes; + } + + static boolean ignoreJarDirectories() { + return _ignoreJarDirectories; + } + + static boolean validateClass() { + return _validateClass; + } + + static void setCheckJarClassOrdering(boolean flag) { + _checkJarClassOrdering = flag; + } + + static boolean checkJarClassOrdering() { + return _checkJarClassOrdering; + } + + static boolean bitWiseClassCompare() { + return _bitWiseClassCompare; + } + + static boolean setBitWiseClassCompare(boolean flag) { + return _bitWiseClassCompare = flag; + } + + public static boolean setIgnoreCompileAttributes(boolean flag) { + return _ignoreCompileAttributes = flag; + } + + static boolean setIgnoreDebugAttributes(boolean flag) { + return _ignoreDebugAttributes = flag; + } + + static boolean setIgnoreUnknownAttributes(boolean flag) { + return _ignoreUnknownAttributes = flag; + } + + static boolean setValidateClass(boolean flag) { + return _validateClass = flag; + } + + static int getErrors() { + return errors; + } + + static void trace(String s) { + if (_verbose) { + println(s); + } + } + + static void print(String s) { + _pw.print(s); + } + + static void println(String s) { + _pw.println(s); + } + + static void log(String s) { + errors++; + _pw.println("ERROR:" + s); + } + + static void lognoln(String s) { + errors++; + _pw.print(s); + } + + private static PrintWriter openFile(String fileName) { + //Lets create the directory if it does not exist. + File f = new File(fileName); + File baseDir = f.getParentFile(); + if (baseDir != null && baseDir.exists() == false) { + baseDir.mkdirs(); + } + try { + return new PrintWriter(new FileWriter(f), true); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static void closeFile() { + _pw.flush(); + _pw.close(); + } + + static void printPropsToLog() { + println("Log started " + new Date(System.currentTimeMillis())); + print(System.getProperty("java.vm.version")); + println("\t" + System.getProperty("java.vm.name")); + + println("System properties"); + println("\tjava.home=" + System.getProperty("java.home")); + println("\tjava.class.version=" + System.getProperty("java.class.version")); + println("\tjava.class.path=" + System.getProperty("java.class.path")); + println("\tjava.ext.dirs=" + System.getProperty("java.ext.dirs")); + println("\tos.name=" + System.getProperty("os.name")); + println("\tos.arch=" + System.getProperty("os.arch")); + println("\tos.version=" + System.getProperty("os.version")); + println("\tuser.name=" + System.getProperty("user.name")); + println("\tuser.home=" + System.getProperty("user.home")); + println("\tuser.dir=" + System.getProperty("user.dir")); + println("\tLocale.getDefault=" + Locale.getDefault()); + println("System properties end"); + } + + static void openLog(String s) { + _logFileName = (s != null) ? s : "." + File.separator + DEFAULT_LOG_FILE; + _logFileName = (new File(_logFileName).isDirectory()) + ? _logFileName + File.separator + DEFAULT_LOG_FILE : _logFileName; + _pw = openFile(_logFileName); + printPropsToLog(); + } + + static void closeLog() { + closeFile(); + } + + static String getLogFileName() { + return _logFileName; + } + + static void diffCharData(String s1, String s2) { + boolean diff = false; + char[] c1 = s1.toCharArray(); + char[] c2 = s2.toCharArray(); + if (c1.length != c2.length) { + diff = true; + Globals.log("Length differs: " + (c1.length - c2.length)); + } + // Take the smaller of the two arrays to prevent Array...Exception + int minlen = (c1.length < c2.length) ? c1.length : c2.length; + for (int i = 0; i < c1.length; i++) { + if (c1[i] != c2[i]) { + diff = true; + Globals.lognoln("\t idx[" + i + "] 0x" + Integer.toHexString(c1[i]) + "<>" + "0x" + Integer.toHexString(c2[i])); + Globals.log(" -> " + c1[i] + "<>" + c2[i]); + } + } + } + + static void diffByteData(String s1, String s2) { + boolean diff = false; + byte[] b1 = s1.getBytes(); + byte[] b2 = s2.getBytes(); + + if (b1.length != b2.length) { + diff = true; + //(+) b1 is greater, (-) b2 is greater + Globals.log("Length differs diff: " + (b1.length - b2.length)); + } + // Take the smaller of the two array to prevent Array...Exception + int minlen = (b1.length < b2.length) ? b1.length : b2.length; + for (int i = 0; i < b1.length; i++) { + if (b1[i] != b2[i]) { + diff = true; + Globals.log("\t" + "idx[" + i + "] 0x" + Integer.toHexString(b1[i]) + "<>" + "0x" + Integer.toHexString(b2[i])); + } + } + } + + static void dumpToHex(String s) { + try { + dumpToHex(s.getBytes("UTF-8")); + } catch (UnsupportedEncodingException uce) { + throw new RuntimeException(uce); + } + } + + static void dumpToHex(byte[] buffer) { + int linecount = 0; + byte[] b = new byte[16]; + for (int i = 0; i < buffer.length; i += 16) { + if (buffer.length - i > 16) { + System.arraycopy(buffer, i, b, 0, 16); + print16Bytes(b, linecount); + linecount += 16; + } else { + System.arraycopy(buffer, i, b, 0, buffer.length - i); + for (int n = buffer.length - (i + 1); n < 16; n++) { + b[n] = 0; + } + print16Bytes(b, linecount); + linecount += 16; + } + } + Globals.log("-----------------------------------------------------------------"); + } + + static void print16Bytes(byte[] buffer, int linecount) { + final int MAX = 4; + Globals.lognoln(paddedHexString(linecount, 4) + " "); + + for (int i = 0; i < buffer.length; i += 2) { + int iOut = pack2Bytes2Int(buffer[i], buffer[i + 1]); + Globals.lognoln(paddedHexString(iOut, 4) + " "); + } + + Globals.lognoln("| "); + + StringBuilder sb = new StringBuilder(new String(buffer)); + + for (int i = 0; i < buffer.length; i++) { + if (Character.isISOControl(sb.charAt(i))) { + sb.setCharAt(i, '.'); + } + } + Globals.log(sb.toString()); + } + + static int pack2Bytes2Int(byte b1, byte b2) { + int out = 0x0; + out += b1; + out <<= 8; + out &= 0x0000ffff; + out |= 0x000000ff & b2; + return out; + } + + static String paddedHexString(int n, int max) { + char[] c = Integer.toHexString(n).toCharArray(); + char[] out = new char[max]; + + for (int i = 0; i < max; i++) { + out[i] = '0'; + } + int offset = (max - c.length < 0) ? 0 : max - c.length; + for (int i = 0; i < c.length; i++) { + out[offset + i] = c[i]; + } + return new String(out); + } +} diff --git a/jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/JarFileCompare.java b/jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/JarFileCompare.java new file mode 100644 index 00000000000..419fda1ba23 --- /dev/null +++ b/jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/JarFileCompare.java @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.tools.pack.verify; + +import java.io.*; +import java.util.*; +import java.util.jar.*; + +class JarFileCompare { + /* + * @author ksrini + */ + + private static VerifyTreeSet getVerifyTreeSet(String jarPath) { + VerifyTreeSet vts = new VerifyTreeSet(); + try { + JarFile j = new JarFile(jarPath); + for (JarEntry je : Collections.list((Enumeration) j.entries())) { + if (!je.isDirectory()) { // totally ignore directories + vts.add(je.getName()); + } + } + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + return vts; + } + + private static LinkedList getListOfClasses(String jarPath) { + LinkedList l = new LinkedList(); + try { + JarFile j = new JarFile(jarPath); + for (JarEntry je : Collections.list((Enumeration) j.entries())) { + if (!je.isDirectory() && je.getName().endsWith(".class")) { + l.add(je.getName()); + } + } + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + return l; + } + + private static void jarDirectoryCompare(String jarPath1, String jarPath2) { + VerifyTreeSet vts1 = getVerifyTreeSet(jarPath1); + VerifyTreeSet vts2 = getVerifyTreeSet(jarPath2); + + TreeSet diff1 = vts1.diff(vts2); + if (diff1.size() > 0) { + Globals.log("Left has the following entries that right does not have"); + Globals.log(diff1.toString()); + } + TreeSet diff2 = vts2.diff(vts1); + if (diff2.size() > 0) { + Globals.log("Right has the following entries that left does not have"); + Globals.log(diff2.toString()); + } + if (Globals.checkJarClassOrdering()) { + boolean error = false; + Globals.println("Checking Class Ordering"); + LinkedList l1 = getListOfClasses(jarPath1); + LinkedList l2 = getListOfClasses(jarPath2); + if (l1.size() != l2.size()) { + error = true; + Globals.log("The number of classes differs"); + Globals.log("\t" + l1.size() + "<>" + l2.size()); + } + for (int i = 0; i < l1.size(); i++) { + String s1 = (String) l1.get(i); + String s2 = (String) l2.get(i); + if (s1.compareTo(s2) != 0) { + error = true; + Globals.log("Ordering differs at[" + i + "] = " + s1); + Globals.log("\t" + s2); + } + } + } + } + + /* + * Returns true if the two Streams are bit identical, and false if they + * are not, no further diagnostics + */ + static boolean compareStreams(InputStream is1, InputStream is2) { + + BufferedInputStream bis1 = new BufferedInputStream(is1, 8192); + BufferedInputStream bis2 = new BufferedInputStream(is2, 8192); + try { + int i1, i2; + int count = 0; + while ((i1 = bis1.read()) == (i2 = bis2.read())) { + count++; + if (i1 < 0) { + // System.out.println("bytes read " + count); + return true; // got all the way to EOF + } + } + return false; // reads returned dif + + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } + + private static void checkEntry(JarFile jf1, JarFile jf2, JarEntry je) throws IOException { + InputStream is1 = jf1.getInputStream(je); + InputStream is2 = jf2.getInputStream(je); + if (is1 != null && is2 != null) { + if (!compareStreams(jf1.getInputStream(je), jf2.getInputStream(je))) { + Globals.println("+++" + je.getName() + "+++"); + Globals.log("Error: File:" + je.getName() + + " differs, use a diff util for further diagnostics"); + } + } else { + Globals.println("+++" + je.getName() + "+++"); + Globals.log("Error: File:" + je.getName() + " not found in " + jf2.getName()); + } + } + + /* + * Given two jar files we compare and see if the jarfiles have all the + * entries. The property ignoreJarDirectories is set to true by default + * which means that Directory entries in a jar may be ignore. + */ + static void jarCompare(String jarPath1, String jarPath2) { + jarDirectoryCompare(jarPath1, jarPath2); + + try { + JarFile jf1 = new JarFile(jarPath1); + JarFile jf2 = new JarFile(jarPath2); + + int nclasses = 0; + int nentries = 0; + int entries_checked = 0; + int classes_checked = 0; + + for (JarEntry je : Collections.list((Enumeration) jf1.entries())) { + if (!je.isDirectory() && !je.getName().endsWith(".class")) { + nentries++; + } else if (je.getName().endsWith(".class")) { + nclasses++; + } + } + + for (JarEntry je : Collections.list((Enumeration) jf1.entries())) { + if (je.isDirectory()) { + continue; // Ignore directories + } + if (!je.getName().endsWith(".class")) { + entries_checked++; + if (je.getName().compareTo("META-INF/MANIFEST.MF") == 0) { + Manifest mf1 = new Manifest(jf1.getInputStream(je)); + Manifest mf2 = new Manifest(jf2.getInputStream(je)); + if (!mf1.equals(mf2)) { + Globals.log("Error: Manifests differ"); + Globals.log("Manifest1"); + Globals.log(mf1.getMainAttributes().entrySet().toString()); + Globals.log("Manifest2"); + Globals.log(mf2.getMainAttributes().entrySet().toString()); + } + } else { + checkEntry(jf1, jf2, je); + } + } else if (Globals.bitWiseClassCompare() == true) { + checkEntry(jf1, jf2, je); + classes_checked++; + } + } + if (Globals.bitWiseClassCompare()) { + Globals.println("Class entries checked (byte wise)/Total Class entries = " + + classes_checked + "/" + nclasses); + } + Globals.println("Non-class entries checked/Total non-class entries = " + + entries_checked + "/" + nentries); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } +} diff --git a/jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/Main.java b/jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/Main.java new file mode 100644 index 00000000000..481c67b15ce --- /dev/null +++ b/jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/Main.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2010, 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. + */ +// The Main Entry point +package sun.tools.pack.verify; + +import java.io.*; + +/** + * This class provides a convenient entry point to the pack200 verifier. This + * compares two classes, either in path or in an archive. + * @see xmlkit.XMLKit + * @author ksrini + */ +public class Main { + + private static void syntax() { + System.out.println("Usage: "); + System.out.println("\tREFERENCE_CLASSPATH COMPARED_CLASSPATH [Options]"); + System.out.println("\tOptions:"); + System.out.println("\t\t-O check jar ordering"); + System.out.println("\t\t-C ignore compile attributes (Deprecated, SourceFile, Synthetic, )"); + System.out.println("\t\t-D ignore debug attributes (LocalVariable, LineNumber)"); + System.out.println("\t\t-u ignore unknown attributes"); + System.out.println("\t\t-V turn off class validation"); + System.out.println("\t\t-c CLASS, compare CLASS only"); + System.out.println("\t\t-b Compares all entries bitwise only"); + System.out.println("\t\t-l Directory or Log File Name"); + } + + /** + * main entry point to the class file comparator, which compares semantically + * class files in a classpath or an archive. + * @param args String array as described below + * @throws RuntimeException + *

+     *  Usage:
+     *     ReferenceClasspath SpecimenClaspath [Options]
+     *     Options:
+     *      -O check jar ordering
+     *      -C do not compare compile attributes (Deprecated, SourceFile, Synthetic)
+     *      -D do not compare debug attribute (LocalVariableTable, LineNumberTable)
+     *      -u ignore unknown attributes
+     *      -V turn off class validation
+     *      -c class, compare a single class
+     *      -b compares all entries bitwise (fastest)
+     *      -l directory or log file name
+     * 
+ */ + public static void main(String args[]) { + Globals.getInstance(); + if (args == null || args.length < 2) { + syntax(); + System.exit(1); + } + String refJarFileName = null; + String cmpJarFileName = null; + String specificClass = null; + String logDirFileName = null; + + for (int i = 0; i < args.length; i++) { + if (i == 0) { + refJarFileName = args[0]; + continue; + } + if (i == 1) { + cmpJarFileName = args[1]; + continue; + } + + if (args[i].startsWith("-O")) { + Globals.setCheckJarClassOrdering(true); + } + + if (args[i].startsWith("-b")) { + Globals.setBitWiseClassCompare(true); + } + + if (args[i].startsWith("-C")) { + Globals.setIgnoreCompileAttributes(true); + } + + if (args[i].startsWith("-D")) { + Globals.setIgnoreDebugAttributes(true); + } + + if (args[i].startsWith("-V")) { + Globals.setValidateClass(false); + } + + if (args[i].startsWith("-c")) { + i++; + specificClass = args[i].trim(); + } + + if (args[i].startsWith("-u")) { + i++; + Globals.setIgnoreUnknownAttributes(true); + } + + if (args[i].startsWith("-l")) { + i++; + logDirFileName = args[i].trim(); + } + } + + Globals.openLog(logDirFileName); + + File refJarFile = new File(refJarFileName); + File cmpJarFile = new File(cmpJarFileName); + + String f1 = refJarFile.getAbsoluteFile().toString(); + String f2 = cmpJarFile.getAbsoluteFile().toString(); + + System.out.println("LogFile:" + Globals.getLogFileName()); + System.out.println("Reference JAR:" + f1); + System.out.println("Compared JAR:" + f2); + + Globals.println("LogFile:" + Globals.getLogFileName()); + Globals.println("Reference JAR:" + f1); + Globals.println("Compared JAR:" + f2); + + Globals.println("Ignore Compile Attributes:" + Globals.ignoreCompileAttributes()); + Globals.println("Ignore Debug Attributes:" + Globals.ignoreDebugAttributes()); + Globals.println("Ignore Unknown Attributes:" + Globals.ignoreUnknownAttributes()); + Globals.println("Class ordering check:" + Globals.checkJarClassOrdering()); + Globals.println("Class validation check:" + Globals.validateClass()); + Globals.println("Bit-wise compare:" + Globals.bitWiseClassCompare()); + Globals.println("ClassName:" + ((specificClass == null) ? "ALL" : specificClass)); + + if (specificClass == null && Globals.bitWiseClassCompare() == true) { + JarFileCompare.jarCompare(refJarFileName, cmpJarFileName); + } else { + try { + ClassCompare.compareClass(refJarFileName, cmpJarFileName, specificClass); + } catch (Exception e) { + Globals.log("Exception " + e); + throw new RuntimeException(e); + } + } + + if (Globals.getErrors() > 0) { + System.out.println("FAIL"); + Globals.println("FAIL"); + System.exit(Globals.getErrors()); + } + + System.out.println("PASS"); + Globals.println("PASS"); + System.exit(Globals.getErrors()); + } +} diff --git a/jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/VerifyTreeSet.java b/jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/VerifyTreeSet.java new file mode 100644 index 00000000000..1e4c32b2af0 --- /dev/null +++ b/jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/VerifyTreeSet.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.tools.pack.verify; + +import java.util.*; +/* + * @author ksrini + */ + +class VerifyTreeSet extends java.util.TreeSet { + + VerifyTreeSet() { + super(); + } + + public VerifyTreeSet(Comparator c) { + super(c); + } + + public TreeSet diff(TreeSet in) { + TreeSet delta = (TreeSet) this.clone(); + delta.removeAll(in); + return delta; + } +} diff --git a/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java new file mode 100644 index 00000000000..4a3af66344e --- /dev/null +++ b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java @@ -0,0 +1,1003 @@ +/* + * Copyright (c) 2010, 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 xmlkit; // -*- mode: java; indent-tabs-mode: nil -*- + +import java.util.*; +import java.util.jar.*; +import java.lang.reflect.*; +import java.io.*; +import xmlkit.XMLKit.Element; + +/* + * @author jrose + */ +public class ClassReader extends ClassSyntax { + + private static final CommandLineParser CLP = new CommandLineParser("" + + "-source: +> = \n" + + "-dest: +> = \n" + + "-encoding: +> = \n" + + "-jcov $ \n -nojcov !-jcov \n" + + "-verbose $ \n -noverbose !-verbose \n" + + "-pretty $ \n -nopretty !-pretty \n" + + "-keepPath $ \n -nokeepPath !-keepPath \n" + + "-keepCP $ \n -nokeepCP !-keepCP \n" + + "-keepBytes $ \n -nokeepBytes !-keepBytes \n" + + "-parseBytes $ \n -noparseBytes !-parseBytes \n" + + "-resolveRefs $ \n -noresolveRefs !-resolveRefs \n" + + "-keepOrder $ \n -nokeepOrder !-keepOrder \n" + + "-keepSizes $ \n -nokeepSizes !-keepSizes \n" + + "-continue $ \n -nocontinue !-continue \n" + + "-attrDef & \n" + + "-@ >-@ . \n" + + "- +? \n" + + "\n"); + + public static void main(String[] ava) throws IOException { + ArrayList av = new ArrayList(Arrays.asList(ava)); + HashMap props = new HashMap(); + props.put("-encoding:", "UTF8"); // default + props.put("-keepOrder", null); // CLI default + props.put("-pretty", "1"); // CLI default + props.put("-continue", "1"); // CLI default + CLP.parse(av, props); + //System.out.println(props+" ++ "+av); + File source = asFile(props.get("-source:")); + File dest = asFile(props.get("-dest:")); + String encoding = props.get("-encoding:"); + boolean contError = props.containsKey("-continue"); + ClassReader options = new ClassReader(); + options.copyOptionsFrom(props); + /* + if (dest == null && av.size() > 1) { + dest = File.createTempFile("TestOut", ".dir", new File(".")); + dest.delete(); + if (!dest.mkdir()) + throw new RuntimeException("Cannot create "+dest); + System.out.println("Writing results to "+dest); + } + */ + if (av.isEmpty()) { + av.add("doit"); //to enter this loop + } + boolean readList = false; + for (String a : av) { + if (readList) { + readList = false; + InputStream fin; + if (a.equals("-")) { + fin = System.in; + } else { + fin = new FileInputStream(a); + } + + BufferedReader files = makeReader(fin, encoding); + for (String file; (file = files.readLine()) != null;) { + doFile(file, source, dest, options, encoding, contError); + } + if (fin != System.in) { + fin.close(); + } + } else if (a.equals("-@")) { + readList = true; + } else if (a.startsWith("-")) { + throw new RuntimeException("Bad flag argument: " + a); + } else if (source.getName().endsWith(".jar")) { + doJar(a, source, dest, options, encoding, contError); + } else { + doFile(a, source, dest, options, encoding, contError); + } + } + } + + private static File asFile(String str) { + return (str == null) ? null : new File(str); + } + + private static void doFile(String a, + File source, File dest, + ClassReader options, String encoding, + boolean contError) throws IOException { + if (!contError) { + doFile(a, source, dest, options, encoding); + } else { + try { + doFile(a, source, dest, options, encoding); + } catch (Exception ee) { + System.out.println("Error processing " + source + ": " + ee); + } + } + } + + private static void doJar(String a, File source, File dest, ClassReader options, + String encoding, Boolean contError) throws IOException { + try { + JarFile jf = new JarFile(source); + for (JarEntry je : Collections.list((Enumeration) jf.entries())) { + String name = je.getName(); + if (!name.endsWith(".class")) { + continue; + } + doStream(name, jf.getInputStream(je), dest, options, encoding); + } + } catch (IOException ioe) { + if (contError) { + System.out.println("Error processing " + source + ": " + ioe); + } else { + throw ioe; + } + } + } + + private static void doStream(String a, InputStream in, File dest, + ClassReader options, String encoding) throws IOException { + + File f = new File(a); + ClassReader cr = new ClassReader(options); + Element e = cr.readFrom(in); + + OutputStream out; + if (dest == null) { + //System.out.println(e.prettyString()); + out = System.out; + } else { + File outf = new File(dest, f.isAbsolute() ? f.getName() : f.getPath()); + String outName = outf.getName(); + File outSubdir = outf.getParentFile(); + outSubdir.mkdirs(); + int extPos = outName.lastIndexOf('.'); + if (extPos > 0) { + outf = new File(outSubdir, outName.substring(0, extPos) + ".xml"); + } + out = new FileOutputStream(outf); + } + + Writer outw = makeWriter(out, encoding); + if (options.pretty || !options.keepOrder) { + e.writePrettyTo(outw); + } else { + e.writeTo(outw); + } + if (out == System.out) { + outw.write("\n"); + outw.flush(); + } else { + outw.close(); + } + } + + private static void doFile(String a, + File source, File dest, + ClassReader options, String encoding) throws IOException { + File inf = new File(source, a); + if (dest != null && options.verbose) { + System.out.println("Reading " + inf); + } + + BufferedInputStream in = new BufferedInputStream(new FileInputStream(inf)); + + doStream(a, in, dest, options, encoding); + + } + + public static BufferedReader makeReader(InputStream in, String encoding) throws IOException { + // encoding in DEFAULT, '', UTF8, 8BIT, , or any valid encoding name + if (encoding.equals("8BIT")) { + encoding = EIGHT_BIT_CHAR_ENCODING; + } + if (encoding.equals("UTF8")) { + encoding = UTF8_ENCODING; + } + if (encoding.equals("DEFAULT")) { + encoding = null; + } + if (encoding.equals("-")) { + encoding = null; + } + Reader inw; + in = new BufferedInputStream(in); // add buffering + if (encoding == null) { + inw = new InputStreamReader(in); + } else { + inw = new InputStreamReader(in, encoding); + } + return new BufferedReader(inw); // add buffering + } + + public static Writer makeWriter(OutputStream out, String encoding) throws IOException { + // encoding in DEFAULT, '', UTF8, 8BIT, , or any valid encoding name + if (encoding.equals("8BIT")) { + encoding = EIGHT_BIT_CHAR_ENCODING; + } + if (encoding.equals("UTF8")) { + encoding = UTF8_ENCODING; + } + if (encoding.equals("DEFAULT")) { + encoding = null; + } + if (encoding.equals("-")) { + encoding = null; + } + Writer outw; + if (encoding == null) { + outw = new OutputStreamWriter(out); + } else { + outw = new OutputStreamWriter(out, encoding); + } + return new BufferedWriter(outw); // add buffering + } + + public Element result() { + return cfile; + } + protected InputStream in; + protected ByteArrayOutputStream buf = new ByteArrayOutputStream(1024); + protected byte cpTag[]; + protected String cpName[]; + protected String[] callables; // varies + public static final String REF_PREFIX = "#"; + // input options + public boolean pretty = false; + public boolean verbose = false; + public boolean keepPath = false; + public boolean keepCP = false; + public boolean keepBytes = false; + public boolean parseBytes = true; + public boolean resolveRefs = true; + public boolean keepOrder = true; + public boolean keepSizes = false; + + public ClassReader() { + super.cfile = new Element("ClassFile"); + } + + public ClassReader(ClassReader options) { + this(); + copyOptionsFrom(options); + } + + public void copyOptionsFrom(ClassReader options) { + pretty = options.pretty; + verbose = options.verbose; + keepPath = options.keepPath; + keepCP = options.keepCP; + keepBytes = options.keepBytes; + parseBytes = options.parseBytes; + resolveRefs = options.resolveRefs; + keepSizes = options.keepSizes; + keepOrder = options.keepOrder; + attrTypes = options.attrTypes; + } + + public void copyOptionsFrom(Map options) { + if (options.containsKey("-pretty")) { + pretty = (options.get("-pretty") != null); + } + if (options.containsKey("-verbose")) { + verbose = (options.get("-verbose") != null); + } + if (options.containsKey("-keepPath")) { + keepPath = (options.get("-keepPath") != null); + } + if (options.containsKey("-keepCP")) { + keepCP = (options.get("-keepCP") != null); + } + if (options.containsKey("-keepBytes")) { + keepBytes = (options.get("-keepBytes") != null); + } + if (options.containsKey("-parseBytes")) { + parseBytes = (options.get("-parseBytes") != null); + } + if (options.containsKey("-resolveRefs")) { + resolveRefs = (options.get("-resolveRefs") != null); + } + if (options.containsKey("-keepSizes")) { + keepSizes = (options.get("-keepSizes") != null); + } + if (options.containsKey("-keepOrder")) { + keepOrder = (options.get("-keepOrder") != null); + } + if (options.containsKey("-attrDef")) { + addAttrTypes(options.get("-attrDef").split(" ")); + } + if (options.get("-jcov") != null) { + addJcovAttrTypes(); + } + } + + public Element readFrom(InputStream in) throws IOException { + this.in = in; + // read the file header + int magic = u4(); + if (magic != 0xCAFEBABE) { + throw new RuntimeException("bad magic number " + Integer.toHexString(magic)); + } + cfile.setAttr("magic", "" + magic); + int minver = u2(); + int majver = u2(); + cfile.setAttr("minver", "" + minver); + cfile.setAttr("majver", "" + majver); + readCP(); + readClass(); + return result(); + } + + public Element readFrom(File file) throws IOException { + InputStream in = null; + try { + in = new FileInputStream(file); + Element e = readFrom(new BufferedInputStream(in)); + if (keepPath) { + e.setAttr("path", file.toString()); + } + return e; + } finally { + if (in != null) { + in.close(); + } + } + } + + private void readClass() throws IOException { + klass = new Element("Class"); + cfile.add(klass); + int flags = u2(); + String thisk = cpRef(); + String superk = cpRef(); + klass.setAttr("name", thisk); + boolean flagsSync = ((flags & Modifier.SYNCHRONIZED) != 0); + flags &= ~Modifier.SYNCHRONIZED; + String flagString = flagString(flags, klass); + if (!flagsSync) { + if (flagString.length() > 0) { + flagString += " "; + } + flagString += "!synchronized"; + } + klass.setAttr("flags", flagString); + klass.setAttr("super", superk); + for (int len = u2(), i = 0; i < len; i++) { + String interk = cpRef(); + klass.add(new Element("Interface", "name", interk)); + } + Element fields = readMembers("Field"); + klass.addAll(fields); + Element methods = readMembers("Method"); + if (!keepOrder) { + methods.sort(); + } + klass.addAll(methods); + readAttributesFor(klass); + klass.trimToSize(); + if (keepSizes) { + attachTo(cfile, formatAttrSizes()); + } + if (paddingSize != 0) { + cfile.setAttr("padding", "" + paddingSize); + } + } + + private Element readMembers(String kind) throws IOException { + int len = u2(); + Element members = new Element(len); + for (int i = 0; i < len; i++) { + Element member = new Element(kind); + int flags = u2(); + String name = cpRef(); + String type = cpRef(); + member.setAttr("name", name); + member.setAttr("type", type); + member.setAttr("flags", flagString(flags, member)); + readAttributesFor(member); + member.trimToSize(); + members.add(member); + } + return members; + } + + protected String flagString(int flags, Element holder) { + // Superset of Modifier.toString. + int kind = 0; + if (holder.getName() == "Field") { + kind = 1; + } + if (holder.getName() == "Method") { + kind = 2; + } + StringBuffer sb = new StringBuffer(); + for (int i = 0; flags != 0; i++, flags >>>= 1) { + if ((flags & 1) != 0) { + if (sb.length() > 0) { + sb.append(' '); + } + if (i < modifierNames.length) { + String[] names = modifierNames[i]; + String name = (kind < names.length) ? names[kind] : null; + for (String name2 : names) { + if (name != null) { + break; + } + name = name2; + } + sb.append(name); + } else { + sb.append("#").append(1 << i); + } + } + } + return sb.toString(); + } + + private void readAttributesFor(Element x) throws IOException { + Element prevCurrent; + Element y = new Element(); + if (x.getName() == "Code") { + prevCurrent = currentCode; + currentCode = x; + } else { + prevCurrent = currentMember; + currentMember = x; + } + for (int len = u2(), i = 0; i < len; i++) { + int ref = u2(); + String uname = cpName(ref).intern(); + String refName = uname; + if (!resolveRefs) { + refName = (REF_PREFIX + ref).intern(); + } + String qname = (x.getName() + "." + uname).intern(); + String wname = ("*." + uname).intern(); + String type = attrTypes.get(qname); + if (type == null || "".equals(type)) { + type = attrTypes.get(wname); + } + if ("".equals(type)) { + type = null; + } + int size = u4(); + int[] countVar = attrSizes.get(qname); + if (countVar == null) { + attrSizes.put(qname, countVar = new int[2]); + } + countVar[0] += 1; + countVar[1] += size; + buf.reset(); + for (int j = 0; j < size; j++) { + buf.write(u1()); + } + if (type == null && size == 0) { + y.add(new Element(uname)); // , etc. + } else if (type == null) { + //System.out.println("Warning: No attribute type description: "+qname); + // write cdata attribute + Element a = new Element("Attribute", + new String[]{"Name", refName}, + buf.toString(EIGHT_BIT_CHAR_ENCODING)); + a.addContent(getCPDigest()); + y.add(a); + } else if (type.equals("")) { + // ignore this attribute... + } else { + InputStream in0 = in; + int fileSize0 = fileSize; + ByteArrayInputStream in1 = new ByteArrayInputStream(buf.toByteArray()); + boolean ok = false; + try { + in = in1; + // parse according to type desc. + Element aval; + if (type.equals("...")) { + // delve into Code attribute + aval = readCode(); + } else if (type.equals("...")) { + // delve into StackMap attribute + aval = readStackMap(false); + } else if (type.equals("...")) { + // delve into StackMap attribute + aval = readStackMap(true); + } else if (type.startsWith("[")) { + aval = readAttributeCallables(type); + } else { + aval = readAttribute(type); + } + //System.out.println("attachTo 1 "+y+" <- "+aval); + attachTo(y, aval); + if (false + && in1.available() != 0) { + throw new RuntimeException("extra bytes in " + qname + " :" + in1.available()); + } + ok = true; + } finally { + in = in0; + fileSize = fileSize0; + if (!ok) { + System.out.println("*** Failed to read " + type); + } + } + } + } + if (x.getName() == "Code") { + currentCode = prevCurrent; + } else { + currentMember = prevCurrent; + } + if (!keepOrder) { + y.sort(); + y.sortAttrs(); + } + //System.out.println("attachTo 2 "+x+" <- "+y); + attachTo(x, y); + } + private int fileSize = 0; + private int paddingSize = 0; + private HashMap attrSizes = new HashMap(); + + private Element formatAttrSizes() { + Element e = new Element("Sizes"); + e.setAttr("fileSize", "" + fileSize); + for (Map.Entry ie : attrSizes.entrySet()) { + int[] countVar = ie.getValue(); + e.add(new Element("AttrSize", + "name", ie.getKey().toString(), + "count", "" + countVar[0], + "size", "" + countVar[1])); + } + return e; + } + + private void attachTo(Element x, Object aval0) { + if (aval0 == null) { + return; + } + //System.out.println("attachTo "+x+" : "+aval0); + if (!(aval0 instanceof Element)) { + x.add(aval0); + return; + } + Element aval = (Element) aval0; + if (!aval.isAnonymous()) { + x.add(aval); + return; + } + for (int imax = aval.attrSize(), i = 0; i < imax; i++) { + //%% + attachAttrTo(x, aval.getAttrName(i), aval.getAttr(i)); + } + x.addAll(aval); + } + + private void attachAttrTo(Element x, String aname, String aval) { + //System.out.println("attachAttrTo "+x+" : "+aname+"="+aval); + String aval0 = x.getAttr(aname); + if (aval0 != null) { + aval = aval0 + " " + aval; + } + x.setAttr(aname, aval); + } + + private Element readAttributeCallables(String type) throws IOException { + assert (callables == null); + callables = getBodies(type); + Element res = readAttribute(callables[0]); + callables = null; + return res; + } + + private Element readAttribute(String type) throws IOException { + //System.out.println("readAttribute "+type); + Element aval = new Element(); + String nextAttrName = null; + for (int len = type.length(), next, i = 0; i < len; i = next) { + String value; + switch (type.charAt(i)) { + case '<': + assert (nextAttrName == null); + next = type.indexOf('>', ++i); + String form = type.substring(i, next++); + if (form.indexOf('=') < 0) { + // elem_placement = '<' elemname '>' + assert (aval.attrSize() == 0); + assert (aval.isAnonymous()); + aval.setName(form.intern()); + } else { + // attr_placement = '<' attrname '=' (value)? '>' + int eqPos = form.indexOf('='); + nextAttrName = form.substring(0, eqPos).intern(); + if (eqPos != form.length() - 1) { + value = form.substring(eqPos + 1); + attachAttrTo(aval, nextAttrName, value); + nextAttrName = null; + } + // ...else subsequent type parsing will find the attr value + // and add it as "nextAttrName". + } + continue; + case '(': + next = type.indexOf(')', ++i); + int callee = Integer.parseInt(type.substring(i, next++)); + attachTo(aval, readAttribute(callables[callee])); + continue; + case 'N': // replication = 'N' int '[' type ... ']' + { + int count = getInt(type.charAt(i + 1), false); + assert (count >= 0); + next = i + 2; + String type1 = getBody(type, next); + next += type1.length() + 2; // skip body and brackets + for (int j = 0; j < count; j++) { + attachTo(aval, readAttribute(type1)); + } + } + continue; + case 'T': // union = 'T' any_int union_case* '(' ')' '[' body ']' + int tagValue; + if (type.charAt(++i) == 'S') { + tagValue = getInt(type.charAt(++i), true); + } else { + tagValue = getInt(type.charAt(i), false); + } + attachAttrTo(aval, "tag", "" + tagValue); // always named "tag" + ++i; // skip the int type char + // union_case = '(' uc_tag (',' uc_tag)* ')' '[' body ']' + // uc_tag = ('-')? digit+ + for (boolean foundCase = false;; i = next) { + assert (type.charAt(i) == '('); + next = type.indexOf(')', ++i); + assert (next >= i); + if (type.charAt(next - 1) == '\\' + && type.charAt(next - 2) != '\\') // Skip an escaped paren. + { + next = type.indexOf(')', next + 1); + } + String caseStr = type.substring(i, next++); + String type1 = getBody(type, next); + next += type1.length() + 2; // skip body and brackets + boolean lastCase = (caseStr.length() == 0); + if (!foundCase + && (lastCase || matchTag(tagValue, caseStr))) { + foundCase = true; + // Execute this body. + attachTo(aval, readAttribute(type1)); + } + if (lastCase) { + break; + } + } + continue; + case 'B': + case 'H': + case 'I': // int = oneof "BHI" + next = i + 1; + value = "" + getInt(type.charAt(i), false); + break; + case 'K': + assert ("IJFDLQ".indexOf(type.charAt(i + 1)) >= 0); + assert (type.charAt(i + 2) == 'H'); // only H works for now + next = i + 3; + value = cpRef(); + break; + case 'R': + assert ("CSDFMIU?".indexOf(type.charAt(i + 1)) >= 0); + assert (type.charAt(i + 2) == 'H'); // only H works for now + next = i + 3; + value = cpRef(); + break; + case 'P': // bci = 'P' int + next = i + 2; + value = "" + getInt(type.charAt(i + 1), false); + break; + case 'S': // signed_int = 'S' int + next = i + 2; + value = "" + getInt(type.charAt(i + 1), true); + break; + case 'F': + next = i + 2; + value = flagString(getInt(type.charAt(i + 1), false), currentMember); + break; + default: + throw new RuntimeException("bad attr format '" + type.charAt(i) + "': " + type); + } + // store the value + if (nextAttrName != null) { + attachAttrTo(aval, nextAttrName, value); + nextAttrName = null; + } else { + attachTo(aval, value); + } + } + //System.out.println("readAttribute => "+aval); + assert (nextAttrName == null); + return aval; + } + + private int getInt(char ch, boolean signed) throws IOException { + if (signed) { + switch (ch) { + case 'B': + return (byte) u1(); + case 'H': + return (short) u2(); + case 'I': + return (int) u4(); + } + } else { + switch (ch) { + case 'B': + return u1(); + case 'H': + return u2(); + case 'I': + return u4(); + } + } + assert ("BHIJ".indexOf(ch) >= 0); + return 0; + } + + private Element readCode() throws IOException { + int stack = u2(); + int local = u2(); + int length = u4(); + StringBuilder sb = new StringBuilder(length); + for (int i = 0; i < length; i++) { + sb.append((char) u1()); + } + String bytecodes = sb.toString(); + Element e = new Element("Code", + "stack", "" + stack, + "local", "" + local); + Element bytes = new Element("Bytes", (String[]) null, bytecodes); + if (keepBytes) { + e.add(bytes); + } + if (parseBytes) { + e.add(parseByteCodes(bytecodes)); + } + for (int len = u2(), i = 0; i < len; i++) { + int start = u2(); + int end = u2(); + int catsh = u2(); + String clasz = cpRef(); + e.add(new Element("Handler", + "start", "" + start, + "end", "" + end, + "catch", "" + catsh, + "class", clasz)); + } + readAttributesFor(e); + e.trimToSize(); + return e; + } + + private Element parseByteCodes(String bytecodes) { + Element e = InstructionSyntax.parse(bytecodes); + for (Element ins : e.elements()) { + Number ref = ins.getAttrNumber("ref"); + if (ref != null && resolveRefs) { + int id = ref.intValue(); + String val = cpName(id); + if (ins.getName().startsWith("ldc")) { + // Yuck: Arb. string cannot be an XML attribute. + ins.add(val); + val = ""; + byte tag = (id >= 0 && id < cpTag.length) ? cpTag[id] : 0; + if (tag != 0) { + ins.setAttrLong("tag", tag); + } + } + if (ins.getName() == "invokeinterface" + && computeInterfaceNum(val) == ins.getAttrLong("num")) { + ins.setAttr("num", null); // garbage bytes + } + ins.setAttr("ref", null); + ins.setAttr("val", val); + } + } + return e; + } + + private Element readStackMap(boolean hasXOption) throws IOException { + Element result = new Element(); + Element bytes = currentCode.findElement("Bytes"); + assert (bytes != null && bytes.size() == 1); + int byteLength = ((String) bytes.get(0)).length(); + boolean uoffsetIsU4 = (byteLength >= (1 << 16)); + boolean ulocalvarIsU4 = currentCode.getAttrLong("local") >= (1 << 16); + boolean ustackIsU4 = currentCode.getAttrLong("stack") >= (1 << 16); + if (hasXOption || uoffsetIsU4 || ulocalvarIsU4 || ustackIsU4) { + Element flags = new Element("StackMapFlags"); + if (hasXOption) { + flags.setAttr("hasXOption", "true"); + } + if (uoffsetIsU4) { + flags.setAttr("uoffsetIsU4", "true"); + } + if (ulocalvarIsU4) { + flags.setAttr("ulocalvarIsU4", "true"); + } + if (ustackIsU4) { + flags.setAttr("ustackIsU4", "true"); + } + currentCode.add(flags); + } + int frame_count = (uoffsetIsU4 ? u4() : u2()); + for (int i = 0; i < frame_count; i++) { + int bci = (uoffsetIsU4 ? u4() : u2()); + int flags = (hasXOption ? u1() : 0); + Element frame = new Element("Frame"); + result.add(frame); + if (flags != 0) { + frame.setAttr("flags", "" + flags); + } + frame.setAttr("bci", "" + bci); + // Scan local and stack types in this frame: + final int LOCALS = 0, STACK = 1; + for (int j = LOCALS; j <= STACK; j++) { + int typeSize; + if (j == LOCALS) { + typeSize = (ulocalvarIsU4 ? u4() : u2()); + } else { // STACK + typeSize = (ustackIsU4 ? u4() : u2()); + } + Element types = new Element(j == LOCALS ? "Local" : "Stack"); + for (int k = 0; k < typeSize; k++) { + int tag = u1(); + Element type = new Element(itemTagName(tag)); + types.add(type); + switch (tag) { + case ITEM_Object: + type.setAttr("class", cpRef()); + break; + case ITEM_Uninitialized: + case ITEM_ReturnAddress: + type.setAttr("bci", "" + (uoffsetIsU4 ? u4() : u2())); + break; + } + } + if (types.size() > 0) { + frame.add(types); + } + } + } + return result; + } + + private void readCP() throws IOException { + int cpLen = u2(); + cpTag = new byte[cpLen]; + cpName = new String[cpLen]; + int cpTem[][] = new int[cpLen][]; + for (int i = 1; i < cpLen; i++) { + cpTag[i] = (byte) u1(); + switch (cpTag[i]) { + case CONSTANT_Utf8: + buf.reset(); + for (int len = u2(), j = 0; j < len; j++) { + buf.write(u1()); + } + cpName[i] = buf.toString(UTF8_ENCODING); + break; + case CONSTANT_Integer: + cpName[i] = String.valueOf((int) u4()); + break; + case CONSTANT_Float: + cpName[i] = String.valueOf(Float.intBitsToFloat(u4())); + break; + case CONSTANT_Long: + cpName[i] = String.valueOf(u8()); + i += 1; + break; + case CONSTANT_Double: + cpName[i] = String.valueOf(Double.longBitsToDouble(u8())); + i += 1; + break; + case CONSTANT_Class: + case CONSTANT_String: + cpTem[i] = new int[]{u2()}; + break; + case CONSTANT_Fieldref: + case CONSTANT_Methodref: + case CONSTANT_InterfaceMethodref: + case CONSTANT_NameAndType: + cpTem[i] = new int[]{u2(), u2()}; + break; + } + } + for (int i = 1; i < cpLen; i++) { + switch (cpTag[i]) { + case CONSTANT_Class: + case CONSTANT_String: + cpName[i] = cpName[cpTem[i][0]]; + break; + case CONSTANT_NameAndType: + cpName[i] = cpName[cpTem[i][0]] + " " + cpName[cpTem[i][1]]; + break; + } + } + // do fieldref et al after nameandtype are all resolved + for (int i = 1; i < cpLen; i++) { + switch (cpTag[i]) { + case CONSTANT_Fieldref: + case CONSTANT_Methodref: + case CONSTANT_InterfaceMethodref: + cpName[i] = cpName[cpTem[i][0]] + " " + cpName[cpTem[i][1]]; + break; + } + } + cpool = new Element("ConstantPool", cpName.length); + for (int i = 0; i < cpName.length; i++) { + if (cpName[i] == null) { + continue; + } + cpool.add(new Element(cpTagName(cpTag[i]), + new String[]{"id", "" + i}, + cpName[i])); + } + if (keepCP) { + cfile.add(cpool); + } + } + + private String cpRef() throws IOException { + int ref = u2(); + if (resolveRefs) { + return cpName(ref); + } else { + return REF_PREFIX + ref; + } + } + + private String cpName(int id) { + if (id >= 0 && id < cpName.length) { + return cpName[id]; + } else { + return "[CP#" + Integer.toHexString(id) + "]"; + } + } + + private long u8() throws IOException { + return ((long) u4() << 32) + (((long) u4() << 32) >>> 32); + } + + private int u4() throws IOException { + return (u2() << 16) + u2(); + } + + private int u2() throws IOException { + return (u1() << 8) + u1(); + } + + private int u1() throws IOException { + int x = in.read(); + if (x < 0) { + paddingSize++; + return 0; // error recovery + } + fileSize++; + assert (x == (x & 0xFF)); + return x; + } +} + diff --git a/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassSyntax.java b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassSyntax.java new file mode 100644 index 00000000000..d34ecbad004 --- /dev/null +++ b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassSyntax.java @@ -0,0 +1,518 @@ +/* + * Copyright (c) 2010, 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 xmlkit; // -*- mode: java; indent-tabs-mode: nil -*- +import xmlkit.XMLKit.*; + +import java.util.*; +import java.security.MessageDigest; +import java.nio.ByteBuffer; +import xmlkit.XMLKit.Element; +/* + * @author jrose + */ +public abstract class ClassSyntax { + + public interface GetCPIndex { + + int getCPIndex(int tag, String name); // cp finder + } + public static final int CONSTANT_Utf8 = 1, + CONSTANT_Integer = 3, + CONSTANT_Float = 4, + CONSTANT_Long = 5, + CONSTANT_Double = 6, + CONSTANT_Class = 7, + CONSTANT_String = 8, + CONSTANT_Fieldref = 9, + CONSTANT_Methodref = 10, + CONSTANT_InterfaceMethodref = 11, + CONSTANT_NameAndType = 12; + private static final String[] cpTagName = { + /* 0: */null, + /* 1: */ "Utf8", + /* 2: */ null, + /* 3: */ "Integer", + /* 4: */ "Float", + /* 5: */ "Long", + /* 6: */ "Double", + /* 7: */ "Class", + /* 8: */ "String", + /* 9: */ "Fieldref", + /* 10: */ "Methodref", + /* 11: */ "InterfaceMethodref", + /* 12: */ "NameAndType", + null + }; + private static final Set cpTagNames; + + static { + Set set = new HashSet(Arrays.asList(cpTagName)); + set.remove(null); + cpTagNames = Collections.unmodifiableSet(set); + } + public static final int ITEM_Top = 0, // replicates by [1..4,1..4] + ITEM_Integer = 1, // (ditto) + ITEM_Float = 2, + ITEM_Double = 3, + ITEM_Long = 4, + ITEM_Null = 5, + ITEM_UninitializedThis = 6, + ITEM_Object = 7, + ITEM_Uninitialized = 8, + ITEM_ReturnAddress = 9, + ITEM_LIMIT = 10; + private static final String[] itemTagName = { + "Top", + "Integer", + "Float", + "Double", + "Long", + "Null", + "UninitializedThis", + "Object", + "Uninitialized", + "ReturnAddress",}; + private static final Set itemTagNames; + + static { + Set set = new HashSet(Arrays.asList(itemTagName)); + set.remove(null); + itemTagNames = Collections.unmodifiableSet(set); + } + protected static final HashMap attrTypesBacking; + protected static final Map attrTypesInit; + + static { + HashMap at = new HashMap(); + + //at.put("*.Deprecated", ""); + //at.put("*.Synthetic", ""); + ////at.put("Field.ConstantValue", "KQH"); + //at.put("Class.SourceFile", "RUH"); + at.put("Method.Bridge", ""); + at.put("Method.Varargs", ""); + at.put("Class.Enum", ""); + at.put("*.Signature", "RSH"); + //at.put("*.Deprecated", ""); + //at.put("*.Synthetic", ""); + at.put("Field.ConstantValue", "KQH"); + at.put("Class.SourceFile", "RUH"); + at.put("Class.InnerClasses", "NH[RCHRCHRUHFH]"); + at.put("Code.LineNumberTable", "NH[PHH]"); + at.put("Code.LocalVariableTable", "NH[PHHRUHRSHH]"); + at.put("Code.LocalVariableTypeTable", "NH[PHHRUHRSHH]"); + at.put("Method.Exceptions", "NH[RCH]"); + at.put("Method.Code", "..."); + at.put("Code.StackMapTable", "..."); + //at.put("Code.StkMapX", "..."); + if (true) { + at.put("Code.StackMapTable", + "[NH[(1)]]" + + "[TB" + + "(64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79" + + ",80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95" + + ",96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111" + + ",112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127" + + ")[(4)]" + + "(247)[H(4)]" + + "(248)[H]" + + "(249)[H]" + + "(250)[H]" + + "(251)[H]" + + "(252)[H(4)]" + + "(253)[H(4)(4)]" + + "(254)[H(4)(4)(4)]" + + "(255)[H(2)(3)]" + + "()[]]" + + "[NH[(4)]]" + + "[NH[(4)]]" + + "[TB" + + ("(0)[]" + + "(1)[](2)[](3)[](4)[]" + + "(5)[](6)[]" + + "(7)[RCH]" + + "(8)[PH]" + + "()[]]")); + } + + at.put("Class.EnclosingMethod", "RCHRDH");//RDNH + + // Layouts of metadata attrs: + String vpf = "["; + String ipf = "["; + String apf = "["; + String mdanno2 = "" + + "RSHNH[RUH(3)]]" + + ("[TB" + + "(\\B,\\C,\\I,\\S,\\Z)[KIH]" + + "(\\D)[KDH]" + + "(\\F)[KFH]" + + "(\\J)[KJH]" + + "(\\c)[RSH]" + + "(\\e)[RSHRUH]" + + "(\\s)[RUH]" + + "(\\@)[(2)]" + + "(\\[)[NH[(3)]]" + + "()[]" + + "]"); + String visanno = "[NH[(2)]][(1)]" + vpf + mdanno2; + String invanno = "[NH[(2)]][(1)]" + ipf + mdanno2; + String vparamanno = "" + + "[NB[(1)]][NH[(2)]]" + + apf + mdanno2; + String iparamanno = "" + + "[NB[(1)]][NH[(2)]]" + + apf + mdanno2; + String mdannodef = "[(3)][(1)]" + apf + mdanno2; + String[] mdplaces = {"Class", "Field", "Method"}; + for (String place : mdplaces) { + at.put(place + ".RuntimeVisibleAnnotations", visanno); + at.put(place + ".RuntimeInvisibleAnnotations", invanno); + } + at.put("Method.RuntimeVisibleParameterAnnotations", vparamanno); + at.put("Method.RuntimeInvisibleParameterAnnotations", iparamanno); + at.put("Method.AnnotationDefault", mdannodef); + + attrTypesBacking = at; + attrTypesInit = Collections.unmodifiableMap(at); + } + + ; + private static final String[] jcovAttrTypes = { + "Code.CoverageTable=NH[PHHII]", + "Code.CharacterRangeTable=NH[PHPOHIIH]", + "Class.SourceID=RUH", + "Class.CompilationID=RUH" + }; + protected static final String[][] modifierNames = { + {"public"}, + {"private"}, + {"protected"}, + {"static"}, + {"final"}, + {"synchronized"}, + {null, "volatile", "bridge"}, + {null, "transient", "varargs"}, + {null, null, "native"}, + {"interface"}, + {"abstract"}, + {"strictfp"}, + {"synthetic"}, + {"annotation"}, + {"enum"},}; + protected static final String EIGHT_BIT_CHAR_ENCODING = "ISO8859_1"; + protected static final String UTF8_ENCODING = "UTF8"; + // What XML tags are used by this syntax, apart from attributes? + protected static final Set nonAttrTags; + + static { + HashSet tagSet = new HashSet(); + Collections.addAll(tagSet, new String[]{ + "ConstantPool",// the CP + "Class", // the class + "Interface", // implemented interfaces + "Method", // methods + "Field", // fields + "Handler", // exception handler pseudo-attribute + "Attribute", // unparsed attribute + "Bytes", // bytecodes + "Instructions" // bytecodes, parsed + }); + nonAttrTags = Collections.unmodifiableSet(tagSet); + } + + // Accessors. + public static Set nonAttrTags() { + return nonAttrTags; + } + + public static String cpTagName(int t) { + t &= 0xFF; + String ts = null; + if (t < cpTagName.length) { + ts = cpTagName[t]; + } + if (ts != null) { + return ts; + } + return ("UnknownTag" + (int) t).intern(); + } + + public static int cpTagValue(String name) { + for (int t = 0; t < cpTagName.length; t++) { + if (name.equals(cpTagName[t])) { + return t; + } + } + return 0; + } + + public static String itemTagName(int t) { + t &= 0xFF; + String ts = null; + if (t < itemTagName.length) { + ts = itemTagName[t]; + } + if (ts != null) { + return ts; + } + return ("UnknownItem" + (int) t).intern(); + } + + public static int itemTagValue(String name) { + for (int t = 0; t < itemTagName.length; t++) { + if (name.equals(itemTagName[t])) { + return t; + } + } + return -1; + } + + public void addJcovAttrTypes() { + addAttrTypes(jcovAttrTypes); + } + // Public methods for declaring attribute types. + protected Map attrTypes = attrTypesInit; + + public void addAttrType(String opt) { + int eqpos = opt.indexOf('='); + addAttrType(opt.substring(0, eqpos), opt.substring(eqpos + 1)); + } + + public void addAttrTypes(String[] opts) { + for (String opt : opts) { + addAttrType(opt); + } + } + + private void checkAttr(String attr) { + if (!attr.startsWith("Class.") + && !attr.startsWith("Field.") + && !attr.startsWith("Method.") + && !attr.startsWith("Code.") + && !attr.startsWith("*.")) { + throw new IllegalArgumentException("attr name must start with 'Class.', etc."); + } + String uattr = attr.substring(attr.indexOf('.') + 1); + if (nonAttrTags.contains(uattr)) { + throw new IllegalArgumentException("attr name must not be one of " + nonAttrTags); + } + } + + private void checkAttrs(Map at) { + for (String attr : at.keySet()) { + checkAttr(attr); + } + } + + private void modAttrs() { + if (attrTypes == attrTypesInit) { + // Make modifiable. + attrTypes = new HashMap(attrTypesBacking); + } + } + + public void addAttrType(String attr, String fmt) { + checkAttr(attr); + modAttrs(); + attrTypes.put(attr, fmt); + } + + public void addAttrTypes(Map at) { + checkAttrs(at); + modAttrs(); + attrTypes.putAll(at); + } + + public Map getAttrTypes() { + if (attrTypes == attrTypesInit) { + return attrTypes; + } + return Collections.unmodifiableMap(attrTypes); + } + + public void setAttrTypes(Map at) { + checkAttrs(at); + modAttrs(); + attrTypes.keySet().retainAll(at.keySet()); + attrTypes.putAll(at); + } + + // attr format helpers + protected static boolean matchTag(int tagValue, String caseStr) { + //System.out.println("matchTag "+tagValue+" in "+caseStr); + for (int pos = 0, max = caseStr.length(), comma; + pos < max; + pos = comma + 1) { + int caseValue; + if (caseStr.charAt(pos) == '\\') { + caseValue = caseStr.charAt(pos + 1); + comma = pos + 2; + assert (comma == max || caseStr.charAt(comma) == ','); + } else { + comma = caseStr.indexOf(',', pos); + if (comma < 0) { + comma = max; + } + caseValue = Integer.parseInt(caseStr.substring(pos, comma)); + } + if (tagValue == caseValue) { + return true; + } + } + return false; + } + + protected static String[] getBodies(String type) { + ArrayList bodies = new ArrayList(); + for (int i = 0; i < type.length();) { + String body = getBody(type, i); + bodies.add(body); + i += body.length() + 2; // skip body and brackets + } + return bodies.toArray(new String[bodies.size()]); + } + + protected static String getBody(String type, int i) { + assert (type.charAt(i) == '['); + int next = ++i; // skip bracket + for (int depth = 1; depth > 0; next++) { + switch (type.charAt(next)) { + case '[': + depth++; + break; + case ']': + depth--; + break; + case '(': + next = type.indexOf(')', next); + break; + case '<': + next = type.indexOf('>', next); + break; + } + assert (next > 0); + } + --next; // get before bracket + assert (type.charAt(next) == ']'); + return type.substring(i, next); + } + + public Element makeCPDigest(int length) { + MessageDigest md; + try { + md = MessageDigest.getInstance("MD5"); + } catch (java.security.NoSuchAlgorithmException ee) { + throw new Error(ee); + } + int items = 0; + for (Element e : cpool.elements()) { + if (items == length) { + break; + } + if (cpTagNames.contains(e.getName())) { + items += 1; + md.update((byte) cpTagValue(e.getName())); + try { + md.update(e.getText().toString().getBytes(UTF8_ENCODING)); + } catch (java.io.UnsupportedEncodingException ee) { + throw new Error(ee); + } + } + } + ByteBuffer bb = ByteBuffer.wrap(md.digest()); + String l0 = Long.toHexString(bb.getLong(0)); + String l1 = Long.toHexString(bb.getLong(8)); + while (l0.length() < 16) { + l0 = "0" + l0; + } + while (l1.length() < 16) { + l1 = "0" + l1; + } + return new Element("Digest", + "length", "" + items, + "bytes", l0 + l1); + } + + public Element getCPDigest(int length) { + if (length == -1) { + length = cpool.countAll(XMLKit.elementFilter(cpTagNames)); + } + for (Element md : cpool.findAllElements("Digest").elements()) { + if (md.getAttrLong("length") == length) { + return md; + } + } + Element md = makeCPDigest(length); + cpool.add(md); + return md; + } + + public Element getCPDigest() { + return getCPDigest(-1); + } + + public boolean checkCPDigest(Element md) { + return md.equals(getCPDigest((int) md.getAttrLong("length"))); + } + + public static int computeInterfaceNum(String intMethRef) { + intMethRef = intMethRef.substring(1 + intMethRef.lastIndexOf(' ')); + if (!intMethRef.startsWith("(")) { + return -1; + } + int signum = 1; // start with one for "this" + scanSig: + for (int i = 1; i < intMethRef.length(); i++) { + char ch = intMethRef.charAt(i); + signum++; + switch (ch) { + case ')': + --signum; + break scanSig; + case 'L': + i = intMethRef.indexOf(';', i); + break; + case '[': + while (ch == '[') { + ch = intMethRef.charAt(++i); + } + if (ch == 'L') { + i = intMethRef.indexOf(';', i); + } + break; + } + } + int num = (signum << 8) | 0; + //System.out.println("computeInterfaceNum "+intMethRef+" => "+num); + return num; + } + // Protected state for representing the class file. + protected Element cfile; // + protected Element cpool; // + protected Element klass; // + protected Element currentMember; // varies during scans + protected Element currentCode; // varies during scans +} diff --git a/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassWriter.java b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassWriter.java new file mode 100644 index 00000000000..037de37e540 --- /dev/null +++ b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassWriter.java @@ -0,0 +1,818 @@ +/* + * Copyright (c) 2010, 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 xmlkit; // -*- mode: java; indent-tabs-mode: nil -*- + +import java.util.*; +import java.lang.reflect.*; +import java.io.*; +import xmlkit.XMLKit.Element; +/* + * @author jrose + */ +public class ClassWriter extends ClassSyntax implements ClassSyntax.GetCPIndex { + + private static final CommandLineParser CLP = new CommandLineParser("" + + "-source: +> = \n" + + "-dest: +> = \n" + + "-encoding: +> = \n" + + "-parseBytes $ \n" + + "- *? \n" + + "\n"); + + public static void main(String[] ava) throws IOException { + ArrayList av = new ArrayList(Arrays.asList(ava)); + HashMap props = new HashMap(); + props.put("-encoding:", "UTF8"); // default + CLP.parse(av, props); + File source = asFile(props.get("-source:")); + File dest = asFile(props.get("-dest:")); + String encoding = props.get("-encoding:"); + boolean parseBytes = props.containsKey("-parseBytes"); + boolean destMade = false; + + for (String a : av) { + File f; + File inf = new File(source, a); + System.out.println("Reading " + inf); + Element e; + if (inf.getName().endsWith(".class")) { + ClassReader cr = new ClassReader(); + cr.parseBytes = parseBytes; + e = cr.readFrom(inf); + f = new File(a); + } else if (inf.getName().endsWith(".xml")) { + InputStream in = new FileInputStream(inf); + Reader inw = ClassReader.makeReader(in, encoding); + e = XMLKit.readFrom(inw); + e.findAllInTree(XMLKit.and(XMLKit.elementFilter(nonAttrTags()), + XMLKit.methodFilter(Element.method("trimText")))); + //System.out.println(e); + inw.close(); + f = new File(a.substring(0, a.length() - ".xml".length()) + ".class"); + } else { + System.out.println("Warning: unknown input " + a); + continue; + } + // Now write it: + if (!destMade) { + destMade = true; + if (dest == null) { + dest = File.createTempFile("TestOut", ".dir", new File(".")); + dest.delete(); + System.out.println("Writing results to " + dest); + } + if (!(dest.isDirectory() || dest.mkdir())) { + throw new RuntimeException("Cannot create " + dest); + } + } + File outf = new File(dest, f.isAbsolute() ? f.getName() : f.getPath()); + outf.getParentFile().mkdirs(); + new ClassWriter(e).writeTo(outf); + } + } + + private static File asFile(String str) { + return (str == null) ? null : new File(str); + } + + public void writeTo(File file) throws IOException { + OutputStream out = null; + try { + out = new BufferedOutputStream(new FileOutputStream(file)); + writeTo(out); + } finally { + if (out != null) { + out.close(); + } + } + } + protected String[] callables; // varies + protected int cpoolSize = 0; + protected HashMap attrTypesByTag; + protected OutputStream out; + protected HashMap cpMap = new HashMap(); + protected ArrayList attrBufs = new ArrayList(); + + private void setupAttrTypes() { + attrTypesByTag = new HashMap(); + for (String key : attrTypes.keySet()) { + String pfx = key.substring(0, key.indexOf('.') + 1); + String val = attrTypes.get(key); + int pos = val.indexOf('<'); + if (pos >= 0) { + String tag = val.substring(pos + 1, val.indexOf('>', pos)); + attrTypesByTag.put(pfx + tag, key); + } + } + //System.out.println("attrTypesByTag: "+attrTypesByTag); + } + + protected ByteArrayOutputStream getAttrBuf() { + int nab = attrBufs.size(); + if (nab == 0) { + return new ByteArrayOutputStream(1024); + } + ByteArrayOutputStream ab = attrBufs.get(nab - 1); + attrBufs.remove(nab - 1); + return ab; + } + + protected void putAttrBuf(ByteArrayOutputStream ab) { + ab.reset(); + attrBufs.add(ab); + } + + public ClassWriter(Element root) { + this(root, null); + } + + public ClassWriter(Element root, ClassSyntax cr) { + if (cr != null) { + attrTypes = cr.attrTypes; + } + setupAttrTypes(); + if (root.getName() == "ClassFile") { + cfile = root; + cpool = root.findElement("ConstantPool"); + klass = root.findElement("Class"); + } else if (root.getName() == "Class") { + cfile = new Element("ClassFile", + new String[]{ + "magic", String.valueOf(0xCAFEBABE), + "minver", "0", "majver", "46",}); + cpool = new Element("ConstantPool"); + klass = root; + } else { + throw new IllegalArgumentException("bad element type " + root.getName()); + } + if (cpool == null) { + cpool = new Element("ConstantPool"); + } + + int cpLen = 1 + cpool.size(); + for (Element c : cpool.elements()) { + int id = (int) c.getAttrLong("id"); + int tag = cpTagValue(c.getName()); + setCPIndex(tag, c.getText().toString(), id); + switch (tag) { + case CONSTANT_Long: + case CONSTANT_Double: + cpLen += 1; + } + } + cpoolSize = cpLen; + } + + public int findCPIndex(int tag, String name) { + if (name == null) { + return 0; + } + int[] ids = cpMap.get(name.toString()); + return (ids == null) ? 0 : ids[tag]; + } + + public int getCPIndex(int tag, String name) { + //System.out.println("getCPIndex "+cpTagName(tag)+" "+name); + if (name == null) { + return 0; + } + int id = findCPIndex(tag, name); + if (id == 0) { + id = cpoolSize; + cpoolSize += 1; + setCPIndex(tag, name, id); + cpool.add(new Element(cpTagName(tag), + new String[]{"id", "" + id}, + new Object[]{name})); + int pos; + switch (tag) { + case CONSTANT_Long: + case CONSTANT_Double: + cpoolSize += 1; + break; + case CONSTANT_Class: + case CONSTANT_String: + getCPIndex(CONSTANT_Utf8, name); + break; + case CONSTANT_Fieldref: + case CONSTANT_Methodref: + case CONSTANT_InterfaceMethodref: + pos = name.indexOf(' '); + getCPIndex(CONSTANT_Class, name.substring(0, pos)); + getCPIndex(CONSTANT_NameAndType, name.substring(pos + 1)); + break; + case CONSTANT_NameAndType: + pos = name.indexOf(' '); + getCPIndex(CONSTANT_Utf8, name.substring(0, pos)); + getCPIndex(CONSTANT_Utf8, name.substring(pos + 1)); + break; + } + } + return id; + } + + public void setCPIndex(int tag, String name, int id) { + //System.out.println("setCPIndex id="+id+" tag="+tag+" name="+name); + int[] ids = cpMap.get(name); + if (ids == null) { + cpMap.put(name, ids = new int[13]); + } + if (ids[tag] != 0 && ids[tag] != id) { + System.out.println("Warning: Duplicate CP entries for " + ids[tag] + " and " + id); + } + //assert(ids[tag] == 0 || ids[tag] == id); + ids[tag] = id; + } + + public int parseFlags(String flagString) { + int flags = 0; + int i = -1; + for (String[] names : modifierNames) { + ++i; + for (String name : names) { + if (name == null) { + continue; + } + int pos = flagString.indexOf(name); + if (pos >= 0) { + flags |= (1 << i); + } + } + } + return flags; + } + + public void writeTo(OutputStream realOut) throws IOException { + OutputStream headOut = realOut; + ByteArrayOutputStream tailOut = new ByteArrayOutputStream(); + + // write the body of the class file first + this.out = tailOut; + writeClass(); + + // write the file header last + this.out = headOut; + u4((int) cfile.getAttrLong("magic")); + u2((int) cfile.getAttrLong("minver")); + u2((int) cfile.getAttrLong("majver")); + writeCP(); + + // recopy the file tail + this.out = null; + tailOut.writeTo(realOut); + } + + void writeClass() throws IOException { + int flags = parseFlags(klass.getAttr("flags")); + flags ^= Modifier.SYNCHRONIZED; + u2(flags); + cpRef(CONSTANT_Class, klass.getAttr("name")); + cpRef(CONSTANT_Class, klass.getAttr("super")); + Element interfaces = klass.findAllElements("Interface"); + u2(interfaces.size()); + for (Element e : interfaces.elements()) { + cpRef(CONSTANT_Class, e.getAttr("name")); + } + for (int isMethod = 0; isMethod <= 1; isMethod++) { + Element members = klass.findAllElements(isMethod != 0 ? "Method" : "Field"); + u2(members.size()); + for (Element m : members.elements()) { + writeMember(m, isMethod != 0); + } + } + writeAttributesFor(klass); + } + + private void writeMember(Element member, boolean isMethod) throws IOException { + //System.out.println("writeMember "+member); + u2(parseFlags(member.getAttr("flags"))); + cpRef(CONSTANT_Utf8, member.getAttr("name")); + cpRef(CONSTANT_Utf8, member.getAttr("type")); + writeAttributesFor(member); + } + + protected void writeAttributesFor(Element x) throws IOException { + LinkedHashSet attrNames = new LinkedHashSet(); + for (Element e : x.elements()) { + attrNames.add(e.getName()); // uniquifying + } + attrNames.removeAll(nonAttrTags()); + u2(attrNames.size()); + if (attrNames.isEmpty()) { + return; + } + Element prevCurrent; + if (x.getName() == "Code") { + prevCurrent = currentCode; + currentCode = x; + } else { + prevCurrent = currentMember; + currentMember = x; + } + OutputStream realOut = this.out; + for (String utag : attrNames) { + String qtag = x.getName() + "." + utag; + String wtag = "*." + utag; + String key = attrTypesByTag.get(qtag); + if (key == null) { + key = attrTypesByTag.get(wtag); + } + String type = attrTypes.get(key); + //System.out.println("tag "+qtag+" => key "+key+"; type "+type); + Element attrs = x.findAllElements(utag); + ByteArrayOutputStream attrBuf = getAttrBuf(); + if (type == null) { + if (attrs.size() != 1 || !attrs.get(0).equals(new Element(utag))) { + System.out.println("Warning: No attribute type description: " + qtag); + } + key = wtag; + } else { + try { + this.out = attrBuf; + // unparse according to type desc. + if (type.equals("...")) { + writeCode((Element) attrs.get(0)); // assume only 1 + } else if (type.equals("...")) { + writeStackMap(attrs, false); + } else if (type.equals("...")) { + writeStackMap(attrs, true); + } else if (type.startsWith("[")) { + writeAttributeRecursive(attrs, type); + } else { + writeAttribute(attrs, type); + } + } finally { + //System.out.println("Attr Bytes = \""+attrBuf.toString(EIGHT_BIT_CHAR_ENCODING).replace('"', (char)('"'|0x80))+"\""); + this.out = realOut; + } + } + cpRef(CONSTANT_Utf8, key.substring(key.indexOf('.') + 1)); + u4(attrBuf.size()); + attrBuf.writeTo(out); + putAttrBuf(attrBuf); + } + if (x.getName() == "Code") { + currentCode = prevCurrent; + } else { + currentMember = prevCurrent; + } + } + + private void writeAttributeRecursive(Element aval, String type) throws IOException { + assert (callables == null); + callables = getBodies(type); + writeAttribute(aval, callables[0]); + callables = null; + } + + private void writeAttribute(Element aval, String type) throws IOException { + //System.out.println("writeAttribute "+aval+" using "+type); + String nextAttrName = null; + boolean afterElemHead = false; + for (int len = type.length(), next, i = 0; i < len; i = next) { + int value; + char intKind; + int tag; + int sigChar; + String attrValue; + switch (type.charAt(i)) { + case '<': + assert (nextAttrName == null); + next = type.indexOf('>', i); + String form = type.substring(i + 1, next++); + if (form.indexOf('=') < 0) { + // elem_placement = '<' elemname '>' + if (aval.isAnonymous()) { + assert (aval.size() == 1); + aval = (Element) aval.get(0); + } + assert (aval.getName().equals(form)) : aval + " // " + form; + afterElemHead = true; + } else { + // attr_placement = '(' attrname '=' (value)? ')' + int eqPos = form.indexOf('='); + assert (eqPos >= 0); + nextAttrName = form.substring(0, eqPos).intern(); + if (eqPos != form.length() - 1) { + // value is implicit, not placed in file + nextAttrName = null; + } + afterElemHead = false; + } + continue; + case '(': + next = type.indexOf(')', ++i); + int callee = Integer.parseInt(type.substring(i, next++)); + writeAttribute(aval, callables[callee]); + continue; + case 'N': // replication = 'N' int '[' type ... ']' + { + assert (nextAttrName == null); + afterElemHead = false; + char countType = type.charAt(i + 1); + next = i + 2; + String type1 = getBody(type, next); + Element elems = aval; + if (type1.startsWith("<")) { + // Select only matching members of aval. + String elemName = type1.substring(1, type1.indexOf('>')); + elems = aval.findAllElements(elemName); + } + putInt(elems.size(), countType); + next += type1.length() + 2; // skip body and brackets + for (Element elem : elems.elements()) { + writeAttribute(elem, type1); + } + } + continue; + case 'T': // union = 'T' any_int union_case* '(' ')' '[' body ']' + // write the value + value = (int) aval.getAttrLong("tag"); + assert (aval.getAttr("tag") != null) : aval; + intKind = type.charAt(++i); + if (intKind == 'S') { + intKind = type.charAt(++i); + } + putInt(value, intKind); + nextAttrName = null; + afterElemHead = false; + ++i; // skip the int type char + // union_case = '(' ('-')? digit+ ')' '[' body ']' + for (boolean foundCase = false;;) { + assert (type.charAt(i) == '('); + next = type.indexOf(')', ++i); + assert (next >= i); + String caseStr = type.substring(i, next++); + String type1 = getBody(type, next); + next += type1.length() + 2; // skip body and brackets + boolean lastCase = (caseStr.length() == 0); + if (!foundCase + && (lastCase || matchTag(value, caseStr))) { + foundCase = true; + // Execute this body. + writeAttribute(aval, type1); + } + if (lastCase) { + break; + } + } + continue; + case 'B': + case 'H': + case 'I': // int = oneof "BHI" + value = (int) aval.getAttrLong(nextAttrName); + intKind = type.charAt(i); + next = i + 1; + break; + case 'K': + sigChar = type.charAt(i + 1); + if (sigChar == 'Q') { + assert (currentMember.getName() == "Field"); + assert (aval.getName() == "ConstantValue"); + String sig = currentMember.getAttr("type"); + sigChar = sig.charAt(0); + switch (sigChar) { + case 'Z': + case 'B': + case 'C': + case 'S': + sigChar = 'I'; + break; + } + } + switch (sigChar) { + case 'I': + tag = CONSTANT_Integer; + break; + case 'J': + tag = CONSTANT_Long; + break; + case 'F': + tag = CONSTANT_Float; + break; + case 'D': + tag = CONSTANT_Double; + break; + case 'L': + tag = CONSTANT_String; + break; + default: + assert (false); + tag = 0; + } + assert (type.charAt(i + 2) == 'H'); // only H works for now + next = i + 3; + assert (afterElemHead || nextAttrName != null); + //System.out.println("get attr "+nextAttrName+" in "+aval); + if (nextAttrName != null) { + attrValue = aval.getAttr(nextAttrName); + assert (attrValue != null); + } else { + assert (aval.isText()) : aval; + attrValue = aval.getText().toString(); + } + value = getCPIndex(tag, attrValue); + intKind = 'H'; //type.charAt(i+2); + break; + case 'R': + sigChar = type.charAt(i + 1); + switch (sigChar) { + case 'C': + tag = CONSTANT_Class; + break; + case 'S': + tag = CONSTANT_Utf8; + break; + case 'D': + tag = CONSTANT_Class; + break; + case 'F': + tag = CONSTANT_Fieldref; + break; + case 'M': + tag = CONSTANT_Methodref; + break; + case 'I': + tag = CONSTANT_InterfaceMethodref; + break; + case 'U': + tag = CONSTANT_Utf8; + break; + //case 'Q': tag = CONSTANT_Class; break; + default: + assert (false); + tag = 0; + } + assert (type.charAt(i + 2) == 'H'); // only H works for now + next = i + 3; + assert (afterElemHead || nextAttrName != null); + //System.out.println("get attr "+nextAttrName+" in "+aval); + if (nextAttrName != null) { + attrValue = aval.getAttr(nextAttrName); + } else if (aval.hasText()) { + attrValue = aval.getText().toString(); + } else { + attrValue = null; + } + value = getCPIndex(tag, attrValue); + intKind = 'H'; //type.charAt(i+2); + break; + case 'P': // bci = 'P' int + case 'S': // signed_int = 'S' int + next = i + 2; + value = (int) aval.getAttrLong(nextAttrName); + intKind = type.charAt(i + 1); + break; + case 'F': + next = i + 2; + value = parseFlags(aval.getAttr(nextAttrName)); + intKind = type.charAt(i + 1); + break; + default: + throw new RuntimeException("bad attr format '" + type.charAt(i) + "': " + type); + } + // write the value + putInt(value, intKind); + nextAttrName = null; + afterElemHead = false; + } + assert (nextAttrName == null); + } + + private void putInt(int x, char ch) throws IOException { + switch (ch) { + case 'B': + u1(x); + break; + case 'H': + u2(x); + break; + case 'I': + u4(x); + break; + } + assert ("BHI".indexOf(ch) >= 0); + } + + private void writeCode(Element code) throws IOException { + //System.out.println("writeCode "+code); + //Element m = new Element(currentMember); m.remove(code); + //System.out.println(" in "+m); + int stack = (int) code.getAttrLong("stack"); + int local = (int) code.getAttrLong("local"); + Element bytes = code.findElement("Bytes"); + Element insns = code.findElement("Instructions"); + String bytecodes; + if (insns == null) { + bytecodes = bytes.getText().toString(); + } else { + bytecodes = InstructionSyntax.assemble(insns, this); + // Cache the assembled bytecodes: + bytes = new Element("Bytes", (String[]) null, bytecodes); + code.add(0, bytes); + } + u2(stack); + u2(local); + int length = bytecodes.length(); + u4(length); + for (int i = 0; i < length; i++) { + u1((byte) bytecodes.charAt(i)); + } + Element handlers = code.findAllElements("Handler"); + u2(handlers.size()); + for (Element handler : handlers.elements()) { + int start = (int) handler.getAttrLong("start"); + int end = (int) handler.getAttrLong("end"); + int catsh = (int) handler.getAttrLong("catch"); + u2(start); + u2(end); + u2(catsh); + cpRef(CONSTANT_Class, handler.getAttr("class")); + } + writeAttributesFor(code); + } + + protected void writeStackMap(Element attrs, boolean hasXOption) throws IOException { + Element bytes = currentCode.findElement("Bytes"); + assert (bytes != null && bytes.size() == 1); + int byteLength = ((String) bytes.get(0)).length(); + boolean uoffsetIsU4 = (byteLength >= (1 << 16)); + boolean ulocalvarIsU4 = currentCode.getAttrLong("local") >= (1 << 16); + boolean ustackIsU4 = currentCode.getAttrLong("stack") >= (1 << 16); + if (uoffsetIsU4) { + u4(attrs.size()); + } else { + u2(attrs.size()); + } + for (Element frame : attrs.elements()) { + int bci = (int) frame.getAttrLong("bci"); + if (uoffsetIsU4) { + u4(bci); + } else { + u2(bci); + } + if (hasXOption) { + u1((int) frame.getAttrLong("flags")); + } + // Scan local and stack types in this frame: + final int LOCALS = 0, STACK = 1; + for (int j = LOCALS; j <= STACK; j++) { + Element types = frame.findElement(j == LOCALS ? "Local" : "Stack"); + int typeSize = (types == null) ? 0 : types.size(); + if (j == LOCALS) { + if (ulocalvarIsU4) { + u4(typeSize); + } else { + u2(typeSize); + } + } else { // STACK + if (ustackIsU4) { + u4(typeSize); + } else { + u2(typeSize); + } + } + if (types == null) { + continue; + } + for (Element type : types.elements()) { + int tag = itemTagValue(type.getName()); + u1(tag); + switch (tag) { + case ITEM_Object: + cpRef(CONSTANT_Class, type.getAttr("class")); + break; + case ITEM_Uninitialized: + case ITEM_ReturnAddress: { + int offset = (int) type.getAttrLong("bci"); + if (uoffsetIsU4) { + u4(offset); + } else { + u2(offset); + } + } + break; + } + } + } + } + } + + public void writeCP() throws IOException { + int cpLen = cpoolSize; + u2(cpLen); + ByteArrayOutputStream buf = getAttrBuf(); + for (Element c : cpool.elements()) { + if (!c.isText()) { + System.out.println("## !isText " + c); + } + int id = (int) c.getAttrLong("id"); + int tag = cpTagValue(c.getName()); + String name = c.getText().toString(); + int pos; + u1(tag); + switch (tag) { + case CONSTANT_Utf8: { + int done = 0; + buf.reset(); + int nameLen = name.length(); + while (done < nameLen) { + int next = name.indexOf((char) 0, done); + if (next < 0) { + next = nameLen; + } + if (done < next) { + buf.write(name.substring(done, next).getBytes(UTF8_ENCODING)); + } + if (next < nameLen) { + buf.write(0300); + buf.write(0200); + next++; + } + done = next; + } + u2(buf.size()); + buf.writeTo(out); + } + break; + case CONSTANT_Integer: + u4(Integer.parseInt(name)); + break; + case CONSTANT_Float: + u4(Float.floatToIntBits(Float.parseFloat(name))); + break; + case CONSTANT_Long: + u8(Long.parseLong(name)); + //i += 1; // no need: extra cp slot is implicit + break; + case CONSTANT_Double: + u8(Double.doubleToLongBits(Double.parseDouble(name))); + //i += 1; // no need: extra cp slot is implicit + break; + case CONSTANT_Class: + case CONSTANT_String: + u2(getCPIndex(CONSTANT_Utf8, name)); + break; + case CONSTANT_Fieldref: + case CONSTANT_Methodref: + case CONSTANT_InterfaceMethodref: + pos = name.indexOf(' '); + u2(getCPIndex(CONSTANT_Class, name.substring(0, pos))); + u2(getCPIndex(CONSTANT_NameAndType, name.substring(pos + 1))); + break; + case CONSTANT_NameAndType: + pos = name.indexOf(' '); + u2(getCPIndex(CONSTANT_Utf8, name.substring(0, pos))); + u2(getCPIndex(CONSTANT_Utf8, name.substring(pos + 1))); + break; + } + } + putAttrBuf(buf); + } + + public void cpRef(int tag, String name) throws IOException { + u2(getCPIndex(tag, name)); + } + + public void u8(long x) throws IOException { + u4((int) (x >>> 32)); + u4((int) (x >>> 0)); + } + + public void u4(int x) throws IOException { + u2(x >>> 16); + u2(x >>> 0); + } + + public void u2(int x) throws IOException { + u1(x >>> 8); + u1(x >>> 0); + } + + public void u1(int x) throws IOException { + out.write(x & 0xFF); + } +} + diff --git a/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/CommandLineParser.java b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/CommandLineParser.java new file mode 100644 index 00000000000..852b4b3f4a3 --- /dev/null +++ b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/CommandLineParser.java @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2010, 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 xmlkit; // -*- mode: java; indent-tabs-mode: nil -*- + +import java.util.*; +/* + * @author jrose + */ +public class CommandLineParser { + + public CommandLineParser(String optionString) { + setOptionMap(optionString); + } + TreeMap optionMap; + + public void setOptionMap(String options) { + // Convert options string into optLines dictionary. + TreeMap optmap = new TreeMap(); + loadOptmap: + for (String optline : options.split("\n")) { + String[] words = optline.split("\\p{Space}+"); + if (words.length == 0) { + continue loadOptmap; + } + String opt = words[0]; + words[0] = ""; // initial word is not a spec + if (opt.length() == 0 && words.length >= 1) { + opt = words[1]; // initial "word" is empty due to leading ' ' + words[1] = ""; + } + if (opt.length() == 0) { + continue loadOptmap; + } + String[] prevWords = optmap.put(opt, words); + if (prevWords != null) { + throw new RuntimeException("duplicate option: " + + optline.trim()); + } + } + optionMap = optmap; + } + + public String getOptionMap() { + TreeMap optmap = optionMap; + StringBuffer sb = new StringBuffer(); + for (String opt : optmap.keySet()) { + sb.append(opt); + for (String spec : optmap.get(opt)) { + sb.append(' ').append(spec); + } + sb.append('\n'); + } + return sb.toString(); + } + + /** + * Remove a set of command-line options from args, + * storing them in the properties map in a canonicalized form. + */ + public String parse(List args, Map properties) { + //System.out.println(args+" // "+properties); + + String resultString = null; + TreeMap optmap = optionMap; + + // State machine for parsing a command line. + ListIterator argp = args.listIterator(); + ListIterator pbp = new ArrayList().listIterator(); + doArgs: + for (;;) { + // One trip through this loop per argument. + // Multiple trips per option only if several options per argument. + String arg; + if (pbp.hasPrevious()) { + arg = pbp.previous(); + pbp.remove(); + } else if (argp.hasNext()) { + arg = argp.next(); + } else { + // No more arguments at all. + break doArgs; + } + tryOpt: + for (int optlen = arg.length();; optlen--) { + // One time through this loop for each matching arg prefix. + String opt; + // Match some prefix of the argument to a key in optmap. + findOpt: + for (;;) { + opt = arg.substring(0, optlen); + if (optmap.containsKey(opt)) { + break findOpt; + } + if (optlen == 0) { + break tryOpt; + } + // Decide on a smaller prefix to search for. + SortedMap pfxmap = optmap.headMap(opt); + // pfxmap.lastKey is no shorter than any prefix in optmap. + int len = pfxmap.isEmpty() ? 0 : pfxmap.lastKey().length(); + optlen = Math.min(len, optlen - 1); + opt = arg.substring(0, optlen); + // (Note: We could cut opt down to its common prefix with + // pfxmap.lastKey, but that wouldn't save many cycles.) + } + opt = opt.intern(); + assert (arg.startsWith(opt)); + assert (opt.length() == optlen); + String val = arg.substring(optlen); // arg == opt+val + + // Execute the option processing specs for this opt. + // If no actions are taken, then look for a shorter prefix. + boolean didAction = false; + boolean isError = false; + + int pbpMark = pbp.nextIndex(); // in case of backtracking + String[] specs = optmap.get(opt); + eachSpec: + for (String spec : specs) { + if (spec.length() == 0) { + continue eachSpec; + } + if (spec.startsWith("#")) { + break eachSpec; + } + int sidx = 0; + char specop = spec.charAt(sidx++); + + // Deal with '+'/'*' prefixes (spec conditions). + boolean ok; + switch (specop) { + case '+': + // + means we want an non-empty val suffix. + ok = (val.length() != 0); + specop = spec.charAt(sidx++); + break; + case '*': + // * means we accept empty or non-empty + ok = true; + specop = spec.charAt(sidx++); + break; + default: + // No condition prefix means we require an exact + // match, as indicated by an empty val suffix. + ok = (val.length() == 0); + break; + } + if (!ok) { + continue eachSpec; + } + + String specarg = spec.substring(sidx); + switch (specop) { + case '.': // terminate the option sequence + resultString = (specarg.length() != 0) ? specarg.intern() : opt; + break doArgs; + case '?': // abort the option sequence + resultString = (specarg.length() != 0) ? specarg.intern() : arg; + isError = true; + break eachSpec; + case '@': // change the effective opt name + opt = specarg.intern(); + break; + case '>': // shift remaining arg val to next arg + pbp.add(specarg + val); // push a new argument + val = ""; + break; + case '!': // negation option + String negopt = (specarg.length() != 0) ? specarg.intern() : opt; + properties.remove(negopt); + properties.put(negopt, null); // leave placeholder + didAction = true; + break; + case '$': // normal "boolean" option + String boolval; + if (specarg.length() != 0) { + // If there is a given spec token, store it. + boolval = specarg; + } else { + String old = properties.get(opt); + if (old == null || old.length() == 0) { + boolval = "1"; + } else { + // Increment any previous value as a numeral. + boolval = "" + (1 + Integer.parseInt(old)); + } + } + properties.put(opt, boolval); + didAction = true; + break; + case '=': // "string" option + case '&': // "collection" option + // Read an option. + boolean append = (specop == '&'); + String strval; + if (pbp.hasPrevious()) { + strval = pbp.previous(); + pbp.remove(); + } else if (argp.hasNext()) { + strval = argp.next(); + } else { + resultString = arg + " ?"; + isError = true; + break eachSpec; + } + if (append) { + String old = properties.get(opt); + if (old != null) { + // Append new val to old with embedded delim. + String delim = specarg; + if (delim.length() == 0) { + delim = " "; + } + strval = old + specarg + strval; + } + } + properties.put(opt, strval); + didAction = true; + break; + default: + throw new RuntimeException("bad spec for " + + opt + ": " + spec); + } + } + + // Done processing specs. + if (didAction && !isError) { + continue doArgs; + } + + // The specs should have done something, but did not. + while (pbp.nextIndex() > pbpMark) { + // Remove anything pushed during these specs. + pbp.previous(); + pbp.remove(); + } + + if (isError) { + throw new IllegalArgumentException(resultString); + } + + if (optlen == 0) { + // We cannot try a shorter matching option. + break tryOpt; + } + } + + // If we come here, there was no matching option. + // So, push back the argument, and return to caller. + pbp.add(arg); + break doArgs; + } + // Report number of arguments consumed. + args.subList(0, argp.nextIndex()).clear(); + // Report any unconsumed partial argument. + while (pbp.hasPrevious()) { + args.add(0, pbp.previous()); + } + //System.out.println(args+" // "+properties+" -> "+resultString); + return resultString; + } +} diff --git a/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/InstructionAssembler.java b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/InstructionAssembler.java new file mode 100644 index 00000000000..cbe34e6b960 --- /dev/null +++ b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/InstructionAssembler.java @@ -0,0 +1,464 @@ +/* + * Copyright (c) 2010, 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 xmlkit; // -*- mode: java; indent-tabs-mode: nil -*- + +import xmlkit.XMLKit.Element; +import java.util.HashMap; +/* + * @author jrose + */ +abstract class InstructionAssembler extends InstructionSyntax { + + InstructionAssembler() { + } + + public static String assemble(Element instructions, String pcAttrName, + ClassSyntax.GetCPIndex getCPI) { + int insCount = instructions.size(); + Element[] insElems = new Element[insCount]; + int[] elemToIndexMap; + int[] insLocs; + byte[] ops = new byte[insCount]; + int[] operands = new int[insCount]; + boolean[] isWide = new boolean[insCount]; + int[] branches; + int[] branchInsLocs; + HashMap labels = new HashMap(); + + final int WIDE = 0xc4; + final int GOTO = 0xa7; + final int GOTO_W = 0xc8; + final int GOTO_LEN = 3; + final int GOTO_W_LEN = 5; + assert ("wide".equals(bcNames[WIDE])); + assert ("goto".equals(bcNames[GOTO])); + assert ("goto_w".equals(bcNames[GOTO_W])); + assert (bcFormats[GOTO].length() == GOTO_LEN); + assert (bcFormats[GOTO_W].length() == GOTO_W_LEN); + + // Unpack instructions into temp. arrays, and find branches and labels. + { + elemToIndexMap = (pcAttrName != null) ? new int[insCount] : null; + int[] buffer = operands; + int id = 0; + int branchCount = 0; + for (int i = 0; i < insCount; i++) { + Element ins = (Element) instructions.get(i); + if (elemToIndexMap != null) { + elemToIndexMap[i] = (ins.getAttr(pcAttrName) != null ? id : -1); + } + String lab = ins.getAttr("pc"); + if (lab != null) { + labels.put(lab, String.valueOf(id)); + } + int op = opCode(ins.getName()); + if (op < 0) { + assert (ins.getAttr(pcAttrName) != null + || ins.getName().equals("label")); + continue; // delete PC holder element + } + if (op == WIDE) { //0xc4 + isWide[id] = true; // force wide format + continue; + } + if (bcFormats[op].indexOf('o') >= 0) { + buffer[branchCount++] = id; + } + if (bcFormats[op] == bcWideFormats[op]) { + isWide[id] = false; + } + insElems[id] = ins; + ops[id] = (byte) op; + id++; + } + insCount = id; // maybe we deleted some wide prefixes, etc. + branches = new int[branchCount + 1]; + System.arraycopy(buffer, 0, branches, 0, branchCount); + branches[branchCount] = -1; // sentinel + } + + // Compute instruction sizes. These sizes are final, + // except for branch instructions, which may need lengthening. + // Some instructions (ldc, bipush, iload, iinc) are automagically widened. + insLocs = new int[insCount + 1]; + int loc = 0; + for (int bn = 0, id = 0; id < insCount; id++) { + insLocs[id] = loc; + Element ins = insElems[id]; + int op = ops[id] & 0xFF; + String format = opFormat(op, isWide[id]); + // Make sure operands fit within the given format. + for (int j = 1, jlimit = format.length(); j < jlimit; j++) { + char fc = format.charAt(j); + int x = 0; + switch (fc) { + case 'l': + x = (int) ins.getAttrLong("loc"); + assert (x >= 0); + if (x > 0xFF && !isWide[id]) { + isWide[id] = true; + format = opFormat(op, isWide[id]); + } + assert (x <= 0xFFFF); + break; + case 'k': + char fc2 = format.charAt(Math.min(j + 1, format.length() - 1)); + x = getCPIndex(ins, fc2, getCPI); + if (x > 0xFF && j == jlimit - 1) { + assert (op == 0x12); //ldc + ops[id] = (byte) (op = 0x13); //ldc_w + format = opFormat(op); + } + assert (x <= 0xFFFF); + j++; // skip type-of-constant marker + break; + case 'x': + x = (int) ins.getAttrLong("num"); + assert (x >= 0 && x <= ((j == jlimit - 1) ? 0xFF : 0xFFFF)); + break; + case 's': + x = (int) ins.getAttrLong("num"); + if (x != (byte) x && j == jlimit - 1) { + switch (op) { + case 0x10: //bipush + ops[id] = (byte) (op = 0x11); //sipush + break; + case 0x84: //iinc + isWide[id] = true; + format = opFormat(op, isWide[id]); + break; + default: + assert (false); // cannot lengthen + } + } + // unsign the value now, to make later steps clearer + if (j == jlimit - 1) { + assert (x == (byte) x); + x = x & 0xFF; + } else { + assert (x == (short) x); + x = x & 0xFFFF; + } + break; + case 'o': + assert (branches[bn] == id); + bn++; + // make local copies of the branches, and fix up labels + insElems[id] = ins = new Element(ins); + String newLab = labels.get(ins.getAttr("lab")); + assert (newLab != null); + ins.setAttr("lab", newLab); + int prevCas = 0; + int k = 0; + for (Element cas : ins.elements()) { + assert (cas.getName().equals("Case")); + ins.set(k++, cas = new Element(cas)); + newLab = labels.get(cas.getAttr("lab")); + assert (newLab != null); + cas.setAttr("lab", newLab); + int thisCas = (int) cas.getAttrLong("num"); + assert (op == 0xab + || op == 0xaa && (k == 0 || thisCas == prevCas + 1)); + prevCas = thisCas; + } + break; + case 't': + // switch table is represented as Switch.Case sub-elements + break; + default: + assert (false); + } + operands[id] = x; // record operand (last if there are 2) + // skip redundant chars + while (j + 1 < jlimit && format.charAt(j + 1) == fc) { + ++j; + } + } + + switch (op) { + case 0xaa: //tableswitch + loc = switchBase(loc); + loc += 4 * (3 + ins.size()); + break; + case 0xab: //lookupswitch + loc = switchBase(loc); + loc += 4 * (2 + 2 * ins.size()); + break; + default: + if (isWide[id]) { + loc++; // 'wide' opcode prefix + } + loc += format.length(); + break; + } + } + insLocs[insCount] = loc; + + // compute branch offsets, and see if any branches need expansion + for (int maxTries = 9, tries = 0;; ++tries) { + boolean overflowing = false; + boolean[] branchExpansions = null; + for (int bn = 0; bn < branches.length - 1; bn++) { + int id = branches[bn]; + Element ins = insElems[id]; + int insSize = insLocs[id + 1] - insLocs[id]; + int origin = insLocs[id]; + int target = insLocs[(int) ins.getAttrLong("lab")]; + int offset = target - origin; + operands[id] = offset; + //System.out.println("branch id="+id+" len="+insSize+" to="+target+" offset="+offset); + assert (insSize == GOTO_LEN || insSize == GOTO_W_LEN || ins.getName().indexOf("switch") > 0); + boolean thisOverflow = (insSize == GOTO_LEN && (offset != (short) offset)); + if (thisOverflow && !overflowing) { + overflowing = true; + branchExpansions = new boolean[branches.length]; + } + if (thisOverflow || tries == maxTries - 1) { + // lengthen the branch + assert (!(thisOverflow && isWide[id])); + isWide[id] = true; + branchExpansions[bn] = true; + } + } + if (!overflowing) { + break; // done, usually on first try + } + assert (tries <= maxTries); + + // Walk over all instructions, expanding branches and updating locations. + int fixup = 0; + for (int bn = 0, id = 0; id < insCount; id++) { + insLocs[id] += fixup; + if (branches[bn] == id) { + int op = ops[id] & 0xFF; + int wop; + boolean invert; + if (branchExpansions[bn]) { + switch (op) { + case GOTO: //0xa7 + wop = GOTO_W; //0xc8 + invert = false; + break; + case 0xa8: //jsr + wop = 0xc9; //jsr_w + invert = false; + break; + default: + wop = invertBranchOp(op); + invert = true; + break; + } + assert (op != wop); + ops[id] = (byte) wop; + isWide[id] = invert; + if (invert) { + fixup += GOTO_W_LEN; //branch around a wide goto + } else { + fixup += (GOTO_W_LEN - GOTO_LEN); + } + // done expanding: ops and isWide reflect the decision + } + bn++; + } + } + insLocs[insCount] += fixup; + } + // we know the layout now + + // notify the caller of offsets, if requested + if (elemToIndexMap != null) { + for (int i = 0; i < elemToIndexMap.length; i++) { + int id = elemToIndexMap[i]; + if (id >= 0) { + Element ins = (Element) instructions.get(i); + ins.setAttr(pcAttrName, "" + insLocs[id]); + } + } + elemToIndexMap = null; // release the pointer + } + + // output the bytes + StringBuffer sbuf = new StringBuffer(insLocs[insCount]); + for (int bn = 0, id = 0; id < insCount; id++) { + //System.out.println("output id="+id+" loc="+insLocs[id]+" len="+(insLocs[id+1]-insLocs[id])+" #sbuf="+sbuf.length()); + assert (sbuf.length() == insLocs[id]); + Element ins; + int pc = insLocs[id]; + int nextpc = insLocs[id + 1]; + int op = ops[id] & 0xFF; + int opnd = operands[id]; + String format; + if (branches[bn] == id) { + bn++; + sbuf.append((char) op); + if (isWide[id]) { + // emit