Merge jdk7-b109
authorduke
Wed, 05 Jul 2017 17:21:32 +0200
changeset 6363 e02b4d709e17
parent 6362 4fa2d855816f (diff)
parent 6241 31c6bb34ec7b (current diff)
child 6364 ef034140f5b6
child 6365 f2b5a19b6265
child 6367 bd27e3b26ea1
child 6370 184ac472c680
child 6372 b9970e9558e6
child 6380 50870108a9a4
child 6382 dcabd2849331
child 6384 763ed87a4ad6
child 6386 e83732b7f531
Merge
--- a/.hgtags-top-repo	Wed Jul 05 17:20:50 2017 +0200
+++ b/.hgtags-top-repo	Wed Jul 05 17:21:32 2017 +0200
@@ -82,3 +82,4 @@
 9f96a4269d7727dad68864eaab795eafce270311 jdk7-b105
 43096cccf1cee749c2f4e7714ee71f4e9e0f4d7f jdk7-b106
 7d396ad455c3b2f68b0d7094891c5aba7c757a6e jdk7-b107
+140fdef4ddf52244013b6157dc542cd9f677bb6f jdk7-b108
--- a/corba/.hgtags	Wed Jul 05 17:20:50 2017 +0200
+++ b/corba/.hgtags	Wed Jul 05 17:21:32 2017 +0200
@@ -82,3 +82,4 @@
 6f21b030092fb61244cc8a0aedf8058f7c022b81 jdk7-b105
 519daea48888196af76a975a3b31258efa860bad jdk7-b106
 232adb83eae8375439ccff65b6e205ca0da0510d jdk7-b107
+8d810527b499a67153365db74421a03c12b46f35 jdk7-b108
--- a/corba/make/jprt.properties	Wed Jul 05 17:20:50 2017 +0200
+++ b/corba/make/jprt.properties	Wed Jul 05 17:21:32 2017 +0200
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2006, 2009, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2006, 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
@@ -24,29 +24,13 @@
 #
 
 # Properties for jprt
-jprt.tools.default.release=jdk1.7.0
 
-# Specific platform list
-jprt.build.platforms=\
-solaris_sparc_5.10,\
-solaris_sparcv9_5.10,\
-solaris_i586_5.10,\
-solaris_x64_5.10,\
-linux_i586_2.6,\
-linux_x64_2.6,\
-windows_i586_5.0,\
-windows_x64_5.2
+# Use whatever release that the submitted job requests
+jprt.tools.default.release=${jprt.submit.release}
 
-# The different build flavors we want
+# The different build flavors we want, we override here so we just get these 2
 jprt.build.flavors=product,fastdebug
 
-# Explicitly designate what the 32bit match is for the 64bit build
-jprt.solaris_sparcv9.build.platform.match32=solaris_sparc_5.10
-jprt.solaris_sparcv9_5.10.build.platform.match32=solaris_sparc_5.10
-jprt.solaris_x64.build.platform.match32=solaris_i586_5.10
-jprt.solaris_x64_5.10.build.platform.match32=solaris_i586_5.10
+# Directories to be excluded from the source bundles
+jprt.bundle.exclude.src.dirs=build dist webrev
 
-# Directories needed to build
-jprt.bundle.src.dirs=make src
-jprt.bundle.exclude.src.dirs=build dist
-
--- a/hotspot/.hgtags	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/.hgtags	Wed Jul 05 17:21:32 2017 +0200
@@ -115,3 +115,6 @@
 cc3fdfeb54b049f18edcf3463e6ab051d0b7b609 hs19-b05
 688a538aa65412178286ae2a6b0c00b6711e121b hs19-b06
 bf496cbe9b74dda5975a1559da7ecfdd313e509e jdk7-b107
+0000000000000000000000000000000000000000 hs19-b06
+6c43216df13513a0f96532aa06f213066c49e27b hs19-b06
+e44a93947ccbfce712b51725f313163606f15486 jdk7-b108
--- a/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -112,6 +112,11 @@
     }
   }
 
+#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" : ""),
--- a/hotspot/src/cpu/x86/vm/vm_version_x86.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/cpu/x86/vm/vm_version_x86.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -482,6 +482,15 @@
     }
   }
 
+#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 @@
     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");
--- a/hotspot/src/cpu/x86/vm/x86_32.ad	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/cpu/x86/vm/x86_32.ad	Wed Jul 05 17:21:32 2017 +0200
@@ -852,6 +852,39 @@
   }
 }
 
+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 @@
   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 @@
     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);
--- a/hotspot/src/cpu/x86/vm/x86_64.ad	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/cpu/x86/vm/x86_64.ad	Wed Jul 05 17:21:32 2017 +0200
@@ -1607,8 +1607,8 @@
           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 @@
           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",
--- a/hotspot/src/cpu/zero/vm/bytecodeInterpreter_zero.inline.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/cpu/zero/vm/bytecodeInterpreter_zero.inline.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
   return op1 - op2;
 }
 
-inline jint BytecodeInterpreter::VMintUshr(jint op1, jint op2) {
+inline juint BytecodeInterpreter::VMintUshr(jint op1, jint op2) {
   return ((juint) op1) >> (op2 & 0x1F);
 }
 
--- a/hotspot/src/cpu/zero/vm/javaFrameAnchor_zero.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/cpu/zero/vm/javaFrameAnchor_zero.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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);
   }
--- a/hotspot/src/os_cpu/linux_sparc/vm/orderAccess_linux_sparc.inline.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/os_cpu/linux_sparc/vm/orderAccess_linux_sparc.inline.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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::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() {
--- a/hotspot/src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/os_cpu/linux_x86/vm/orderAccess_linux_x86.inline.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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::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() {
--- a/hotspot/src/os_cpu/linux_zero/vm/os_linux_zero.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/os_cpu/linux_zero/vm/os_linux_zero.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -435,22 +435,22 @@
   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);
   }
 };
 
--- a/hotspot/src/os_cpu/linux_zero/vm/thread_linux_zero.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/os_cpu/linux_zero/vm/thread_linux_zero.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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
+}
--- a/hotspot/src/os_cpu/solaris_sparc/vm/orderAccess_solaris_sparc.inline.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/os_cpu/solaris_sparc/vm/orderAccess_solaris_sparc.inline.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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::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::release() {
-  dummy = 0;
+  // Avoid hitting the same cache-line from
+  // different threads.
+  volatile jint local_dummy = 0;
 }
 
 inline void OrderAccess::fence() {
--- a/hotspot/src/os_cpu/solaris_x86/vm/orderAccess_solaris_x86.inline.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/os_cpu/solaris_x86/vm/orderAccess_solaris_x86.inline.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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::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 @@
 
 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() {
--- a/hotspot/src/os_cpu/windows_x86/vm/orderAccess_windows_x86.inline.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/os_cpu/windows_x86/vm/orderAccess_windows_x86.inline.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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::release() {
   // A volatile store has release semantics.
-  dummy = 0;
+  volatile jint local_dummy = 0;
 }
 
 inline void OrderAccess::fence() {
--- a/hotspot/src/share/vm/code/nmethod.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/code/nmethod.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -433,6 +433,10 @@
   _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 @@
 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 @@
   // 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");
   }
--- a/hotspot/src/share/vm/code/nmethod.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/code/nmethod.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -177,6 +177,10 @@
   // 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 @@
   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];
   }
 
--- a/hotspot/src/share/vm/compiler/compileBroker.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/compiler/compileBroker.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -1652,12 +1652,10 @@
 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=");
--- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
   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 @@
 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 @@
 
 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 @@
       // 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 @@
         // 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 @@
     // 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 @@
     // 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 @@
   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 @@
   // 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::print() const {
-  tty->print(" CompactibleFreeListSpace");
-  Space::print();
+  Space::print_on(tty);
 }
 
 void CompactibleFreeListSpace::prepare_for_verify() {
@@ -2253,18 +2278,28 @@
  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 @@
                   "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 @@
 
 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 @@
          (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 @@
             // 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 @@
             // 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->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads.
               ffc->linkNext(NULL);
-              ffc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads.
               // 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 @@
                                   _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 @@
       }
     }
     if (fc == NULL) return;
+    // Otherwise, split up that block.
     assert((ssize_t)n >= 1, "Control point invariant");
-    // Otherwise, split up that block.
+    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 @@
     // 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 @@
       size_t prefix_size = n * word_sz;
       rem_fc = (FreeChunk*)((HeapWord*)fc + prefix_size);
       rem_fc->setSize(rem);
+      rem_fc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads.
       rem_fc->linkNext(NULL);
-      rem_fc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads.
       // 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 @@
   for (ssize_t i = n-1; i > 0; i--) {
     FreeChunk* ffc = (FreeChunk*)((HeapWord*)fc + i * word_sz);
     ffc->setSize(word_sz);
+    ffc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads.
     ffc->linkNext(NULL);
-    ffc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads.
     // 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 @@
     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);
--- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
   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 @@
   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 @@
 
   // 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;
--- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -1019,7 +1019,7 @@
 }
 
 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 @@
   // 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 @@
     // [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 @@
     // 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 @@
   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 @@
     }
   }
   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 @@
   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());
-
-  assert(old->is_oop(), "Will dereference klass ptr below");
+  // 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");
+
   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;
@@ -1965,6 +2034,9 @@
                                             _intra_sweep_estimate.padded_average());
   }
 
+  {
+    TraceCMSMemoryManagerStats();
+  }
   GenMarkSweep::invoke_at_safepoint(_cmsGen->level(),
     ref_processor(), clear_all_soft_refs);
   #ifdef ASSERT
@@ -3415,6 +3487,7 @@
 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");
@@ -4748,6 +4821,7 @@
   // 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();
 
@@ -5849,6 +5923,8 @@
   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());
@@ -7861,14 +7937,20 @@
   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);
-  }
-  assert(addr <= _limit, "sweep invariant");
+    return pointer_delta(_sp->end(), addr);
+  }
+  assert(addr < _limit, "sweep invariant");
 
   // check if we should yield
   do_yield_check(addr);
@@ -9121,3 +9203,57 @@
   }
   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 */ );
+}
+
--- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -507,6 +507,7 @@
   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;
@@ -1009,10 +1010,10 @@
 
   // 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
@@ -1858,3 +1859,11 @@
     _dead_bit_map(dead_bit_map) {}
   size_t do_blk(HeapWord* addr);
 };
+
+class TraceCMSMemoryManagerStats : public TraceMemoryManagerStats {
+
+ public:
+  TraceCMSMemoryManagerStats(CMSCollector::CollectorState phase);
+  TraceCMSMemoryManagerStats();
+};
+
--- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
   }
   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
--- a/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/promotionInfo.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/promotionInfo.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -330,7 +330,7 @@
 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 @@
     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 {
--- a/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -339,7 +339,9 @@
   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 @@
       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);
+          }
         }
       }
     }
--- a/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentG1Refine.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
   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() {
--- a/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/g1/concurrentMark.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -2586,9 +2586,6 @@
   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);
--- a/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
 }
 
 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,15 +200,22 @@
   }
 }
 
+bool DirtyCardQueueSet::apply_closure_to_completed_buffer(CardTableEntryClosure* cl,
+                                                          int worker_i,
+                                                          int stop_at,
+                                                          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(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)
-{
-  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);
-  if (res) Atomic::inc(&_processed_buffers_rs_thread);
-  return res;
+                                                          bool during_pause) {
+  return apply_closure_to_completed_buffer(_closure, worker_i,
+                                           stop_at, during_pause);
 }
 
 void DirtyCardQueueSet::apply_closure_to_all_completed_buffers() {
@@ -222,8 +230,8 @@
   }
 }
 
-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 @@
     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()) {
--- a/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/g1/dirtyCardQueue.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
                                          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 @@
     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();
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -56,7 +56,12 @@
     _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;
@@ -1039,29 +1044,56 @@
   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);
-
-  // 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 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");
+  const size_t min_heap_size = collector_policy()->min_heap_byte_size();
+  const size_t max_heap_size = collector_policy()->max_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;
+
+  // 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",
@@ -1073,45 +1105,47 @@
                            "  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);
     }
   }
 }
@@ -1322,6 +1356,7 @@
   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 +1607,16 @@
                                       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 +1648,16 @@
   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,
@@ -2147,9 +2194,12 @@
   }
 }
 
-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() {
@@ -2692,6 +2742,35 @@
   }
 };
 
+#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()) {
@@ -2825,93 +2904,57 @@
       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);
-
-        setup_surviving_young_words();
-
-        // Set up the gc allocation regions.
-        get_gc_alloc_regions();
-
-        // Actually do the work...
-        evacuate_collection_set();
-
-        free_collection_set(g1_policy()->collection_set());
-        g1_policy()->clear_collection_set();
-
-        cleanup_surviving_young_words();
-
-        // 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();
-
-        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");
+      PrepareForRSScanningClosure prepare_for_rs_scan;
+      collection_set_iterate(&prepare_for_rs_scan);
+
+      setup_surviving_young_words();
+
+      // Set up the gc allocation regions.
+      get_gc_alloc_regions();
+
+      // Actually do the work...
+      evacuate_collection_set();
+
+      free_collection_set(g1_policy()->collection_set());
+      g1_policy()->clear_collection_set();
+
+      cleanup_surviving_young_words();
+
+      // 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();
+
+      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");
 
 #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()) {
@@ -2945,7 +2988,7 @@
       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.");
 
@@ -2988,6 +3031,9 @@
     }
   }
 
+  TASKQUEUE_STATS_ONLY(if (ParallelGCVerbose) print_taskqueue_stats());
+  TASKQUEUE_STATS_ONLY(reset_taskqueue_stats());
+
   if (PrintHeapAtGC) {
     Universe::print_heap_after_gc();
   }
@@ -3346,25 +3392,6 @@
   }
 };
 
-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 <class T> 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 +3416,6 @@
   }
 };
 
-
-
 class RemoveSelfPointerClosure: public ObjectClosure {
 private:
   G1CollectedHeap* _g1;
@@ -3453,7 +3478,7 @@
 };
 
 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;
@@ -3583,7 +3608,7 @@
   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());
     }
@@ -3617,6 +3642,10 @@
 
 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;
@@ -3649,6 +3678,10 @@
                                          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.
@@ -3754,10 +3787,6 @@
     _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)
 {
@@ -3777,14 +3806,41 @@
   _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) { }
@@ -3991,12 +4047,9 @@
     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
@@ -4115,35 +4168,9 @@
     // 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");
@@ -4260,6 +4287,7 @@
   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);
@@ -4293,7 +4321,7 @@
   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("--");
     }
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -46,17 +46,7 @@
 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<StarTask>          RefToScanQueue;
+typedef OverflowTaskQueue<StarTask>         RefToScanQueue;
 typedef GenericTaskQueueSet<RefToScanQueue> RefToScanQueueSet;
 
 typedef int RegionIdx_t;   // needs to hold [ 0..max_regions() )
@@ -471,6 +461,12 @@
   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();
@@ -505,6 +501,12 @@
   // 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);
@@ -656,11 +658,18 @@
 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; }
 
+  // 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 +724,9 @@
     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; }
@@ -1021,7 +1032,7 @@
   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
@@ -1564,9 +1575,6 @@
   CardTableModRefBS* _ct_bs;
   G1RemSet* _g1_rem;
 
-  typedef GrowableArray<StarTask> OverflowQueue;
-  OverflowQueue* _overflowed_refs;
-
   G1ParGCAllocBuffer  _surviving_alloc_buffer;
   G1ParGCAllocBuffer  _tenured_alloc_buffer;
   G1ParGCAllocBuffer* _alloc_buffers[GCAllocPurposeCount];
@@ -1583,10 +1591,6 @@
   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;
@@ -1600,7 +1604,7 @@
   // 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; }
 
@@ -1635,15 +1639,14 @@
   }
 
   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 <class T> void push_on_queue(T* ref) {
     assert(ref != NULL, "invariant");
@@ -1656,12 +1659,7 @@
       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) {
@@ -1672,7 +1670,6 @@
              _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;
@@ -1680,7 +1677,8 @@
   }
 
   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) ||
@@ -1690,8 +1688,8 @@
     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 <class T> void update_rs(HeapRegion* from, T* p, int tid) {
     if (G1DeferredRSUpdate) {
@@ -1760,30 +1758,16 @@
   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();
@@ -1792,12 +1776,17 @@
   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)
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.inline.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -57,8 +57,9 @@
   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,23 +70,23 @@
     } 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);
 }
 
-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);
 }
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -88,7 +88,6 @@
   _all_mod_union_times_ms(new NumberSeq()),
 
   _summary(new Summary()),
-  _abandoned_summary(new AbandonedSummary()),
 
 #ifndef PRODUCT
   _cur_clear_ct_time_ms(0.0),
@@ -238,7 +237,6 @@
   _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 +840,6 @@
     _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;
@@ -1126,7 +1123,7 @@
 // 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;
@@ -1136,7 +1133,7 @@
   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) {
@@ -1275,12 +1272,7 @@
     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);
@@ -1348,61 +1340,58 @@
 
   double other_time_ms = elapsed_ms;
 
-  if (!abandoned) {
-    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 (_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 (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);
@@ -2178,33 +2167,27 @@
     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);
     }
@@ -2215,20 +2198,6 @@
   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");
@@ -2242,9 +2211,6 @@
     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);
@@ -2870,19 +2836,12 @@
 }
 #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,
@@ -2986,10 +2945,6 @@
     }
 
     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 =
@@ -3011,10 +2966,6 @@
     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);
@@ -3041,12 +2992,6 @@
     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:
@@ -3059,19 +3004,6 @@
   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() {
@@ -3086,7 +3018,7 @@
 }
 
 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.");
 }
--- a/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1CollectorPolicy.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -63,8 +63,6 @@
     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
@@ -78,9 +76,6 @@
   virtual MainBodySummary*    main_body_summary()    { return this; }
 };
 
-class AbandonedSummary: public PauseSummary {
-};
-
 class G1CollectorPolicy: public CollectorPolicy {
 protected:
   // The number of pauses during the execution.
@@ -150,7 +145,6 @@
   TruncatedSeq* _concurrent_mark_cleanup_times_ms;
 
   Summary*           _summary;
-  AbandonedSummary*  _abandoned_summary;
 
   NumberSeq* _all_pause_times_ms;
   NumberSeq* _all_full_gc_times_ms;
@@ -177,7 +171,6 @@
   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;
@@ -576,7 +569,6 @@
                          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;
@@ -889,7 +881,7 @@
   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();
@@ -933,14 +925,6 @@
     _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;
   }
@@ -1010,7 +994,7 @@
   // 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.
@@ -1267,7 +1251,7 @@
   // 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,
@@ -1278,7 +1262,7 @@
   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
--- a/hotspot/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1OopClosures.inline.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
       _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 @@
     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);
     }
   }
 }
+
--- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -122,23 +122,24 @@
 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),
+    _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<OopOrNarrowOopStar>*, 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<OopOrNarrowOopStar>(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<OopOrNarrowOopStar>*, _new_refs);
+  FREE_C_HEAP_ARRAY(OopsInHeapRegionClosure*, _cset_rs_update_cl);
 }
 
 void CountNonCleanMemRegionClosure::do_MemRegion(MemRegion mr) {
@@ -306,12 +307,45 @@
   _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 @@
   }
 };
 
-template <class T> 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,32 +464,48 @@
     count_cl.print_histo();
   }
 
-  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(worker_i);
-      scanNewRefsRS(oc, 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);
-    } else {
-      _g1p->record_scan_rs_time(worker_i, 0.0);
-    }
+  // 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());
+
+  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(0);
-    scanNewRefsRS(oc, 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
+  _cset_rs_update_cl[worker_i] = NULL;
 }
 
 void HRInto_G1RemSet::
@@ -497,9 +520,9 @@
   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" );
@@ -519,49 +542,65 @@
   }
 };
 
-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 <class T> 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 <class T> 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 <class T> 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;
@@ -580,27 +619,42 @@
   // 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();
 
   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();
-  }
 
-  assert(!_par_traversal_in_progress, "Invariant between iterations.");
+  // 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(!_traversal_in_progress, "Invariant between iterations.");
 }
 
 class UpdateRSObjectClosure: public ObjectClosure {
@@ -652,7 +706,43 @@
 
 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 <class T> 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 <class T> 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 <class T> 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 +759,16 @@
 
   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 +816,18 @@
     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 +835,9 @@
   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 +851,7 @@
   // 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 +864,7 @@
   // 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 +905,14 @@
   //                  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 +929,31 @@
         // 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 +1051,7 @@
 
   }
 }
+
 void HRInto_G1RemSet::prepare_for_verify() {
   if (G1HRRSFlushLogBuffersOnVerify &&
       (VerifyBeforeGC || VerifyAfterGC)
@@ -932,7 +1064,9 @@
     }
     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");
--- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
   // 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() {}
@@ -142,24 +148,22 @@
   size_t*             _cards_scanned;
   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".
-  bool _par_traversal_in_progress;
-  void set_par_traversal(bool b) { _par_traversal_in_progress = b; }
-  GrowableArray<OopOrNarrowOopStar>** _new_refs;
-  template <class T> void new_refs_iterate_work(OopClosure* cl);
-  void new_refs_iterate(OopClosure* cl) {
-    if (UseCompressedOops) {
-      new_refs_iterate_work<narrowOop>(cl);
-    } else {
-      new_refs_iterate_work<oop>(cl);
-    }
-  }
+  // _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.
+  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 <class T> void write_ref_nv(HeapRegion* from, T* p);
@@ -188,7 +192,7 @@
       scanNewRefsRS_work<oop>(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 @@
   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 @@
   //  bool idempotent() { return true; }
   bool apply_to_weak_ref_discovered_field() { return true; }
 };
+
+class UpdateRSetImmediate: public OopsInHeapRegionClosure {
+private:
+  G1RemSet* _g1_rem_set;
+
+  template <class T> 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); }
+};
--- a/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.inline.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/g1/g1RemSet.inline.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
     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.
-    if (_par_traversal_in_progress &&
+    // 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 (_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 @@
   assert(_from != NULL, "from region must be non-NULL");
   _rs->par_write_ref(_from, p, _worker_i);
 }
+
+template <class T> 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);
+  }
+}
+
--- a/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/g1/heapRegion.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -683,6 +683,8 @@
     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());
@@ -788,8 +790,18 @@
   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) {
@@ -855,6 +867,13 @@
     }
   }
 
+  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());
--- a/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/g1/sparsePRT.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -424,7 +424,7 @@
 
 
 SparsePRT::SparsePRT(HeapRegion* hr) :
-  _expanded(false), _next_expanded(NULL)
+  _hr(hr), _expanded(false), _next_expanded(NULL)
 {
   _cur = new RSHashTable(InitialCapacity);
   _next = _cur;
--- a/hotspot/src/share/vm/gc_implementation/includeDB_gc_concurrentMarkSweep	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/includeDB_gc_concurrentMarkSweep	Wed Jul 05 17:21:32 2017 +0200
@@ -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.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.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            vmThread.hpp
 
 compactibleFreeListSpace.hpp            binaryTreeDictionary.hpp
+compactibleFreeListSpace.hpp            blockOffsetTable.inline.hpp
 compactibleFreeListSpace.hpp            freeList.hpp
 compactibleFreeListSpace.hpp            promotionInfo.hpp
 compactibleFreeListSpace.hpp            space.hpp
@@ -149,6 +152,7 @@
 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 +169,7 @@
 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
--- a/hotspot/src/share/vm/gc_implementation/includeDB_gc_g1	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/includeDB_gc_g1	Wed Jul 05 17:21:32 2017 +0200
@@ -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.hpp			debug.hpp
 g1MMUTracker.hpp			allocation.hpp
+
 g1RemSet.cpp				bufferingOopClosure.hpp
 g1RemSet.cpp				concurrentG1Refine.hpp
 g1RemSet.cpp				concurrentG1RefineThread.hpp
--- a/hotspot/src/share/vm/gc_implementation/includeDB_gc_parallelScavenge	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/includeDB_gc_parallelScavenge	Wed Jul 05 17:21:32 2017 +0200
@@ -330,7 +330,6 @@
 psPromotionManager.cpp                  psScavenge.inline.hpp
 
 psPromotionManager.hpp                  allocation.hpp
-psPromotionManager.hpp                  prefetchQueue.hpp
 psPromotionManager.hpp                  psPromotionLAB.hpp
 psPromotionManager.hpp                  taskqueue.hpp
 
--- a/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -51,9 +51,14 @@
   _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 @@
     // 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 @@
     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 @@
                         GrowableArray<oop>**    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 @@
   GrowableArray<oop>** 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 @@
   }
 }
 
+#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 @@
     // 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 @@
     // 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 @@
   }
   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,11 @@
     gch->print_heap_change(gch_prev_used);
   }
 
+  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());
     size_policy->avg_survived()->sample(from()->used());
@@ -1104,9 +1147,8 @@
         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 +1269,8 @@
     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 +1507,7 @@
     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);
--- a/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/parNew/parNewGeneration.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -36,9 +36,6 @@
 typedef Padded<OopTaskQueue> ObjToScanQueue;
 typedef GenericTaskQueueSet<ObjToScanQueue> 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 @@
 
   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 @@
   }
   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; }
+
+  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_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;
-    }
+  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;
   }
 };
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/cardTableExtension.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -123,7 +123,6 @@
   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 @@
           *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 @@
                                                     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 @@
         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;
       }
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/prefetchQueue.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -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<PREFETCH_QUEUE_SIZE; i++) {
-      _prefetch_queue[i] = NULL;
-    }
-    _prefetch_index = 0;
-  }
-
-  template <class T> 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];
-  }
-};
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
   _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; i<ParallelGCThreads; i++) {
     _manager_array[i] = new PSPromotionManager();
     guarantee(_manager_array[i] != NULL, "Could not create PSPromotionManager");
-    if (UseDepthFirstScavengeOrder) {
-      stack_array_depth()->register_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 @@
   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 @@
 #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 @@
 
 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 @@
 
 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::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 @@
   _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 @@
   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<oop>* 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 @@
 // 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 @@
         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 @@
     // 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);
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -48,7 +48,6 @@
  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 @@
   PSOldPromotionLAB                   _old_lab;
   bool                                _young_gen_is_full;
   bool                                _old_gen_is_full;
-  PrefetchQueue                       _prefetch_queue;
 
   OopStarTaskQueue                    _claimed_stack_depth;
   OverflowTaskQueue<oop>              _claimed_stack_breadth;
 
-  bool                                _depth_first;
   bool                                _totally_drain;
   uint                                _target_stack_size;
 
@@ -87,7 +84,6 @@
 
   inline static PSPromotionManager* manager_array(int index);
   template <class T> inline void claim_or_forward_internal_depth(T* p);
-  template <class T> 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 @@
   void process_array_chunk(oop old);
 
   template <class T> 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 @@
     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<oop>* claimed_stack_breadth() {
-    return &_claimed_stack_breadth;
-  }
 
   bool young_gen_is_full()             { return _young_gen_is_full; }
 
@@ -183,18 +164,14 @@
   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 @@
     }
   }
   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 <class T> inline void claim_or_forward_depth(T* p);
-  template <class T> inline void claim_or_forward_breadth(T* p);
 
   TASKQUEUE_STATS_ONLY(inline void record_steal(StarTask& p);)
 };
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.inline.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -46,32 +46,7 @@
 }
 
 template <class T>
-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 <class T>
 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 @@
   claim_or_forward_internal_depth(p);
 }
 
-template <class T>
-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");
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -157,10 +157,8 @@
     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; j<ParallelGCThreads; j++) {
       q->enqueue(new StealTask(&terminator));
@@ -375,10 +373,8 @@
       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; j<ParallelGCThreads; j++) {
           q->enqueue(new StealTask(&terminator));
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.inline.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.inline.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -65,7 +65,7 @@
   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
--- a/hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/gc_implementation/parallelScavenge/psTasks.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -144,29 +144,15 @@
             "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;
       }
     }
   }
--- a/hotspot/src/share/vm/includeDB_core	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/includeDB_core	Wed Jul 05 17:21:32 2017 +0200
@@ -225,7 +225,6 @@
 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                           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                    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                    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                      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_<os_family>.cpp              resourceArea.hpp
 perfMemory_<os_family>.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.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                               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
--- a/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/interpreter/bytecodeInterpreter.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -421,7 +421,9 @@
 #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;
--- a/hotspot/src/share/vm/memory/allocation.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/memory/allocation.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -58,7 +58,7 @@
 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() {
     // 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
--- a/hotspot/src/share/vm/memory/blockOffsetTable.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/memory/blockOffsetTable.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
 //////////////////////////////////////////////////////////////////////
 
 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 @@
 // 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 @@
   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 @@
 // 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 @@
     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 @@
     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 @@
     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 @@
 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 @@
     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 @@
   // 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 @@
   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 @@
             // 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 @@
             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 @@
 // 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 @@
     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 @@
   _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
--- a/hotspot/src/share/vm/memory/blockOffsetTable.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/memory/blockOffsetTable.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
     N_words = 1 << LogN_words
   };
 
+  bool _init_to_zero;
+
   // The reserved region covered by the shared array.
   MemRegion _reserved;
 
@@ -125,17 +127,28 @@
     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,15 @@
       size_t i = index_for(left);
       const size_t end = i + num_cards;
       for (; i < end; i++) {
+        // Elided until CR 6977974 is fixed properly.
+        // 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 +185,8 @@
       size_t i = left;
       const size_t end = i + num_cards;
       for (; i < end; i++) {
+        // Elided until CR 6977974 is fixed properly.
+        // assert(!reducing || _offset_array[i] >= offset, "Not reducing");
         _offset_array[i] = offset;
       }
     }
@@ -212,6 +230,11 @@
 
   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 +308,23 @@
   // 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 +332,7 @@
   // 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 +387,12 @@
   // 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 +459,16 @@
   // 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 +476,12 @@
     }
   }
 
-  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 +489,6 @@
   // 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
--- a/hotspot/src/share/vm/memory/blockOffsetTable.inline.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/memory/blockOffsetTable.inline.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
   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.
--- a/hotspot/src/share/vm/oops/arrayKlassKlass.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/oops/arrayKlassKlass.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -108,10 +108,6 @@
 }
 
 #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");
 }
--- a/hotspot/src/share/vm/oops/compiledICHolderKlass.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/oops/compiledICHolderKlass.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -120,10 +120,6 @@
 }
 
 #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");
 }
--- a/hotspot/src/share/vm/oops/constMethodKlass.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/oops/constMethodKlass.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -157,10 +157,6 @@
 }
 
 #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");
 }
--- a/hotspot/src/share/vm/oops/constantPoolKlass.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/oops/constantPoolKlass.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -268,21 +268,6 @@
   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;
--- a/hotspot/src/share/vm/oops/cpCacheKlass.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/oops/cpCacheKlass.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -166,29 +166,6 @@
 }
 
 #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");
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/oops/instanceKlass.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -1809,18 +1809,7 @@
 }
 
 #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 @@
   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)) { \
--- a/hotspot/src/share/vm/oops/instanceKlass.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/oops/instanceKlass.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -711,7 +711,6 @@
 
 #ifndef SERIALGC
   // Parallel Scavenge
-  void copy_static_fields(PSPromotionManager* pm);
   void push_static_fields(PSPromotionManager* pm);
 
   // Parallel Old
--- a/hotspot/src/share/vm/oops/instanceKlassKlass.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/oops/instanceKlassKlass.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -292,41 +292,7 @@
 }
 
 #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 @@
     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) {
--- a/hotspot/src/share/vm/oops/instanceRefKlass.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/oops/instanceRefKlass.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -273,41 +273,8 @@
 
 #ifndef SERIALGC
 template <class T>
-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<narrowOop>(this, pm, obj);
-  } else {
-    specialized_oop_copy_contents<oop>(this, pm, obj);
-  }
-}
-
-template <class T>
 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();
--- a/hotspot/src/share/vm/oops/klassKlass.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/oops/klassKlass.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -161,9 +161,6 @@
 }
 
 #ifndef SERIALGC
-void klassKlass::oop_copy_contents(PSPromotionManager* pm, oop obj) {
-}
-
 void klassKlass::oop_push_contents(PSPromotionManager* pm, oop obj) {
 }
 
--- a/hotspot/src/share/vm/oops/klassPS.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/oops/klassPS.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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;  \
--- a/hotspot/src/share/vm/oops/methodDataKlass.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/oops/methodDataKlass.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -154,13 +154,6 @@
 
 
 #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);
--- a/hotspot/src/share/vm/oops/methodKlass.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/oops/methodKlass.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -184,10 +184,6 @@
 }
 
 #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");
 }
--- a/hotspot/src/share/vm/oops/objArrayKlass.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/oops/objArrayKlass.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -426,18 +426,7 @@
 }
 
 #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, \
--- a/hotspot/src/share/vm/oops/objArrayKlassKlass.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/oops/objArrayKlassKlass.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -229,10 +229,6 @@
 }
 
 #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");
 }
--- a/hotspot/src/share/vm/oops/oop.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/oops/oop.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -306,7 +306,6 @@
 
 #ifndef SERIALGC
   // Parallel Scavenge
-  void copy_contents(PSPromotionManager* pm);
   void push_contents(PSPromotionManager* pm);
 
   // Parallel Old
--- a/hotspot/src/share/vm/oops/oop.psgc.inline.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/oops/oop.psgc.inline.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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()) {
--- a/hotspot/src/share/vm/oops/symbolKlass.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/oops/symbolKlass.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -184,10 +184,6 @@
 
 
 #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");
 }
--- a/hotspot/src/share/vm/oops/typeArrayKlass.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/oops/typeArrayKlass.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -228,10 +228,6 @@
 }
 
 #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");
 }
--- a/hotspot/src/share/vm/opto/c2_globals.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/opto/c2_globals.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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")                                                    \
--- a/hotspot/src/share/vm/opto/coalesce.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/opto/coalesce.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -780,6 +780,14 @@
   // 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
--- a/hotspot/src/share/vm/opto/matcher.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/opto/matcher.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -456,6 +456,23 @@
   *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.
--- a/hotspot/src/share/vm/opto/reg_split.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/opto/reg_split.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -975,6 +975,19 @@
               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
             //
--- a/hotspot/src/share/vm/runtime/arguments.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/runtime/arguments.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -184,6 +184,8 @@
   { "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) }
 };
 
@@ -3003,10 +3005,6 @@
     CommandLineFlags::printSetFlags();
   }
 
-  if (PrintFlagsFinal) {
-    CommandLineFlags::printFlags();
-  }
-
   // Apply CPU specific policy for the BiasedLocking
   if (UseBiasedLocking) {
     if (!VM_Version::use_biased_locking() &&
--- a/hotspot/src/share/vm/runtime/frame.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/runtime/frame.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -215,17 +215,15 @@
   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 @@
     // 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.
--- a/hotspot/src/share/vm/runtime/frame.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/runtime/frame.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -174,7 +174,7 @@
   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.
--- a/hotspot/src/share/vm/runtime/globals.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/runtime/globals.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -1712,7 +1712,7 @@
   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)")                  \
                                                                             \
@@ -3092,10 +3092,6 @@
                                                                             \
   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")                           \
                                                                             \
--- a/hotspot/src/share/vm/runtime/init.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/runtime/init.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -128,6 +128,12 @@
     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;
 }
 
--- a/hotspot/src/share/vm/runtime/orderAccess.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/runtime/orderAccess.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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
--- a/hotspot/src/share/vm/runtime/orderAccess.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/runtime/orderAccess.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
   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
--- a/hotspot/src/share/vm/runtime/safepoint.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/runtime/safepoint.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -782,6 +782,9 @@
 
   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.
--- a/hotspot/src/share/vm/runtime/safepoint.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/runtime/safepoint.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -185,6 +185,7 @@
 
   JavaThread *                   _thread;
   volatile suspend_type          _type;
+  JavaThreadState                _orig_thread_state;
 
 
  public:
@@ -199,6 +200,7 @@
   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; }
--- a/hotspot/src/share/vm/runtime/sharedRuntime.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/runtime/sharedRuntime.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -2493,15 +2493,13 @@
   }
 
   // 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;
--- a/hotspot/src/share/vm/runtime/thread.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/runtime/thread.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -2110,8 +2110,7 @@
     }
     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!");
     }
--- a/hotspot/src/share/vm/services/management.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/services/management.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -1900,16 +1900,15 @@
 
   // 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());
--- a/hotspot/src/share/vm/services/memoryManager.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/services/memoryManager.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -166,17 +166,6 @@
   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 @@
   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();
+
+      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);
+    }
 
-    // Compare with GC usage threshold
-    pool->set_last_collection_usage(usage);
-    LowMemoryDetector::detect_after_gc_memory(pool);
+    // 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();
+}
--- a/hotspot/src/share/vm/services/memoryManager.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/services/memoryManager.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -131,6 +131,9 @@
     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 @@
     set_gc_usage(pool_index, usage, false /* after gc */);
   }
 
-  void copy_stat(GCStatInfo* stat);
+  void clear();
 };
 
 class GCMemoryManager : public MemoryManager {
@@ -153,6 +156,8 @@
   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 @@
   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;
 };
--- a/hotspot/src/share/vm/services/memoryService.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/services/memoryService.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -509,7 +509,10 @@
   }
 }
 
-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 @@
     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 @@
   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 @@
   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 @@
     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,
+                                                 bool recordGCBeginTime,
+                                                 bool recordPreGCUsage,
+                                                 bool recordPeakUsage,
+                                                 bool recordPostGCUsage,
+                                                 bool recordAccumulatedGCTime,
+                                                 bool recordGCEndTime,
+                                                 bool countCollection) {
+  initialize(fullGC, recordGCBeginTime, recordPreGCUsage, recordPeakUsage,
+             recordPostGCUsage, recordAccumulatedGCTime, recordGCEndTime,
+             countCollection);
 }
-TraceMemoryManagerStats::TraceMemoryManagerStats(bool fullGC) {
+
+// 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);
 }
+
--- a/hotspot/src/share/vm/services/memoryService.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/services/memoryService.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -149,8 +149,13 @@
   }
   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 @@
 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();
 };
--- a/hotspot/src/share/vm/utilities/taskqueue.cpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/utilities/taskqueue.cpp	Wed Jul 05 17:21:32 2017 +0200
@@ -36,6 +36,14 @@
   "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 @@
   }
   #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) {
--- a/hotspot/src/share/vm/utilities/taskqueue.hpp	Wed Jul 05 17:20:50 2017 +0200
+++ b/hotspot/src/share/vm/utilities/taskqueue.hpp	Wed Jul 05 17:21:32 2017 +0200
@@ -59,15 +59,21 @@
   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];
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/6894807/IsInstanceTest.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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 {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/6894807/Test6894807.sh	Wed Jul 05 17:21:32 2017 +0200
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/gc/6581734/Test6581734.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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<MemoryPoolMXBean> pools = ManagementFactory.getMemoryPoolMXBeans();
+        int poolsFound = 0;
+        int poolsWithStats = 0;
+        for (int i=0; i<pools.size(); i++) {
+            MemoryPoolMXBean pool = pools.get(i);
+            String name = pool.getName();
+            System.out.println("found pool: " + name);
+
+            if (name.contains(poolName)) {
+                long usage = pool.getCollectionUsage().getUsed();
+                System.out.println(name + ": usage after GC = " + usage);
+                poolsFound++;
+                if (usage > 0) {
+                    poolsWithStats++;
+                }
+            }
+        }
+        if (poolsFound == 0) {
+            throw new RuntimeException("No matching memory pools found: test with -XX:+UseConcMarkSweepGC");
+        }
+
+        List<GarbageCollectorMXBean> collectors = ManagementFactory.getGarbageCollectorMXBeans();
+        int collectorsFound = 0;
+        int collectorsWithTime= 0;
+        for (int i=0; i<collectors.size(); i++) {
+            GarbageCollectorMXBean collector = collectors.get(i);
+            String name = collector.getName();
+            System.out.println("found collector: " + name);
+            if (name.contains(collectorName)) {
+                collectorsFound++;
+                System.out.println(name + ": collection count = "
+                                   + collector.getCollectionCount());
+                System.out.println(name + ": collection time  = "
+                                   + collector.getCollectionTime());
+                if (collector.getCollectionCount() <= 0) {
+                    throw new RuntimeException("collection count <= 0");
+                }
+                if (collector.getCollectionTime() > 0) {
+                    collectorsWithTime++;
+                }
+            }
+        }
+        // verify:
+        if (poolsWithStats < poolsFound) {
+            throw new RuntimeException("pools found with zero stats");
+        }
+
+        if (collectorsWithTime<collectorsFound) {
+            throw new RuntimeException("collectors found with zero time";
+        }
+        System.out.println("Test passed.");
+    }
+
+    public void allocationWork(long target) {
+
+        long sizeAllocated = 0;
+        List list = new LinkedList();
+        long delay = 50;
+        long count = 0;
+
+        while (sizeAllocated < target) {
+            int size = 1024*1024;
+            byte [] alloc = new byte[size];
+            if (count % 2 == 0) {
+                list.add(alloc);
+                sizeAllocated+=size;
+                System.out.print(".");
+            }
+            try { Thread.sleep(delay); } catch (InterruptedException ie) { }
+            count++;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/6626217/IFace.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/6626217/Loader2.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/6626217/Test6626217.sh	Wed Jul 05 17:21:32 2017 +0200
@@ -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 $?
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/6626217/You_Have_Been_P0wned.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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;
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/6626217/bug_21227.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/6626217/from_loader2.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/6626217/many_loader1.java.foo	Wed Jul 05 17:21:32 2017 +0200
@@ -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(); 
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/6626217/many_loader2.java.foo	Wed Jul 05 17:21:32 2017 +0200
@@ -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");
+  }
+}
--- a/jaxp/.hgtags	Wed Jul 05 17:20:50 2017 +0200
+++ b/jaxp/.hgtags	Wed Jul 05 17:21:32 2017 +0200
@@ -82,3 +82,4 @@
 3233b9a4c12ef2663a356d08bb141c02736c7f49 jdk7-b105
 5ba8469212a6cab95ca652eea414b753be7d245a jdk7-b106
 20ee37c1372a3eaefa49b426c6eb68a2e8f5d6e2 jdk7-b107
+7d379f8934caf255f53def1310c0ef0f1b512601 jdk7-b108
--- a/jaxp/make/jprt.properties	Wed Jul 05 17:20:50 2017 +0200
+++ b/jaxp/make/jprt.properties	Wed Jul 05 17:21:32 2017 +0200
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2006, 2009, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2006, 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
@@ -24,31 +24,13 @@
 #
 
 # Properties for jprt
-jprt.tools.default.release=jdk1.7.0
 
-# Specific platform list
-jprt.build.platforms=\
-solaris_sparc_5.10,\
-solaris_sparcv9_5.10,\
-solaris_i586_5.10,\
-solaris_x64_5.10,\
-linux_i586_2.6,\
-linux_x64_2.6,\
-windows_i586_5.0,\
-windows_x64_5.2
+# Use whatever release that the submitted job requests
+jprt.tools.default.release=${jprt.submit.release}
 
-# The different build flavors we want
+# The different build flavors we want, we override here so we just get these 2
 jprt.build.flavors=product,fastdebug
 
-# Explicitly designate what the 32bit match is for the 64bit build
-jprt.solaris_sparcv9.build.platform.match32=solaris_sparc_5.10
-jprt.solaris_sparcv9_5.10.build.platform.match32=solaris_sparc_5.10
-jprt.solaris_x64.build.platform.match32=solaris_i586_5.10
-jprt.solaris_x64_5.10.build.platform.match32=solaris_i586_5.10
+# Directories to be excluded from the source bundles
+jprt.bundle.exclude.src.dirs=build dist webrev
 
-# Standard list of jprt test targets for this workspace
-jprt.test.targets=
-
-# Directories needing to exclude from source bundles
-jprt.bundle.exclude.src.dirs=build dist
-
--- a/jaxws/.hgtags	Wed Jul 05 17:20:50 2017 +0200
+++ b/jaxws/.hgtags	Wed Jul 05 17:21:32 2017 +0200
@@ -82,3 +82,4 @@
 39eb4f3031f4a985664cace00fca3bd1eab1e0aa jdk7-b105
 bc45ccc5bcca6cbe4ea433e279d4a93b06ab38c6 jdk7-b106
 017612ea6af417a5e378619704da01bb3a583bdb jdk7-b107
+b1ca39340238a239ba6d8489ad5315215e1366ca jdk7-b108
--- a/jaxws/make/jprt.properties	Wed Jul 05 17:20:50 2017 +0200
+++ b/jaxws/make/jprt.properties	Wed Jul 05 17:21:32 2017 +0200
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2006, 2009, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2006, 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
@@ -24,31 +24,13 @@
 #
 
 # Properties for jprt
-jprt.tools.default.release=jdk1.7.0
 
-# Specific platform list
-jprt.build.platforms=\
-solaris_sparc_5.10,\
-solaris_sparcv9_5.10,\
-solaris_i586_5.10,\
-solaris_x64_5.10,\
-linux_i586_2.6,\
-linux_x64_2.6,\
-windows_i586_5.0,\
-windows_x64_5.2
+# Use whatever release that the submitted job requests
+jprt.tools.default.release=${jprt.submit.release}
 
-# The different build flavors we want
+# The different build flavors we want, we override here so we just get these 2
 jprt.build.flavors=product,fastdebug
 
-# Explicitly designate what the 32bit match is for the 64bit build
-jprt.solaris_sparcv9.build.platform.match32=solaris_sparc_5.10
-jprt.solaris_sparcv9_5.10.build.platform.match32=solaris_sparc_5.10
-jprt.solaris_x64.build.platform.match32=solaris_i586_5.10
-jprt.solaris_x64_5.10.build.platform.match32=solaris_i586_5.10
+# Directories to be excluded from the source bundles
+jprt.bundle.exclude.src.dirs=build dist webrev
 
-# Standard list of jprt test targets for this workspace
-jprt.test.targets=
-
-# Directories needing to exclude from source bundles
-jprt.bundle.exclude.src.dirs=build dist
-
--- a/jdk/.hgtags	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/.hgtags	Wed Jul 05 17:21:32 2017 +0200
@@ -82,3 +82,4 @@
 3b0abcb512807bb6f6d27755bc50103211bde6ee jdk7-b105
 b91ef6b60f4e19bf4592c6dd594c9bac62487519 jdk7-b106
 882103f334bb23745d3fd70fb7928c347478b0f4 jdk7-b107
+17a5d84b75610255a3527e8ede1da19c91ba7a7e jdk7-b108
--- a/jdk/make/common/shared/Defs-windows.gmk	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/make/common/shared/Defs-windows.gmk	Wed Jul 05 17:21:32 2017 +0200
@@ -89,7 +89,7 @@
 $(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
--- a/jdk/make/common/shared/Defs.gmk	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/make/common/shared/Defs.gmk	Wed Jul 05 17:21:32 2017 +0200
@@ -136,15 +136,20 @@
 $(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
--- a/jdk/make/jdk_generic_profile.sh	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/make/jdk_generic_profile.sh	Wed Jul 05 17:21:32 2017 +0200
@@ -340,6 +340,10 @@
 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 @@
   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
--- a/jdk/make/jprt.properties	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/make/jprt.properties	Wed Jul 05 17:21:32 2017 +0200
@@ -24,32 +24,12 @@
 #
 
 # Properties for jprt
-jprt.tools.default.release=jdk1.7.0
-
-# Specific platform list
-jprt.build.platforms=\
-solaris_sparc_5.10,\
-solaris_sparcv9_5.10,\
-solaris_i586_5.10,\
-solaris_x64_5.10,\
-linux_i586_2.6,\
-linux_x64_2.6,\
-windows_i586_5.0,\
-windows_x64_5.2
 
-# The different build flavors we want
+# Use whatever release that the submitted job requests
+jprt.tools.default.release=${jprt.submit.release}
+
+# The different build flavors we want, we override here so we just get these 2
 jprt.build.flavors=product,fastdebug
-jprt.run.flavors=c1,c2
-jprt.solaris_sparcv9.run.flavors=c2
-jprt.solaris_x64.run.flavors=c2
-jprt.windows_x64.run.flavors=c2
-jprt.linux_x64.run.flavors=c2
-jprt.run.flavor.c1.option=-client
-jprt.run.flavor.c2.option=-server
-
-# Explicitly designate what the 32bit match is for the 64bit build
-jprt.solaris_sparcv9.build.platform.match32=solaris_sparc_5.10
-jprt.solaris_x64.build.platform.match32=solaris_i586_5.10
 
 # Standard test target for everybody
 jprt.test.targets=*-*-*-jvm98
@@ -83,6 +63,6 @@
    *-product-*-jdk_rmi,         \
    *-product-*-jdk_swing,       \
 
-# Directories needed to build
-jprt.bundle.exclude.src.dirs=build
+# Directories to be excluded from the source bundles
+jprt.bundle.exclude.src.dirs=build dist webrev
 
--- a/jdk/make/sun/javazic/tzdata/VERSION	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/make/sun/javazic/tzdata/VERSION	Wed Jul 05 17:21:32 2017 +0200
@@ -21,4 +21,4 @@
 # or visit www.oracle.com if you need additional information or have any
 # questions.
 #
-tzdata2010i
+tzdata2010l
--- a/jdk/make/sun/javazic/tzdata/africa	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/make/sun/javazic/tzdata/africa	Wed Jul 05 17:21:32 2017 +0200
@@ -316,8 +316,25 @@
 # and can be found by searching for "winter" in their search engine
 # (at least today).
 
+# From Alexander Krivenyshev (2010-07-20):
+# According to News from Egypt -  Al-Masry Al-Youm Egypt's cabinet has
+# decided that Daylight Saving Time will not be used in Egypt during
+# Ramadan.
+#
+# Arabic translation:
+# "Clocks to go back during Ramadan--and then forward again"
+# <a href="http://www.almasryalyoum.com/en/news/clocks-go-back-during-ramadan-and-then-forward-again">
+# http://www.almasryalyoum.com/en/news/clocks-go-back-during-ramadan-and-then-forward-again
+# </a>
+# or
+# <a href="http://www.worldtimezone.com/dst_news/dst_news_egypt02.html">
+# http://www.worldtimezone.com/dst_news/dst_news_egypt02.html
+# </a>
+
 Rule	Egypt	2008	only	-	Aug	lastThu	23:00s	0	-
 Rule	Egypt	2009	only	-	Aug	20	23:00s	0	-
+Rule	Egypt	2010	only	-	Aug	11	0:00	0	-
+Rule	Egypt	2010	only	-	Sep	10	0:00	1:00	S
 Rule	Egypt	2010	max	-	Sep	lastThu	23:00s	0	-
 
 # Zone	NAME		GMTOFF	RULES	FORMAT	[UNTIL]
--- a/jdk/make/sun/javazic/tzdata/asia	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/make/sun/javazic/tzdata/asia	Wed Jul 05 17:21:32 2017 +0200
@@ -2200,6 +2200,18 @@
 # "At 12:01am Friday, clocks in Israel and the West Bank will change to
 # 1:01am, while Gaza clocks will change at 12:01am Saturday morning."
 
+# From Steffen Thorsen (2010-08-11):
+# According to several sources, including
+# <a href="http://www.maannews.net/eng/ViewDetails.aspx?ID=306795">
+# http://www.maannews.net/eng/ViewDetails.aspx?ID=306795
+# </a>
+# the clocks were set back one hour at 2010-08-11 00:00:00 local time in 
+# Gaza and the West Bank.
+# Some more background info:
+# <a href="http://www.timeanddate.com/news/time/westbank-gaza-end-dst-2010.html">
+# http://www.timeanddate.com/news/time/westbank-gaza-end-dst-2010.html
+# </a>
+
 # The rules for Egypt are stolen from the `africa' file.
 # Rule	NAME	FROM	TO	TYPE	IN	ON	AT	SAVE	LETTER/S
 Rule EgyptAsia	1957	only	-	May	10	0:00	1:00	S
@@ -2220,6 +2232,7 @@
 Rule Palestine	2009	only	-	Mar	lastFri	0:00	1:00	S
 Rule Palestine	2010	max	-	Mar	lastSat	0:01	1:00	S
 Rule Palestine	2009	max	-	Sep	Fri>=1	2:00	0	-
+Rule Palestine	2010	only	-	Aug	11	0:00	0	-
 
 # Zone	NAME		GMTOFF	RULES	FORMAT	[UNTIL]
 Zone	Asia/Gaza	2:17:52	-	LMT	1900 Oct
--- a/jdk/make/sun/javazic/tzdata/australasia	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/make/sun/javazic/tzdata/australasia	Wed Jul 05 17:21:32 2017 +0200
@@ -368,10 +368,10 @@
 
 # Micronesia
 # Zone	NAME		GMTOFF	RULES	FORMAT	[UNTIL]
-Zone Pacific/Truk	10:07:08 -	LMT	1901
-			10:00	-	TRUT			# Truk Time
-Zone Pacific/Ponape	10:32:52 -	LMT	1901		# Kolonia
-			11:00	-	PONT			# Ponape Time
+Zone Pacific/Chuuk	10:07:08 -	LMT	1901
+			10:00	-	CHUT			# Chuuk Time
+Zone Pacific/Pohnpei	10:32:52 -	LMT	1901		# Kolonia
+			11:00	-	PONT			# Pohnpei Time
 Zone Pacific/Kosrae	10:51:56 -	LMT	1901
 			11:00	-	KOST	1969 Oct	# Kosrae Time
 			12:00	-	KOST	1999
--- a/jdk/make/sun/javazic/tzdata/backward	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/make/sun/javazic/tzdata/backward	Wed Jul 05 17:21:32 2017 +0200
@@ -112,7 +112,9 @@
 Link	America/Denver		Navajo
 Link	Asia/Shanghai		PRC
 Link	Pacific/Pago_Pago	Pacific/Samoa
-Link	Pacific/Truk		Pacific/Yap
+Link	Pacific/Chuuk		Pacific/Yap
+Link	Pacific/Chuuk		Pacific/Truk
+Link	Pacific/Pohnpei		Pacific/Ponape
 Link	Europe/Warsaw		Poland
 Link	Europe/Lisbon		Portugal
 Link	Asia/Taipei		ROC
--- a/jdk/make/sun/javazic/tzdata/europe	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/make/sun/javazic/tzdata/europe	Wed Jul 05 17:21:32 2017 +0200
@@ -1035,22 +1035,47 @@
 			2:00	EU	EE%sT
 
 # Finland
-#
+
 # From Hannu Strang (1994-09-25 06:03:37 UTC):
 # Well, here in Helsinki we're just changing from summer time to regular one,
 # and it's supposed to change at 4am...
+
+# From Janne Snabb (2010-0715):
 #
-# From Paul Eggert (2006-03-22):
-# Shanks & Pottenger say Finland has switched at 02:00 standard time
-# since 1981.  Go with Strang instead.
+# I noticed that the Finland data is not accurate for years 1981 and 1982.
+# During these two first trial years the DST adjustment was made one hour
+# earlier than in forthcoming years. Starting 1983 the adjustment was made
+# according to the central European standards.
+#
+# This is documented in Heikki Oja: Aikakirja 2007, published by The Almanac
+# Office of University of Helsinki, ISBN 952-10-3221-9, available online (in
+# Finnish) at
+#
+# <a href="http://almanakka.helsinki.fi/aikakirja/Aikakirja2007kokonaan.pdf">
+# http://almanakka.helsinki.fi/aikakirja/Aikakirja2007kokonaan.pdf
+# </a>
 #
+# Page 105 (56 in PDF version) has a handy table of all past daylight savings
+# transitions. It is easy enough to interpret without Finnish skills.
+#
+# This is also confirmed by Finnish Broadcasting Company's archive at:
+#
+# <a href="http://www.yle.fi/elavaarkisto/?s=s&g=1&ag=5&t=&a=3401">
+# http://www.yle.fi/elavaarkisto/?s=s&g=1&ag=5&t=&a=3401
+# </a>
+#
+# The news clip from 1981 says that "the time between 2 and 3 o'clock does not
+# exist tonight."
+
 # Rule	NAME	FROM	TO	TYPE	IN	ON	AT	SAVE	LETTER/S
 Rule	Finland	1942	only	-	Apr	3	0:00	1:00	S
 Rule	Finland	1942	only	-	Oct	3	0:00	0	-
+Rule	Finland	1981	1982	-	Mar	lastSun	2:00	1:00	S
+Rule	Finland	1981	1982	-	Sep	lastSun	3:00	0	-
 # Zone	NAME		GMTOFF	RULES	FORMAT	[UNTIL]
 Zone	Europe/Helsinki	1:39:52 -	LMT	1878 May 31
 			1:39:52	-	HMT	1921 May    # Helsinki Mean Time
-			2:00	Finland	EE%sT	1981 Mar 29 2:00
+			2:00	Finland	EE%sT	1983
 			2:00	EU	EE%sT
 
 # Aaland Is
--- a/jdk/make/sun/javazic/tzdata/leapseconds	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/make/sun/javazic/tzdata/leapseconds	Wed Jul 05 17:21:32 2017 +0200
@@ -82,9 +82,9 @@
 # FAX       : 33 (0) 1 40 51 22 91
 # Internet  : services.iers@obspm.fr
 #
-# Paris, 4 July 2009
+# Paris, 14 July 2010
 #
-# Bulletin C 38
+# Bulletin C 40
 #
 # To authorities responsible
 # for the measurement and
@@ -92,9 +92,9 @@
 #
 # INFORMATION ON UTC - TAI
 #
-# NO positive leap second will be introduced at the end of December 2009.
+# NO positive leap second will be introduced at the end of December 2010.
 # The difference between Coordinated Universal Time UTC and the
-# International Atomic Time TAI is :		
+# International Atomic Time TAI is :
 #
 # from 2009 January 1, 0h UTC, until further notice : UTC-TAI = -34 s
 #
@@ -104,6 +104,6 @@
 # will be no time step at the next possible date.
 #
 # Daniel GAMBIS
-# Director			
+# Director
 # Earth Orientation Center of IERS
 # Observatoire de Paris, France
--- a/jdk/make/sun/javazic/tzdata/northamerica	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/make/sun/javazic/tzdata/northamerica	Wed Jul 05 17:21:32 2017 +0200
@@ -1346,6 +1346,83 @@
 # entry since our cutoff date of 1970, so we can move
 # America/Coral_Harbour to the 'backward' file.
 
+# From Mark Brader (2010-03-06):
+#
+# Currently the database has:
+#
+# # Ontario
+#
+# # From Paul Eggert (2006-07-09):
+# # Shanks & Pottenger write that since 1970 most of Ontario has been like
+# # Toronto.
+# # Thunder Bay skipped DST in 1973.
+# # Many smaller locales did not observe peacetime DST until 1974;
+# # Nipigon (EST) and Rainy River (CST) are the largest that we know of.
+#
+# In the (Toronto) Globe and Mail for Saturday, 1955-09-24, in the bottom
+# right corner of page 1, it says that Toronto will return to standard
+# time at 2 am Sunday morning (which agrees with the database), and that:
+#
+#     The one-hour setback will go into effect throughout most of Ontario,
+#     except in areas like Windsor which remains on standard time all year.
+#
+# Windsor is, of course, a lot larger than Nipigon.
+#
+# I only came across this incidentally.  I don't know if Windsor began
+# observing DST when Detroit did, or in 1974, or on some other date.
+#
+# By the way, the article continues by noting that:
+#
+#     Some cities in the United States have pushed the deadline back
+#     three weeks and will change over from daylight saving in October.
+
+# From Arthur David Olson (2010-07-17):
+#
+# "Standard Time and Time Zones in Canada" appeared in
+# The Journal of The Royal Astronomical Society of Canada,
+# volume 26, number 2 (February 1932) and, as of 2010-07-17,
+# was available at
+# <a href="http://adsabs.harvard.edu/full/1932JRASC..26...49S">
+# http://adsabs.harvard.edu/full/1932JRASC..26...49S
+# </a>
+#
+# It includes the text below (starting on page 57):
+#
+#   A list of the places in Canada using daylight saving time would
+# require yearly revision. From information kindly furnished by
+# the provincial governments and by the postmasters in many cities
+# and towns, it is found that the following places used daylight sav-
+# ing in 1930. The information for the province of Quebec is definite,
+# for the other provinces only approximate:
+#
+# 	Province	Daylight saving time used
+# Prince Edward Island	Not used.
+# Nova Scotia		In Halifax only.
+# New Brunswick		In St. John only.
+# Quebec		In the following places:
+# 			Montreal	Lachine
+# 			Quebec		Mont-Royal
+# 			Levis		Iberville
+# 			St. Lambert	Cap de la Madeleine
+# 			Verdun		Loretteville
+# 			Westmount	Richmond
+# 			Outremont	St. Jerome
+# 			Longueuil	Greenfield Park
+# 			Arvida		Waterloo
+# 			Chambly-Canton	Beaulieu
+# 			Melbourne	La Tuque
+# 			St. Theophile	Buckingham
+# Ontario		Used generally in the cities and towns along
+# 			the southerly part of the province. Not
+# 			used in the northwesterlhy part.
+# Manitoba		Not used.
+# Saskatchewan		In Regina only.
+# Alberta		Not used.
+# British Columbia	Not used.
+#
+#   With some exceptions, the use of daylight saving may be said to be limited
+# to those cities and towns lying between Quebec city and Windsor, Ont.
+
 # Rule	NAME	FROM	TO	TYPE	IN	ON	AT	SAVE	LETTER/S
 Rule	Toronto	1919	only	-	Mar	30	23:30	1:00	D
 Rule	Toronto	1919	only	-	Oct	26	0:00	0	S
@@ -2111,7 +2188,44 @@
 			-8:00	-	PST	1970
 			-7:00	Mexico	M%sT	1999
 			-7:00	-	MST
+
+# From Alexander Krivenyshev (2010-04-21):
+# According to news, Bah&iacute;a de Banderas (Mexican state of Nayarit)
+# changed time zone UTC-7 to new time zone UTC-6 on April 4, 2010 (to
+# share the same time zone as nearby city Puerto Vallarta, Jalisco).
+#
+# (Spanish)
+# Bah&iacute;a de Banderas homologa su horario al del centro del
+# pa&iacute;s, a partir de este domingo
+# <a href="http://www.nayarit.gob.mx/notes.asp?id=20748">
+# http://www.nayarit.gob.mx/notes.asp?id=20748
+# </a>
+#
+# Bah&iacute;a de Banderas homologa su horario con el del Centro del
+# Pa&iacute;s
+# <a href="http://www.bahiadebanderas.gob.mx/principal/index.php?option=com_content&view=article&id=261:bahia-de-banderas-homologa-su-horario-con-el-del-centro-del-pais&catid=42:comunicacion-social&Itemid=50">
+# http://www.bahiadebanderas.gob.mx/principal/index.php?option=com_content&view=article&id=261:bahia-de-banderas-homologa-su-horario-con-el-del-centro-del-pais&catid=42:comunicacion-social&Itemid=50"
+# </a>
+#
+# (English)
+# Puerto Vallarta and Bah&iacute;a de Banderas: One Time Zone
+# <a href="http://virtualvallarta.com/puertovallarta/puertovallarta/localnews/2009-12-03-Puerto-Vallarta-and-Bahia-de-Banderas-One-Time-Zone.shtml">
+# http://virtualvallarta.com/puertovallarta/puertovallarta/localnews/2009-12-03-Puerto-Vallarta-and-Bahia-de-Banderas-One-Time-Zone.shtml
+# </a>
+#
+# or
+# <a href="http://www.worldtimezone.com/dst_news/dst_news_mexico08.html">
+# http://www.worldtimezone.com/dst_news/dst_news_mexico08.html
+# </a>
+#
+# "Mexico's Senate approved the amendments to the Mexican Schedule System that
+# will allow Bah&iacute;a de Banderas and Puerto Vallarta to share the same time
+# zone ..."
 # Baja California Sur, Nayarit, Sinaloa
+
+# From Arthur David Olson (2010-05-01):
+# Use "Bahia_Banderas" to keep the name to fourteen characters.
+
 Zone America/Mazatlan	-7:05:40 -	LMT	1921 Dec 31 23:54:20
 			-7:00	-	MST	1927 Jun 10 23:00
 			-6:00	-	CST	1930 Nov 15
@@ -2122,6 +2236,19 @@
 			-7:00	-	MST	1949 Jan 14
 			-8:00	-	PST	1970
 			-7:00	Mexico	M%sT
+
+Zone America/Bahia_Banderas	-7:01:00 -	LMT	1921 Dec 31 23:59:00
+			-7:00	-	MST	1927 Jun 10 23:00
+			-6:00	-	CST	1930 Nov 15
+			-7:00	-	MST	1931 May  1 23:00
+			-6:00	-	CST	1931 Oct
+			-7:00	-	MST	1932 Apr  1
+			-6:00	-	CST	1942 Apr 24
+			-7:00	-	MST	1949 Jan 14
+			-8:00	-	PST	1970
+			-7:00	Mexico	M%sT	2010 Apr 4 2:00
+			-6:00	Mexico	C%sT
+
 # Baja California (near US border)
 Zone America/Tijuana	-7:48:04 -	LMT	1922 Jan  1  0:11:56
 			-7:00	-	MST	1924
--- a/jdk/make/sun/javazic/tzdata/zone.tab	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/make/sun/javazic/tzdata/zone.tab	Wed Jul 05 17:21:32 2017 +0200
@@ -199,8 +199,8 @@
 FI	+6010+02458	Europe/Helsinki
 FJ	-1808+17825	Pacific/Fiji
 FK	-5142-05751	Atlantic/Stanley
-FM	+0725+15147	Pacific/Truk	Truk (Chuuk) and Yap
-FM	+0658+15813	Pacific/Ponape	Ponape (Pohnpei)
+FM	+0725+15147	Pacific/Chuuk	Chuuk (Truk) and Yap
+FM	+0658+15813	Pacific/Pohnpei	Pohnpei (Ponape)
 FM	+0519+16259	Pacific/Kosrae	Kosrae
 FO	+6201-00646	Atlantic/Faroe
 FR	+4852+00220	Europe/Paris
@@ -310,6 +310,7 @@
 MX	+2904-11058	America/Hermosillo	Mountain Standard Time - Sonora
 MX	+3232-11701	America/Tijuana	US Pacific Time - Baja California near US border
 MX	+3018-11452	America/Santa_Isabel	Mexican Pacific Time - Baja California away from US border
+MX	+2048-10515	America/Bahia_Banderas	Mexican Central Time - Bahia de Banderas
 MY	+0310+10142	Asia/Kuala_Lumpur	peninsular Malaysia
 MY	+0133+11020	Asia/Kuching	Sabah & Sarawak
 MZ	-2558+03235	Africa/Maputo
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/Attribute.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/Attribute.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
 
 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 @@
         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<Attribute>, List<Attribute>> canonLists = new HashMap<>();
+    private static final Map<Layout, Attribute> attributes = new HashMap<>();
+    private static final Map<Layout, Attribute> 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<Attribute> al) {
         synchronized (canonLists) {
-            List cl = (List) canonLists.get(al);
+            List<Attribute> 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 @@
     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 @@
         }
     }
 
-    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<Layout, Attribute> 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<Layout, Attribute> 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<Layout, Attribute> 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 @@
              +"\n    ()[] ]"
              )
         };
-        Map sd = standardDefs;
+        Map<Layout, Attribute> sd = standardDefs;
         String defaultLayout     = mdLayouts[2];
         String annotationsLayout = mdLayouts[1] + mdLayouts[2];
         String paramsLayout      = mdLayouts[0] + annotationsLayout;
@@ -275,10 +279,6 @@
         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 @@
         protected abstract Entry[] getCPMap();
 
         protected int flags;             // defined here for convenience
-        protected List attributes;
+        protected List<Attribute> attributes;
 
         public int attributeSize() {
             return (attributes == null) ? 0 : attributes.size();
@@ -301,16 +301,15 @@
             if (attributes == null) {
                 return;
             }
-            if (attributes.size() == 0) {
+            if (attributes.isEmpty()) {
                 attributes = null;
                 return;
             }
             if (attributes instanceof ArrayList) {
-                ArrayList al = (ArrayList) attributes;
+                ArrayList<Attribute> al = (ArrayList<Attribute>)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 @@
 
         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 @@
             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<Entry> 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<Attribute> noAttributes = Arrays.asList(new Attribute[0]);
 
-        public List getAttributes() {
+        public List<Attribute> getAttributes() {
             if (attributes == null)
                 return noAttributes;
             return attributes;
         }
 
-        public void setAttributes(List attrList) {
+        public void setAttributes(List<Attribute> attrList) {
             if (attrList.isEmpty())
                 attributes = null;
             else
@@ -374,8 +372,7 @@
 
         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 @@
 
         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 @@
         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 @@
     */
     static //private
     Layout.Element[] tokenizeLayout(Layout self, int curCble, String layout) {
-        ArrayList col = new ArrayList(layout.length());
+        ArrayList<Layout.Element> 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<Layout.Element> col) {
         boolean prevBCI = false;
         for (int len = layout.length(), i = 0; i < len; ) {
             int start = i;
@@ -899,7 +889,7 @@
             case 'T': // union: 'T' any_int union_case* '(' ')' '[' body ']'
                 kind = EK_UN;
                 i = tokenizeSInt(e, layout, i);
-                ArrayList cases = new ArrayList();
+                ArrayList<Layout.Element> cases = new ArrayList<>();
                 for (;;) {
                     // Keep parsing cases until we hit the default case.
                     if (layout.charAt(i++) != '(')
@@ -1053,7 +1043,7 @@
     }
     static //private
     String[] splitBodies(String layout) {
-        ArrayList bodies = new ArrayList();
+        ArrayList<String> 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 @@
     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 @@
         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));
     }
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/ConstantPool.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/ConstantPool.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
         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", "<init>", 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<String, Utf8Entry> utf8Entries  = Utils.getUtf8Entries();
+        Utf8Entry e = utf8Entries.get(value);
         if (e == null) {
             e = new Utf8Entry(value);
             utf8Entries.put(e.stringValue(), e);
@@ -62,9 +54,10 @@
     }
     /** Factory for Class constants. */
     public static synchronized ClassEntry getClassEntry(String name) {
-        ClassEntry e = (ClassEntry) classEntries.get(name);
+        Map<String, ClassEntry> 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 @@
     }
     /** Factory for literal constants (String, Integer, etc.). */
     public static synchronized LiteralEntry getLiteralEntry(Comparable value) {
-        LiteralEntry e = (LiteralEntry) literalEntries.get(value);
+        Map<Object, LiteralEntry> 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 @@
 
     /** Factory for signature (type) constants. */
     public static synchronized SignatureEntry getSignatureEntry(String type) {
-        SignatureEntry e = (SignatureEntry) signatureEntries.get(type);
+        Map<String, SignatureEntry> signatureEntries = Utils.getSignatureEntries();
+        SignatureEntry e = signatureEntries.get(type);
         if (e == null) {
             e = new SignatureEntry(type);
             assert(e.stringValue().equals(type));
@@ -104,8 +99,9 @@
 
     /** Factory for descriptor (name-and-type) constants. */
     public static synchronized DescriptorEntry getDescriptorEntry(Utf8Entry nameRef, SignatureEntry typeRef) {
+        Map<String, DescriptorEntry> 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 @@
 
     /** Factory for member reference constants. */
     public static synchronized MemberEntry getMemberEntry(byte tag, ClassEntry classRef, DescriptorEntry descRef) {
+        Map<String, MemberEntry> 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 @@
             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 @@
         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 @@
             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 @@
                 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 @@
             clearIndex();
             this.cpMap = cpMap;
         }
-        protected Index(String debugName, Collection cpMapList) {
+        protected Index(String debugName, Collection<Entry> cpMapList) {
             this(debugName);
             setMap(cpMapList);
         }
-        protected void setMap(Collection cpMapList) {
+        protected void setMap(Collection<Entry> cpMapList) {
             cpMap = new Entry[cpMapList.size()];
             cpMapList.toArray(cpMap);
             setMap(cpMap);
@@ -756,11 +760,13 @@
         //
         // 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 @@
                 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 @@
             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 @@
     public static
     Index[] partition(Index ix, int[] keys) {
         // %%% Should move this into class Index.
-        ArrayList parts = new ArrayList();
+        ArrayList<List<Entry>> 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<Entry> 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<Entry> 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 @@
                     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 @@
      *  Also, discard null from cpRefs.
      */
     public static
-    void completeReferencesIn(Set cpRefs, boolean flattenSigs) {
+    void completeReferencesIn(Set<Entry> cpRefs, boolean flattenSigs) {
         cpRefs.remove(null);
         for (ListIterator work =
                  new ArrayList(cpRefs).listIterator(cpRefs.size());
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/Driver.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/Driver.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
 /** 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<String> av = new ArrayList<String>(Arrays.asList(ava));
+        ArrayList<String> av = new ArrayList<>(Arrays.asList(ava));
 
         boolean doPack   = true;
         boolean doUnpack = false;
@@ -61,7 +61,7 @@
         }
 
         // Collect engine properties here:
-        HashMap<String,String> engProps = new HashMap<String,String>();
+        HashMap<String,String> engProps = new HashMap<>();
         engProps.put(verboseProp, System.getProperty(verboseProp));
 
         String optionMap;
@@ -75,7 +75,7 @@
         }
 
         // Collect argument properties here:
-        HashMap<String,String> avProps = new HashMap<String,String>();
+        HashMap<String,String> avProps = new HashMap<>();
         try {
             for (;;) {
                 String state = parseCommandOptions(av, optionMap, avProps);
@@ -133,8 +133,9 @@
                     if (engProps.get(verboseProp) != null)
                         fileProps.list(System.out);
                     propIn.close();
-                    for (Map.Entry<Object,Object> me : fileProps.entrySet())
-                        engProps.put((String)me.getKey(), (String)me.getValue());
+                    for (Map.Entry<Object,Object> 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 @@
         String resultString = null;
 
         // Convert options string into optLines dictionary.
-        TreeMap<String,String[]> optmap = new TreeMap<String,String[]>();
+        TreeMap<String,String[]> optmap = new TreeMap<>();
     loadOptmap:
         for (String optline : options.split("\n")) {
             String[] words = optline.split("\\p{Space}+");
@@ -687,7 +688,9 @@
         // 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;
     }
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/NativeUnpack.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/NativeUnpack.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
 
 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 @@
     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;
     }
 
@@ -216,10 +211,10 @@
                 ++_fileCount;
                 updateProgress();
             }
+            presetInput = getUnusedInput();
             long consumed = finish();
             if (_verbose > 0)
                 Utils.log.info("bytes consumed = "+consumed);
-            presetInput = getUnusedInput();
             if (presetInput == null &&
                 !Utils.isPackMagic(Utils.readMagic(in))) {
                 break;
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/Package.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/Package.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
         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 @@
     public static final Attribute.Layout attrSourceFileSpecial;
     public static final Map attrDefs;
     static {
-        HashMap ad = new HashMap(2);
+        HashMap<Layout, Attribute> ad = new HashMap<>(3);
         attrCodeEmpty = Attribute.define(ad, ATTR_CONTEXT_METHOD,
                                          "Code", "").layout();
         attrInnerClassesEmpty = Attribute.define(ad, ATTR_CONTEXT_CLASS,
@@ -159,9 +160,9 @@
         }
     }
 
-    ArrayList classes = new ArrayList();
+    ArrayList<Package.Class> classes = new ArrayList<>();
 
-    public List getClasses() {
+    public List<Package.Class> getClasses() {
         return classes;
     }
 
@@ -186,11 +187,11 @@
         ClassEntry[] interfaces;
 
         // Class parts
-        ArrayList fields;
-        ArrayList methods;
+        ArrayList<Field> fields;
+        ArrayList<Method> methods;
         //ArrayList attributes;  // in Attribute.Holder.this.attributes
         // Note that InnerClasses may be collected at the package level.
-        ArrayList innerClasses;
+        ArrayList<InnerClass> innerClasses;
 
         Class(int flags, ClassEntry thisClass, ClassEntry superClass, ClassEntry[] interfaces) {
             this.magic      = JAVA_MAGIC;
@@ -270,7 +271,7 @@
             if (a != olda) {
                 if (verbose > 2)
                     Utils.log.fine("recoding obvious SourceFile="+obvious);
-                List newAttrs = new ArrayList(getAttributes());
+                List<Attribute> newAttrs = new ArrayList<>(getAttributes());
                 int where = newAttrs.indexOf(olda);
                 newAttrs.set(where, a);
                 setAttributes(newAttrs);
@@ -295,12 +296,12 @@
         boolean hasInnerClasses() {
             return innerClasses != null;
         }
-        List getInnerClasses() {
+        List<InnerClass> getInnerClasses() {
             return innerClasses;
         }
 
-        public void setInnerClasses(Collection ics) {
-            innerClasses = (ics == null) ? null : new ArrayList(ics);
+        public void setInnerClasses(Collection<InnerClass> ics) {
+            innerClasses = (ics == null) ? null : new ArrayList<InnerClass>(ics);
             // Edit the attribute list, if necessary.
             Attribute a = getAttribute(attrInnerClassesEmpty);
             if (innerClasses != null && a == null)
@@ -318,19 +319,18 @@
          *  The order of the resulting list is consistent
          *  with that of Package.this.allInnerClasses.
          */
-        public List computeGloballyImpliedICs() {
-            HashSet cpRefs = new HashSet();
+        public List<InnerClass> computeGloballyImpliedICs() {
+            HashSet<Entry> cpRefs = new HashSet<>();
             {   // This block temporarily displaces this.innerClasses.
-                ArrayList innerClassesSaved = innerClasses;
+                ArrayList<InnerClass> 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<Entry> 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 @@
             // 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<InnerClass> 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 @@
 
         // 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<InnerClass> computeICdiff() {
+            List<InnerClass> impliedICs = computeGloballyImpliedICs();
+            List<InnerClass> 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 @@
                 // Diff is A since I is empty.
             }
             // (I*A) is non-trivial
-            HashSet center = new HashSet(actualICs);
-            center.retainAll(new HashSet(impliedICs));
+            HashSet<InnerClass> 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 @@
          *  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<InnerClass> diff = computeICdiff();
+            List<InnerClass> actualICs = innerClasses;
+            List<InnerClass> localICs;  // will be the diff, modulo edge cases
             if (diff.isEmpty()) {
                 // No diff, so transmit no attribute.
                 localICs = null;
@@ -439,12 +439,12 @@
          *  Otherwise, return positive if any IC tuples were added.
          */
         int expandLocalICs() {
-            List localICs = innerClasses;
-            List actualICs;
+            List<InnerClass> localICs = innerClasses;
+            List<InnerClass> actualICs;
             int changed;
             if (localICs == null) {
                 // Diff was empty.  (Common case.)
-                List impliedICs = computeGloballyImpliedICs();
+                List<InnerClass> impliedICs = computeGloballyImpliedICs();
                 if (impliedICs.isEmpty()) {
                     actualICs = null;
                     changed = 0;
@@ -490,7 +490,7 @@
             protected Entry[] getCPMap() {
                 return cpMap;
             }
-            protected void visitRefs(int mode, Collection refs) {
+            protected void visitRefs(int mode, Collection<Entry> 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 @@
                 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 @@
                 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 @@
                     code.strip(attrName);
                 super.strip(attrName);
             }
-            protected void visitRefs(int mode, Collection refs) {
+            protected void visitRefs(int mode, Collection<Entry> refs) {
                 super.visitRefs(mode, refs);
                 if (code != null) {
                     if (mode == VRM_CLASSIC) {
@@ -614,7 +614,7 @@
             super.strip(attrName);
         }
 
-        protected void visitRefs(int mode, Collection refs) {
+        protected void visitRefs(int mode, Collection<Entry> refs) {
             if (verbose > 2)  Utils.log.fine("visitRefs "+this);
             refs.add(thisClass);
             refs.add(superClass);
@@ -641,7 +641,7 @@
             super.visitRefs(mode, refs);
         }
 
-        protected void visitInnerClassRefs(int mode, Collection refs) {
+        protected void visitInnerClassRefs(int mode, Collection<Entry> refs) {
             Package.visitInnerClassRefs(innerClasses, mode, refs);
         }
 
@@ -713,16 +713,15 @@
     }
 
     // What non-class files are in this unit?
-    ArrayList files = new ArrayList();
+    ArrayList<File> files = new ArrayList<>();
 
-    public List getFiles() {
+    public List<File> 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<File> getClassStubs() {
+        ArrayList<File> classStubs = new ArrayList<>(classes.size());
+        for (Class cls : classes) {
             assert(cls.file.isClassStub());
             classStubs.add(cls.file);
         }
@@ -840,7 +839,7 @@
         public InputStream getInputStream() {
             InputStream in = new ByteArrayInputStream(append.toByteArray());
             if (prepend.size() == 0)  return in;
-            ArrayList isa = new ArrayList(prepend.size()+1);
+            ArrayList<InputStream> 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 @@
             return new SequenceInputStream(Collections.enumeration(isa));
         }
 
-        protected void visitRefs(int mode, Collection refs) {
+        protected void visitRefs(int mode, Collection<Entry> refs) {
             assert(name != null);
             refs.add(name);
         }
@@ -877,8 +876,8 @@
     }
 
     // Is there a globally declared table of inner classes?
-    ArrayList allInnerClasses = new ArrayList();
-    HashMap   allInnerClassesByThis;
+    ArrayList<InnerClass> allInnerClasses = new ArrayList<>();
+    HashMap<ClassEntry, InnerClass>   allInnerClassesByThis;
 
     public
     List getAllInnerClasses() {
@@ -886,15 +885,14 @@
     }
 
     public
-    void setAllInnerClasses(Collection ics) {
+    void setAllInnerClasses(Collection<InnerClass> 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 @@
     public
     InnerClass getGlobalInnerClass(Entry thisClass) {
         assert(thisClass instanceof ClassEntry);
-        return (InnerClass) allInnerClassesByThis.get(thisClass);
+        return allInnerClassesByThis.get(thisClass);
     }
 
     static
@@ -963,7 +961,7 @@
             return this.thisClass.compareTo(that.thisClass);
         }
 
-        protected void visitRefs(int mode, Collection refs) {
+        protected void visitRefs(int mode, Collection<Entry> refs) {
             refs.add(thisClass);
             if (mode == VRM_CLASSIC || !predictable) {
                 // If the name can be demangled, the package omits
@@ -980,7 +978,7 @@
 
     // Helper for building InnerClasses attributes.
     static private
-    void visitInnerClassRefs(Collection innerClasses, int mode, Collection refs) {
+    void visitInnerClassRefs(Collection innerClasses, int mode, Collection<Entry> refs) {
         if (innerClasses == null) {
             return;  // no attribute; nothing to do
         }
@@ -1165,9 +1163,8 @@
         }
     }
 
-    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<Entry> refs) {
+        for ( Class c : classes) {
             c.visitRefs(mode, refs);
         }
         if (mode != VRM_CLASSIC) {
@@ -1259,7 +1256,7 @@
     }
 
     // Use this before writing the package file.
-    void buildGlobalConstantPool(Set requiredEntries) {
+    void buildGlobalConstantPool(Set<Entry> requiredEntries) {
         if (verbose > 1)
             Utils.log.fine("Checking for unused CP entries");
         requiredEntries.add(getRefString(""));  // uconditionally present
@@ -1291,9 +1288,8 @@
 
     // 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<File> fileSet = new HashSet<>(files);
+        for (Class cls : classes) {
             // Add to the end of ths list:
             if (!fileSet.contains(cls.file))
                 files.add(cls.file);
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/PackerImpl.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/PackerImpl.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
  */
 
 
-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<String, String> properties() {
+        return props;
     }
 
-
     //Driver routines
 
     /**
@@ -78,21 +68,22 @@
      */
     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 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 @@
      * @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 @@
      * @param listener  The PropertyChange listener to be removed.
      */
     public void removePropertyChangeListener(PropertyChangeListener listener) {
-        _props.removeListener(listener);
+        props.removeListener(listener);
     }
 
 
@@ -151,11 +141,11 @@
 
     // 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 @@
 
         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 @@
             };
             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<String, String> 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 @@
         }
 
         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 @@
         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 @@
             segmentLimit = limit;
         }
 
-        final List passFiles;  // parsed pack.pass.file options
+        final List<String> 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 @@
         {
             // 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 @@
                 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 @@
         }
 
         void run(JarFile in, OutputStream out) throws IOException {
-            List inFiles = scanJar(in);
+            List<InFile> 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 @@
         }
 
         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 @@
         }
 
         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 @@
             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 @@
             }
         }
 
-        List scanJar(JarFile jf) throws IOException {
+        List<InFile> 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<InFile> 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);
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/PropMap.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/PropMap.java	Wed Jul 05 17:21:32 2017 +0200
@@ -91,7 +91,7 @@
                   String.valueOf(Boolean.getBoolean(Utils.PACK_DEFAULT_TIMEZONE)));
 
         // The segment size is unlimited
-        props.put(Pack200.Packer.SEGMENT_LIMIT, "");
+        props.put(Pack200.Packer.SEGMENT_LIMIT, "-1");
 
         // Preserve file ordering by default.
         props.put(Pack200.Packer.KEEP_FILE_ORDER, Pack200.Packer.TRUE);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/TLGlobals.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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<String, Utf8Entry> utf8Entries;
+    private final Map<String, ClassEntry> classEntries;
+    private final Map<Object, LiteralEntry> literalEntries;
+    private final Map<String, SignatureEntry> signatureEntries;
+    private final Map<String, DescriptorEntry> descriptorEntries;
+    private final Map<String, MemberEntry> memberEntries;
+
+    TLGlobals() {
+        utf8Entries = new HashMap<>();
+        classEntries = new HashMap<>();
+        literalEntries = new HashMap<>();
+        signatureEntries = new HashMap<>();
+        descriptorEntries = new HashMap<>();
+        memberEntries = new HashMap<>();
+        props = new PropMap();
+    }
+
+    SortedMap<Object, Object> getPropMap() {
+        return props;
+    }
+
+    Map<String, Utf8Entry> getUtf8Entries() {
+        return utf8Entries;
+    }
+
+    Map<String, ClassEntry> getClassEntries() {
+        return classEntries;
+    }
+
+    Map<Object, LiteralEntry> getLiteralEntries() {
+        return literalEntries;
+    }
+
+    Map<String, DescriptorEntry> getDescriptorEntries() {
+         return descriptorEntries;
+    }
+
+    Map<String, SignatureEntry> getSignatureEntries() {
+        return signatureEntries;
+    }
+
+    Map<String, MemberEntry> getMemberEntries() {
+        return memberEntries;
+    }
+}
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/UnpackerImpl.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/UnpackerImpl.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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.zip.*;
 import java.io.*;
 import java.beans.PropertyChangeListener;
-import java.beans.PropertyChangeEvent;
 
 /*
  * Implementation of the Pack provider.
@@ -40,7 +39,7 @@
  */
 
 
-public class UnpackerImpl implements Pack200.Unpacker {
+public class UnpackerImpl extends TLGlobals implements Pack200.Unpacker {
 
 
     /**
@@ -48,7 +47,7 @@
      * @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 @@
      * @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<String, String> properties() {
+        return props;
     }
 
     // Back-pointer to NativeUnpacker, when active.
@@ -101,19 +94,20 @@
      */
     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 @@
         // %%% 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 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 @@
         }
 
         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<Package.Class> 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 @@
                     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
         }
     }
--- a/jdk/src/share/classes/com/sun/java/util/jar/pack/Utils.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/com/sun/java/util/jar/pack/Utils.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
      */
     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<TLGlobals> currentInstance = new ThreadLocal<>();
+
+    // convenience methods to access the TL globals
+    static TLGlobals getTLGlobals() {
+        return currentInstance.get();
+    }
+
+    static Map<String, Utf8Entry> getUtf8Entries() {
+        return getTLGlobals().getUtf8Entries();
+    }
+
+    static Map<String, ClassEntry> getClassEntries() {
+        return getTLGlobals().getClassEntries();
+    }
+
+    static Map<Object, LiteralEntry> getLiteralEntries() {
+        return getTLGlobals().getLiteralEntries();
+    }
+
+    static Map<String, DescriptorEntry> getDescriptorEntries() {
+         return getTLGlobals().getDescriptorEntries();
+    }
+
+    static Map<String, SignatureEntry> getSignatureEntries() {
+        return getTLGlobals().getSignatureEntries();
+    }
+
+    static Map<String, MemberEntry> 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;
     }
 
--- a/jdk/src/share/classes/com/sun/jndi/ldap/Connection.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/com/sun/jndi/ldap/Connection.java	Wed Jul 05 17:21:32 2017 +0200
@@ -813,7 +813,8 @@
         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;
--- a/jdk/src/share/classes/java/lang/AbstractStringBuilder.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/java/lang/AbstractStringBuilder.java	Wed Jul 05 17:21:32 2017 +0200
@@ -470,7 +470,7 @@
     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 @@
      *         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;
--- a/jdk/src/share/classes/java/lang/Thread.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/java/lang/Thread.java	Wed Jul 05 17:21:32 2017 +0200
@@ -414,6 +414,18 @@
     }
 
     /**
+     * Throws CloneNotSupportedException as a Thread can not be meaningfully
+     * cloned. Construct a new Thread instead.
+     *
+     * @throws  CloneNotSupportedException
+     *          always
+     */
+    @Override
+    protected Object clone() throws CloneNotSupportedException {
+        throw new CloneNotSupportedException();
+    }
+
+    /**
      * Allocates a new {@code Thread} object. This constructor has the same
      * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
      * {@code (null, null, gname)}, where {@code gname} is a newly generated
--- a/jdk/src/share/classes/java/lang/Throwable.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/java/lang/Throwable.java	Wed Jul 05 17:21:32 2017 +0200
@@ -200,7 +200,16 @@
      * @serial
      * @since 1.7
      */
-    private List<Throwable> suppressedExceptions = Collections.emptyList();
+    private List<Throwable> 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 @@
      *          cause is nonexistent or unknown.
      * @since 1.4
      */
-    public Throwable getCause() {
+    public synchronized Throwable getCause() {
         return (cause==this ? null : cause);
     }
 
@@ -563,7 +572,7 @@
                 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 @@
                 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 @@
             if (defensiveCopy[i] == null)
                 throw new NullPointerException("stackTrace[" + i + "]");
 
-        this.stackTrace = defensiveCopy;
+        synchronized (this) {
+            this.stackTrace = defensiveCopy;
+        }
     }
 
     /**
@@ -772,11 +783,11 @@
     private void readObject(ObjectInputStream s)
         throws IOException, ClassNotFoundException {
         s.defaultReadObject();     // read in all fields
-        List<Throwable> suppressed = Collections.emptyList();
+        List<Throwable> suppressed = null;
         if (suppressedExceptions != null &&
             !suppressedExceptions.isEmpty()) { // Copy Throwables to new list
             suppressed = new ArrayList<Throwable>();
-            for(Throwable t : suppressedExceptions) {
+            for (Throwable t : suppressedExceptions) {
                 if (t == null)
                     throw new NullPointerException(NULL_CAUSE_MESSAGE);
                 suppressed.add(t);
@@ -819,7 +830,7 @@
         if (exception == this)
             throw new IllegalArgumentException("Self-suppression not permitted");
 
-        if (suppressedExceptions.size() == 0)
+        if (suppressedExceptions == null)
             suppressedExceptions = new ArrayList<Throwable>();
         suppressedExceptions.add(exception);
     }
@@ -835,7 +846,10 @@
      *         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);
     }
 }
--- a/jdk/src/share/classes/java/net/HttpCookie.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/java/net/HttpCookie.java	Wed Jul 05 17:21:32 2017 +0200
@@ -1093,14 +1093,8 @@
         return sb.toString();
     }
 
-    private static SimpleDateFormat[] cDateFormats = null;
-    static {
-            cDateFormats = new SimpleDateFormat[COOKIE_DATE_FORMATS.length];
-            for (int i = 0; i < COOKIE_DATE_FORMATS.length; i++) {
-                cDateFormats[i] = new SimpleDateFormat(COOKIE_DATE_FORMATS[i], Locale.US);
-                cDateFormats[i].setTimeZone(TimeZone.getTimeZone("GMT"));
-            }
-    }
+    static final TimeZone GMT = TimeZone.getTimeZone("GMT");
+
     /*
      * @param dateString        a date string in one of the formats
      *                          defined in Netscape cookie spec
@@ -1109,12 +1103,14 @@
      *                          time and the time specified by dateString
      */
     private long expiryDate2DeltaSeconds(String dateString) {
-        for (SimpleDateFormat df : cDateFormats) {
+        for (int i = 0; i < COOKIE_DATE_FORMATS.length; i++) {
+            SimpleDateFormat df = new SimpleDateFormat(COOKIE_DATE_FORMATS[i], Locale.US);
+            df.setTimeZone(GMT);
             try {
                 Date date = df.parse(dateString);
                 return (date.getTime() - whenCreated) / 1000;
             } catch (Exception e) {
-
+                // Ignore, try the next date format
             }
         }
         return 0;
--- a/jdk/src/share/classes/java/net/URI.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/java/net/URI.java	Wed Jul 05 17:21:32 2017 +0200
@@ -856,9 +856,7 @@
         try {
             return new URI(str);
         } catch (URISyntaxException x) {
-            IllegalArgumentException y = new IllegalArgumentException();
-            y.initCause(x);
-            throw y;
+            throw new IllegalArgumentException(x.getMessage(), x);
         }
     }
 
--- a/jdk/src/share/classes/java/security/KeyStore.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/java/security/KeyStore.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
  * to read existing entries from the keystore, or to write new entries
  * into the keystore:
  * <pre>
+ *    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;
--- a/jdk/src/share/classes/java/sql/Date.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/java/sql/Date.java	Wed Jul 05 17:21:32 2017 +0200
@@ -103,27 +103,46 @@
      *         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;
-
-        if (s == null) throw new java.lang.IllegalArgumentException();
+        Date d = null;
 
-        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.
      * <P>
--- a/jdk/src/share/classes/javax/swing/JTable.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/javax/swing/JTable.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
 
     /**
      * 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
--- a/jdk/src/share/classes/sun/java2d/pisces/Dasher.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/java2d/pisces/Dasher.java	Wed Jul 05 17:21:32 2017 +0200
@@ -36,117 +36,71 @@
  * 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;
-
-    int idx;
-    boolean dashOn;
-    int phase;
-
-    int sx, sy;
-    int x0, y0;
+    private final float m00, m10, m01, m11;
+    private final float det;
 
-    int m00, m01;
-    int m10, m11;
-
-    Transform4 transform;
-
-    boolean symmetric;
-    long ldet;
+    private boolean firstDashOn;
+    private boolean starting;
 
-    boolean firstDashOn;
-    boolean starting;
-    int sx1, sy1;
+    private int idx;
+    private boolean dashOn;
+    private float phase;
 
-    /**
-     * Empty constructor.  <code>setOutput</code> and
-     * <code>setParameters</code> must be called prior to calling any
-     * other methods.
-     */
-    public Dasher() {}
+    private float sx, sy;
+    private float x0, y0;
+    private float sx1, sy1;
+
 
     /**
      * Constructs a <code>Dasher</code>.
      *
      * @param output an output <code>LineSink</code>.
-     * @param dash an array of <code>int</code>s containing the dash
-     * pattern in S15.16 format.
-     * @param phase an <code>int</code> containing the dash phase in
-     * S15.16 format.
+     * @param dash an array of <code>int</code>s containing the dash pattern
+     * @param phase an <code>int</code> containing the dash phase
      * @param transform a <code>Transform4</code> 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 <code>LineSink</code> of this
-     * <code>Dasher</code>.
-     *
-     * @param output an output <code>LineSink</code>.
-     */
-    public void setOutput(LineSink output) {
-        this.output = output;
-    }
-
-    /**
-     * Sets the parameters of this <code>Dasher</code>.
-     *
-     * @param dash an array of <code>int</code>s containing the dash
-     * pattern in S15.16 format.
-     * @param phase an <code>int</code> containing the dash phase in
-     * S15.16 format.
-     * @param transform a <code>Transform4</code> 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 @@
         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 @@
         this.y0 = y1;
     }
 
-    public void lineTo(int x1, int y1) {
-        while (true) {
-            int d = dash[idx] - phase;
-            int lx = x1 - x0;
-            int ly = y1 - y0;
+    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
-            // IMPL NOTE - use fixed point
+
+        // Compute segment length in the untransformed
+        // coordinate system
 
-            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);
-            }
+        float la = (dy*m00 - dx*m10)/det;
+        float lb = (dy*m01 - dx*m11)/det;
+        float origLen = (float) Math.hypot(la, lb);
 
-            if (l < d) {
+        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) {
+            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 void close() {
         lineTo(sx, sy);
         if (firstDashOn) {
--- a/jdk/src/share/classes/sun/java2d/pisces/LineSink.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/java2d/pisces/LineSink.java	Wed Jul 05 17:21:32 2017 +0200
@@ -39,16 +39,16 @@
  * <code>LineSink</code> interface.
  *
  */
-public abstract class LineSink {
+public interface LineSink {
 
     /**
      * Moves the current drawing position to the point <code>(x0,
      * y0)</code>.
      *
-     * @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 @@
      * <p> Other <code>LineSink</code> 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
      * <code>(x1, y1)</code> and sets the current drawing position to
      * <code>(x1, y1)</code>.
      *
-     * @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
      * <code>moveTo</code> 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();
 
 }
--- a/jdk/src/share/classes/sun/java2d/pisces/PiscesMath.java	Wed Jul 05 17:20:50 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -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;
-    }
-}
--- a/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/java2d/pisces/PiscesRenderingEngine.java	Wed Jul 05 17:21:32 2017 +0200
@@ -27,6 +27,7 @@
 
 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;
@@ -37,23 +38,9 @@
 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);
-    }
+    private static enum NormMode {OFF, ON_NO_AA, ON_WITH_AA}
 
     /**
      * Create a widened path as specified by the parameters.
@@ -85,18 +72,19 @@
         strokeTo(src,
                  null,
                  width,
+                 NormMode.OFF,
                  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();
@@ -142,14 +130,17 @@
                          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(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();
@@ -164,7 +155,7 @@
                   AffineTransform at,
                   BasicStroke bs,
                   boolean thin,
-                  boolean normalize,
+                  NormMode normalize,
                   boolean antialias,
                   LineSink lsink)
     {
@@ -181,6 +172,7 @@
         strokeTo(src,
                  at,
                  lw,
+                 normalize,
                  bs.getEndCap(),
                  bs.getLineJoin(),
                  bs.getMiterLimit(),
@@ -258,6 +250,7 @@
     void strokeTo(Shape src,
                   AffineTransform at,
                   float width,
+                  NormMode normalize,
                   int caps,
                   int join,
                   float miterlimit,
@@ -265,36 +258,139 @@
                   float dashphase,
                   LineSink lsink)
     {
-        Transform4 t4;
-
-        if (at == null || at.isIdentity()) {
-            t4 = IdentT4;
+        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, width, caps, join, miterlimit, a00, a01, a10, a11);
+        if (dashes != null) {
+            lsink = new Dasher(lsink, dashes, dashphase, a00, a01, a10, a11);
+        }
+        PathIterator pi;
+        if (normalize != NormMode.OFF) {
+            pi = new FlatteningPathIterator(
+                    new NormalizingPathIterator(src.getPathIterator(at), normalize),
+                    defaultFlat);
         } else {
-            t4 = new Transform4(FloatToS15_16((float) at.getScaleX()),
-                                FloatToS15_16((float) at.getShearX()),
-                                FloatToS15_16((float) at.getShearY()),
-                                FloatToS15_16((float) at.getScaleY()));
+            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");
+            }
         }
 
-        lsink = new Stroker(lsink,
-                            FloatToS15_16(width),
-                            caps,
-                            join,
-                            FloatToS15_16(miterlimit),
-                            t4);
-        if (dashes != null) {
-            int fdashes[] = new int[dashes.length];
-            for (int i = 0; i < dashes.length; i++) {
-                fdashes[i] = FloatToS15_16(dashes[i]);
+        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");
             }
-            lsink = new Dasher(lsink,
-                               fdashes,
-                               FloatToS15_16(dashphase),
-                               t4);
+
+            // 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;
         }
 
-        PathIterator pi = src.getPathIterator(at, defaultFlat);
-        pathTo(pi, lsink);
+        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) {
@@ -302,13 +398,11 @@
         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,18 +472,28 @@
                                               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;
+        NormMode norm = (normalize) ? NormMode.ON_WITH_AA : NormMode.OFF;
         if (bs == null) {
-            PathIterator pi = s.getPathIterator(at, defaultFlat);
-            r.setWindingRule(pi.getWindingRule());
+            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(),
+                             pi.getWindingRule(), pc);
             pathTo(pi, r);
         } else {
-            r.setWindingRule(PathIterator.WIND_NON_ZERO);
-            strokeTo(s, at, bs, thin, normalize, true, r);
+            r = new Renderer(3, 3,
+                             clip.getLoX(), clip.getLoY(),
+                             clip.getWidth(), clip.getHeight(),
+                             PathIterator.WIND_NON_ZERO, pc);
+            strokeTo(s, at, bs, thin, norm, true, r);
         }
         r.endRendering();
         PiscesTileGenerator ptg = new PiscesTileGenerator(pc, r.MAX_AA_ALPHA);
@@ -420,3 +524,4 @@
         }
     }
 }
+
--- a/jdk/src/share/classes/sun/java2d/pisces/Renderer.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/java2d/pisces/Renderer.java	Wed Jul 05 17:21:32 2017 +0200
@@ -25,446 +25,440 @@
 
 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;
-
-    // Buffer to be filled with one row's worth of alpha values
-    private byte[] rowAA; // needs to be short if 16x16 subsampling
+    private float pix_sx0, pix_sy0;
 
-    // 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;
+
+        this.windingRule = windingRule;
+        this.cache = cache;
 
-    public int getSubpixelLgPositionsX() {
-        return SUBPIXEL_LG_POSITIONS_X;
-    }
+        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;
 
-    public int getSubpixelLgPositionsY() {
-        return SUBPIXEL_LG_POSITIONS_Y;
-    }
-
-    public void setWindingRule(int windingRule) {
-        this.windingRule = windingRule;
-    }
-
-    public int getWindingRule() {
-        return windingRule;
+        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 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;
+    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 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;
+
+        // 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];
 
-        // 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)
-        {
+        // 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;
 
-            int x0 = edges[0] >> XSHIFT;
-            int y0 = edges[1] >> YSHIFT;
-            int x1 = edges[5] >> XSHIFT;
-            int y1 = edges[3] >> YSHIFT;
+        int y = boundsMinY; // needs to be declared here so we emit the last row properly.
+        ScanLineItInitialize();
+        for ( ; ScanLineItHasNext(); ) {
+            int numCrossings = ScanLineItGoToNextYAndComputeCrossings();
+            y = ScanLineItCurrentY();
 
-            if (x0 > x1) {
-                int tmp = x0;
-                x0 = x1;
-                x1 = tmp;
-            }
-            if (y0 > y1) {
-                int tmp = y0;
-                y0 = y1;
-                y1 = tmp;
+            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);
-            */
+                        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);
+                        }
+                    }
+                }
+                sum += crorientation;
+                prev = curx;
+            }
 
-            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;
+            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;
+            }
         }
 
-        // 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;
+        // Emit final row
+        if (pix_maxX >= pix_minX) {
+            emitRow(alpha, y >> SUBPIXEL_LG_POSITIONS_Y, pix_minX, pix_maxX);
         }
-
-        // 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;
+        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;
+    }
 
-            // 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;
-                }
-
-                // Test y0 > max:
-                //
-                // If edge lies entirely below current strip,
-                // skip it for now
-                if (edges[index + 1] > bmaxY) {
-                    continue;
-                }
-
-                computeCrossingsForEdge(index, bminY, bmaxY);
-            }
-
-            computeBounds();
-            if (rasterMaxX < rasterMinX) {
-                continue;
-            }
-
-            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();
-    }
 
     public void endRendering() {
         // Set up the cache to accumulate the bounding box
@@ -478,176 +472,31 @@
         _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 @@
                 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);
     }
 }
--- a/jdk/src/share/classes/sun/java2d/pisces/Stroker.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/java2d/pisces/Stroker.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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 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 @@
     //
     // 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;
-
-    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 int prev;
+    private int rindex;
+    private boolean started;
+    private boolean lineToOrigin;
+    private boolean joinToOrigin;
 
-    double m00_2_m01_2;
-    double m10_2_m11_2;
-    double m00_m10_m01_m11;
+    private float sx0, sy0, sx1, sy1, x0, y0, px0, py0;
+    private float mx0, my0, omx, omy;
 
-    /**
-     * Empty constructor.  <code>setOutput</code> and
-     * <code>setParameters</code> 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 <code>Stroker</code>.
      *
      * @param output an output <code>LineSink</code>.
-     * @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
      * <code>CAP_BUTT</code>, <code>CAP_ROUND</code> or
      * <code>CAP_SQUARE</code>.
      * @param joinStyle the desired line join style, one of
      * <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or
      * <code>JOIN_BEVEL</code>.
-     * @param miterLimit the desired miter limit, in S15.16 format.
+     * @param miterLimit the desired miter limit
      * @param transform a <code>Transform4</code> 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 <code>LineSink</code> of this
-     * <code>Stroker</code>.
-     *
-     * @param output an output <code>LineSink</code>.
-     */
-    public void setOutput(LineSink output) {
+                   float miterLimit,
+                   float m00, float m01, float m10, float m11) {
         this.output = output;
-    }
 
-    /**
-     * Sets the parameters of this <code>Stroker</code>.
-     * @param lineWidth the desired line width in pixels, in S15.16
-     * format.
-     * @param capStyle the desired end cap style, one of
-     * <code>CAP_BUTT</code>, <code>CAP_ROUND</code> or
-     * <code>CAP_SQUARE</code>.
-     * @param joinStyle the desired line join style, one of
-     * <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or
-     * <code>JOIN_BEVEL</code>.
-     * @param miterLimit the desired miter limit, in S15.16 format.
-     * @param transform a <code>Transform4</code> 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;
-
-        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;
+        m00_2_m01_2 = m00*m00 + m01*m01;
+        m10_2_m11_2 = m10*m10 + m11*m11;
+        m00_m10_m01_m11 = m00*m10 + m01*m11;
 
-        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;
+        this.m00 = m00;
+        this.m01 = m01;
+        this.m10 = m10;
+        this.m11 = m11;
+        det = m00*m11 - m01*m10;
 
-        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);
-        }
+        float limit = miterLimit * lineWidth2 * det;
+        this.miterLimitSq = limit*limit;
 
-        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 @@
         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 @@
 
     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 @@
         }
 
         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 @@
         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 @@
             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 @@
 
     // 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 @@
                      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 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 @@
         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 @@
         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 @@
         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 @@
         }
 
         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 @@
         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 @@
                           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 @@
                           -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 @@
         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 @@
         output.close();
     }
 }
+
--- a/jdk/src/share/classes/sun/java2d/pisces/Transform4.java	Wed Jul 05 17:20:50 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -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 <code>(x0, y0)</code> and <code>(x1,
-//      * y1)</code>.
-//      */
-//     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() {
-//     }
-
-}
--- a/jdk/src/share/classes/sun/net/www/protocol/file/FileURLConnection.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/net/www/protocol/file/FileURLConnection.java	Wed Jul 05 17:21:32 2017 +0200
@@ -59,7 +59,7 @@
     String filename;
     boolean isDirectory = false;
     boolean exists = false;
-    List files;
+    List<String> files;
 
     long length = -1;
     long lastModified = 0;
@@ -81,7 +81,10 @@
                 filename = file.toString();
                 isDirectory = file.isDirectory();
                 if (isDirectory) {
-                    files = (List) Arrays.asList(file.list());
+                    String[] fileList = file.list();
+                    if (fileList == null)
+                        throw new FileNotFoundException(filename + " exists, but is not accessible");
+                    files = Arrays.<String>asList(fileList);
                 } else {
 
                     is = new BufferedInputStream(new FileInputStream(filename));
@@ -197,7 +200,7 @@
                 Collections.sort(files, Collator.getInstance());
 
                 for (int i = 0 ; i < files.size() ; i++) {
-                    String fileName = (String)files.get(i);
+                    String fileName = files.get(i);
                     buf.append(fileName);
                     buf.append("\n");
                 }
--- a/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java	Wed Jul 05 17:21:32 2017 +0200
@@ -1768,6 +1768,10 @@
         // 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 */
--- a/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/nio/ch/DatagramChannelImpl.java	Wed Jul 05 17:21:32 2017 +0200
@@ -536,9 +536,11 @@
         }
     }
 
-    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 @@
                     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 @@
         }
     }
 
-    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 @@
         }
     }
 
-    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 @@
                     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 @@
         }
     }
 
-    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);
     }
--- a/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/nio/ch/FileChannelImpl.java	Wed Jul 05 17:21:32 2017 +0200
@@ -143,7 +143,11 @@
         }
     }
 
-    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 @@
                 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 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 @@
         }
     }
 
-    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 @@
                 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 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 {
@@ -440,24 +429,45 @@
         }
     }
 
-    private long transferToTrustedChannel(long position, int icount,
+    // Maximum size to map when using a mapped buffer
+    private static final long MAPPED_TRANSFER_SIZE = 8L*1024L*1024L;
+
+    private long transferToTrustedChannel(long position, long count,
                                           WritableByteChannel target)
         throws IOException
     {
-        if (  !((target instanceof FileChannelImpl)
-                || (target instanceof SelChImpl)))
+        boolean isSelChImpl = (target instanceof SelChImpl);
+        if (!((target instanceof FileChannelImpl) || isSelChImpl))
             return IOStatus.UNSUPPORTED;
 
         // Trusted target: Use a mapped buffer
-        MappedByteBuffer dbb = null;
-        try {
-            dbb = map(MapMode.READ_ONLY, position, icount);
-            // ## Bug: Closing this channel will not terminate the write
-            return target.write(dbb);
-        } finally {
-            if (dbb != null)
-                unmap(dbb);
+        long remaining = count;
+        while (remaining > 0L) {
+            long size = Math.min(remaining, MAPPED_TRANSFER_SIZE);
+            try {
+                MappedByteBuffer dbb = map(MapMode.READ_ONLY, position, size);
+                try {
+                    // ## Bug: Closing this channel will not terminate the write
+                    int n = target.write(dbb);
+                    assert n >= 0;
+                    remaining -= n;
+                    if (isSelChImpl) {
+                        // one attempt to write to selectable channel
+                        break;
+                    }
+                    assert n > 0;
+                    position += n;
+                } finally {
+                    unmap(dbb);
+                }
+            } catch (IOException ioe) {
+                // Only throw exception if no bytes have been written
+                if (remaining == count)
+                    throw ioe;
+                break;
+            }
         }
+        return count - remaining;
     }
 
     private long transferToArbitraryChannel(long position, int icount,
@@ -535,20 +545,34 @@
                                          long position, long count)
         throws IOException
     {
-        // Note we could loop here to accumulate more at once
         synchronized (src.positionLock) {
-            long p = src.position();
-            int icount = (int)Math.min(Math.min(count, Integer.MAX_VALUE),
-                                       src.size() - p);
-            // ## Bug: Closing this channel will not terminate the write
-            MappedByteBuffer bb = src.map(MapMode.READ_ONLY, p, icount);
-            try {
-                long n = write(bb, position);
-                src.position(p + n);
-                return n;
-            } finally {
-                unmap(bb);
+            long pos = src.position();
+            long max = Math.min(count, src.size() - pos);
+
+            long remaining = max;
+            long p = pos;
+            while (remaining > 0L) {
+                long size = Math.min(remaining, MAPPED_TRANSFER_SIZE);
+                // ## Bug: Closing this channel will not terminate the write
+                MappedByteBuffer bb = src.map(MapMode.READ_ONLY, p, size);
+                try {
+                    long n = write(bb, position);
+                    assert n > 0;
+                    p += n;
+                    position += n;
+                    remaining -= n;
+                } catch (IOException ioe) {
+                    // Only throw exception if no bytes have been written
+                    if (remaining == max)
+                        throw ioe;
+                    break;
+                } finally {
+                    unmap(bb);
+                }
             }
+            long nwritten = max - remaining;
+            src.position(pos + nwritten);
+            return nwritten;
         }
     }
 
--- a/jdk/src/share/classes/sun/nio/ch/IOUtil.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/nio/ch/IOUtil.java	Wed Jul 05 17:21:32 2017 +0200
@@ -38,34 +38,6 @@
 
     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<numBufs; i++) {
-            if (bufs[i].hasRemaining()) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    /*
-     * Returns a new ByteBuffer array with only unfinished buffers in it
-     */
-    private static ByteBuffer[] skipBufs(ByteBuffer[] bufs,
-                                         int nextWithRemaining)
-    {
-        int newSize = bufs.length - nextWithRemaining;
-        ByteBuffer[] temp = new ByteBuffer[newSize];
-        for (int i=0; i<newSize; i++) {
-            temp[i] = bufs[i + nextWithRemaining];
-        }
-        return temp;
-    }
-
     static int write(FileDescriptor fd, ByteBuffer src, long position,
                      NativeDispatcher nd, Object lock)
         throws IOException
@@ -93,7 +65,7 @@
             }
             return n;
         } finally {
-            Util.releaseTemporaryDirectBuffer(bb);
+            Util.offerFirstTemporaryDirectBuffer(bb);
         }
     }
 
@@ -125,88 +97,81 @@
     static long write(FileDescriptor fd, ByteBuffer[] bufs, NativeDispatcher nd)
         throws IOException
     {
-        int nextWithRemaining = remaining(bufs);
-        // if all bufs are empty we should return immediately
-        if (nextWithRemaining < 0)
-            return 0;
-        // If some bufs are empty we should skip them
-        if (nextWithRemaining > 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<numBufs; i++) {
-                if (!(bufs[i] instanceof DirectBuffer)) {
-                    int pos = bufs[i].position();
-                    int lim = bufs[i].limit();
-                    assert (pos <= lim);
-                    int rem = (pos <= lim ? lim - pos : 0);
+
+            // Iterate over buffers to populate native iovec array.
+            int count = offset + length;
+            for (int i=offset; i<count; i++) {
+                ByteBuffer buf = bufs[i];
+                int pos = buf.position();
+                int lim = buf.limit();
+                assert (pos <= lim);
+                int rem = (pos <= lim ? lim - pos : 0);
+                if (rem > 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();
+                    }
 
-                    ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);
-                    shadow[i] = bb;
-                    // Leave slow buffer position untouched; it will be updated
-                    // after we see how many bytes were really written out
-                    bb.put(bufs[i]);
-                    bufs[i].position(pos);
-                    bb.flip();
-                } else {
-                    shadow[i] = bufs[i];
+                    vec.putBase(iov_len, ((DirectBuffer)buf).address() + pos);
+                    vec.putLen(iov_len, rem);
+                    iov_len++;
                 }
             }
+            if (iov_len == 0)
+                return 0L;
+
+            long bytesWritten = nd.writev(fd, vec.address, iov_len);
+
+            // Notify the buffers how many bytes were taken
+            long left = bytesWritten;
+            for (int j=0; j<iov_len; j++) {
+                if (left > 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);
+            }
 
-            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<numBufs; i++) {
-                    ByteBuffer nextBuffer = shadow[i];
-                    // put in the buffer addresses
-                    long pos = nextBuffer.position();
-                    long len = nextBuffer.limit() - pos;
-                    vec.putBase(i, ((DirectBuffer)nextBuffer).address() + pos);
-                    vec.putLen(i, len);
-                }
-
-                // Invoke native call to fill the buffers
-                bytesWritten = nd.writev(fd, vec.address, numBufs);
-            } finally {
-                vec.free();
-            }
-            long returnVal = bytesWritten;
+            completed = true;
+            return bytesWritten;
 
-            // Notify the buffers how many bytes were taken
-            for (int i=0; i<numBufs; i++) {
-                ByteBuffer nextBuffer = bufs[i];
-                int pos = nextBuffer.position();
-                int lim = nextBuffer.limit();
-                assert (pos <= lim);
-                int len = (pos <= lim ? lim - pos : lim);
-                if (bytesWritten >= 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;
-                }
-            }
-            return returnVal;
         } finally {
-            // return any substituted buffers to cache
-            for (int i=0; i<numBufs; i++) {
-                ByteBuffer bb = shadow[i];
-                if (bb != null && bb != bufs[i]) {
-                    Util.releaseTemporaryDirectBuffer(bb);
+            // if an error occurred then clear refs to buffers and return any shadow
+            // buffers to cache
+            if (!completed) {
+                for (int j=0; j<iov_len; j++) {
+                    ByteBuffer shadow = vec.getShadow(j);
+                    if (shadow != null)
+                        Util.offerLastTemporaryDirectBuffer(shadow);
+                    vec.clearRefs(j);
                 }
             }
         }
@@ -231,7 +196,7 @@
                 dst.put(bb);
             return n;
         } finally {
-            Util.releaseTemporaryDirectBuffer(bb);
+            Util.offerFirstTemporaryDirectBuffer(bb);
         }
     }
 
@@ -262,92 +227,85 @@
     static long read(FileDescriptor fd, ByteBuffer[] bufs, NativeDispatcher nd)
         throws IOException
     {
-        int nextWithRemaining = remaining(bufs);
-        // if all bufs are empty we should return immediately
-        if (nextWithRemaining < 0)
-            return 0;
-        // If some bufs are empty we should skip them
-        if (nextWithRemaining > 0)
-            bufs = skipBufs(bufs, nextWithRemaining);
+        return read(fd, bufs, 0, bufs.length, nd);
+    }
+
+    static long read(FileDescriptor fd, ByteBuffer[] bufs, int offset, int length,
+                     NativeDispatcher nd)
+        throws IOException
+    {
+        IOVecWrapper vec = IOVecWrapper.get(length);
+
+        boolean completed = false;
+        int iov_len = 0;
+        try {
 
-        int numBufs = bufs.length;
+            // Iterate over buffers to populate native iovec array.
+            int count = offset + length;
+            for (int i=offset; i<count; i++) {
+                ByteBuffer buf = bufs[i];
+                if (buf.isReadOnly())
+                    throw new IllegalArgumentException("Read-only buffer");
+                int pos = buf.position();
+                int lim = buf.limit();
+                assert (pos <= lim);
+                int rem = (pos <= lim ? lim - pos : 0);
+
+                if (rem > 0) {
+                    vec.setBuffer(iov_len, buf, pos, rem);
 
-        // Read into the shadow to ensure DirectByteBuffers are used
-        ByteBuffer[] shadow = new ByteBuffer[numBufs];
-        boolean usingSlowBuffers = false;
-        try {
-            for (int i=0; i<numBufs; i++) {
-                if (bufs[i].isReadOnly())
-                    throw new IllegalArgumentException("Read-only buffer");
-                if (!(bufs[i] instanceof DirectBuffer)) {
-                    shadow[i] = Util.getTemporaryDirectBuffer(bufs[i].remaining());
-                    usingSlowBuffers = true;
-                } else {
-                    shadow[i] = bufs[i];
+                    // 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;
+
+            long bytesRead = nd.readv(fd, vec.address, iov_len);
+
+            // Notify the buffers how many bytes were read
+            long left = bytesRead;
+            for (int j=0; j<iov_len; j++) {
+                ByteBuffer shadow = vec.getShadow(j);
+                if (left > 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);
+                    }
+                    left -= n;
+                }
+                if (shadow != null)
+                    Util.offerLastTemporaryDirectBuffer(shadow);
+                vec.clearRefs(j);
+            }
 
-            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<numBufs; i++) {
-                    ByteBuffer nextBuffer = shadow[i];
-                    // put in the buffer addresses
-                    long pos = nextBuffer.position();
-                    long len = nextBuffer.remaining();
-                    vec.putBase(i, ((DirectBuffer)nextBuffer).address() + pos);
-                    vec.putLen(i, len);
-                }
-
-                // Invoke native call to fill the buffers
-                bytesRead = nd.readv(fd, vec.address, numBufs);
-            } finally {
-                vec.free();
-            }
-            long returnVal = bytesRead;
+            completed = true;
+            return bytesRead;
 
-            // Notify the buffers how many bytes were read
-            for (int i=0; i<numBufs; i++) {
-                ByteBuffer nextBuffer = shadow[i];
-                // Note: should this have been cached from above?
-                int pos = nextBuffer.position();
-                int len = nextBuffer.remaining();
-                if (bytesRead >= 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);
-                    }
-                    break;
-                }
-            }
-
-            // Put results from shadow into the slow buffers
-            if (usingSlowBuffers) {
-                for (int i=0; i<numBufs; i++) {
-                    if (!(bufs[i] instanceof DirectBuffer)) {
-                        shadow[i].flip();
-                        bufs[i].put(shadow[i]);
-                    }
-                }
-            }
-            return returnVal;
         } finally {
-            // return any substituted buffers to cache
-            if (usingSlowBuffers) {
-                for (int i=0; i<numBufs; i++) {
-                    ByteBuffer bb = shadow[i];
-                    if (bb != null && bb != bufs[i]) {
-                        Util.releaseTemporaryDirectBuffer(bb);
-                    }
+            // if an error occurred then clear refs to buffers and return any shadow
+            // buffers to cache
+            if (!completed) {
+                for (int j=0; j<iov_len; j++) {
+                    ByteBuffer shadow = vec.getShadow(j);
+                    if (shadow != null)
+                        Util.offerLastTemporaryDirectBuffer(shadow);
+                    vec.clearRefs(j);
                 }
             }
         }
--- a/jdk/src/share/classes/sun/nio/ch/IOVecWrapper.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/nio/ch/IOVecWrapper.java	Wed Jul 05 17:21:32 2017 +0200
@@ -25,6 +25,7 @@
 
 package sun.nio.ch;
 
+import java.nio.ByteBuffer;
 import sun.misc.*;
 
 
@@ -43,23 +44,98 @@
 class IOVecWrapper {
 
     // Miscellaneous constants
-    static int BASE_OFFSET = 0;
-    static int LEN_OFFSET;
-    static int SIZE_IOVEC;
+    private static final int BASE_OFFSET = 0;
+    private static final int LEN_OFFSET;
+    private static final int SIZE_IOVEC;
 
     // The iovec array
-    private AllocatedNativeObject vecArray;
+    private final AllocatedNativeObject vecArray;
+
+    // Number of elements in iovec array
+    private final int size;
+
+    // Buffers and position/remaining corresponding to elements in iovec array
+    private final ByteBuffer[] buf;
+    private final int[] position;
+    private final int[] remaining;
+
+    // Shadow buffers for cases when original buffer is substituted
+    private final ByteBuffer[] shadow;
 
     // Base address of this array
-    long address;
+    final long address;
 
     // Address size in bytes
     static int addressSize;
 
-    IOVecWrapper(int newSize) {
-        newSize = (newSize + 1) * SIZE_IOVEC;
-        vecArray = new AllocatedNativeObject(newSize, false);
-        address = vecArray.address();
+    private static class Deallocator implements Runnable {
+        private final AllocatedNativeObject obj;
+        Deallocator(AllocatedNativeObject obj) {
+            this.obj = obj;
+        }
+        public void run() {
+            obj.free();
+        }
+    }
+
+    // per thread IOVecWrapper
+    private static final ThreadLocal<IOVecWrapper> cached =
+        new ThreadLocal<IOVecWrapper>();
+
+    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 @@
             vecArray.putLong(offset, len);
     }
 
-    void free() {
-        vecArray.free();
-    }
-
     static {
         addressSize = Util.unsafe().addressSize();
         LEN_OFFSET = addressSize;
--- a/jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/nio/ch/SocketChannelImpl.java	Wed Jul 05 17:21:32 2017 +0200
@@ -385,9 +385,11 @@
         }
     }
 
-    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 @@
                 }
 
                 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 @@
         }
     }
 
-    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 @@
         }
     }
 
-    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 @@
                     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 @@
         }
     }
 
-    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) {
--- a/jdk/src/share/classes/sun/nio/ch/Util.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/nio/ch/Util.java	Wed Jul 05 17:21:32 2017 +0200
@@ -41,67 +41,180 @@
 
 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<SoftReference<ByteBuffer>>[] bufferPool;
+    // Per-thread cache of temporary direct buffers
+    private static ThreadLocal<BufferCache> bufferCache =
+        new ThreadLocal<BufferCache>()
+    {
+        @Override
+        protected BufferCache initialValue() {
+            return new BufferCache();
+        }
+    };
+
+    /**
+     * A simple cache of direct buffers.
+     */
+    private static class BufferCache {
+        // the array of buffers
+        private ByteBuffer[] buffers;
 
-    @SuppressWarnings("unchecked")
-    static ThreadLocal<SoftReference<ByteBuffer>>[] createThreadLocalBufferPool() {
-        return new ThreadLocal[TEMP_BUF_POOL_SIZE];
-    }
+        // 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
 
-    static {
-        bufferPool = createThreadLocalBufferPool();
-        for (int i=0; i<TEMP_BUF_POOL_SIZE; i++)
-            bufferPool[i] = new ThreadLocal<SoftReference<ByteBuffer>>();
-    }
+            ByteBuffer[] buffers = this.buffers;
 
-    static ByteBuffer getTemporaryDirectBuffer(int size) {
-        ByteBuffer buf = null;
-        // Grab a buffer if available
-        for (int i=0; i<TEMP_BUF_POOL_SIZE; i++) {
-            SoftReference<ByteBuffer> 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;
+            // 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;
             }
         }
 
-        // Make a new one
-        return ByteBuffer.allocateDirect(size);
-    }
-
-    static void releaseTemporaryDirectBuffer(ByteBuffer buf) {
-        if (buf == null)
-            return;
-        // Put it in an empty slot if such exists
-        for (int i=0; i<TEMP_BUF_POOL_SIZE; i++) {
-            SoftReference<ByteBuffer> ref = bufferPool[i].get();
-            if ((ref == null) || (ref.get() == null)) {
-                bufferPool[i].set(new SoftReference<ByteBuffer>(buf));
-                return;
-            }
-        }
-        // Otherwise replace a smaller one in the cache if such exists
-        for (int i=0; i<TEMP_BUF_POOL_SIZE; i++) {
-            SoftReference<ByteBuffer> ref = bufferPool[i].get();
-            ByteBuffer inCacheBuf = ref.get();
-            if ((inCacheBuf == null) || (buf.capacity() > inCacheBuf.capacity())) {
-                bufferPool[i].set(new SoftReference<ByteBuffer>(buf));
-                return;
+        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;
             }
         }
 
-        // release memory
-       ((DirectBuffer)buf).cleaner().clean();
+        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) {
+        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);
+        }
+    }
+
+    /**
+     * Releases a temporary buffer by returning to the cache or freeing it.
+     */
+    static void releaseTemporaryDirectBuffer(ByteBuffer buf) {
+        offerFirstTemporaryDirectBuffer(buf);
+    }
+
+    /**
+     * 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 {
--- a/jdk/src/share/classes/sun/util/resources/TimeZoneNames.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/util/resources/TimeZoneNames.java	Wed Jul 05 17:21:32 2017 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 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
@@ -75,6 +75,8 @@
                                      "Central European Summer Time", "CEST"};
         String CHAST[] = new String[] {"Chatham Standard Time", "CHAST",
                                        "Chatham Daylight Time", "CHADT"};
+        String CHUT[] = new String[] {"Chuuk Time", "CHUT",
+                                      "Chuuk Summer Time", "CHUST"};
         String CIT[] = new String[] {"Central Indonesia Time", "CIT",
                                      "Central Indonesia Summer Time", "CIST"};
         String CLT[] = new String[] {"Chile Time", "CLT",
@@ -153,6 +155,8 @@
                                           "Pitcairn Daylight Time", "PDT"};
         String PKT[] = new String[] {"Pakistan Time", "PKT",
                                      "Pakistan Summer Time", "PKST"};
+        String PONT[] = new String[] {"Pohnpei Time", "PONT",
+                                      "Pohnpei Summer Time", "PONST"};
         String PST[] = new String[] {"Pacific Standard Time", "PST",
                                      "Pacific Daylight Time", "PDT"};
         String RST[] = new String[] {"Eastern Standard Time", "EST",
@@ -169,8 +173,6 @@
                                           "Eastern Summer Time (Tasmania)", "EST"};
         String TMT[] = new String[] {"Turkmenistan Time", "TMT",
                                      "Turkmenistan Summer Time", "TMST"};
-        String TRUT[] = new String[] {"Truk Time", "TRUT",
-                                      "Truk Summer Time", "TRUST"};
         String ULAT[]= new String[] {"Ulaanbaatar Time", "ULAT",
                                      "Ulaanbaatar Summer Time", "ULAST"};
         String WART[] = new String[] {"Western Argentine Time", "WART",
@@ -309,6 +311,7 @@
             {"America/Atikokan", EST},
             {"America/Atka", HAST},
             {"America/Bahia", BRT},
+            {"America/Bahia_Banderas", CST},
             {"America/Barbados", AST},
             {"America/Belem", BRT},
             {"America/Belize", CST},
@@ -755,6 +758,7 @@
             {"Pacific/Apia", WST_SAMOA},
             {"Pacific/Auckland", NZST},
             {"Pacific/Chatham", CHAST},
+            {"Pacific/Chuuk", CHUT},
             {"Pacific/Easter", EASTER},
             {"Pacific/Efate", new String[] {"Vanuatu Time", "VUT",
                                             "Vanuatu Summer Time", "VUST"}},
@@ -793,8 +797,8 @@
             {"Pacific/Palau", new String[] {"Palau Time", "PWT",
                                             "Palau Summer Time", "PWST"}},
             {"Pacific/Pitcairn", PITCAIRN},
-            {"Pacific/Ponape", new String[] {"Ponape Time", "PONT",
-                                             "Ponape Summer Time", "PONST"}},
+            {"Pacific/Pohnpei", PONT},
+            {"Pacific/Ponape", PONT},
             {"Pacific/Port_Moresby", new String[] {"Papua New Guinea Time", "PGT",
                                                    "Papua New Guinea Summer Time", "PGST"}},
             {"Pacific/Rarotonga", new String[] {"Cook Is. Time", "CKT",
@@ -807,12 +811,12 @@
                                              "Gilbert Is. Summer Time", "GILST"}},
             {"Pacific/Tongatapu", new String[] {"Tonga Time", "TOT",
                                                 "Tonga Summer Time", "TOST"}},
-            {"Pacific/Truk", TRUT},
+            {"Pacific/Truk", CHUT},
             {"Pacific/Wake", new String[] {"Wake Time", "WAKT",
                                            "Wake Summer Time", "WAKST"}},
             {"Pacific/Wallis", new String[] {"Wallis & Futuna Time", "WFT",
                                              "Wallis & Futuna Summer Time", "WFST"}},
-            {"Pacific/Yap", TRUT},
+            {"Pacific/Yap", CHUT},
             {"Poland", CET},
             {"PRC", CTT},
             {"PST8PDT", PST},
--- a/jdk/src/share/classes/sun/util/resources/TimeZoneNames_de.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/util/resources/TimeZoneNames_de.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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
@@ -75,6 +75,8 @@
                                      "Mitteleurop\u00e4ische Sommerzeit", "MESZ"};
         String CHAST[] = new String[] {"Chatham Normalzeit", "CHAST",
                                        "Chatham Sommerzeit", "CHADT"};
+        String CHUT[] = new String[] {"Chuuk Time", "CHUT",
+                                      "Chuuk Summer Time", "CHUST"};
         String CIT[] = new String[] {"Zentralindonesische Zeit", "CIT",
                                      "Zentralindonesische Sommerzeit", "CIST"};
         String CLT[] = new String[] {"Chilenische Zeit", "CLT",
@@ -153,6 +155,8 @@
                                           "Pitcairn Sommerzeit", "PDT"};
         String PKT[] = new String[] {"Pakistanische Zeit", "PKT",
                                      "Pakistanische Sommerzeit", "PKST"};
+        String PONT[] = new String[] {"Pohnpei Time", "PONT",
+                                      "Pohnpei Summer Time", "PONST"};
         String PST[] = new String[] {"Pazifische Normalzeit", "PST",
                                      "Pazifische Sommerzeit", "PDT"};
         String RST[] = new String[] {"\u00d6stliche Normalzeit", "EST",
@@ -169,8 +173,6 @@
                                           "\u00d6stliche Sommerzeit (Tasmanien)", "EST"};
         String TMT[] = new String[] {"Turkmenische Zeit", "TMT",
                                      "Turkmenische Sommerzeit", "TMST"};
-        String TRUT[] = new String[] {"Truk Zeit", "TRUT",
-                                      "Truk Sommerzeit", "TRUST"};
         String ULAT[]= new String[] {"Ulaanbaatar Zeit", "ULAT",
                                      "Ulaanbaatar Sommerzeit", "ULAST"};
         String WART[] = new String[] {"Westargentinische Zeit", "WART",
@@ -309,6 +311,7 @@
             {"America/Atikokan", EST},
             {"America/Atka", HAST},
             {"America/Bahia", BRT},
+            {"America/Bahia_Banderas", CST},
             {"America/Barbados", AST},
             {"America/Belem", BRT},
             {"America/Belize", CST},
@@ -446,10 +449,13 @@
             {"America/Winnipeg", CST},
             {"America/Yakutat", AKST},
             {"America/Yellowknife", MST},
+            {"Antarctica/Casey", WST_AUS},
             {"Antarctica/Davis", new String[] {"Davis Zeit", "DAVT",
                                                "Davis Sommerzeit", "DAVST"}},
             {"Antarctica/DumontDUrville", new String[] {"Dumont-d'Urville Zeit", "DDUT",
                                                         "Dumont-d'Urville Sommerzeit", "DDUST"}},
+            {"Antarctica/Macquarie", new String[] {"Macquarie Island Time", "MIST",
+                                                   "Macquarie Island Summer Time", "MIST"}},
             {"Antarctica/Mawson", new String[] {"Mawson Zeit", "MAWT",
                                                 "Mawson Sommerzeit", "MAWST"}},
             {"Antarctica/McMurdo", NZST},
@@ -752,6 +758,7 @@
             {"Pacific/Apia", WST_SAMOA},
             {"Pacific/Auckland", NZST},
             {"Pacific/Chatham", CHAST},
+            {"Pacific/Chuuk", CHUT},
             {"Pacific/Easter", EASTER},
             {"Pacific/Efate", new String[] {"Vanuatu Zeit", "VUT",
                                             "Vanuatu Sommerzeit", "VUST"}},
@@ -790,8 +797,8 @@
             {"Pacific/Palau", new String[] {"Palau Zeit", "PWT",
                                             "Palau Sommerzeit", "PWST"}},
             {"Pacific/Pitcairn", PITCAIRN},
-            {"Pacific/Ponape", new String[] {"Ponape Zeit", "PONT",
-                                             "Ponape Sommerzeit", "PONST"}},
+            {"Pacific/Pohnpei", PONT},
+            {"Pacific/Ponape", PONT},
             {"Pacific/Port_Moresby", new String[] {"Papua-Neuguinea Zeit", "PGT",
                                                    "Papua-Neuguinea Sommerzeit", "PGST"}},
             {"Pacific/Rarotonga", new String[] {"Cook-Inseln Zeit", "CKT",
@@ -804,12 +811,12 @@
                                              "Gilbert-Inseln Sommerzeit", "GILST"}},
             {"Pacific/Tongatapu", new String[] {"Tonga Zeit", "TOT",
                                                 "Tonga Sommerzeit", "TOST"}},
-            {"Pacific/Truk", TRUT},
+            {"Pacific/Truk", CHUT},
             {"Pacific/Wake", new String[] {"Wake Zeit", "WAKT",
                                            "Wake Sommerzeit", "WAKST"}},
             {"Pacific/Wallis", new String[] {"Wallis u. Futuna Zeit", "WFT",
                                              "Wallis u. Futuna Sommerzeit", "WFST"}},
-            {"Pacific/Yap", TRUT},
+            {"Pacific/Yap", CHUT},
             {"Poland", CET},
             {"PRC", CTT},
             {"PST8PDT", PST},
--- a/jdk/src/share/classes/sun/util/resources/TimeZoneNames_es.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/util/resources/TimeZoneNames_es.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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
@@ -75,6 +75,8 @@
                                      "Hora de verano de Europa Central", "CEST"};
         String CHAST[] = new String[] {"Hora est\u00e1ndar de Chatham", "CHAST",
                                        "Hora de verano de Chatham", "CHADT"};
+        String CHUT[] = new String[] {"Chuuk Time", "CHUT",
+                                      "Chuuk Summer Time", "CHUST"};
         String CIT[] = new String[] {"Hora de Indonesia Central", "CIT",
                                      "Hora de verano de Indonesia Central", "CIST"};
         String CLT[] = new String[] {"Hora de Chile", "CLT",
@@ -153,6 +155,8 @@
                                           "Hora de verano de Pitcairn", "PDT"};
         String PKT[] = new String[] {"Hora de Pakist\u00e1n", "PKT",
                                      "Hora de verano de Pakist\u00e1n", "PKST"};
+        String PONT[] = new String[] {"Pohnpei Time", "PONT",
+                                      "Pohnpei Summer Time", "PONST"};
         String PST[] = new String[] {"Hora est\u00e1ndar del Pac\u00edfico", "PST",
                                      "Hora de verano del Pac\u00edfico", "PDT"};
         String RST[] = new String[] {"Hora est\u00e1ndar Oriental", "EST",
@@ -169,8 +173,6 @@
                                           "Hora de verano del Este (Tasmania)", "EST"};
         String TMT[] = new String[] {"Hora de Turkmenist\u00e1n", "TMT",
                                      "Hora de verano de Turkmenist\u00e1n", "TMST"};
-        String TRUT[] =new String[] {"Hora de Truk", "TRUT",
-                                     "Hora de verano de Truk", "TRUST"};
         String ULAT[]= new String[] {"Hora de Ulan Bator", "ULAT",
                                      "Hora de verano de Ulan Bator", "ULAST"};
         String WART[] = new String[] {"Hora de Argentina Occidental", "WART",
@@ -309,6 +311,7 @@
             {"America/Atikokan", EST},
             {"America/Atka", HAST},
             {"America/Bahia", BRT},
+            {"America/Bahia_Banderas", CST},
             {"America/Barbados", AST},
             {"America/Belem", BRT},
             {"America/Belize", CST},
@@ -446,10 +449,13 @@
             {"America/Winnipeg", CST},
             {"America/Yakutat", AKST},
             {"America/Yellowknife", MST},
+            {"Antarctica/Casey", WST_AUS},
             {"Antarctica/Davis", new String[] {"Hora de Davis", "DAVT",
                                                "Hora de verano de Davis", "DAVST"}},
             {"Antarctica/DumontDUrville", new String[] {"Hora de Dumont-d'Urville", "DDUT",
                                                         "Hora de verano de Dumont-d'Urville", "DDUST"}},
+            {"Antarctica/Macquarie", new String[] {"Macquarie Island Time", "MIST",
+                                                   "Macquarie Island Summer Time", "MIST"}},
             {"Antarctica/Mawson", new String[] {"Hora de Mawson", "MAWT",
                                                 "Hora de verano de Mawson", "MAWST"}},
             {"Antarctica/McMurdo", NZST},
@@ -533,7 +539,6 @@
                                           "Hora de verano de Filipinas", "PHST"}},
             {"Asia/Muscat", GST},
             {"Asia/Nicosia", EET},
-
             {"Asia/Novokuznetsk", NOVT},
             {"Asia/Novosibirsk", NOVT},
             {"Asia/Oral", new String[] {"Hora de Uralsk", "ORAT",
@@ -753,6 +758,7 @@
             {"Pacific/Apia", WST_SAMOA},
             {"Pacific/Auckland", NZST},
             {"Pacific/Chatham", CHAST},
+            {"Pacific/Chuuk", CHUT},
             {"Pacific/Easter", EASTER},
             {"Pacific/Efate", new String[] {"Hora de Vanuatu", "VUT",
                                             "Hora de verano de Vanuatu", "VUST"}},
@@ -791,8 +797,8 @@
             {"Pacific/Palau", new String[] {"Hora de Palau", "PWT",
                                             "Hora de verano de Palau", "PWST"}},
             {"Pacific/Pitcairn", PITCAIRN},
-            {"Pacific/Ponape", new String[] {"Hora de Ponape", "PONT",
-                                             "Hora de verano de Ponape", "PONST"}},
+            {"Pacific/Pohnpei", PONT},
+            {"Pacific/Ponape", PONT},
             {"Pacific/Port_Moresby", new String[] {"Hora de Pap\u00faa-Nueva Guinea", "PGT",
                                                    "Hora de verano de Pap\u00faa-Nueva Guinea", "PGST"}},
             {"Pacific/Rarotonga", new String[] {"Hora de las islas Cook", "CKT",
@@ -805,12 +811,12 @@
                                              "Hora de verano de las islas Gilbert", "GILST"}},
             {"Pacific/Tongatapu", new String[] {"Hora de Tonga", "TOT",
                                                 "Hora de verano de Tonga", "TOST"}},
-            {"Pacific/Truk", TRUT},
+            {"Pacific/Truk", CHUT},
             {"Pacific/Wake", new String[] {"Hora de Wake", "WAKT",
                                            "Hora de verano de Wake", "WAKST"}},
             {"Pacific/Wallis", new String[] {"Hora de Wallis y Futuna", "WFT",
                                              "Hora de verano de Wallis y Futuna", "WFST"}},
-            {"Pacific/Yap", TRUT},
+            {"Pacific/Yap", CHUT},
             {"Poland", CET},
             {"PRC", CTT},
             {"PST8PDT", PST},
--- a/jdk/src/share/classes/sun/util/resources/TimeZoneNames_fr.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/util/resources/TimeZoneNames_fr.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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
@@ -75,6 +75,8 @@
                                      "Heure d'\u00e9t\u00e9 d'Europe centrale", "CEST"} ;
         String CHAST[] = new String[] {"Heure standard de Chatham", "CHAST",
                                        "Heure avanc\u00e9e de Chatham", "CHADT"};
+        String CHUT[] = new String[] {"Chuuk Time", "CHUT",
+                                      "Chuuk Summer Time", "CHUST"};
         String CIT[] = new String[] {"Heure d'Indon\u00e9sie centrale", "CIT",
                                      "Heure d'\u00e9t\u00e9 d'Indon\u00e9sie centrale", "CIST"};
         String CLT[] = new String[] {"Heure du Chili", "CLT",
@@ -153,6 +155,8 @@
                                           "heure avanc\u00e9e des Pitcairn", "PDT"};
         String PKT[] = new String[] {"Heure du Pakistan", "PKT",
                                      "Heure d'\u00e9t\u00e9 du Pakistan", "PKST"} ;
+        String PONT[] = new String[] {"Pohnpei Time", "PONT",
+                                      "Pohnpei Summer Time", "PONST"};
         String PST[] = new String[] {"Heure normale du Pacifique", "PST",
                                      "Heure avanc\u00e9e du Pacifique", "PDT"} ;
         String RST[] = new String[] {"Heure normale de l'Est", "EST",
@@ -169,8 +173,6 @@
                                           "Heure d'\u00e9t\u00e9 d'Australie orientale (Tasmanie)", "EST"};
         String TMT[] = new String[] {"Heure du Turkm\u00e9nistan", "TMT",
                                      "Heure d'\u00e9t\u00e9 du Turkm\u00e9nistan", "TMST"} ;
-        String TRUT[] = new String[] {"Heure de Truk", "TRUT",
-                                      "Heure d'\u00e9t\u00e9 de Truk", "TRUST"};
         String ULAT[]= new String[] {"Heure de l'Ulaanbaatar", "ULAT",
                                      "Heure d'\u00e9t\u00e9 de l'Ulaanbaatar", "ULAST"} ;
         String WART[] = new String[] {"Heure D'Argentine de l'Ouest", "WART",
@@ -309,6 +311,7 @@
             {"America/Atikokan", EST},
             {"America/Atka", HAST},
             {"America/Bahia", BRT},
+            {"America/Bahia_Banderas", CST},
             {"America/Barbados", AST},
             {"America/Belem", BRT},
             {"America/Belize", CST},
@@ -446,10 +449,13 @@
             {"America/Winnipeg", CST},
             {"America/Yakutat", AKST},
             {"America/Yellowknife", MST},
+            {"Antarctica/Casey", WST_AUS},
             {"Antarctica/Davis", new String[] {"Heure de Davis", "DAVT",
                                                "Heure d'\u00e9t\u00e9 de Davis", "DAVST"}},
             {"Antarctica/DumontDUrville", new String[] {"Heure de Dumont-d'Urville", "DDUT",
                                                         "Heure d'\u00e9t\u00e9 de Dumont-d'Urville", "DDUST"}},
+            {"Antarctica/Macquarie", new String[] {"Macquarie Island Time", "MIST",
+                                                   "Macquarie Island Summer Time", "MIST"}},
             {"Antarctica/Mawson", new String[] {"Heure de Mawson", "MAWT",
                                                 "Heure d'\u00e9t\u00e9 de Mawson", "MAWST"}},
             {"Antarctica/McMurdo", NZST},
@@ -752,6 +758,7 @@
             {"Pacific/Apia", WST_SAMOA},
             {"Pacific/Auckland", NZST},
             {"Pacific/Chatham", CHAST},
+            {"Pacific/Chuuk", CHUT},
             {"Pacific/Easter", EASTER},
             {"Pacific/Efate", new String[] {"Heure du Vanuatu", "VUT",
                                             "Heure d'\u00e9t\u00e9 du Vanuatu", "VUST"}},
@@ -790,8 +797,8 @@
             {"Pacific/Palau", new String[] {"Heure de Palaos", "PWT",
                                             "Heure d'\u00e9t\u00e9 de Palaos", "PWST"}},
             {"Pacific/Pitcairn", PITCAIRN},
-            {"Pacific/Ponape", new String[] {"Heure de Ponap\u00e9", "PONT",
-                                             "Heure d'\u00e9t\u00e9 de Ponap\u00e9", "PONST"}},
+            {"Pacific/Pohnpei", PONT},
+            {"Pacific/Ponape", PONT},
             {"Pacific/Port_Moresby", new String[] {"Heure de Papouasie-Nouvelle-Guin\u00e9e", "PGT",
                                                    "Heure d'\u00e9t\u00e9 de de Papouasie-Nouvelle-Guin\u00e9e", "PGST"}},
             {"Pacific/Rarotonga", new String[] {"Heure des \u00celes Cook", "CKT",
@@ -804,12 +811,12 @@
                                              "Heure d'\u00e9t\u00e9 de Kiribati", "GILST"}},
             {"Pacific/Tongatapu", new String[] {"Heure de Tonga", "TOT",
                                                 "Heure d'\u00e9t\u00e9 de Tonga", "TOST"}},
-            {"Pacific/Truk", TRUT},
+            {"Pacific/Truk", CHUT},
             {"Pacific/Wake", new String[] {"Heure de Wake", "WAKT",
                                            "Heure d'\u00e9t\u00e9 de Wake", "WAKST"}},
             {"Pacific/Wallis", new String[] {"Heure de Wallis et Futuna", "WFT",
                                              "Heure d'\u00e9t\u00e9 de Wallis et Futuna", "WFST"}},
-            {"Pacific/Yap", TRUT},
+            {"Pacific/Yap", CHUT},
             {"Poland", CET},
             {"PRC", CTT},
             {"PST8PDT", PST},
--- a/jdk/src/share/classes/sun/util/resources/TimeZoneNames_it.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/util/resources/TimeZoneNames_it.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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
@@ -75,6 +75,8 @@
                                      "Ora estiva dell'Europa centrale", "CEST"};
         String CHAST[] = new String[] {"Ora di Chatham standard", "CHAST",
                                        "Ora legale di Chatham", "CHADT"};
+        String CHUT[] = new String[] {"Chuuk Time", "CHUT",
+                                      "Chuuk Summer Time", "CHUST"};
         String CIT[] = new String[] {"Ora dell'Indonesia centrale", "CIT",
                                      "Ora estiva dell'Indonesia centrale", "CIST"};
         String CLT[] = new String[] {"Ora del Cile", "CLT",
@@ -153,6 +155,8 @@
                                           "Ora legale di Pitcairn", "PDT"};
         String PKT[] = new String[] {"Ora del Pakistan", "PKT",
                                      "Ora estiva del Pakistan", "PKST"};
+        String PONT[] = new String[] {"Pohnpei Time", "PONT",
+                                      "Pohnpei Summer Time", "PONST"};
         String PST[] = new String[] {"Ora solare della costa occidentale USA", "PST",
                                      "Ora legale della costa occidentale USA", "PDT"};
         String RST[] = new String[] {"Ora solare USA orientale", "EST",
@@ -169,8 +173,6 @@
                                           "Ora estiva orientale (Tasmania)", "EST"};
         String TMT[] = new String[] {"Ora del Turkmenistan", "TMT",
                                      "Ora estiva del Turkmenistan", "TMST"};
-        String TRUT[] = new String[] {"Ora di Truk", "TRUT",
-                                      "Ora estiva di Truk", "TRUST"};
         String ULAT[]= new String[] {"Ora di Ulaanbaatar", "ULAT",
                                      "Ora estiva di Ulaanbaatar", "ULAST"};
         String WART[] = new String[] {"Ora dell'Argentina occidentale", "WART",
@@ -309,6 +311,7 @@
             {"America/Atikokan", EST},
             {"America/Atka", HAST},
             {"America/Bahia", BRT},
+            {"America/Bahia_Banderas", CST},
             {"America/Barbados", AST},
             {"America/Belem", BRT},
             {"America/Belize", CST},
@@ -446,10 +449,13 @@
             {"America/Winnipeg", CST},
             {"America/Yakutat", AKST},
             {"America/Yellowknife", MST},
+            {"Antarctica/Casey", WST_AUS},
             {"Antarctica/Davis", new String[] {"Ora di Davis", "DAVT",
                                                "Ora estiva di Davis", "DAVST"}},
             {"Antarctica/DumontDUrville", new String[] {"Ora di Dumont-d'Urville", "DDUT",
                                                         "Ora estiva di Dumont-d'Urville", "DDUST"}},
+            {"Antarctica/Macquarie", new String[] {"Macquarie Island Time", "MIST",
+                                                   "Macquarie Island Summer Time", "MIST"}},
             {"Antarctica/Mawson", new String[] {"Ora di Mawson", "MAWT",
                                                 "Ora estiva di Mawson", "MAWST"}},
             {"Antarctica/McMurdo", NZST},
@@ -752,6 +758,7 @@
             {"Pacific/Apia", WST_SAMOA},
             {"Pacific/Auckland", NZST},
             {"Pacific/Chatham", CHAST},
+            {"Pacific/Chuuk", CHUT},
             {"Pacific/Easter", EASTER},
             {"Pacific/Efate", new String[] {"Ora di Vanuatu", "VUT",
                                             "Ora estiva di Vanuatu", "VUST"}},
@@ -790,8 +797,8 @@
             {"Pacific/Palau", new String[] {"Ora di Palau", "PWT",
                                             "Ora estiva di Palau", "PWST"}},
             {"Pacific/Pitcairn", PITCAIRN},
-            {"Pacific/Ponape", new String[] {"Ora di Ponape", "PONT",
-                                             "Ora estiva di Ponape", "PONST"}},
+            {"Pacific/Pohnpei", PONT},
+            {"Pacific/Ponape", PONT},
             {"Pacific/Port_Moresby", new String[] {"Ora di Papua Nuova Guinea", "PGT",
                                                    "Ora estiva di Papua Nuova Guinea", "PGST"}},
             {"Pacific/Rarotonga", new String[] {"Ora delle Isole Cook", "CKT",
@@ -804,12 +811,12 @@
                                              "Ora estiva delle Isole Gilbert", "GILST"}},
             {"Pacific/Tongatapu", new String[] {"Ora di Tonga", "TOT",
                                                 "Ora estiva di Tonga", "TOST"}},
-            {"Pacific/Truk", TRUT},
+            {"Pacific/Truk", CHUT},
             {"Pacific/Wake", new String[] {"Ora di Wake", "WAKT",
                                            "Ora estiva di Wake", "WAKST"}},
             {"Pacific/Wallis", new String[] {"Ora di Wallis e Futuna", "WFT",
                                              "Ora estiva di Wallis e Futuna", "WFST"}},
-            {"Pacific/Yap", TRUT},
+            {"Pacific/Yap", CHUT},
             {"Poland", CET},
             {"PRC", CTT},
             {"PST8PDT", PST},
--- a/jdk/src/share/classes/sun/util/resources/TimeZoneNames_ja.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/util/resources/TimeZoneNames_ja.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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
@@ -75,6 +75,8 @@
                                      "\u4e2d\u90e8\u30e8\u30fc\u30ed\u30c3\u30d1\u590f\u6642\u9593", "CEST"};
         String CHAST[] = new String[] {"\u30c1\u30e3\u30bf\u30e0\u6a19\u6e96\u6642", "CHAST",
                                        "\u30c1\u30e3\u30bf\u30e0\u590f\u6642\u9593", "CHADT"};
+        String CHUT[] = new String[] {"Chuuk Time", "CHUT",
+                                      "Chuuk Summer Time", "CHUST"};
         String CIT[] = new String[] {"\u4e2d\u592e\u30a4\u30f3\u30c9\u30cd\u30b7\u30a2\u6642\u9593", "CIT",
                                      "\u4e2d\u592e\u30a4\u30f3\u30c9\u30cd\u30b7\u30a2\u590f\u6642\u9593", "CIST"};
         String CLT[] = new String[] {"\u30c1\u30ea\u6642\u9593", "CLT",
@@ -153,6 +155,8 @@
                                           "\u30d4\u30c8\u30b1\u30eb\u30f3\u5cf6\u590f\u6642\u9593", "PDT"};
         String PKT[] = new String[] {"\u30d1\u30ad\u30b9\u30bf\u30f3\u6642\u9593", "PKT",
                                      "\u30d1\u30ad\u30b9\u30bf\u30f3\u590f\u6642\u9593", "PKST"};
+        String PONT[] = new String[] {"Pohnpei Time", "PONT",
+                                      "Pohnpei Summer Time", "PONST"};
         String PST[] = new String[] {"\u592a\u5e73\u6d0b\u6a19\u6e96\u6642", "PST",
                                      "\u592a\u5e73\u6d0b\u590f\u6642\u9593", "PDT"};
         String RST[] = new String[] {"\u6771\u90e8\u6a19\u6e96\u6642", "EST",
@@ -169,8 +173,6 @@
                                           "\u6771\u90e8\u590f\u6642\u9593 (\u30bf\u30b9\u30de\u30cb\u30a2)", "EST"};
         String TMT[] = new String[] {"\u30c8\u30eb\u30af\u30e1\u30cb\u30b9\u30bf\u30f3\u6642\u9593", "TMT",
                                      "\u30c8\u30eb\u30af\u30e1\u30cb\u30b9\u30bf\u30f3\u590f\u6642\u9593", "TMST"};
-        String TRUT[] = new String[] {"\u30c8\u30e9\u30c3\u30af\u6642\u9593", "TRUT",
-                                      "\u30c8\u30e9\u30c3\u30af\u590f\u6642\u9593", "TRUST"};
         String ULAT[]= new String[] {"\u30a6\u30e9\u30fc\u30f3\u30d0\u30fc\u30c8\u30eb\u6642\u9593", "ULAT",
                                      "\u30a6\u30e9\u30fc\u30f3\u30d0\u30fc\u30c8\u30eb\u590f\u6642\u9593", "ULAST"};
         String WART[] = new String[] {"\u897f\u30a2\u30eb\u30bc\u30f3\u30c1\u30f3\u6642\u9593", "WART",
@@ -309,6 +311,7 @@
             {"America/Atikokan", EST},
             {"America/Atka", HAST},
             {"America/Bahia", BRT},
+            {"America/Bahia_Banderas", CST},
             {"America/Barbados", AST},
             {"America/Belem", BRT},
             {"America/Belize", CST},
@@ -446,10 +449,13 @@
             {"America/Winnipeg", CST},
             {"America/Yakutat", AKST},
             {"America/Yellowknife", MST},
+            {"Antarctica/Casey", WST_AUS},
             {"Antarctica/Davis", new String[] {"\u30c7\u30a4\u30d3\u30b9\u6642\u9593", "DAVT",
                                                "\u30c7\u30a4\u30d3\u30b9\u590f\u6642\u9593", "DAVST"}},
             {"Antarctica/DumontDUrville", new String[] {"\u30c7\u30e5\u30e2\u30f3\u30c7\u30e5\u30eb\u30f4\u30a3\u30eb\u6642\u9593", "DDUT",
                                                         "\u30c7\u30e5\u30e2\u30f3\u30c7\u30e5\u30eb\u30f4\u30a3\u30eb\u590f\u6642\u9593", "DDUST"}},
+            {"Antarctica/Macquarie", new String[] {"Macquarie Island Time", "MIST",
+                                                   "Macquarie Island Summer Time", "MIST"}},
             {"Antarctica/Mawson", new String[] {"\u30e2\u30fc\u30bd\u30f3\u6642\u9593", "MAWT",
                                                 "\u30e2\u30fc\u30bd\u30f3\u590f\u6642\u9593", "MAWST"}},
             {"Antarctica/McMurdo", NZST},
@@ -752,6 +758,7 @@
             {"Pacific/Apia", WST_SAMOA},
             {"Pacific/Auckland", NZST},
             {"Pacific/Chatham", CHAST},
+            {"Pacific/Chuuk", CHUT},
             {"Pacific/Easter", EASTER},
             {"Pacific/Efate", new String[] {"\u30d0\u30cc\u30a2\u30c4\u6642\u9593", "VUT",
                                             "\u30d0\u30cc\u30a2\u30c4\u590f\u6642\u9593", "VUST"}},
@@ -790,8 +797,8 @@
             {"Pacific/Palau", new String[] {"\u30d1\u30e9\u30aa\u6642\u9593", "PWT",
                                             "\u30d1\u30e9\u30aa\u590f\u6642\u9593", "PWST"}},
             {"Pacific/Pitcairn", PITCAIRN},
-            {"Pacific/Ponape", new String[] {"\u30dd\u30ca\u30da\u6642\u9593", "PONT",
-                                             "\u30dd\u30ca\u30da\u590f\u6642\u9593", "PONST"}},
+            {"Pacific/Pohnpei", PONT},
+            {"Pacific/Ponape", PONT},
             {"Pacific/Port_Moresby", new String[] {"\u30d1\u30d7\u30a2\u30cb\u30e5\u30fc\u30ae\u30cb\u30a2\u6642\u9593", "PGT",
                                                    "\u30d1\u30d7\u30a2\u30cb\u30e5\u30fc\u30ae\u30cb\u30a2\u590f\u6642\u9593", "PGST"}},
             {"Pacific/Rarotonga", new String[] {"\u30af\u30c3\u30af\u8af8\u5cf6\u6642\u9593", "CKT",
@@ -804,12 +811,12 @@
                                              "\u30ae\u30eb\u30d0\u30fc\u30c8\u8af8\u5cf6\u590f\u6642\u9593", "GILST"}},
             {"Pacific/Tongatapu", new String[] {"\u30c8\u30f3\u30ac\u6642\u9593", "TOT",
                                                 "\u30c8\u30f3\u30ac\u590f\u6642\u9593", "TOST"}},
-            {"Pacific/Truk", TRUT},
+            {"Pacific/Truk", CHUT},
             {"Pacific/Wake", new String[] {"\u30a6\u30a7\u30fc\u30af\u6642\u9593", "WAKT",
                                            "\u30a6\u30a7\u30fc\u30af\u590f\u6642\u9593", "WAKST"}},
             {"Pacific/Wallis", new String[] {"\u30ef\u30ea\u30b9\u53ca\u3073\u30d5\u30c4\u30ca\u6642\u9593", "WFT",
                                              "\u30ef\u30ea\u30b9\u53ca\u3073\u30d5\u30c4\u30ca\u590f\u6642\u9593", "WFST"}},
-            {"Pacific/Yap", TRUT},
+            {"Pacific/Yap", CHUT},
             {"Poland", CET},
             {"PRC", CTT},
             {"PST8PDT", PST},
--- a/jdk/src/share/classes/sun/util/resources/TimeZoneNames_ko.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/util/resources/TimeZoneNames_ko.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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
@@ -75,6 +75,8 @@
                                      "\uc911\uc559 \uc720\ub7fd \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "CEST"};
         String CHAST[] = new String[] {"Chatham \ud45c\uc900\uc2dc", "CHAST",
                                        "Chatham \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "CHADT"};
+        String CHUT[] = new String[] {"Chuuk Time", "CHUT",
+                                      "Chuuk Summer Time", "CHUST"};
         String CIT[] = new String[] {"\uc911\uc559 \uc778\ub3c4\ub124\uc2dc\uc544 \uc2dc\uac04", "CIT",
                                      "\uc911\uc559 \uc778\ub3c4\ub124\uc2dc\uc544 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "CIST"};
         String CLT[] = new String[] {"\uce60\ub808 \uc2dc\uac04", "CLT",
@@ -153,6 +155,8 @@
                                           "Pitcairn \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "PDT"};
         String PKT[] = new String[] {"\ud30c\ud0a4\uc2a4\ud0c4 \uc2dc\uac04", "PKT",
                                      "\ud30c\ud0a4\uc2a4\ud0c4 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "PKST"};
+        String PONT[] = new String[] {"Pohnpei Time", "PONT",
+                                      "Pohnpei Summer Time", "PONST"};
         String PST[] = new String[] {"\ud0dc\ud3c9\uc591 \ud45c\uc900\uc2dc", "PST",
                                      "\ud0dc\ud3c9\uc591 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "PDT"};
         String RST[] = new String[] {"\ub3d9\ubd80 \ud45c\uc900\uc2dc", "EST",
@@ -169,8 +173,6 @@
                                           "\ub3d9\ubd80 \uc77c\uad11\uc808\uc57d\uc2dc\uac04(\ud0dc\uc988\uba54\uc774\ub2c8\uc544)", "EST"};
         String TMT[] = new String[] {"\ud22c\ub974\ud06c\uba54\ub2c8\uc2a4\ud0c4 \uc2dc\uac04", "TMT",
                                      "\ud22c\ub974\ud06c\uba54\ub2c8\uc2a4\ud0c4 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "TMST"};
-        String TRUT[] = new String[] {"\ud2b8\ub8e8\ud06c \uc2dc\uac04", "TRUT",
-                                      "\ud2b8\ub8e8\ud06c \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "TRUST"};
         String ULAT[]= new String[] {"\uc6b8\ub780\ubc14\ud0c0\ub974 \uc2dc\uac04", "ULAT",
                                      "\uc6b8\ub780\ubc14\ud0c0\ub974 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "ULAST"};
         String WART[] = new String[] {"\uc11c\ubd80 \uc544\ub974\ud5e8\ud2f0\ub098 \uc2dc\uac04", "WART",
@@ -309,6 +311,7 @@
             {"America/Atikokan", EST},
             {"America/Atka", HAST},
             {"America/Bahia", BRT},
+            {"America/Bahia_Banderas", CST},
             {"America/Barbados", AST},
             {"America/Belem", BRT},
             {"America/Belize", CST},
@@ -446,10 +449,13 @@
             {"America/Winnipeg", CST},
             {"America/Yakutat", AKST},
             {"America/Yellowknife", MST},
+            {"Antarctica/Casey", WST_AUS},
             {"Antarctica/Davis", new String[] {"Davis \uc2dc\uac04", "DAVT",
                                                "Davis \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "DAVST"}},
             {"Antarctica/DumontDUrville", new String[] {"\ub4a4\ubabd \ub4a4\ub974\ube4c \uc2dc\uac04", "DDUT",
                                                         "\ub4a4\ubabd \ub4a4\ub974\ube4c \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "DDUST"}},
+            {"Antarctica/Macquarie", new String[] {"Macquarie Island Time", "MIST",
+                                                   "Macquarie Island Summer Time", "MIST"}},
             {"Antarctica/Mawson", new String[] {"\ubaa8\uc2a8 \uc2dc\uac04", "MAWT",
                                                 "\ubaa8\uc2a8 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "MAWST"}},
             {"Antarctica/McMurdo", NZST},
@@ -752,6 +758,7 @@
             {"Pacific/Apia", WST_SAMOA},
             {"Pacific/Auckland", NZST},
             {"Pacific/Chatham", CHAST},
+            {"Pacific/Chuuk", CHUT},
             {"Pacific/Easter", EASTER},
             {"Pacific/Efate", new String[] {"\ube44\ub204\uc544\ud22c \uc2dc\uac04", "VUT",
                                             "\ubc14\ub204\uc544\ud22c \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "VUST"}},
@@ -790,8 +797,8 @@
             {"Pacific/Palau", new String[] {"\ud314\ub77c\uc6b0 \uc2dc\uac04", "PWT",
                                             "\ud314\ub77c\uc6b0 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "PWST"}},
             {"Pacific/Pitcairn", PITCAIRN},
-            {"Pacific/Ponape", new String[] {"\ud3ec\ub098\ud504 \uc2dc\uac04", "PONT",
-                                            "\ud3ec\ub098\ud504 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "PONST"}},
+            {"Pacific/Pohnpei", PONT},
+            {"Pacific/Ponape", PONT},
             {"Pacific/Port_Moresby", new String[] {"\ud30c\ud478\uc544\ub274\uae30\ub2c8 \uc2dc\uac04", "PGT",
                                                   "\ud30c\ud478\uc544\ub274\uae30\ub2c8 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "PGST"}},
             {"Pacific/Rarotonga", new String[] {"\ucfe0\ud06c \uad70\ub3c4 \uc2dc\uac04", "CKT",
@@ -804,12 +811,12 @@
                                              "\uae38\ubc84\ud2b8 \uad70\ub3c4 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "GILST"}},
             {"Pacific/Tongatapu", new String[] {"\ud1b5\uac00 \uc2dc\uac04", "TOT",
                                                 "\ud1b5\uac00 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "TOST"}},
-            {"Pacific/Truk", TRUT},
+            {"Pacific/Truk", CHUT},
             {"Pacific/Wake", new String[] {"\uc6e8\uc774\ud06c \uc2dc\uac04", "WAKT",
                                            "\uc6e8\uc774\ud06c \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "WAKST"}},
             {"Pacific/Wallis", new String[] {"\uc6d4\ub9ac\uc2a4 \ud6c4\ud22c\ub098 \uc2dc\uac04", "WFT",
                                              "\uc6d4\ub9ac\uc2a4 \ud6c4\ud2b8\ub098 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "WFST"}},
-            {"Pacific/Yap", TRUT},
+            {"Pacific/Yap", CHUT},
             {"Poland", CET},
             {"PRC", CTT},
             {"PST8PDT", PST},
--- a/jdk/src/share/classes/sun/util/resources/TimeZoneNames_sv.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/util/resources/TimeZoneNames_sv.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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
@@ -75,6 +75,8 @@
                                      "Centraleuropeisk sommartid", "CEST"};
         String CHAST[] = new String[] {"Chatham, normaltid", "CHAST",
                                        "Chatham, sommartid", "CHADT"};
+        String CHUT[] = new String[] {"Chuuk Time", "CHUT",
+                                      "Chuuk Summer Time", "CHUST"};
         String CIT[] = new String[] {"Centralindonesisk tid", "CIT",
                                      "Centralindonesisk sommartid", "CIST"};
         String CLT[] = new String[] {"Chile, normaltid", "CLT",
@@ -153,6 +155,8 @@
                                           "Pitcairn, sommartid", "PDT"};
         String PKT[] = new String[] {"Pakistan, normaltid", "PKT",
                                      "Pakistan, sommartid", "PKST"};
+        String PONT[] = new String[] {"Pohnpei Time", "PONT",
+                                      "Pohnpei Summer Time", "PONST"};
         String PST[] = new String[] {"Stilla havet, normaltid", "PST",
                                      "Stilla havet, sommartid", "PDT"};
         String RST[] = new String[] {"Eastern, normaltid", "EST",
@@ -169,8 +173,6 @@
                                           "Eastern, sommartid (Tasmanien)", "EST"};
         String TMT[] = new String[] {"Turkmenistan, normaltid", "TMT",
                                      "Turkmenistan, sommartid", "TMST"};
-        String TRUT[] = new String[] {"Truk, normaltid", "TRUT",
-                                      "Truk, sommartid", "TRUST"};
         String ULAT[]= new String[] {"Ulaanbaatar, normaltid", "ULAT",
                                      "Ulaanbaatar, sommartid", "ULAST"};
         String WART[] = new String[] {"V\u00e4stargentina, normaltid", "WART",
@@ -309,6 +311,7 @@
             {"America/Atikokan", EST},
             {"America/Atka", HAST},
             {"America/Bahia", BRT},
+            {"America/Bahia_Banderas", CST},
             {"America/Barbados", AST},
             {"America/Belem", BRT},
             {"America/Belize", CST},
@@ -446,10 +449,13 @@
             {"America/Winnipeg", CST},
             {"America/Yakutat", AKST},
             {"America/Yellowknife", MST},
+            {"Antarctica/Casey", WST_AUS},
             {"Antarctica/Davis", new String[] {"Davis, normaltid", "DAVT",
                                                "Davis, sommartid", "DAVST"}},
             {"Antarctica/DumontDUrville", new String[] {"Dumont-d'Urville, normaltid", "DDUT",
                                                         "Dumont-d'Urville, sommartid", "DDUST"}},
+            {"Antarctica/Macquarie", new String[] {"Macquarie Island Time", "MIST",
+                                                   "Macquarie Island Summer Time", "MIST"}},
             {"Antarctica/Mawson", new String[] {"Mawson, normaltid", "MAWT",
                                                 "Mawson, sommartid", "MAWST"}},
             {"Antarctica/McMurdo", NZST},
@@ -752,6 +758,7 @@
             {"Pacific/Apia", WST_SAMOA},
             {"Pacific/Auckland", NZST},
             {"Pacific/Chatham", CHAST},
+            {"Pacific/Chuuk", CHUT},
             {"Pacific/Easter", EASTER},
             {"Pacific/Efate", new String[] {"Vanuatu, normaltid", "VUT",
                                             "Vanuatu, sommartid", "VUST"}},
@@ -790,8 +797,8 @@
             {"Pacific/Palau", new String[] {"Palau, normaltid", "PWT",
                                             "Palau, sommartid", "PWST"}},
             {"Pacific/Pitcairn", PITCAIRN},
-            {"Pacific/Ponape", new String[] {"Ponape, normaltid", "PONT",
-                                             "Ponape, sommartid", "PONST"}},
+            {"Pacific/Pohnpei", PONT},
+            {"Pacific/Ponape", PONT},
             {"Pacific/Port_Moresby", new String[] {"Papua Nya Guinea, normaltid", "PGT",
                                                    "Papua Nya Guinea, sommartid", "PGST"}},
             {"Pacific/Rarotonga", new String[] {"Cook\u00f6arna, normaltid", "CKT",
@@ -804,12 +811,12 @@
                                              "Gilbert\u00f6arna, sommartid", "GILST"}},
             {"Pacific/Tongatapu", new String[] {"Tonga, normaltid", "TOT",
                                                 "Tonga, sommartid", "TOST"}},
-            {"Pacific/Truk", TRUT},
+            {"Pacific/Truk", CHUT},
             {"Pacific/Wake", new String[] {"Wake, normaltid", "WAKT",
                                            "Wake, sommartid", "WAKST"}},
             {"Pacific/Wallis", new String[] {"Wallis & Futuna, normaltid", "WFT",
                                              "Wallis & Futuna, sommartid", "WFST"}},
-            {"Pacific/Yap", TRUT},
+            {"Pacific/Yap", CHUT},
             {"Poland", CET},
             {"PRC", CTT},
             {"PST8PDT", PST},
--- a/jdk/src/share/classes/sun/util/resources/TimeZoneNames_zh_CN.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/util/resources/TimeZoneNames_zh_CN.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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
@@ -75,6 +75,8 @@
                                      "\u4e2d\u6b27\u590f\u4ee4\u65f6", "CEST"};
         String CHAST[] = new String[] {"\u67e5\u8428\u59c6\u6807\u51c6\u65f6\u95f4", "CHAST",
                                        "\u67e5\u8428\u59c6\u590f\u4ee4\u65f6", "CHADT"};
+        String CHUT[] = new String[] {"Chuuk Time", "CHUT",
+                                      "Chuuk Summer Time", "CHUST"};
         String CIT[] = new String[] {"\u4e2d\u90e8\u5370\u5ea6\u5c3c\u897f\u4e9a\u65f6\u95f4", "CIT",
                                      "\u4e2d\u90e8\u5370\u5ea6\u5c3c\u897f\u4e9a\u590f\u4ee4\u65f6", "CIST"};
         String CLT[] = new String[] {"\u667a\u5229\u65f6\u95f4", "CLT",
@@ -153,6 +155,8 @@
                                           "\u76ae\u7279\u5eb7\u5c9b\u590f\u4ee4\u65f6", "PDT"};
         String PKT[] = new String[] {"\u5df4\u57fa\u65af\u5766\u65f6\u95f4", "PKT",
                                      "\u5df4\u57fa\u65af\u5766\u590f\u4ee4\u65f6", "PKST"};
+        String PONT[] = new String[] {"Pohnpei Time", "PONT",
+                                      "Pohnpei Summer Time", "PONST"};
         String PST[] = new String[] {"\u592a\u5e73\u6d0b\u6807\u51c6\u65f6\u95f4", "PST",
                                      "\u592a\u5e73\u6d0b\u590f\u4ee4\u65f6", "PDT"};
         String RST[] = new String[] {"\u4e1c\u90e8\u6807\u51c6\u65f6\u95f4", "EST",
@@ -169,8 +173,6 @@
                                           "\u4e1c\u90e8\u590f\u4ee4\u65f6\uff08\u5854\u65af\u9a6c\u5c3c\u4e9a\uff09", "EST"};
         String TMT[] = new String[] {"\u571f\u5e93\u66fc\u65f6\u95f4", "TMT",
                                      "\u571f\u5e93\u66fc\u590f\u4ee4\u65f6", "TMST"};
-        String TRUT[] = new String[] {"\u7279\u9c81\u514b\u65f6\u95f4", "TRUT",
-                                      "\u7279\u9c81\u514b\u590f\u4ee4\u65f6", "TRUST"};
         String ULAT[]= new String[] {"\u5e93\u4f26\u65f6\u95f4", "ULAT",
                                      "\u5e93\u4f26\u590f\u4ee4\u65f6", "ULAST"};
         String WART[] = new String[] {"\u897f\u963f\u6839\u5ef7\u65f6\u95f4", "WART",
@@ -309,6 +311,7 @@
             {"America/Atikokan", EST},
             {"America/Atka", HAST},
             {"America/Bahia", BRT},
+            {"America/Bahia_Banderas", CST},
             {"America/Barbados", AST},
             {"America/Belem", BRT},
             {"America/Belize", CST},
@@ -446,10 +449,13 @@
             {"America/Winnipeg", CST},
             {"America/Yakutat", AKST},
             {"America/Yellowknife", MST},
+            {"Antarctica/Casey", WST_AUS},
             {"Antarctica/Davis", new String[] {"\u6234\u7ef4\u65af\u65f6\u95f4", "DAVT",
                                                "\u6234\u7ef4\u65af\u590f\u4ee4\u65f6", "DAVST"}},
             {"Antarctica/DumontDUrville", new String[] {"Dumont-d'Urville \u65f6\u95f4", "DDUT",
                                                         "Dumont-d'Urville \u590f\u4ee4\u65f6", "DDUST"}},
+            {"Antarctica/Macquarie", new String[] {"Macquarie Island Time", "MIST",
+                                                   "Macquarie Island Summer Time", "MIST"}},
             {"Antarctica/Mawson", new String[] {"\u83ab\u68ee\u65f6\u95f4", "MAWT",
                                                 "\u83ab\u68ee\u590f\u4ee4\u65f6", "MAWST"}},
             {"Antarctica/McMurdo", NZST},
@@ -752,6 +758,7 @@
             {"Pacific/Apia", WST_SAMOA},
             {"Pacific/Auckland", NZST},
             {"Pacific/Chatham", CHAST},
+            {"Pacific/Chuuk", CHUT},
             {"Pacific/Easter", EASTER},
             {"Pacific/Efate", new String[] {"\u74e6\u5974\u963f\u56fe\u65f6\u95f4", "VUT",
                                             "\u74e6\u5974\u963f\u56fe\u590f\u4ee4\u65f6", "VUST"}},
@@ -790,8 +797,8 @@
             {"Pacific/Palau", new String[] {"\u5e1b\u7409\u65f6\u95f4", "PWT",
                                             "\u5e1b\u7409\u590f\u4ee4\u65f6", "PWST"}},
             {"Pacific/Pitcairn", PITCAIRN},
-            {"Pacific/Ponape", new String[] {"Ponape \u65f6\u95f4", "PONT",
-                                             "Ponape \u590f\u4ee4\u65f6", "PONST"}},
+            {"Pacific/Pohnpei", PONT},
+            {"Pacific/Ponape", PONT},
             {"Pacific/Port_Moresby", new String[] {"\u5df4\u5e03\u4e9a\u65b0\u51e0\u5185\u4e9a\u65f6\u95f4", "PGT",
                                                    "\u5df4\u5e03\u4e9a\u65b0\u51e0\u5185\u4e9a\u590f\u4ee4\u65f6", "PGST"}},
             {"Pacific/Rarotonga", new String[] {"\u5e93\u514b\u7fa4\u5c9b\u65f6\u95f4", "CKT",
@@ -804,12 +811,12 @@
                                              "\u5409\u4f2f\u7279\u7fa4\u5c9b\u590f\u4ee4\u65f6", "GILST"}},
             {"Pacific/Tongatapu", new String[] {"\u4e1c\u52a0\u65f6\u95f4", "TOT",
                                                 "\u4e1c\u52a0\u590f\u4ee4\u65f6", "TOST"}},
-            {"Pacific/Truk", TRUT},
+            {"Pacific/Truk", CHUT},
             {"Pacific/Wake", new String[] {"\u5a01\u514b\u65f6\u95f4", "WAKT",
                                            "\u5a01\u514b\u590f\u4ee4\u65f6", "WAKST"}},
             {"Pacific/Wallis", new String[] {"\u74e6\u5229\u65af\u53ca\u798f\u675c\u7eb3\u7fa4\u5c9b\u65f6\u95f4", "WFT",
                                              "\u74e6\u5229\u65af\u53ca\u798f\u675c\u7eb3\u7fa4\u5c9b\u590f\u4ee4\u65f6", "WFST"}},
-            {"Pacific/Yap", TRUT},
+            {"Pacific/Yap", CHUT},
             {"Poland", CET},
             {"PRC", CTT},
             {"PST8PDT", PST},
--- a/jdk/src/share/classes/sun/util/resources/TimeZoneNames_zh_TW.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/classes/sun/util/resources/TimeZoneNames_zh_TW.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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
@@ -75,6 +75,8 @@
                                      "\u4e2d\u6b50\u590f\u4ee4\u6642\u9593", "CEST"};
         String CHAST[] = new String[] {"\u67e5\u5766\u6a19\u6e96\u6642\u9593", "CHAST",
                                        "\u67e5\u5766\u65e5\u5149\u7bc0\u7d04\u6642\u9593", "CHADT"};
+        String CHUT[] = new String[] {"Chuuk Time", "CHUT",
+                                      "Chuuk Summer Time", "CHUST"};
         String CIT[] = new String[] {"\u4e2d\u5370\u5ea6\u5c3c\u897f\u4e9e\u6642\u9593", "CIT",
                                      "\u4e2d\u5370\u5ea6\u5c3c\u897f\u4e9e\u590f\u4ee4\u6642\u9593", "CIST"};
         String CLT[] = new String[] {"\u667a\u5229\u6642\u9593", "CLT",
@@ -153,6 +155,8 @@
                                         "\u76ae\u7279\u5eb7\u65e5\u5149\u7bc0\u7d04\u6642\u9593", "PDT"};
         String PKT[] = new String[] {"\u5df4\u57fa\u65af\u5766\u6642\u9593", "PKT",
                                      "\u5df4\u57fa\u65af\u5766\u590f\u4ee4\u6642\u9593", "PKST"};
+        String PONT[] = new String[] {"Pohnpei Time", "PONT",
+                                      "Pohnpei Summer Time", "PONST"};
         String PST[] = new String[] {"\u592a\u5e73\u6d0b\u6a19\u6e96\u6642\u9593", "PST",
                                      "\u592a\u5e73\u6d0b\u65e5\u5149\u7bc0\u7d04\u6642\u9593", "PDT"};
         String RST[] = new String[] {"\u6771\u65b9\u6a19\u6e96\u6642\u9593", "EST",
@@ -169,8 +173,6 @@
                                           "\u6771\u90e8\u590f\u4ee4\u6642\u9593 (\u5854\u65af\u6885\u5c3c\u4e9e\u5cf6)", "EST"};
         String TMT[] = new String[] {"\u571f\u5eab\u66fc\u6642\u9593", "TMT",
                                      "\u571f\u5eab\u66fc\u590f\u4ee4\u6642\u9593", "TMST"};
-        String TRUT[] = new String[] {"\u7279\u9b6f\u514b\u6642\u9593", "TRUT",
-                                      "\u7279\u9b6f\u514b\u590f\u4ee4\u6642\u9593", "TRUST"};
         String ULAT[]= new String[] {"\u5eab\u502b\u6642\u9593", "ULAT",
                                      "\u5eab\u502b\u590f\u4ee4\u6642\u9593", "ULAST"};
           String WART[] = new String[] {"\u897f\u963f\u6839\u5ef7\u6642\u9593", "WART",
@@ -309,6 +311,7 @@
             {"America/Atikokan", EST},
             {"America/Atka", HAST},
             {"America/Bahia", BRT},
+            {"America/Bahia_Banderas", CST},
             {"America/Barbados", AST},
             {"America/Belem", BRT},
             {"America/Belize", CST},
@@ -446,10 +449,13 @@
             {"America/Winnipeg", CST},
             {"America/Yakutat", AKST},
             {"America/Yellowknife", MST},
+            {"Antarctica/Casey", WST_AUS},
             {"Antarctica/Davis", new String[] {"\u81fa\u7dad\u65af\u6642\u9593", "DAVT",
                                                "\u81fa\u7dad\u65af\u590f\u4ee4\u6642\u9593", "DAVST"}},
             {"Antarctica/DumontDUrville", new String[] {"Dumont-d'Urville \u6642\u9593", "DDUT",
                                                         "Dumont-d'Urville \u590f\u4ee4\u6642\u9593", "DDUST"}},
+            {"Antarctica/Macquarie", new String[] {"Macquarie Island Time", "MIST",
+                                                   "Macquarie Island Summer Time", "MIST"}},
             {"Antarctica/Mawson", new String[] {"\u83ab\u68ee\u6642\u9593", "MAWT",
                                                 "\u83ab\u68ee\u590f\u4ee4\u6642\u9593", "MAWST"}},
             {"Antarctica/McMurdo", NZST},
@@ -753,6 +759,7 @@
             {"Pacific/Apia", WST_SAMOA},
             {"Pacific/Auckland", NZST},
             {"Pacific/Chatham", CHAST},
+            {"Pacific/Chuuk", CHUT},
             {"Pacific/Easter", EASTER},
             {"Pacific/Efate", new String[] {"\u74e6\u5974\u963f\u5716\u6642\u9593", "VUT",
                                             "\u74e6\u5974\u963f\u5716\u590f\u4ee4\u6642\u9593", "VUST"}},
@@ -791,8 +798,8 @@
             {"Pacific/Palau", new String[] {"\u5e1b\u7409\u6642\u9593", "PWT",
                                             "\u5e1b\u7409\u590f\u4ee4\u6642\u9593", "PWST"}},
             {"Pacific/Pitcairn", PITCAIRN},
-            {"Pacific/Ponape", new String[] {"Ponape \u6642\u9593", "PONT",
-                                             "Ponape \u590f\u4ee4\u6642\u9593", "PONST"}},
+            {"Pacific/Pohnpei", PONT},
+            {"Pacific/Ponape", PONT},
             {"Pacific/Port_Moresby", new String[] {"\u5df4\u5e03\u4e9e\u65b0\u5e7e\u5167\u4e9e\u6642\u9593", "PGT",
                                                    "\u5df4\u5e03\u4e9e\u65b0\u5e7e\u5167\u4e9e\u590f\u4ee4\u6642\u9593", "PGST"}},
             {"Pacific/Rarotonga", new String[] {"\u5eab\u514b\u7fa4\u5cf6\u6642\u9593", "CKT",
@@ -805,12 +812,12 @@
                                              "\u5409\u4f2f\u7279\u7fa4\u5cf6\u590f\u4ee4\u6642\u9593", "GILST"}},
             {"Pacific/Tongatapu", new String[] {"\u6771\u52a0\u6642\u9593", "TOT",
                                                 "\u6771\u52a0\u590f\u4ee4\u6642\u9593", "TOST"}},
-            {"Pacific/Truk", TRUT},
+            {"Pacific/Truk", CHUT},
             {"Pacific/Wake", new String[] {"\u5a01\u514b\u6642\u9593", "WAKT",
                                            "\u5a01\u514b\u590f\u4ee4\u6642\u9593", "WAKST"}},
             {"Pacific/Wallis", new String[] {"\u74e6\u5229\u65af\u53ca\u798f\u675c\u7d0d\u7fa4\u5cf6\u6642\u9593", "WFT",
                                              "\u74e6\u5229\u65af\u53ca\u798f\u675c\u7d0d\u7fa4\u5cf6\u590f\u4ee4\u6642\u9593", "WFST"}},
-            {"Pacific/Yap", TRUT},
+            {"Pacific/Yap", CHUT},
             {"Poland", CET},
             {"PRC", CTT},
             {"PST8PDT", PST},
--- a/jdk/src/share/lib/security/java.security-solaris	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/lib/security/java.security-solaris	Wed Jul 05 17:21:32 2017 +0200
@@ -260,3 +260,30 @@
 # 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
+
--- a/jdk/src/share/lib/security/java.security-windows	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/lib/security/java.security-windows	Wed Jul 05 17:21:32 2017 +0200
@@ -260,3 +260,30 @@
 # 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
+
--- a/jdk/src/share/native/common/check_code.c	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/share/native/common/check_code.c	Wed Jul 05 17:21:32 2017 +0200
@@ -2730,7 +2730,10 @@
                                                                 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 @@
                        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 @@
                 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);
--- a/jdk/src/solaris/classes/java/io/UnixFileSystem.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/solaris/classes/java/io/UnixFileSystem.java	Wed Jul 05 17:21:32 2017 +0200
@@ -187,7 +187,6 @@
                     }
                 }
             }
-            assert canonicalize0(path).equals(res) || path.startsWith(javaHome);
             return res;
         }
     }
--- a/jdk/src/solaris/classes/sun/nio/fs/UnixPath.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/solaris/classes/sun/nio/fs/UnixPath.java	Wed Jul 05 17:21:32 2017 +0200
@@ -1141,6 +1141,13 @@
             }
             result = result.resolve(element);
         }
+
+        // check file exists (without following links)
+        try {
+            UnixFileAttributes.get(result, false);
+        } catch (UnixException x) {
+            x.rethrowAsIOException(result);
+        }
         return result;
     }
 
--- a/jdk/src/solaris/native/java/net/PlainDatagramSocketImpl.c	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/solaris/native/java/net/PlainDatagramSocketImpl.c	Wed Jul 05 17:21:32 2017 +0200
@@ -1052,30 +1052,38 @@
 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 @@
      * 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));
--- a/jdk/src/solaris/native/java/net/PlainSocketImpl.c	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/solaris/native/java/net/PlainSocketImpl.c	Wed Jul 05 17:21:32 2017 +0200
@@ -181,6 +181,12 @@
                                            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 @@
         (*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 @@
     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);
 }
 
 /*
--- a/jdk/src/solaris/native/sun/nio/ch/Net.c	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/solaris/native/sun/nio/ch/Net.c	Wed Jul 05 17:21:32 2017 +0200
@@ -170,6 +170,22 @@
     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,
--- a/jdk/src/windows/classes/java/io/Win32FileSystem.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/src/windows/classes/java/io/Win32FileSystem.java	Wed Jul 05 17:21:32 2017 +0200
@@ -424,7 +424,6 @@
                     }
                 }
             }
-            assert canonicalize0(path).equalsIgnoreCase(res);
             return res;
         }
     }
--- a/jdk/test/java/lang/ClassLoader/deadlock/TestCrossDelegate.sh	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/test/java/lang/ClassLoader/deadlock/TestCrossDelegate.sh	Wed Jul 05 17:21:32 2017 +0200
@@ -55,7 +55,7 @@
   Linux )
     FS="/"
     ;;
-  Windows* )
+  Windows* | CYGWIN* )
     FS="\\"
     ;;
 esac
--- a/jdk/test/java/net/URI/Test.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/test/java/net/URI/Test.java	Wed Jul 05 17:21:32 2017 +0200
@@ -1536,6 +1536,7 @@
         serial();
         urls();
         npes();
+        bugs();
     }
 
 
@@ -1572,6 +1573,19 @@
     }
 
 
+    // 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) {
 
--- a/jdk/test/java/nio/file/Path/Misc.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/test/java/nio/file/Path/Misc.java	Wed Jul 05 17:21:32 2017 +0200
@@ -261,6 +261,21 @@
         assertTrue(file.toRealPath(true).isSameFile(file.toRealPath(false)));
 
         /**
+         * Test: toRealPath should fail if file does not exist
+         */
+        Path doesNotExist = dir.resolve("DoesNotExist");
+        try {
+            doesNotExist.toRealPath(true);
+            throw new RuntimeException("IOException expected");
+        } catch (IOException expected) {
+        }
+        try {
+            doesNotExist.toRealPath(false);
+            throw new RuntimeException("IOException expected");
+        } catch (IOException expected) {
+        }
+
+        /**
          * Test: toRealPath(true) should resolve links
          */
         if (supportsLinks) {
--- a/jdk/test/sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.sh	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/test/sun/jvmstat/monitor/MonitoredVm/MonitorVmStartTerminate.sh	Wed Jul 05 17:21:32 2017 +0200
@@ -23,6 +23,7 @@
 
 #
 # @test
+# @ignore until 6543856 is fixed
 # @bug 4990825
 # @summary attach to external but local JVM processes
 # @library ../../testlibrary
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/net/www/protocol/file/DirPermissionDenied.java	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+import java.net.URL;
+import java.net.URLConnection;
+import java.io.IOException;
+
+public class DirPermissionDenied {
+    public static void main(String[] args) throws Exception {
+        URL url = new URL("file:" + args[0]);
+
+        try {
+            URLConnection uc = url.openConnection();
+            uc.connect();
+        } catch (IOException e) {
+            // OK
+        } catch (Exception e) {
+            throw new RuntimeException("Failed " + e);
+        }
+
+        try {
+            URLConnection uc = url.openConnection();
+            uc.getInputStream();
+        } catch (IOException e) {
+            // OK
+        } catch (Exception e) {
+            throw new RuntimeException("Failed " + e);
+        }
+
+        try {
+            URLConnection uc = url.openConnection();
+            uc.getContentLengthLong();
+        } catch (IOException e) {
+            // OK
+        } catch (Exception e) {
+            throw new RuntimeException("Failed " + e);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/net/www/protocol/file/DirPermissionDenied.sh	Wed Jul 05 17:21:32 2017 +0200
@@ -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 6977851
+# @summary NPE from FileURLConnection.connect
+# @build DirPermissionDenied
+# @run shell DirPermissionDenied.sh
+
+TESTDIR="${TESTCLASSES}/DirPermissionDeniedDirectory"
+echo ${TESTDIR}
+
+rm -rf ${TESTDIR}
+mkdir -p ${TESTDIR}
+chmod 333 ${TESTDIR}
+
+$TESTJAVA/bin/java -classpath $TESTCLASSES DirPermissionDenied ${TESTDIR}
+result=$?
+rm -rf ${TESTDIR}
+exit $result
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/BadKdcDefaultValue.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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");
+        }
+    }
+}
+
--- a/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/B6226610.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/B6226610.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
             }
             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<NetworkInterface> nics = NetworkInterface.getNetworkInterfaces();
-        while (nics.hasMoreElements()) {
-            NetworkInterface nic = nics.nextElement();
-            if (!nic.getName().equalsIgnoreCase(loNIC.getName())) {
-                Enumeration<InetAddress> 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 @@
        }
     }
 
+    void shutdown() {
+        try { ss.close(); } catch (IOException e) {}
+    }
+
     public void run()
     {
         try {
@@ -178,6 +142,15 @@
            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;
            }
 
--- a/jdk/test/tools/jar/JarEntryTime.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/test/tools/jar/JarEntryTime.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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 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 @@
         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);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/CommandLineTests.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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<File> 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<File>();
+        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<String> cmdsList = new ArrayList<String>();
+        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<String> cmdsList = new ArrayList<String>();
+        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<String> cmdsList = new ArrayList<String>();
+        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);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/Pack200Props.java	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,128 @@
+/*
+ * 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 6575373 6969063
+ * @summary verify default properties of the packer/unpacker and segment limit
+ * @compile -XDignore.symbol.file Utils.java Pack200Props.java
+ * @run main Pack200Props
+ * @author ksrini
+ */
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.jar.Pack200;
+import java.util.jar.Pack200.Packer;
+
+/*
+ * Run this against a large jar file, by default the packer should generate only
+ * one segment, parse the output of the packer to verify if this is indeed true.
+ */
+
+public class Pack200Props {
+
+    public static void main(String... args) {
+        verifyDefaults();
+        File out = new File("test" + Utils.PACK_FILE_EXT);
+        out.delete();
+        verifySegmentLimit(out);
+    }
+
+    static void verifySegmentLimit(File outFile) {
+        File sdkHome = Utils.JavaSDK;
+        File testJar = new File(new File(sdkHome, "lib"), "tools.jar");
+
+        System.out.println("using pack200: " + Utils.getPack200Cmd());
+
+        List<String> 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<String> outList = Utils.runExec(cmdsList);
+
+        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++;
+            }
+        }
+        if (count == 0) {
+            throw new RuntimeException("no segments or no output ????");
+        } else if (count > 1) {
+            throw new RuntimeException("multiple segments detected, expected 1");
+        }
+    }
+
+    private static void verifyDefaults() {
+        Map<String, String> expectedDefaults = new HashMap<>();
+        Packer p = Pack200.newPacker();
+        expectedDefaults.put("com.sun.java.util.jar.pack.default.timezone",
+                p.FALSE);
+        expectedDefaults.put("com.sun.java.util.jar.pack.disable.native",
+                p.FALSE);
+        expectedDefaults.put("com.sun.java.util.jar.pack.verbose", "0");
+        expectedDefaults.put(p.CLASS_ATTRIBUTE_PFX + "CompilationID", "RUH");
+        expectedDefaults.put(p.CLASS_ATTRIBUTE_PFX + "SourceID", "RUH");
+        expectedDefaults.put(p.CODE_ATTRIBUTE_PFX + "CharacterRangeTable",
+                "NH[PHPOHIIH]");
+        expectedDefaults.put(p.CODE_ATTRIBUTE_PFX + "CoverageTable",
+                "NH[PHHII]");
+        expectedDefaults.put(p.DEFLATE_HINT, p.KEEP);
+        expectedDefaults.put(p.EFFORT, "5");
+        expectedDefaults.put(p.KEEP_FILE_ORDER, p.TRUE);
+        expectedDefaults.put(p.MODIFICATION_TIME, p.KEEP);
+        expectedDefaults.put(p.SEGMENT_LIMIT, "-1");
+        expectedDefaults.put(p.UNKNOWN_ATTRIBUTE, p.PASS);
+
+        Map<String, String> props = p.properties();
+        int errors = 0;
+        for (String key : expectedDefaults.keySet()) {
+            String def = expectedDefaults.get(key);
+            String x = props.get(key);
+            if (x == null) {
+                System.out.println("Error: key not found:" + key);
+                errors++;
+            } else {
+                if (!def.equals(x)) {
+                    System.out.println("Error: key " + key
+                            + "\n  value expected: " + def
+                            + "\n  value obtained: " + x);
+                    errors++;
+                }
+            }
+        }
+        if (errors > 0) {
+            throw new RuntimeException(errors +
+                    " error(s) encountered in default properties verification");
+        }
+    }
+}
+
--- a/jdk/test/tools/pack200/Pack200Simple.sh	Wed Jul 05 17:20:50 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -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
--- a/jdk/test/tools/pack200/Pack200Test.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/test/tools/pack200/Pack200Test.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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 <File> jarList = new ArrayList<File>();
-    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() {}
 
-    private static void doPackUnpack() {
-        for (File in : jarList) {
-            Pack200.Packer packer = Pack200.newPacker();
-            Map<String, String> 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);
-
-            try {
-                JarFile jarFile = new JarFile(in);
-
-                // Write out to a jtreg scratch area
-                FileOutputStream fos = new FileOutputStream(in.getName() + PACKEXT);
+    static long getUsedMemory() {
+        mmxbean.gc();
+        mmxbean.gc();
+        mmxbean.gc();
+        return mmxbean.getHeapMemoryUsage().getUsed()/1024;
+    }
 
-                System.out.print("Packing [" + in.toString() + "]...");
-                // Call the packer
-                packer.pack(jarFile, fos);
-                jarFile.close();
-                fos.close();
-
-                System.out.print("Unpacking...");
-                File f = new File(in.getName() + PACKEXT);
-
-                // 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...");
-                // Ok we have unpacked the file, lets test it.
-                doTest(in);
-                System.out.println("Done.");
-            } catch (Exception e) {
-                System.out.println("ERROR: " + e.getMessage());
-                System.exit(1);
-            }
+    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 ArrayList <String> getZipFileEntryNames(ZipFile z) {
-        ArrayList <String> out = new ArrayList<String>();
-        for (ZipEntry ze : Collections.list(z.entries())) {
-            out.add(ze.getName());
-        }
-        return out;
-    }
+    private static void doPackUnpack() {
+        for (File in : jarList) {
+            JarOutputStream javaUnpackerStream = null;
+            JarOutputStream nativeUnpackerStream = null;
+            JarFile jarFile = null;
+            try {
+                jarFile = new JarFile(in);
 
-    private static void doTest(File in) throws Exception {
-       // make sure all the files in the original jar exists in the other
-       ArrayList <String> refList = getZipFileEntryNames(new ZipFile(in));
-       ArrayList <String> cmpList = getZipFileEntryNames(new ZipFile(in.getName()));
+                // Write out to a jtreg scratch area
+                File packFile = new File(in.getName() + Utils.PACK_FILE_EXT);
 
-       System.out.print(refList.size() + "/" + cmpList.size() + " entries...");
+                System.out.println("Packing [" + in.toString() + "]");
+                // Call the packer
+                Utils.pack(jarFile, packFile);
+                jarFile.close();
+                leakCheck();
 
-       if (refList.size() != cmpList.size()) {
-           throw new Exception("Missing: files ?, entries don't match");
-       }
+                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
+                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.
+                Utils.doCompareVerify(in.getAbsoluteFile(), javaUnpackedJar);
 
-       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.");
+                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) {
+                throw new RuntimeException(e);
+            } finally {
+                Utils.close(nativeUnpackerStream);
+                Utils.close(javaUnpackerStream);
+                Utils.close((Closeable) jarFile);
             }
         }
     }
@@ -137,11 +123,12 @@
      * @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();
     }
 }
--- a/jdk/test/tools/pack200/PackageVersionTest.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/jdk/test/tools/pack200/PackageVersionTest.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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 @@
                 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 @@
             name.substring(name.length() - 1),
             name + ".java"
         };
-        compileJava(javacCmds);
+        Utils.compiler(javacCmds);
     }
 
     static void createJavaFile(String name) {
@@ -108,22 +101,8 @@
         } 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 @@
             jarFileName.getName(),
             filename
         };
-        makeJar(jargs);
+        Utils.jar(jargs);
         JarFile jfin = null;
 
         try {
@@ -163,7 +142,7 @@
         } catch (IOException ioe) {
             throw new RuntimeException(ioe.getMessage());
         } finally {
-            close(jfin);
+            Utils.close((Closeable) jfin);
         }
     }
 }
--- a/jdk/test/tools/pack200/SegmentLimit.java	Wed Jul 05 17:20:50 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,134 +0,0 @@
-/*
- * 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 6575373
- * @summary verify default segment limit
- * @compile SegmentLimit.java
- * @run main SegmentLimit
- */
-
-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;
-
-/*
- * Run this against a large jar file, by default the packer should generate only
- * one segment, parse the output of the packer to verify if this is indeed true.
- */
-
-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);
-        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 testJar = new File(new File(sdkHome, "lib"), "tools.jar");
-
-        System.out.println("using pack200: " + pack200Exe.getAbsolutePath());
-
-        String[] cmds = { pack200Exe.getAbsolutePath(),
-                          "--effort=1",
-                          "--verbose",
-                          "--no-gzip",
-                          outFile.getName(),
-                          testJar.getAbsolutePath()
-        };
-        InputStream is = null;
-        BufferedReader br = null;
-        InputStreamReader ir = null;
-
-        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();
-            }
-            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);
-        }
-    }
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/TimeStamp.java	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.TimeZone;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+
+/*
+ * @test
+ * @bug 6966740
+ * @summary verify identical timestamps, unpacked in any timezone
+ * @compile -XDignore.symbol.file Utils.java TimeStamp.java
+ * @run main/othervm TimeStamp
+ * @author ksrini
+ */
+
+/**
+ * First we pack the file in some time zone say India, then we unpack the  file
+ * in the current time zone, and ensure the timestamp recorded in the unpacked
+ * jar are the same.
+ */
+public class TimeStamp {
+    static final TimeZone tz = TimeZone.getDefault();
+
+
+    public static void main(String... args) throws IOException {
+
+        // make a local copy of our test file
+        File srcFile = Utils.locateJar("golden.jar");
+        File goldenFile = new File("golden.jar");
+        Utils.copyFile(srcFile, goldenFile.getAbsoluteFile());
+
+        JarFile goldenJarFile = new JarFile(goldenFile);
+        File packFile = new File("golden.pack");
+
+        // set the test timezone and pack the file
+        TimeZone.setDefault(TimeZone.getTimeZone("IST"));
+        Utils.pack(goldenJarFile, packFile);
+        TimeZone.setDefault(tz);   // reset the timezone
+
+        // unpack in the  test timezone
+        File istFile = new File("golden.jar.java.IST");
+        unpackJava(packFile, istFile);
+        verifyJar(goldenFile, istFile);
+        istFile.delete();
+
+        // unpack in some other timezone
+        File pstFile = new File("golden.jar.java.PST");
+        unpackJava(packFile, pstFile);
+        verifyJar(goldenFile, pstFile);
+        pstFile.delete();
+
+        // repeat the test for unpack200 tool.
+        istFile = new File("golden.jar.native.IST");
+        unpackNative(packFile, istFile);
+        verifyJar(goldenFile, istFile);
+        istFile.delete();
+
+        pstFile = new File("golden.jar.native.PST");
+        unpackNative(packFile, pstFile);
+        verifyJar(goldenFile, pstFile);
+        pstFile.delete();
+    }
+
+    static void unpackNative(File packFile, File outFile) {
+        String name = outFile.getName();
+        String tzname = name.substring(name.lastIndexOf(".") + 1);
+        HashMap<String, String> env = new HashMap<>();
+        switch(tzname) {
+            case "PST":
+                env.put("TZ", "US/Pacific");
+                break;
+            case "IST":
+                env.put("TZ", "Asia/Calcutta");
+                break;
+            default:
+                throw new RuntimeException("not implemented: " + tzname);
+        }
+        List<String> cmdsList = new ArrayList<>();
+        cmdsList.add(Utils.getUnpack200Cmd());
+        cmdsList.add(packFile.getName());
+        cmdsList.add(outFile.getName());
+        Utils.runExec(cmdsList, env);
+    }
+
+    static void unpackJava(File packFile, File outFile) throws IOException {
+        String name = outFile.getName();
+        String tzname = name.substring(name.lastIndexOf(".") + 1);
+        JarOutputStream jos = null;
+        try {
+            TimeZone.setDefault(TimeZone.getTimeZone(tzname));
+            jos = new JarOutputStream(new FileOutputStream(outFile));
+            System.out.println("Using timezone: " + TimeZone.getDefault());
+            Utils.unpackj(packFile, jos);
+        } finally {
+            Utils.close(jos);
+            TimeZone.setDefault(tz); // always reset
+        }
+    }
+
+    static void verifyJar(File f1, File f2) throws IOException {
+        int errors = 0;
+        JarFile jf1 = null;
+        JarFile jf2 = null;
+        try {
+            jf1 = new JarFile(f1);
+            jf2 = new JarFile(f2);
+            System.out.println("Verifying: " + f1 + " and " + f2);
+            for (JarEntry je1 : Collections.list(jf1.entries())) {
+                JarEntry je2 = jf2.getJarEntry(je1.getName());
+                if (je1.getTime() != je2.getTime()) {
+                    System.out.println("Error:");
+                    System.out.println("  expected:" + jf1.getName() + ":"
+                            + je1.getName() + ":" + je1.getTime());
+                    System.out.println("  obtained:" + jf2.getName() + ":"
+                            + je2.getName() + ":" + je2.getTime());
+                    errors++;
+                }
+            }
+        } finally {
+            Utils.close(jf1);
+            Utils.close(jf2);
+        }
+        if (errors > 0) {
+            throw new RuntimeException("FAIL:" + errors + " error(s) encounted");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/UnpackerMemoryTest.java	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,86 @@
+/*
+ * 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 6531345
+ * @summary check for unpacker memory leaks
+ * @compile -XDignore.symbol.file Utils.java UnpackerMemoryTest.java
+ * @run main/othervm/timeout=1200 -Xmx32m UnpackerMemoryTest
+ * @author ksrini
+ */
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.io.IOException;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+
+public class UnpackerMemoryTest {
+
+    private static void createPackFile(File packFile) throws IOException {
+        File tFile = new File("test.dat");
+        FileOutputStream fos = null;
+        PrintStream ps = null;
+        String jarFileName = Utils.baseName(packFile, Utils.PACK_FILE_EXT)
+                + Utils.JAR_FILE_EXT;
+        JarFile jarFile = null;
+        try {
+            fos = new FileOutputStream(tFile);
+            ps = new PrintStream(fos);
+            ps.println("A quick brown fox");
+            Utils.jar("cvf", jarFileName, tFile.getName());
+            jarFile = new JarFile(jarFileName);
+            Utils.pack(jarFile, packFile);
+        } finally {
+            Utils.close(ps);
+            tFile.delete();
+            Utils.close(jarFile);
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        String name = "foo";
+        File packFile = new File(name + Utils.PACK_FILE_EXT);
+        createPackFile(packFile);
+        if (!packFile.exists()) {
+           throw new RuntimeException(packFile + " not found");
+        }
+        File jarOut = new File(name + ".out");
+        for (int i = 0; i < 2000; i++) {
+            JarOutputStream jarOS = null;
+            FileOutputStream fos = null;
+            try {
+                fos = new FileOutputStream(jarOut);
+                jarOS = new JarOutputStream(fos);
+                System.out.println("Unpacking[" + i + "]" + packFile);
+                Utils.unpackn(packFile, jarOS);
+            }  finally {
+                Utils.close(jarOS);
+                Utils.close(fos);
+            }
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/Utils.java	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,516 @@
+/*
+ * 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<File> 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<String> cmds = new ArrayList<String>();
+        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<String> cmds = new ArrayList<String>();
+        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);
+    }
+
+    static String baseName(File file, String extension) {
+        return baseName(file.getAbsolutePath(), extension);
+    }
+
+    static String baseName(String name, String extension) {
+        int cut = name.length() - extension.length();
+        return name.lastIndexOf(extension) == cut
+                ? name.substring(0, cut)
+                : name;
+
+    }
+
+    /*
+     * 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<File> a = directoryList(src);
+        for (File f : a) {
+            copyFile(f, new File(dest, getEntryPath(src, f)));
+        }
+    }
+
+    static List<File> directoryList(File dirname) {
+        List<File>  dirList = new ArrayList<File>();
+        return directoryList(dirname, dirList, null);
+    }
+
+    private static List<File> directoryList(File dirname, List<File> 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<File> findFiles(File startDir, FileFilter filter)
+            throws IOException {
+        List<File> list = new ArrayList<File>();
+        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<File> 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<String, String> 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<String, String> 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<String> getZipFileEntryNames(ZipFile z) {
+        ArrayList<String> out = new ArrayList<String>();
+        for (ZipEntry ze : Collections.list(z.entries())) {
+            out.add(ze.getName());
+        }
+        return out;
+    }
+    static List<String> runExec(List<String> cmdsList) {
+        return runExec(cmdsList, null);
+    }
+    static List<String> runExec(List<String> cmdsList, Map<String, String> penv) {
+        ArrayList<String> alist = new ArrayList<String>();
+        ProcessBuilder pb =
+                new ProcessBuilder(cmdsList);
+        Map<String, String> env = pb.environment();
+        if (penv != null && !penv.isEmpty()) {
+            env.putAll(penv);
+        }
+        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<File> 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<File>();
+                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);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/pack200-verifier/data/README	Wed Jul 05 17:21:32 2017 +0200
@@ -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
Binary file jdk/test/tools/pack200/pack200-verifier/data/golden.jar has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/pack200-verifier/make/build.xml	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,59 @@
+<project name="PackageVerify" default="dist" basedir="..">
+  <!-- Requires ant 1.6.1+ and JDK 1.6+-->
+
+  <!-- set global properties for this build -->
+  <property name="src"      value="${basedir}/src"/>
+  <property name="build"    value="${basedir}/build"/>
+  <property name="dist"     value="${basedir}/dist"/>
+  <property name="make"     value="${basedir}/make"/>
+  <property name="classes"  value="${build}/classes"/>
+  <property name="api"      value="${build}/api"/>
+
+  <target name="init">
+    <!-- Create the time stamp -->
+    <tstamp/>
+    <!-- Create the build directory structure used by compile -->
+    <mkdir dir="${build}"/>
+    <mkdir dir="${dist}"/>
+    <mkdir dir="${classes}"/>
+    <mkdir dir="${api}"/>
+  </target>
+
+  <target name="compile" depends="init">
+    <!-- Compile the java code from ${src} into ${build} -->
+    <javac 
+	source="1.6"
+	srcdir="${src}"
+	destdir="${build}/classes"
+	verbose="no"
+	debug="on"
+    />
+  </target>
+
+  <target name="doc" depends="init, compile">
+      <javadoc
+        source="1.6"
+        sourcepath="${src}"
+        destdir="${api}"
+      />
+  </target>
+  <target name="dist" depends="compile, doc">
+    <!-- Put everything in jar file -->
+    <jar destfile="${dist}/pack200-verifier.jar">
+	<manifest>
+	   <attribute name="Main-Class" value="sun.tools.pack.verify.Main"/>
+	</manifest>
+	<fileset dir="${classes}"/>
+    </jar>
+    <zip destfile="dist/pack200-verifier-doc.zip">
+        <fileset dir="${api}"/>
+    </zip>
+  </target>
+
+  <target name="clean">
+    <!-- Delete the ${build} and ${dist} directory trees -->
+    <delete dir="${build}"/>
+    <delete dir="${dist}"/>
+  </target>
+
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/ClassCompare.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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<String> 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<String> 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<String> 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<JarEntry>) 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<String> 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);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/Globals.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/JarFileCompare.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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<JarEntry>) 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<JarEntry>) 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<JarEntry>) jf1.entries())) {
+                if (!je.isDirectory() && !je.getName().endsWith(".class")) {
+                    nentries++;
+                } else if (je.getName().endsWith(".class")) {
+                    nclasses++;
+                }
+            }
+
+            for (JarEntry je : Collections.list((Enumeration<JarEntry>) 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);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/Main.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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
+     * <pre>
+     *  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
+     * </pre>
+     */
+    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());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/pack200-verifier/src/sun/tools/pack/verify/VerifyTreeSet.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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<K> extends java.util.TreeSet {
+
+    VerifyTreeSet() {
+        super();
+    }
+
+    public VerifyTreeSet(Comparator c) {
+        super(c);
+    }
+
+    public TreeSet<K> diff(TreeSet in) {
+        TreeSet<K> delta = (TreeSet<K>) this.clone();
+        delta.removeAll(in);
+        return delta;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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<String> av = new ArrayList<String>(Arrays.asList(ava));
+        HashMap<String, String> props = new HashMap<String, String>();
+        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<JarEntry>) 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<String, String> 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)); // <Bridge>, 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("<Code>...")) {
+                        // delve into Code attribute
+                        aval = readCode();
+                    } else if (type.equals("<Frame>...")) {
+                        // delve into StackMap attribute
+                        aval = readStackMap(false);
+                    } else if (type.equals("<FrameX>...")) {
+                        // 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<String, int[]> attrSizes = new HashMap<String, int[]>();
+
+    private Element formatAttrSizes() {
+        Element e = new Element("Sizes");
+        e.setAttr("fileSize", "" + fileSize);
+        for (Map.Entry<String, int[]> 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;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassSyntax.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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<String> cpTagNames;
+
+    static {
+        Set<String> set = new HashSet<String>(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<String> itemTagNames;
+
+    static {
+        Set<String> set = new HashSet<String>(Arrays.asList(itemTagName));
+        set.remove(null);
+        itemTagNames = Collections.unmodifiableSet(set);
+    }
+    protected static final HashMap<String, String> attrTypesBacking;
+    protected static final Map<String, String> attrTypesInit;
+
+    static {
+        HashMap<String, String> at = new HashMap<String, String>();
+
+        //at.put("*.Deprecated", "<deprecated=true>");
+        //at.put("*.Synthetic", "<synthetic=true>");
+        ////at.put("Field.ConstantValue", "<constantValue=>KQH");
+        //at.put("Class.SourceFile", "<sourceFile=>RUH");
+        at.put("Method.Bridge", "<Bridge>");
+        at.put("Method.Varargs", "<Varargs>");
+        at.put("Class.Enum", "<Enum>");
+        at.put("*.Signature", "<Signature>RSH");
+        //at.put("*.Deprecated", "<Deprecated>");
+        //at.put("*.Synthetic", "<Synthetic>");
+        at.put("Field.ConstantValue", "<ConstantValue>KQH");
+        at.put("Class.SourceFile", "<SourceFile>RUH");
+        at.put("Class.InnerClasses", "NH[<InnerClass><class=>RCH<outer=>RCH<name=>RUH<flags=>FH]");
+        at.put("Code.LineNumberTable", "NH[<LineNumber><bci=>PH<line=>H]");
+        at.put("Code.LocalVariableTable", "NH[<LocalVariable><bci=>PH<span=>H<name=>RUH<type=>RSH<slot=>H]");
+        at.put("Code.LocalVariableTypeTable", "NH[<LocalVariableType><bci=>PH<span=>H<name=>RUH<type=>RSH<slot=>H]");
+        at.put("Method.Exceptions", "NH[<Exception><name=>RCH]");
+        at.put("Method.Code", "<Code>...");
+        at.put("Code.StackMapTable", "<Frame>...");
+        //at.put("Code.StkMapX", "<FrameX>...");
+        if (true) {
+            at.put("Code.StackMapTable",
+                    "[NH[<Frame>(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"
+                    + ")[<SameLocals1StackItemFrame>(4)]"
+                    + "(247)[<SameLocals1StackItemExtended>H(4)]"
+                    + "(248)[<Chop3>H]"
+                    + "(249)[<Chop2>H]"
+                    + "(250)[<Chop1>H]"
+                    + "(251)[<SameFrameExtended>H]"
+                    + "(252)[<Append1>H(4)]"
+                    + "(253)[<Append2>H(4)(4)]"
+                    + "(254)[<Append3>H(4)(4)(4)]"
+                    + "(255)[<FullFrame>H(2)(3)]"
+                    + "()[<SameFrame>]]"
+                    + "[NH[<Local>(4)]]"
+                    + "[NH[<Stack>(4)]]"
+                    + "[TB"
+                    + ("(0)[<Top>]"
+                    + "(1)[<ItemInteger>](2)[<ItemFloat>](3)[<ItemDouble>](4)[<ItemLong>]"
+                    + "(5)[<ItemNull>](6)[<ItemUninitializedThis>]"
+                    + "(7)[<ItemObject><class=>RCH]"
+                    + "(8)[<ItemUninitialized><bci=>PH]"
+                    + "()[<ItemUnknown>]]"));
+        }
+
+        at.put("Class.EnclosingMethod", "<EnclosingMethod><class=>RCH<desc=>RDH");//RDNH
+
+        // Layouts of metadata attrs:
+        String vpf = "[<RuntimeVisibleAnnotation>";
+        String ipf = "[<RuntimeInvisibleAnnotation>";
+        String apf = "[<Annotation>";
+        String mdanno2 = ""
+                + "<type=>RSHNH[<Member><name=>RUH(3)]]"
+                + ("[TB"
+                + "(\\B,\\C,\\I,\\S,\\Z)[<value=>KIH]"
+                + "(\\D)[<value=>KDH]"
+                + "(\\F)[<value=>KFH]"
+                + "(\\J)[<value=>KJH]"
+                + "(\\c)[<class=>RSH]"
+                + "(\\e)[<type=>RSH<name=>RUH]"
+                + "(\\s)[<String>RUH]"
+                + "(\\@)[(2)]"
+                + "(\\[)[NH[<Element>(3)]]"
+                + "()[]"
+                + "]");
+        String visanno = "[NH[(2)]][(1)]" + vpf + mdanno2;
+        String invanno = "[NH[(2)]][(1)]" + ipf + mdanno2;
+        String vparamanno = ""
+                + "[NB[<RuntimeVisibleParameterAnnotation>(1)]][NH[(2)]]"
+                + apf + mdanno2;
+        String iparamanno = ""
+                + "[NB[<RuntimeInvisibleParameterAnnotation>(1)]][NH[(2)]]"
+                + apf + mdanno2;
+        String mdannodef = "[<AnnotationDefault>(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[<Coverage><bci=>PH<type=>H<line=>I<pos=>I]",
+        "Code.CharacterRangeTable=NH[<CharacterRange><bci=>PH<endbci=>POH<from=>I<to=>I<flag=>H]",
+        "Class.SourceID=<SourceID><id=>RUH",
+        "Class.CompilationID=<CompilationID><id=>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<String> nonAttrTags;
+
+    static {
+        HashSet<String> tagSet = new HashSet<String>();
+        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<String> 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<String, String> 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<String, String> at) {
+        for (String attr : at.keySet()) {
+            checkAttr(attr);
+        }
+    }
+
+    private void modAttrs() {
+        if (attrTypes == attrTypesInit) {
+            // Make modifiable.
+            attrTypes = new HashMap<String, String>(attrTypesBacking);
+        }
+    }
+
+    public void addAttrType(String attr, String fmt) {
+        checkAttr(attr);
+        modAttrs();
+        attrTypes.put(attr, fmt);
+    }
+
+    public void addAttrTypes(Map<String, String> at) {
+        checkAttrs(at);
+        modAttrs();
+        attrTypes.putAll(at);
+    }
+
+    public Map<String, String> getAttrTypes() {
+        if (attrTypes == attrTypesInit) {
+            return attrTypes;
+        }
+        return Collections.unmodifiableMap(attrTypes);
+    }
+
+    public void setAttrTypes(Map<String, String> 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<String> bodies = new ArrayList<String>();
+        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;          // <ClassFile ...>
+    protected Element cpool;          // <ConstantPool ...>
+    protected Element klass;          // <Class ...>
+    protected Element currentMember;  // varies during scans
+    protected Element currentCode;    // varies during scans
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassWriter.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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<String> av = new ArrayList<String>(Arrays.asList(ava));
+        HashMap<String, String> props = new HashMap<String, String>();
+        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<String, String> attrTypesByTag;
+    protected OutputStream out;
+    protected HashMap<String, int[]> cpMap = new HashMap<String, int[]>();
+    protected ArrayList<ByteArrayOutputStream> attrBufs = new ArrayList<ByteArrayOutputStream>();
+
+    private void setupAttrTypes() {
+        attrTypesByTag = new HashMap<String, String>();
+        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<String> attrNames = new LinkedHashSet<String>();
+        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("<Code>...")) {
+                        writeCode((Element) attrs.get(0));  // assume only 1
+                    } else if (type.equals("<Frame>...")) {
+                        writeStackMap(attrs, false);
+                    } else if (type.equals("<FrameX>...")) {
+                        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);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/CommandLineParser.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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<String, String[]> optionMap;
+
+    public void setOptionMap(String options) {
+        // Convert options string into optLines dictionary.
+        TreeMap<String, String[]> optmap = new TreeMap<String, String[]>();
+        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<String, String[]> 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<String> args, Map<String, String> properties) {
+        //System.out.println(args+" // "+properties);
+
+        String resultString = null;
+        TreeMap<String, String[]> optmap = optionMap;
+
+        // State machine for parsing a command line.
+        ListIterator<String> argp = args.listIterator();
+        ListIterator<String> pbp = new ArrayList<String>().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<String, String[]> 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;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/InstructionAssembler.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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<String, String> labels = new HashMap<String, String>();
+
+        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 <ifop lab=1f> <goto_w target> <label pc=1f>
+                    int target = pc + opnd;
+                    putInt(sbuf, nextpc - pc, -2);
+                    assert (sbuf.length() == pc + GOTO_LEN);
+                    sbuf.append((char) GOTO_W);
+                    putInt(sbuf, target - (pc + GOTO_LEN), 4);
+                } else if (op == 0xaa || //tableswitch
+                        op == 0xab) {  //lookupswitch
+                    ins = insElems[id];
+                    for (int pad = switchBase(pc) - (pc + 1); pad > 0; pad--) {
+                        sbuf.append((char) 0);
+                    }
+                    assert (pc + opnd == insLocs[(int) ins.getAttrLong("lab")]);
+                    putInt(sbuf, opnd, 4); // default label
+                    if (op == 0xaa) {  //tableswitch
+                        Element cas0 = (Element) ins.get(0);
+                        int lowCase = (int) cas0.getAttrLong("num");
+                        Element casN = (Element) ins.get(ins.size() - 1);
+                        int highCase = (int) casN.getAttrLong("num");
+                        assert (highCase - lowCase + 1 == ins.size());
+                        putInt(sbuf, lowCase, 4);
+                        putInt(sbuf, highCase, 4);
+                        int caseForAssert = lowCase;
+                        for (Element cas : ins.elements()) {
+                            int target = insLocs[(int) cas.getAttrLong("lab")];
+                            assert (cas.getAttrLong("num") == caseForAssert++);
+                            putInt(sbuf, target - pc, 4);
+                        }
+                    } else {  //lookupswitch
+                        int caseCount = ins.size();
+                        putInt(sbuf, caseCount, 4);
+                        for (Element cas : ins.elements()) {
+                            int target = insLocs[(int) cas.getAttrLong("lab")];
+                            putInt(sbuf, (int) cas.getAttrLong("num"), 4);
+                            putInt(sbuf, target - pc, 4);
+                        }
+                    }
+                    assert (nextpc == sbuf.length());
+                } else {
+                    putInt(sbuf, opnd, -(nextpc - (pc + 1)));
+                }
+            } else if (nextpc == pc + 1) {
+                // a single-byte instruction
+                sbuf.append((char) op);
+            } else {
+                // picky stuff
+                boolean wide = isWide[id];
+                if (wide) {
+                    sbuf.append((char) WIDE);
+                    pc++;
+                }
+                sbuf.append((char) op);
+                int opnd1;
+                int opnd2 = opnd;
+                switch (op) {
+                    case 0x84:  //iinc
+                        ins = insElems[id];
+                        opnd1 = (int) ins.getAttrLong("loc");
+                        if (isWide[id]) {
+                            putInt(sbuf, opnd1, 2);
+                            putInt(sbuf, opnd2, 2);
+                        } else {
+                            putInt(sbuf, opnd1, 1);
+                            putInt(sbuf, opnd2, 1);
+                        }
+                        break;
+                    case 0xc5: //multianewarray
+                        ins = insElems[id];
+                        opnd1 = getCPIndex(ins, 'c', getCPI);
+                        putInt(sbuf, opnd1, 2);
+                        putInt(sbuf, opnd2, 1);
+                        break;
+                    case 0xb9: //invokeinterface
+                        ins = insElems[id];
+                        opnd1 = getCPIndex(ins, 'n', getCPI);
+                        putInt(sbuf, opnd1, 2);
+                        opnd2 = (int) ins.getAttrLong("num");
+                        if (opnd2 == 0) {
+                            opnd2 = ClassSyntax.computeInterfaceNum(ins.getAttr("val"));
+                        }
+                        putInt(sbuf, opnd2, 2);
+                        break;
+                    default:
+                        // put the single operand and be done
+                        putInt(sbuf, opnd, nextpc - (pc + 1));
+                        break;
+                }
+            }
+        }
+        assert (sbuf.length() == insLocs[insCount]);
+
+        return sbuf.toString();
+    }
+
+    static int getCPIndex(Element ins, char ctype,
+            ClassSyntax.GetCPIndex getCPI) {
+        int x = (int) ins.getAttrLong("ref");
+        if (x == 0 && getCPI != null) {
+            String val = ins.getAttr("val");
+            if (val == null || val.equals("")) {
+                val = ins.getText().toString();
+            }
+            byte tag;
+            switch (ctype) {
+                case 'k':
+                    tag = (byte) ins.getAttrLong("tag");
+                    break;
+                case 'c':
+                    tag = ClassSyntax.CONSTANT_Class;
+                    break;
+                case 'f':
+                    tag = ClassSyntax.CONSTANT_Fieldref;
+                    break;
+                case 'm':
+                    tag = ClassSyntax.CONSTANT_Methodref;
+                    break;
+                case 'n':
+                    tag = ClassSyntax.CONSTANT_InterfaceMethodref;
+                    break;
+                default:
+                    throw new Error("bad ctype " + ctype + " in " + ins);
+            }
+            x = getCPI.getCPIndex(tag, val);
+            //System.out.println("getCPIndex "+ins+" => "+tag+"/"+val+" => "+x);
+        } else {
+            assert (x > 0);
+        }
+        return x;
+    }
+
+    static void putInt(StringBuffer sbuf, int x, int len) {
+        //System.out.println("putInt x="+x+" len="+len);
+        boolean isSigned = false;
+        if (len < 0) {
+            len = -len;
+            isSigned = true;
+        }
+        assert (len == 1 || len == 2 || len == 4);
+        int insig = ((4 - len) * 8);  // how many insignificant bits?
+        int sx = x << insig;
+        ;
+        assert (x == (isSigned ? (sx >> insig) : (sx >>> insig)));
+        for (int i = 0; i < len; i++) {
+            sbuf.append((char) (sx >>> 24));
+            sx <<= 8;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/InstructionSyntax.java	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,483 @@
+/*
+ * 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;
+import java.util.Map;
+/*
+ * @author jrose
+ */
+public abstract class InstructionSyntax {
+
+    InstructionSyntax() {
+    }
+    static final String[] bcNames;
+    static final String[] bcFormats;
+    static final String[] bcWideFormats;
+    static final HashMap<String, Integer> bcCodes;
+    static final HashMap<String, Element> abbrevs;
+    static final HashMap<Element, String> rabbrevs;
+
+    static {
+        TokenList tl = new TokenList(
+                " nop aconst_null iconst_m1 iconst_0 iconst_1 iconst_2 iconst_3"
+                + " iconst_4 iconst_5 lconst_0 lconst_1 fconst_0 fconst_1 fconst_2"
+                + " dconst_0 dconst_1 bipush/s sipush/ss ldc/k ldc_w/kk ldc2_w/kk"
+                + " iload/wl lload/wl fload/wl dload/wl aload/wl iload_0 iload_1"
+                + " iload_2 iload_3 lload_0 lload_1 lload_2 lload_3 fload_0 fload_1"
+                + " fload_2 fload_3 dload_0 dload_1 dload_2 dload_3 aload_0 aload_1"
+                + " aload_2 aload_3 iaload laload faload daload aaload baload caload"
+                + " saload istore/wl lstore/wl fstore/wl dstore/wl astore/wl"
+                + " istore_0 istore_1 istore_2 istore_3 lstore_0 lstore_1 lstore_2"
+                + " lstore_3 fstore_0 fstore_1 fstore_2 fstore_3 dstore_0 dstore_1"
+                + " dstore_2 dstore_3 astore_0 astore_1 astore_2 astore_3 iastore"
+                + " lastore fastore dastore aastore bastore castore sastore pop pop2"
+                + " dup dup_x1 dup_x2 dup2 dup2_x1 dup2_x2 swap iadd ladd fadd dadd"
+                + " isub lsub fsub dsub imul lmul fmul dmul idiv ldiv fdiv ddiv irem"
+                + " lrem frem drem ineg lneg fneg dneg ishl lshl ishr lshr iushr"
+                + " lushr iand land ior lor ixor lxor iinc/wls i2l i2f i2d l2i l2f"
+                + " l2d f2i f2l f2d d2i d2l d2f i2b i2c i2s lcmp fcmpl fcmpg dcmpl"
+                + " dcmpg ifeq/oo ifne/oo iflt/oo ifge/oo ifgt/oo ifle/oo"
+                + " if_icmpeq/oo if_icmpne/oo if_icmplt/oo if_icmpge/oo if_icmpgt/oo"
+                + " if_icmple/oo if_acmpeq/oo if_acmpne/oo goto/oo jsr/oo ret/wl"
+                + " tableswitch/oooot lookupswitch/oooot ireturn lreturn freturn dreturn areturn"
+                + " return getstatic/kf putstatic/kf getfield/kf putfield/kf"
+                + " invokevirtual/km invokespecial/km invokestatic/km"
+                + " invokeinterface/knxx xxxunusedxxx new/kc newarray/x anewarray/kc"
+                + " arraylength athrow checkcast/kc instanceof/kc monitorenter"
+                + " monitorexit wide multianewarray/kcx ifnull/oo ifnonnull/oo"
+                + " goto_w/oooo jsr_w/oooo");
+        assert (tl.size() == 202);  // this many instructions!
+        HashMap<String, Integer> map = new HashMap<String, Integer>(tl.size());
+        String[] names = tl.toArray(new String[tl.size()]);
+        String[] formats = new String[names.length];
+        String[] wideFormats = new String[names.length];
+        StringBuilder sbuf = new StringBuilder();
+        sbuf.append('i');  // all op formats begin with "i"
+        int i = 0;
+        for (String ins : names) {
+            assert (ins == ins.trim());  // no whitespace
+            int sfx = ins.indexOf('/');
+            String format = "i";
+            String wideFormat = null;
+            if (sfx >= 0) {
+                format = ins.substring(sfx + 1);
+                ins = ins.substring(0, sfx);
+                if (format.charAt(0) == 'w') {
+                    format = format.substring(1);
+                    sbuf.setLength(1);
+                    for (int j = 0; j < format.length(); j++) {
+                        // double everything except the initial 'i'
+                        sbuf.append(format.charAt(j));
+                        sbuf.append(format.charAt(j));
+                    }
+                    wideFormat = sbuf.toString().intern();
+                }
+                sbuf.setLength(1);
+                sbuf.append(format);
+                format = sbuf.toString().intern();
+            }
+            ins = ins.intern();
+            names[i] = ins;
+            formats[i] = format;
+            wideFormats[i] = (wideFormat != null) ? wideFormat : format;
+            //System.out.println(ins+" "+format+" "+wideFormat);
+            map.put(ins, i++);
+        }
+        //map = Collections.unmodifiableMap(map);
+
+        HashMap<String, Element> abb = new HashMap<String, Element>(tl.size() / 2);
+        abb.put("iconst_m1", new Element("bipush", "num", "-1"));
+        for (String ins : names) {
+            int sfx = ins.indexOf('_');
+            if (sfx >= 0 && Character.isDigit(ins.charAt(sfx + 1))) {
+                String pfx = ins.substring(0, sfx).intern();
+                String num = ins.substring(sfx + 1);
+                String att = pfx.endsWith("const") ? "num" : "loc";
+                Element exp = new Element(pfx, att, num).deepFreeze();
+                abb.put(ins, exp);
+            }
+        }
+        //abb = Collections.unmodifiableMap(abb);
+        HashMap<Element, String> rabb = new HashMap<Element, String>(tl.size() / 2);
+        for (Map.Entry<String, Element> e : abb.entrySet()) {
+            rabb.put(e.getValue(), e.getKey());
+        }
+        //rabb = Collections.unmodifiableMap(rabb);
+
+
+        bcNames = names;
+        bcFormats = formats;
+        bcWideFormats = wideFormats;
+        bcCodes = map;
+        abbrevs = abb;
+        rabbrevs = rabb;
+    }
+
+    public static String opName(int op) {
+        if (op >= 0 && op < bcNames.length) {
+            return bcNames[op];
+        }
+        return "unknown#" + op;
+    }
+
+    public static String opFormat(int op) {
+        return opFormat(op, false);
+    }
+
+    public static String opFormat(int op, boolean isWide) {
+        if (op >= 0 && op < bcFormats.length) {
+            return (isWide ? bcWideFormats[op] : bcFormats[op]);
+        }
+        return "?";
+    }
+
+    public static int opCode(String opName) {
+        Integer op = (Integer) bcCodes.get(opName);
+        if (op != null) {
+            return op.intValue();
+        }
+        return -1;
+    }
+
+    public static Element expandAbbrev(String opName) {
+        return abbrevs.get(opName);
+    }
+
+    public static String findAbbrev(Element op) {
+        return rabbrevs.get(op);
+    }
+
+    public static int invertBranchOp(int op) {
+        assert (opFormat(op).indexOf('o') >= 0);
+        final int IFMIN = 0x99;
+        final int IFMAX = 0xa6;
+        final int IFMIN2 = 0xc6;
+        final int IFMAX2 = 0xc7;
+        assert (bcNames[IFMIN] == "ifeq");
+        assert (bcNames[IFMAX] == "if_acmpne");
+        assert (bcNames[IFMIN2] == "ifnonnull");
+        assert (bcNames[IFMAX2] == "ifnull");
+        int rop;
+        if (op >= IFMIN && op <= IFMAX) {
+            rop = IFMIN + ((op - IFMIN) ^ 1);
+        } else if (op >= IFMIN2 && op <= IFMAX2) {
+            rop = IFMIN2 + ((op - IFMIN2) ^ 1);
+        } else {
+            assert (false);
+            rop = op;
+        }
+        assert (opFormat(rop).indexOf('o') >= 0);
+        return rop;
+    }
+
+    public static Element parse(String bytes) {
+        Element e = new Element("Instructions", bytes.length());
+        boolean willBeWide;
+        boolean isWide = false;
+        Element[] tempMap = new Element[bytes.length()];
+        for (int pc = 0, nextpc; pc < bytes.length(); pc = nextpc) {
+            int op = bytes.charAt(pc);
+            Element i = new Element(opName(op));
+
+            nextpc = pc + 1;
+            int locarg = 0;
+            int cparg = 0;
+            int intarg = 0;
+            int labelarg = 0;
+
+            willBeWide = false;
+            switch (op) {
+                case 0xc4: //wide
+                    willBeWide = true;
+                    break;
+                case 0x10: //bipush
+                    intarg = nextpc++;
+                    intarg *= -1;  //mark signed
+                    break;
+                case 0x11: //sipush
+                    intarg = nextpc;
+                    nextpc += 2;
+                    intarg *= -1;  //mark signed
+                    break;
+                case 0x12: //ldc
+                    cparg = nextpc++;
+                    break;
+                case 0x13: //ldc_w
+                case 0x14: //ldc2_w
+                case 0xb2: //getstatic
+                case 0xb3: //putstatic
+                case 0xb4: //getfield
+                case 0xb5: //putfield
+                case 0xb6: //invokevirtual
+                case 0xb7: //invokespecial
+                case 0xb8: //invokestatic
+                case 0xbb: //new
+                case 0xbd: //anewarray
+                case 0xc0: //checkcast
+                case 0xc1: //instanceof
+                    cparg = nextpc;
+                    nextpc += 2;
+                    break;
+                case 0xb9: //invokeinterface
+                    cparg = nextpc;
+                    nextpc += 2;
+                    intarg = nextpc;
+                    nextpc += 2;
+                    break;
+                case 0xc5: //multianewarray
+                    cparg = nextpc;
+                    nextpc += 2;
+                    intarg = nextpc++;
+                    break;
+                case 0x15: //iload
+                case 0x16: //lload
+                case 0x17: //fload
+                case 0x18: //dload
+                case 0x19: //aload
+                case 0x36: //istore
+                case 0x37: //lstore
+                case 0x38: //fstore
+                case 0x39: //dstore
+                case 0x3a: //astore
+                case 0xa9: //ret
+                    locarg = nextpc++;
+                    if (isWide) {
+                        nextpc++;
+                    }
+                    break;
+                case 0x84: //iinc
+                    locarg = nextpc++;
+                    if (isWide) {
+                        nextpc++;
+                    }
+                    intarg = nextpc++;
+                    if (isWide) {
+                        nextpc++;
+                    }
+                    intarg *= -1;  //mark signed
+                    break;
+                case 0x99: //ifeq
+                case 0x9a: //ifne
+                case 0x9b: //iflt
+                case 0x9c: //ifge
+                case 0x9d: //ifgt
+                case 0x9e: //ifle
+                case 0x9f: //if_icmpeq
+                case 0xa0: //if_icmpne
+                case 0xa1: //if_icmplt
+                case 0xa2: //if_icmpge
+                case 0xa3: //if_icmpgt
+                case 0xa4: //if_icmple
+                case 0xa5: //if_acmpeq
+                case 0xa6: //if_acmpne
+                case 0xa7: //goto
+                case 0xa8: //jsr
+                    labelarg = nextpc;
+                    nextpc += 2;
+                    break;
+                case 0xbc: //newarray
+                    intarg = nextpc++;
+                    break;
+                case 0xc6: //ifnull
+                case 0xc7: //ifnonnull
+                    labelarg = nextpc;
+                    nextpc += 2;
+                    break;
+                case 0xc8: //goto_w
+                case 0xc9: //jsr_w
+                    labelarg = nextpc;
+                    nextpc += 4;
+                    break;
+
+                // save the best for last:
+                case 0xaa: //tableswitch
+                    nextpc = parseSwitch(bytes, pc, true, i);
+                    break;
+                case 0xab: //lookupswitch
+                    nextpc = parseSwitch(bytes, pc, false, i);
+                    break;
+            }
+
+            String format = null;
+            assert ((format = opFormat(op, isWide)) != null);
+            //System.out.println("pc="+pc+" len="+(nextpc - pc)+" w="+isWide+" op="+op+" name="+opName(op)+" format="+format);
+            assert ((nextpc - pc) == format.length() || format.indexOf('t') >= 0);
+
+            // Parse out instruction fields.
+            if (locarg != 0) {
+                int len = nextpc - locarg;
+                if (intarg != 0) {
+                    len /= 2;  // split
+                }
+                i.setAttr("loc", "" + getInt(bytes, locarg, len));
+                assert ('l' == format.charAt(locarg - pc + 0));
+                assert ('l' == format.charAt(locarg - pc + len - 1));
+            }
+            if (cparg != 0) {
+                int len = nextpc - cparg;
+                if (len > 2) {
+                    len = 2;
+                }
+                i.setAttr("ref", "" + getInt(bytes, cparg, len));
+                assert ('k' == format.charAt(cparg - pc + 0));
+            }
+            if (intarg != 0) {
+                boolean isSigned = (intarg < 0);
+                if (isSigned) {
+                    intarg *= -1;
+                }
+                int len = nextpc - intarg;
+                i.setAttr("num", "" + getInt(bytes, intarg, isSigned ? -len : len));
+                assert ((isSigned ? 's' : 'x') == format.charAt(intarg - pc + 0));
+                assert ((isSigned ? 's' : 'x') == format.charAt(intarg - pc + len - 1));
+            }
+            if (labelarg != 0) {
+                int len = nextpc - labelarg;
+                int offset = getInt(bytes, labelarg, -len);
+                int target = pc + offset;
+                i.setAttr("lab", "" + target);
+                assert ('o' == format.charAt(labelarg - pc + 0));
+                assert ('o' == format.charAt(labelarg - pc + len - 1));
+            }
+
+            e.add(i);
+            tempMap[pc] = i;
+            isWide = willBeWide;
+        }
+
+        // Mark targets of branches.
+        for (Element i : e.elements()) {
+            for (int j = -1; j < i.size(); j++) {
+                Element c = (j < 0) ? i : (Element) i.get(j);
+                Number targetNum = c.getAttrNumber("lab");
+                if (targetNum != null) {
+                    int target = targetNum.intValue();
+                    Element ti = null;
+                    if (target >= 0 && target < tempMap.length) {
+                        ti = tempMap[target];
+                    }
+                    if (ti != null) {
+                        ti.setAttr("pc", "" + target);
+                    } else {
+                        c.setAttr("lab.error", "");
+                    }
+                }
+            }
+        }
+
+        // Shrink to fit:
+        for (Element i : e.elements()) {
+            i.trimToSize();
+        }
+        e.trimToSize();
+
+        /*
+        String assem = assemble(e);
+        if (!assem.equals(bytes)) {
+        System.out.println("Bytes: "+bytes);
+        System.out.println("Insns: "+e);
+        System.out.println("Assem: "+parse(assem));
+        }
+         */
+
+        return e;
+    }
+
+    static int switchBase(int pc) {
+        int apc = pc + 1;
+        apc += (-apc) & 3;
+        return apc;
+    }
+
+    static int parseSwitch(String s, int pc, boolean isTable, Element i) {
+        int apc = switchBase(pc);
+        int defLabel = pc + getInt(s, apc + 4 * 0, 4);
+        i.setAttr("lab", "" + defLabel);
+        if (isTable) {
+            int lowCase = getInt(s, apc + 4 * 1, 4);
+            int highCase = getInt(s, apc + 4 * 2, 4);
+            int caseCount = highCase - lowCase + 1;
+            for (int n = 0; n < caseCount; n++) {
+                Element c = new Element("Case", 4);
+                int caseVal = lowCase + n;
+                int caseLab = getInt(s, apc + 4 * (3 + n), 4) + pc;
+                c.setAttr("num", "" + caseVal);
+                c.setAttr("lab", "" + caseLab);
+                assert (c.getExtraCapacity() == 0);
+                i.add(c);
+            }
+            return apc + 4 * (3 + caseCount);
+        } else {
+            int caseCount = getInt(s, apc + 4 * 1, 4);
+            for (int n = 0; n < caseCount; n++) {
+                Element c = new Element("Case", 4);
+                int caseVal = getInt(s, apc + 4 * (2 + (2 * n) + 0), 4);
+                int caseLab = getInt(s, apc + 4 * (2 + (2 * n) + 1), 4) + pc;
+                c.setAttr("num", "" + caseVal);
+                c.setAttr("lab", "" + caseLab);
+                assert (c.getExtraCapacity() == 0);
+                i.add(c);
+            }
+            return apc + 4 * (2 + 2 * caseCount);
+        }
+    }
+
+    static int getInt(String s, int pc, int len) {
+        //System.out.println("getInt s["+s.length()+"] pc="+pc+" len="+len);
+        int result = s.charAt(pc);
+        if (len < 0) {
+            len = -len;
+            result = (byte) result;
+        }
+        if (!(len == 1 || len == 2 || len == 4)) {
+            System.out.println("len=" + len);
+        }
+        assert (len == 1 || len == 2 || len == 4);
+        for (int i = 1; i < len; i++) {
+            result <<= 8;
+            result += s.charAt(pc + i) & 0xFF;
+        }
+        return result;
+    }
+
+    public static String assemble(Element instructions) {
+        return InstructionAssembler.assemble(instructions, null, null);
+    }
+
+    public static String assemble(Element instructions, String pcAttrName) {
+        return InstructionAssembler.assemble(instructions, pcAttrName, null);
+    }
+
+    public static String assemble(Element instructions, ClassSyntax.GetCPIndex getCPI) {
+        return InstructionAssembler.assemble(instructions, null, getCPI);
+    }
+
+    public static String assemble(Element instructions, String pcAttrName,
+            ClassSyntax.GetCPIndex getCPI) {
+        return InstructionAssembler.assemble(instructions, pcAttrName, getCPI);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/TokenList.java	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,449 @@
+/*
+ * 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.*;
+
+/**
+ * A List of Strings each representing a word or token.
+ * This object itself is a CharSequence whose characters consist
+ * of all the tokens, separated by blanks.
+ *
+ * @author jrose
+ */
+public class TokenList extends ArrayList<String> implements CharSequence {
+
+    protected String separator;
+    protected boolean frozen;
+
+    public TokenList() {
+        this.separator = " ";
+    }
+
+    public TokenList(Collection<? extends Object> tokens) {
+        super(tokens.size());
+        this.separator = " ";
+        addTokens(tokens);
+    }
+
+    public TokenList(Collection<? extends Object> tokens, String separator) {
+        super(tokens.size());
+        this.separator = separator;
+        addTokens(tokens);
+    }
+
+    public TokenList(Object[] tokens) {
+        super(tokens.length);
+        this.separator = " ";
+        addTokens(tokens, 0, tokens.length);
+    }
+
+    public TokenList(Object[] tokens, int beg, int end) {
+        super(end - beg);  // capacity
+        this.separator = " ";
+        addTokens(tokens, beg, end);
+    }
+
+    public TokenList(Object[] tokens, int beg, int end, String separator) {
+        super(end - beg);  // capacity
+        this.separator = separator;
+        addTokens(tokens, beg, end);
+    }
+
+    public TokenList(String tokenStr) {
+        this(tokenStr, " ", false);
+    }
+
+    public TokenList(String tokenStr, String separator) {
+        this(tokenStr, separator, true);
+    }
+
+    public TokenList(String tokenStr, String separator, boolean allowNulls) {
+        super(tokenStr.length() / 5);
+        this.separator = separator;
+        addTokens(tokenStr, allowNulls);
+    }
+    static public final TokenList EMPTY;
+
+    static {
+        TokenList tl = new TokenList(new Object[0]);
+        tl.freeze();
+        EMPTY = tl;
+    }
+
+    public void freeze() {
+        if (!frozen) {
+            for (ListIterator<String> i = listIterator(); i.hasNext();) {
+                i.set(i.next().toString());
+            }
+            trimToSize();
+            frozen = true;
+        }
+    }
+
+    public boolean isFrozen() {
+        return frozen;
+    }
+
+    void checkNotFrozen() {
+        if (isFrozen()) {
+            throw new UnsupportedOperationException("cannot modify frozen TokenList");
+        }
+    }
+
+    public String getSeparator() {
+        return separator;
+    }
+
+    public void setSeparator(String separator) {
+        checkNotFrozen();
+        this.separator = separator;
+    }
+
+    /// All normal List mutators must check the frozen bit:
+    public String set(int index, String o) {
+        checkNotFrozen();
+        return super.set(index, o);
+    }
+
+    public boolean add(String o) {
+        checkNotFrozen();
+        return super.add(o);
+    }
+
+    public void add(int index, String o) {
+        checkNotFrozen();
+        super.add(index, o);
+    }
+
+    public boolean addAll(Collection<? extends String> c) {
+        checkNotFrozen();
+        return super.addAll(c);
+    }
+
+    public boolean addAll(int index, Collection<? extends String> c) {
+        checkNotFrozen();
+        return super.addAll(index, c);
+    }
+
+    public boolean remove(Object o) {
+        checkNotFrozen();
+        return super.remove(o);
+    }
+
+    public String remove(int index) {
+        checkNotFrozen();
+        return super.remove(index);
+    }
+
+    public void clear() {
+        checkNotFrozen();
+        super.clear();
+    }
+
+    /** Add a collection of tokens to the list, applying toString to each. */
+    public boolean addTokens(Collection<? extends Object> tokens) {
+        // Note that if this sequence is empty, no tokens are added.
+        // This is different from adding a null string, which is
+        // a single token.
+        boolean added = false;
+        for (Object token : tokens) {
+            add(token.toString());
+            added = true;
+        }
+        return added;
+    }
+
+    public boolean addTokens(Object[] tokens, int beg, int end) {
+        boolean added = false;
+        for (int i = beg; i < end; i++) {
+            add(tokens[i].toString());
+            added = true;
+        }
+        return added;
+    }
+
+    public boolean addTokens(String tokenStr) {
+        return addTokens(tokenStr, false);
+    }
+
+    public boolean addTokens(String tokenStr, boolean allowNulls) {
+        boolean added = false;
+        int pos = 0, limit = tokenStr.length(), sep = limit;
+        while (pos < limit) {
+            sep = tokenStr.indexOf(separator, pos);
+            if (sep < 0) {
+                sep = limit;
+            }
+            if (sep == pos) {
+                if (allowNulls) {
+                    add("");
+                    added = true;
+                }
+                pos += separator.length();
+            } else {
+                add(tokenStr.substring(pos, sep));
+                added = true;
+                pos = sep + separator.length();
+            }
+        }
+        if (allowNulls && sep < limit) {
+            // Input was something like "tok1 tok2 ".
+            add("");
+            added = true;
+        }
+        return added;
+    }
+
+    public boolean addToken(Object token) {
+        return add(token.toString());
+    }
+
+    /** Format the token string, using quotes and escapes.
+     *  Quotes must contain an odd number of 3 or more elements,
+     *  a sequence of begin/end quote pairs, plus a superquote.
+     *  For each token, the first begin/end pair is used for
+     *  which the end quote does not occur in the token.
+     *  If the token contains all end quotes, the last pair
+     *  is used, with all occurrences of the end quote replaced
+     *  by the superquote.  If an end quote is the empty string,
+     *  the separator is used instead.
+     */
+    public String format(String separator, String[] quotes) {
+        return ""; //@@
+    }
+    protected int[] lengths;
+    protected static final int MODC = 0, HINT = 1, BEG0 = 2, END0 = 3;
+
+    // Layout of lengths:
+    //   { modCount, hint, -1==beg[0], end[0]==beg[1], ..., length }
+    // Note that each beg[i]..end[i] span includes a leading separator,
+    // which is not part of the corresponding token.
+    protected final CharSequence getCS(int i) {
+        return (CharSequence) get(i);
+    }
+
+    // Produce (and cache) an table of indexes for each token.
+    protected int[] getLengths() {
+        int[] lengths = this.lengths;
+        ;
+        int sepLength = separator.length();
+        if (lengths == null || lengths[MODC] != modCount) {
+            int size = this.size();
+            lengths = new int[END0 + size + (size == 0 ? 1 : 0)];
+            lengths[MODC] = modCount;
+            int end = -sepLength;  // cancels leading separator
+            lengths[BEG0] = end;
+            for (int i = 0; i < size; i++) {
+                end += sepLength;  // count leading separator
+                end += getCS(i).length();
+                lengths[END0 + i] = end;
+            }
+            this.lengths = lengths;
+        }
+        return lengths;
+    }
+
+    public int length() {
+        int[] lengths = getLengths();
+        return lengths[lengths.length - 1];
+    }
+
+    // Which token does the given index belong to?
+    protected int which(int i) {
+        if (i < 0) {
+            return -1;
+        }
+        int[] lengths = getLengths();
+        for (int hint = lengths[HINT];; hint = 0) {
+            for (int wh = hint; wh < lengths.length - END0; wh++) {
+                int beg = lengths[BEG0 + wh];
+                int end = lengths[END0 + wh];
+                if (i >= beg && i < end) {
+                    lengths[HINT] = wh;
+                    return wh;
+                }
+            }
+            if (hint == 0) {
+                return size();  // end of the line
+            }
+        }
+    }
+
+    public char charAt(int i) {
+        if (i < 0) {
+            return "".charAt(i);
+        }
+        int wh = which(i);
+        int beg = lengths[BEG0 + wh];
+        int j = i - beg;
+        int sepLength = separator.length();
+        if (j < sepLength) {
+            return separator.charAt(j);
+        }
+        return getCS(wh).charAt(j - sepLength);
+    }
+
+    public CharSequence subSequence(int beg, int end) {
+        //System.out.println("i: "+beg+".."+end);
+        if (beg == end) {
+            return "";
+        }
+        if (beg < 0) {
+            charAt(beg);  // raise exception
+        }
+        if (beg > end) {
+            charAt(-1);   // raise exception
+        }
+        int begWh = which(beg);
+        int endWh = which(end);
+        if (endWh == size() || end == lengths[BEG0 + endWh]) {
+            --endWh;
+        }
+        //System.out.println("wh: "+begWh+".."+endWh);
+        int begBase = lengths[BEG0 + begWh];
+        int endBase = lengths[BEG0 + endWh];
+        int sepLength = separator.length();
+        int begFrag = 0;
+        if ((beg - begBase) < sepLength) {
+            begFrag = sepLength - (beg - begBase);
+            beg += begFrag;
+        }
+        int endFrag = 0;
+        if ((end - endBase) < sepLength) {
+            endFrag = (end - endBase);
+            end = endBase;
+            endBase = lengths[BEG0 + --endWh];
+        }
+        if (false) {
+            System.out.print("beg[wbf]end[wbf]");
+            int pr[] = {begWh, begBase, begFrag, beg, endWh, endBase, endFrag, end};
+            for (int k = 0; k < pr.length; k++) {
+                System.out.print((k == 4 ? "   " : " ") + (pr[k]));
+            }
+            System.out.println();
+        }
+        if (begFrag > 0 && (end + endFrag) - begBase <= sepLength) {
+            // Special case:  Slice the separator.
+            beg -= begFrag;
+            end += endFrag;
+            return separator.substring(beg - begBase, end - begBase);
+        }
+        if (begWh == endWh && (begFrag + endFrag) == 0) {
+            // Special case:  Slice a single token.
+            return getCS(begWh).subSequence(beg - begBase - sepLength,
+                    end - endBase - sepLength);
+        }
+        Object[] subTokens = new Object[1 + (endWh - begWh) + 1];
+        int fillp = 0;
+        if (begFrag == sepLength) {
+            // Insert a leading null token to force an initial separator.
+            subTokens[fillp++] = "";
+            begFrag = 0;
+        }
+        for (int wh = begWh; wh <= endWh; wh++) {
+            CharSequence cs = getCS(wh);
+            if (wh == begWh || wh == endWh) {
+                // Slice it.
+                int csBeg = (wh == begWh) ? (beg - begBase) - sepLength : 0;
+                int csEnd = (wh == endWh) ? (end - endBase) - sepLength : cs.length();
+                cs = cs.subSequence(csBeg, csEnd);
+                if (begFrag > 0 && wh == begWh) {
+                    cs = separator.substring(sepLength - begFrag) + cs;
+                }
+                if (endFrag > 0 && wh == endWh) {
+                    cs = cs.toString() + separator.substring(0, endFrag);
+                }
+            }
+            subTokens[fillp++] = cs;
+        }
+        return new TokenList(subTokens, 0, fillp, separator);
+    }
+
+    /** Returns the concatenation of all tokens,
+     *  with intervening separator characters.
+     */
+    public String toString() {
+        StringBuilder buf = new StringBuilder(length());
+        int size = this.size();
+        for (int i = 0; i < size; i++) {
+            if (i > 0) {
+                buf.append(separator);
+            }
+            buf.append(get(i));
+        }
+        return buf.toString();
+    }
+
+    /*---- TESTING CODE ----
+    public static void main(String[] av) {
+    if (av.length == 0)  av = new String[]{"one", "2", "", "four"};
+    TokenList ts = new TokenList();
+    final String SEP = ", ";
+    ts.setSeparator(SEP);
+    for (int i = -1; i < av.length; i++) {
+    if (i >= 0)  ts.addToken(av[i]);
+    {
+    TokenList tsCopy = new TokenList(ts.toString(), SEP);
+    if (!tsCopy.equals(ts)) {
+    tsCopy.setSeparator(")(");
+    System.out.println("!= ("+tsCopy+")");
+    }
+    }
+    {
+    TokenList tsBar = new TokenList(ts, "|");
+    tsBar.add(0, "[");
+    tsBar.add("]");
+    System.out.println(tsBar);
+    }
+    if (false) {
+    int[] ls = ts.getLengths();
+    System.out.println("ts: "+ts);
+    System.out.print("ls: {");
+    for (int j = 0; j < ls.length; j++)  System.out.print(" "+ls[j]);
+    System.out.println(" }");
+    }
+    assert0(ts.size() == i+1);
+    assert0(i < 0 || ts.get(i) == av[i]);
+    String tss = ts.toString();
+    int tslen = tss.length();
+    assert0(ts.length() == tss.length());
+    for (int n = 0; n < tslen; n++) {
+    assert0(ts.charAt(n) == tss.charAt(n));
+    }
+    for (int j = 0; j < tslen; j++) {
+    for (int k = tslen; k >= j; k--) {
+    CharSequence sub = ts.subSequence(j, k);
+    //System.out.println("|"+sub+"|");
+    assert0(sub.toString().equals(tss.substring(j, k)));
+    }
+    }
+    }
+    }
+    static void assert0(boolean z) {
+    if (!z)  throw new RuntimeException("assert failed");
+    }
+    // ---- TESTING CODE ----*/
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/XMLKit.java	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,4330 @@
+/*
+ * 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 -*-
+
+// XML Implementation packages:
+import java.util.*;
+
+import java.io.Reader;
+import java.io.Writer;
+import java.io.OutputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.BufferedReader;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.StringReader;
+
+import java.io.IOException;
+
+import org.xml.sax.XMLReader;
+import org.xml.sax.InputSource;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.Attributes;
+import org.xml.sax.ext.LexicalHandler;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * A kit of methods and classes useful for manipulating XML trees in
+ * memory. They are very compact and easy to use. An XML element
+ * occupies six pointers of overhead (like two arrays) plus a pointer
+ * for its name, each attribute name and value, and each sub-element.
+ * Many useful XML operations (or Lisp-like calls) can be accomplished
+ * with a single method call on an element itself.
+ * <p>
+ * There is strong integration with the Java collection classes.
+ * There are viewing and conversion operators to and from various
+ * collection types. Elements directly support list iterators.
+ * Most <tt>List</tt> methods work analogously on elements.
+ * <p>
+ * Because of implementation compromises, these XML trees are less
+ * functional than many standard XML classes.
+ * <ul>
+ * <li>There are no parent or sibling pointers in the tree.</li>
+ * <li>Attribute names are simple strings, with no namespaces.</li>
+ * <li>There is no internal support for schemas or validation.</li>
+ * </ul>
+ * <p>
+ * Here is a summary of functionality in <tt>XMLKit</tt>.
+ * (Overloaded groups of methods are summarized by marking some
+ * arguments optional with their default values. Some overloaded
+ * arguments are marked with their alternative types separated by
+ * a bar "|". Arguments or return values for which a null is
+ * specially significant are marked by an alternative "|null".
+ * Accessors which have corresponding setters are marked
+ * by "/set". Removers which have corresponding retainers are marked
+ * by "/retain".)
+ * <pre>
+ * --- element construction
+ * new Element(int elemCapacity=4), String name=""
+ * new Element(String name, String[] attrs={}, Element[] elems={}, int elemCapacity=4)
+ * new Element(String name, String[] attrs, Object[] elems, int elemCapacity=4)
+ * new Element(Element original) // shallow copy
+ * new Element(String name="", Collection elems) // coercion
+ *
+ * Element shallowCopy()
+ * Element shallowFreeze() // side-effecting
+ * Element deepCopy()
+ * Element deepFreeze() // not side-effecting
+ *
+ * EMPTY // frozen empty anonymous element
+ * void ensureExtraCapacity(int)
+ * void trimToSize()
+ * void sortAttrs() // sort by key
+ *
+ * --- field accessors
+ * String getName()/set
+ * int size()
+ * boolean isEmpty()
+ * boolean isFrozen()
+ * boolean isAnonymous()
+ * int getExtraCapacity()/set
+ * int attrSize()
+ *
+ * --- attribute accessors
+ * String getAttr(int i)/set
+ * String getAttrName(int i)
+ *
+ * String getAttr(String key)/set
+ * List getAttrList(String key)/set
+ * Number getAttrNumber(String key)/set
+ * long getAttrLong(String key)/set
+ * double getAttrDouble(String key)/set
+ *
+ * String getAttr(String key, String dflt=null)
+ * long getAttrLong(String key, long dflt=0)
+ * double getAttrDouble(String key, double dflt=0)
+ *
+ * Element copyAttrsOnly()
+ * Element getAttrs()/set =&gt; <em>&lt;&gt;&lt;key&gt;value&lt;/key&gt;...&lt;/&gt;</em>
+ * void addAttrs(Element attrs)
+ *
+ * void removeAttr(int i)
+ * void clearAttrs()
+ *
+ * --- element accessors
+ * Object get(int i)/set
+ * Object getLast() | null
+ * Object[] toArray()
+ * Element copyContentOnly()
+ *
+ * void add(int i=0, Object subElem)
+ * int addAll(int i=0, Collection | Element elems)
+ * int addContent(int i=0, TokenList|Element|Object|null)
+ * void XMLKit.addContent(TokenList|Element|Object|null, Collection sink|null)
+ *
+ * void clear(int beg=0, int end=size)
+ * void sort(Comparator=contentOrder())
+ * void reverse()
+ * void shuffle(Random rnd=(anonymous))
+ * void rotate(int distance)
+ * Object min/max(Comparator=contentOrder())
+ *
+ * --- text accessors
+ * CharSequence getText()/set
+ * CharSequence getUnmarkedText()
+ * int addText(int i=size, CharSequence)
+ * void trimText();
+ *
+ * --- views
+ * List asList() // element view
+ * ListIterator iterator()
+ * PrintWriter asWriter()
+ * Map asAttrMap()
+ * Iterable<CharSequence> texts()
+ * Iterable<Element> elements()
+ * Iterable<T> partsOnly(Class<T>)
+ * String[] toStrings()
+ *
+ * --- queries
+ * boolean equals(Element | Object)
+ * int compareTo(Element | Object)
+ * boolean equalAttrs(Element)
+ * int hashCode()
+ * boolean isText() // every sub-elem is CharSequence
+ * boolean hasText() // some sub-elem is CharSequence
+ *
+ * boolean contains(Object)
+ * boolean containsAttr(String)
+ *
+ * int indexOf(Object)
+ * int indexOf(Filter, int fromIndex=0)
+ * int lastIndexOf(Object)
+ * int lastIndexOf(Filter, int fromIndex=size-1)
+ *
+ * int indexOfAttr(String)
+ *
+ * // finders, removers, and replacers do addContent of each filtered value
+ * // (i.e., TokenLists and anonymous Elements are broken out into their parts)
+ * boolean matches(Filter)
+ *
+ * Object find(Filter, int fromIndex=0)
+ * Object findLast(Filter, int fromIndex=size-1)
+ * Element findAll(Filter, int fromIndex=0 &amp; int toIndex=size)
+ * int findAll(Filter, Collection sink | null, int fromIndex=0 &amp; int toIndex=size)
+ *
+ * Element removeAllInTree(Filter)/retain
+ * int findAllInTree(Filter, Collection sink | null)
+ * int countAllInTree(Filter)
+ * Element removeAllInTree(Filter)/retain
+ * int removeAllInTree(Filter, Collection sink | null)/retain
+ * void replaceAllInTree(Filter)
+ *
+ * Element findElement(String name=any)
+ * Element findAllElements(String name=any)
+ *
+ * Element findWithAttr(String key, String value=any)
+ * Element findAllWithAttr(String key, String value=any)
+ *
+ * Element removeElement(String name=any)
+ * Element removeAllElements(String name=any)/retain
+ *
+ * Element removeWithAttr(String key, String value=any)
+ * Element removeAllWithAttr(String key, String value=any)/retain
+ *
+ * //countAll is the same as findAll but with null sink
+ * int countAll(Filter)
+ * int countAllElements(String name=any)
+ * int countAllWithAttr(String key, String value=any)
+ *
+ * void replaceAll(Filter, int fromIndex=0 &amp; int toIndex=size)
+ * void replaceAllInTree(Filter)
+ * void XMLKit.replaceAll(Filter, List target) //if(fx){remove x;addContent fx}
+ *
+ * --- element mutators
+ * boolean remove(Object)
+ * Object remove(int)
+ * Object removeLast() | null
+ *
+ * Object remove(Filter, int fromIndex=0)
+ * Object removeLast(Filter, int fromIndex=size-1)
+ * Element sink = removeAll(Filter, int fromIndex=0 &amp; int toIndex=size)/retain
+ * int count = removeAll(Filter, int fromIndex=0 &amp; int toIndex=size, Collection sink | null)/retain
+ *
+ * Element removeAllElements(String name=any)
+ *
+ * --- attribute mutators
+ * ??int addAllAttrsFrom(Element attrSource)
+ *
+ * --- parsing and printing
+ * void tokenize(String delims=whitespace, returnDelims=false)
+ * void writeTo(Writer)
+ * void writePrettyTo(Writer)
+ * String prettyString()
+ * String toString()
+ *
+ * ContentHandler XMLKit.makeBuilder(Collection sink, tokenizing=false, makeFrozen=false) // for standard XML parser
+ * Element XMLKit.readFrom(Reader, tokenizing=false, makeFrozen=false)
+ * void XMLKit.prettyPrintTo(Writer | OutputStream, Element)
+ * class XMLKit.Printer(Writer) { void print/Recursive(Element) }
+ * void XMLKit.output(Object elem, ContentHandler, LexicalHandler=null)
+ * void XMLKit.writeToken(String, char quote, Writer)
+ * void XMLKit.writeCData(String, Writer)
+ * Number XMLKit.convertToNumber(String, Number dflt=null)
+ * long XMLKit.convertToLong(String, long dflt=0)
+ * double XMLKit.convertToDouble(String, double dflt=0)
+ *
+ * --- filters
+ * XMLKit.ElementFilter { Element filter(Element) }
+ * XMLKit.elementFilter(String name=any | Collection nameSet)
+ * XMLKit.AttrFilter(String key) { boolean test(String value) }
+ * XMLKit.attrFilter(String key, String value=any)
+ * XMLKit.attrFilter(Element matchThis, String key)
+ * XMLKit.classFilter(Class)
+ * XMLKit.textFilter() // matches any CharSequence
+ * XMLKit.specialFilter() // matches any Special element
+ * XMLKit.methodFilter(Method m, Object[] args=null, falseResult=null)
+ * XMLKit.testMethodFilter(Method m, Object[] args=null)
+ * XMLKit.not(Filter) // inverts sense of Filter
+ * XMLKit.and(Filter&amp;Filter | Filter[])
+ * XMLKit.or(Filter&amp;Filter | Filter[])
+ * XMLKit.stack(Filter&amp;Filter | Filter[]) // result is (fx && g(fx))
+ * XMLKit.content(Filter, Collection sink) // copies content to sink
+ * XMLKit.replaceInTree(Filter pre, Filter post=null) // pre-replace else recur
+ * XMLKit.findInTree(Filter pre, Collection sink=null) // pre-find else recur
+ * XMLKit.nullFilter() // ignores input, always returns null (i.e., false)
+ * XMLKit.selfFilter( ) // always returns input (i.e., true)
+ * XMLKit.emptyFilter() // ignores input, always returns EMPTY
+ * XMLKit.constantFilter(Object) // ignores input, always returns constant
+ *
+ * --- misc
+ * Comparator XMLKit.contentOrder() // for comparing/sorting mixed content
+ * Method XMLKit.Element.method(String name) // returns Element method
+ * </pre>
+ *
+ * @author jrose
+ */
+public abstract class XMLKit {
+
+    private XMLKit() {
+    }
+    // We need at least this much slop if the element is to stay unfrozen.
+    static final int NEED_SLOP = 1;
+    static final Object[] noPartsFrozen = {};
+    static final Object[] noPartsNotFrozen = new Object[NEED_SLOP];
+    static final String WHITESPACE_CHARS = " \t\n\r\f";
+    static final String ANON_NAME = new String("*");  // unique copy of "*"
+
+    public static final class Element implements Comparable<Element>, Iterable<Object> {
+        // Note:  Does not implement List, because it has more
+        // significant parts besides its sub-elements.  Therefore,
+        // hashCode and equals must be more distinctive than Lists.
+
+        // <name> of element
+        String name;
+        // number of child elements, in parts[0..size-1]
+        int size;
+        // The parts start with child elements::  {e0, e1, e2, ...}.
+        // Following that are optional filler elements, all null.
+        // Following that are attributes as key/value pairs.
+        // They are in reverse: {...key2, val2, key1, val1, key0, val0}.
+        // Child elements and attr keys and values are never null.
+        Object[] parts;
+
+        // Build a partially-constructed node.
+        // Caller is responsible for initializing promised attributes.
+        Element(String name, int size, int capacity) {
+            this.name = name.toString();
+            this.size = size;
+            assert (size <= capacity);
+            this.parts = capacity > 0 ? new Object[capacity] : noPartsFrozen;
+        }
+
+        /** An anonymous, empty element.
+         *  Optional elemCapacity argument is expected number of sub-elements.
+         */
+        public Element() {
+            this(ANON_NAME, 0, NEED_SLOP + 4);
+        }
+
+        public Element(int extraCapacity) {
+            this(ANON_NAME, 0, NEED_SLOP + Math.max(0, extraCapacity));
+        }
+
+        /** An empty element with the given name.
+         *  Optional extraCapacity argument is expected number of sub-elements.
+         */
+        public Element(String name) {
+            this(name, 0, NEED_SLOP + 4);
+        }
+
+        public Element(String name, int extraCapacity) {
+            this(name, 0, NEED_SLOP + Math.max(0, extraCapacity));
+        }
+
+        /** An empty element with the given name and attributes.
+         *  Optional extraCapacity argument is expected number of sub-elements.
+         */
+        public Element(String name, String... attrs) {
+            this(name, attrs, (Element[]) null, 0);
+        }
+
+        public Element(String name, String[] attrs, int extraCapacity) {
+            this(name, attrs, (Element[]) null, extraCapacity);
+        }
+
+        /** An empty element with the given name and sub-elements.
+         *  Optional extraCapacity argument is expected extra sub-elements.
+         */
+        public Element(String name, Element... elems) {
+            this(name, (String[]) null, elems, 0);
+        }
+
+        public Element(String name, Element[] elems, int extraCapacity) {
+            this(name, (String[]) null, elems, extraCapacity);
+        }
+
+        /** An empty element with the given name, attributes, and sub-elements.
+         *  Optional extraCapacity argument is expected extra sub-elements.
+         */
+        public Element(String name, String[] attrs, Object... elems) {
+            this(name, attrs, elems, 0);
+        }
+
+        public Element(String name, String[] attrs, Object[] elems, int extraCapacity) {
+            this(name, 0,
+                    ((elems == null) ? 0 : elems.length)
+                    + Math.max(0, extraCapacity)
+                    + NEED_SLOP
+                    + ((attrs == null) ? 0 : attrs.length));
+            int ne = ((elems == null) ? 0 : elems.length);
+            int na = ((attrs == null) ? 0 : attrs.length);
+            int fillp = 0;
+            for (int i = 0; i < ne; i++) {
+                if (elems[i] != null) {
+                    parts[fillp++] = elems[i];
+                }
+            }
+            size = fillp;
+            for (int i = 0; i < na; i += 2) {
+                setAttr(attrs[i + 0], attrs[i + 1]);
+            }
+        }
+
+        public Element(Collection c) {
+            this(c.size());
+            addAll(c);
+        }
+
+        public Element(String name, Collection c) {
+            this(name, c.size());
+            addAll(c);
+        }
+
+        /** Shallow copy.  Same as old.shallowCopy().
+         *  Optional extraCapacity argument is expected extra sub-elements.
+         */
+        public Element(Element old) {
+            this(old, 0);
+        }
+
+        public Element(Element old, int extraCapacity) {
+            this(old.name, old.size,
+                    old.size
+                    + Math.max(0, extraCapacity) + NEED_SLOP
+                    + old.attrLength());
+            // copy sub-elements
+            System.arraycopy(old.parts, 0, parts, 0, size);
+            int alen = parts.length
+                    - (size + Math.max(0, extraCapacity) + NEED_SLOP);
+            // copy attributes
+            System.arraycopy(old.parts, old.parts.length - alen,
+                    parts, parts.length - alen,
+                    alen);
+            assert (!isFrozen());
+        }
+
+        /** Shallow copy.  Same as new Element(this). */
+        public Element shallowCopy() {
+            return new Element(this);
+        }
+        static public final Element EMPTY = new Element(ANON_NAME, 0, 0);
+
+        Element deepFreezeOrCopy(boolean makeFrozen) {
+            if (makeFrozen && isFrozen()) {
+                return this;  // no need to copy it
+            }
+            int alen = attrLength();
+            int plen = size + (makeFrozen ? 0 : NEED_SLOP) + alen;
+            Element copy = new Element(name, size, plen);
+            // copy attributes
+            System.arraycopy(parts, parts.length - alen, copy.parts, plen - alen, alen);
+            // copy sub-elements
+            for (int i = 0; i < size; i++) {
+                Object e = parts[i];
+                String str;
+                if (e instanceof Element) {  // recursion is common case
+                    e = ((Element) e).deepFreezeOrCopy(makeFrozen);
+                } else if (makeFrozen) {
+                    // Freeze StringBuffers, etc.
+                    e = fixupString(e);
+                }
+                copy.setRaw(i, e);
+            }
+            return copy;
+        }
+
+        /** Returns new Element(this), and also recursively copies sub-elements. */
+        public Element deepCopy() {
+            return deepFreezeOrCopy(false);
+        }
+
+        /** Returns frozen version of deepCopy. */
+        public Element deepFreeze() {
+            return deepFreezeOrCopy(true);
+        }
+
+        /** Freeze this element.
+         *  Throw an IllegalArgumentException if any sub-element is not already frozen.
+         *  (Use deepFreeze() to make a frozen copy of an entire element tree.)
+         */
+        public void shallowFreeze() {
+            if (isFrozen()) {
+                return;
+            }
+            int alen = attrLength();
+            Object[] nparts = new Object[size + alen];
+            // copy attributes
+            System.arraycopy(parts, parts.length - alen, nparts, size, alen);
+            // copy sub-elements
+            for (int i = 0; i < size; i++) {
+                Object e = parts[i];
+                String str;
+                if (e instanceof Element) {  // recursion is common case
+                    if (!((Element) e).isFrozen()) {
+                        throw new IllegalArgumentException("Sub-element must be frozen.");
+                    }
+                } else {
+                    // Freeze StringBuffers, etc.
+                    e = fixupString(e);
+                }
+                nparts[i] = e;
+            }
+            parts = nparts;
+            assert (isFrozen());
+        }
+
+        /** Return the name of this element. */
+        public String getName() {
+            return name;
+        }
+
+        /** Change the name of this element. */
+        public void setName(String name) {
+            checkNotFrozen();
+            this.name = name.toString();
+        }
+
+        /** Reports if the element's name is a particular string (spelled "*").
+         *  Such elements are created by the nullary Element constructor,
+         *  and by query functions which return multiple values,
+         *  such as <tt>findAll</tt>.
+         */
+        public boolean isAnonymous() {
+            return name == ANON_NAME;
+        }
+
+        /** Return number of elements.  (Does not include attributes.) */
+        public int size() {
+            return size;
+        }
+
+        /** True if no elements.  (Does not consider attributes.) */
+        public boolean isEmpty() {
+            return size == 0;
+        }
+
+        /** True if this element does not allow modification. */
+        public boolean isFrozen() {
+            // It is frozen iff there is no slop space.
+            return !hasNulls(NEED_SLOP);
+        }
+
+        void checkNotFrozen() {
+            if (isFrozen()) {
+                throw new UnsupportedOperationException("cannot modify frozen element");
+            }
+        }
+
+        /** Remove specified elements.  (Does not affect attributes.) */
+        public void clear() {
+            clear(0, size);
+        }
+
+        public void clear(int beg) {
+            clear(beg, size);
+        }
+
+        public void clear(int beg, int end) {
+            if (end > size) {
+                badIndex(end);
+            }
+            if (beg < 0 || beg > end) {
+                badIndex(beg);
+            }
+            if (beg == end) {
+                return;
+            }
+            checkNotFrozen();
+            if (end == size) {
+                if (beg == 0
+                        && parts.length > 0 && parts[parts.length - 1] == null) {
+                    // If no attributes, free the parts array.
+                    parts = noPartsNotFrozen;
+                    size = 0;
+                } else {
+                    clearParts(beg, size);
+                    size = beg;
+                }
+            } else {
+                close(beg, end - beg);
+            }
+        }
+
+        void clearParts(int beg, int end) {
+            for (int i = beg; i < end; i++) {
+                parts[i] = null;
+            }
+        }
+
+        /** True if name, attributes, and elements are the same. */
+        public boolean equals(Element that) {
+            if (!this.name.equals(that.name)) {
+                return false;
+            }
+            if (this.size != that.size) {
+                return false;
+            }
+            // elements must be equal and ordered
+            Object[] thisParts = this.parts;
+            Object[] thatParts = that.parts;
+            for (int i = 0; i < size; i++) {
+                Object thisPart = thisParts[i];
+                Object thatPart = thatParts[i];
+
+                if (thisPart instanceof Element) { // recursion is common case
+                    if (!thisPart.equals(thatPart)) {
+                        return false;
+                    }
+                } else {
+                    // If either is a non-string char sequence, normalize it.
+                    thisPart = fixupString(thisPart);
+                    thatPart = fixupString(thatPart);
+                    if (!thisPart.equals(thatPart)) {
+                        return false;
+                    }
+                }
+            }
+            // finally, attributes must be equal (unordered)
+            return this.equalAttrs(that);
+        }
+        // bridge method
+
+        public boolean equals(Object o) {
+            if (!(o instanceof Element)) {
+                return false;
+            }
+            return equals((Element) o);
+        }
+
+        public int hashCode() {
+            int hc = 0;
+            int alen = attrLength();
+            for (int i = parts.length - alen; i < parts.length; i += 2) {
+                hc += (parts[i + 0].hashCode() ^ parts[i + 1].hashCode());
+            }
+            hc ^= hc << 11;
+            hc += name.hashCode();
+            for (int i = 0; i < size; i++) {
+                hc ^= hc << 7;
+                Object p = parts[i];
+                if (p instanceof Element) {
+                    hc += p.hashCode();  // recursion is common case
+                } else {
+                    hc += fixupString(p).hashCode();
+                }
+            }
+            hc ^= hc >>> 19;
+            return hc;
+        }
+
+        /** Compare lexicographically.  Earlier-spelled attrs are more sigificant. */
+        public int compareTo(Element that) {
+            int r;
+            // Primary key is element name.
+            r = this.name.compareTo(that.name);
+            if (r != 0) {
+                return r;
+            }
+
+            // Secondary key is attributes, as if in normal key order.
+            // The key/value pairs are sorted as a token sequence.
+            int thisAlen = this.attrLength();
+            int thatAlen = that.attrLength();
+            if (thisAlen != 0 || thatAlen != 0) {
+                r = compareAttrs(thisAlen, that, thatAlen, true);
+                assert (assertAttrCompareOK(r, that));
+                if (r != 0) {
+                    return r;
+                }
+            }
+
+            // Finally, elements should be equal and ordered,
+            // and the first difference rules.
+            Object[] thisParts = this.parts;
+            Object[] thatParts = that.parts;
+            int minSize = this.size;
+            if (minSize > that.size) {
+                minSize = that.size;
+            }
+            Comparator<Object> cc = contentOrder();
+            for (int i = 0; i < minSize; i++) {
+                r = cc.compare(thisParts[i], thatParts[i]);
+                if (r != 0) {
+                    return r;
+                }
+            }
+            //if (this.size < that.size)  return -1;
+            return this.size - that.size;
+        }
+
+        private boolean assertAttrCompareOK(int r, Element that) {
+            Element e0 = this.copyAttrsOnly();
+            Element e1 = that.copyAttrsOnly();
+            e0.sortAttrs();
+            e1.sortAttrs();
+            int r2;
+            for (int k = 0;; k++) {
+                boolean con0 = e0.containsAttr(k);
+                boolean con1 = e1.containsAttr(k);
+                if (con0 != con1) {
+                    if (!con0) {
+                        r2 = 0 - 1;
+                        break;
+                    }
+                    if (!con1) {
+                        r2 = 1 - 0;
+                        break;
+                    }
+                }
+                if (!con0) {
+                    r2 = 0;
+                    break;
+                }
+                String k0 = e0.getAttrName(k);
+                String k1 = e1.getAttrName(k);
+                r2 = k0.compareTo(k1);
+                if (r2 != 0) {
+                    break;
+                }
+                String v0 = e0.getAttr(k);
+                String v1 = e1.getAttr(k);
+                r2 = v0.compareTo(v1);
+                if (r2 != 0) {
+                    break;
+                }
+            }
+            if (r != 0) {
+                r = (r > 0) ? 1 : -1;
+            }
+            if (r2 != 0) {
+                r2 = (r2 > 0) ? 1 : -1;
+            }
+            if (r != r2) {
+                System.out.println("*** wrong attr compare, " + r + " != " + r2);
+                System.out.println(" this = " + this);
+                System.out.println("  attr->" + e0);
+                System.out.println(" that = " + that);
+                System.out.println("  attr->" + e1);
+            }
+            return r == r2;
+        }
+
+        private void badIndex(int i) {
+            Object badRef = (new Object[0])[i];
+        }
+
+        public Object get(int i) {
+            if (i >= size) {
+                badIndex(i);
+            }
+            return parts[i];
+        }
+
+        public Object set(int i, Object e) {
+            if (i >= size) {
+                badIndex(i);
+            }
+            e.getClass();  // null check
+            checkNotFrozen();
+            Object old = parts[i];
+            setRaw(i, e);
+            return old;
+        }
+
+        void setRaw(int i, Object e) {
+            parts[i] = e;
+        }
+
+        public boolean remove(Object e) {
+            int i = indexOf(e);
+            if (i < 0) {
+                return false;
+            }
+            close(i, 1);
+            return true;
+        }
+
+        public Object remove(int i) {
+            if (i >= size) {
+                badIndex(i);
+            }
+            Object e = parts[i];
+            close(i, 1);
+            return e;
+        }
+
+        public Object removeLast() {
+            if (size == 0) {
+                return null;
+            }
+            return remove(size - 1);
+        }
+
+        /** Remove the first element matching the given filter.
+         *  Return the filtered value.
+         */
+        public Object remove(Filter f) {
+            return findOrRemove(f, 0, true);
+        }
+
+        public Object remove(Filter f, int fromIndex) {
+            if (fromIndex < 0) {
+                fromIndex = 0;
+            }
+            return findOrRemove(f, fromIndex, true);
+        }
+
+        /** Remove the last element matching the given filter.
+         *  Return the filtered value.
+         */
+        public Object removeLast(Filter f) {
+            return findOrRemoveLast(f, size - 1, true);
+        }
+
+        public Object removeLast(Filter f, int fromIndex) {
+            if (fromIndex >= size) {
+                fromIndex = size - 1;
+            }
+            return findOrRemoveLast(f, fromIndex, true);
+        }
+
+        /** Remove all elements matching the given filter.
+         *  If there is a non-null collection given as a sink,
+         *  transfer removed elements to the given collection.
+         *  The int result is the number of removed elements.
+         *  If there is a null sink given, the removed elements
+         *  are discarded.  If there is no sink given, the removed
+         *  elements are returned in an anonymous container element.
+         */
+        public Element removeAll(Filter f) {
+            Element result = new Element();
+            findOrRemoveAll(f, false, 0, size, result.asList(), true);
+            return result;
+        }
+
+        public Element removeAll(Filter f, int fromIndex, int toIndex) {
+            Element result = new Element();
+            findOrRemoveAll(f, true, fromIndex, toIndex, result.asList(), true);
+            return result;
+        }
+
+        public int removeAll(Filter f, Collection<Object> sink) {
+            return findOrRemoveAll(f, false, 0, size, sink, true);
+        }
+
+        public int removeAll(Filter f, int fromIndex, int toIndex, Collection<Object> sink) {
+            return findOrRemoveAll(f, false, fromIndex, toIndex, sink, true);
+        }
+
+        /** Remove all elements not matching the given filter.
+         *  If there is a non-null collection given as a sink,
+         *  transfer removed elements to the given collection.
+         *  The int result is the number of removed elements.
+         *  If there is a null sink given, the removed elements
+         *  are discarded.  If there is no sink given, the removed
+         *  elements are returned in an anonymous container element.
+         */
+        public Element retainAll(Filter f) {
+            Element result = new Element();
+            findOrRemoveAll(f, true, 0, size, result.asList(), true);
+            return result;
+        }
+
+        public Element retainAll(Filter f, int fromIndex, int toIndex) {
+            Element result = new Element();
+            findOrRemoveAll(f, true, fromIndex, toIndex, result.asList(), true);
+            return result;
+        }
+
+        public int retainAll(Filter f, Collection<Object> sink) {
+            return findOrRemoveAll(f, true, 0, size, sink, true);
+        }
+
+        public int retainAll(Filter f, int fromIndex, int toIndex, Collection<Object> sink) {
+            return findOrRemoveAll(f, true, fromIndex, toIndex, sink, true);
+        }
+
+        public void add(int i, Object e) {
+            // (The shape of this method is tweaked for common cases.)
+            e.getClass();  // force a null check on e
+            if (hasNulls(1 + NEED_SLOP)) {
+                // Common case:  Have some slop space.
+                if (i == size) {
+                    // Most common case:  Append.
+                    setRaw(i, e);
+                    size++;
+                    return;
+                }
+                if (i > size) {
+                    badIndex(i);
+                }
+                // Second most common case:  Shift right by one.
+                open(i, 1);
+                setRaw(i, e);
+                return;
+            }
+            // Ran out of space.  Do something complicated.
+            size = expand(i, 1);
+            setRaw(i, e);
+        }
+
+        public boolean add(Object e) {
+            add(size, e);
+            return true;
+        }
+
+        public Object getLast() {
+            return size == 0 ? null : parts[size - 1];
+        }
+
+        /** Returns the text of this Element.
+         *  All sub-elements of this Element must be of type CharSequence.
+         *  A ClassCastException is raised if there are non-character sub-elements.
+         *  If there is one sub-element, return it.
+         *  Otherwise, returns a TokenList of all sub-elements.
+         *  This results in a space being placed between each adjacent pair of sub-elements.
+         */
+        public CharSequence getText() {
+            checkTextOnly();
+            if (size == 1) {
+                return parts[0].toString();
+            } else {
+                return new TokenList(parts, 0, size);
+            }
+        }
+
+        /** Provides an iterable view of this object as a series of texts.
+         *  All sub-elements of this Element must be of type CharSequence.
+         *  A ClassCastException is raised if there are non-character sub-elements.
+         */
+        public Iterable<CharSequence> texts() {
+            checkTextOnly();
+            return (Iterable<CharSequence>) (Iterable) this;
+        }
+
+        /** Returns an array of strings derived from the sub-elements of this object.
+         *  All sub-elements of this Element must be of type CharSequence.
+         *  A ClassCastException is raised if there are non-character sub-elements.
+         */
+        public String[] toStrings() {
+            //checkTextOnly();
+            String[] result = new String[size];
+            for (int i = 0; i < size; i++) {
+                result[i] = ((CharSequence) parts[i]).toString();
+            }
+            return result;
+        }
+
+        /** Like getText, except that it disregards non-text elements.
+         *  Non-text elements are replaced by their textual contents, if any.
+         *  Text elements which were separated only by non-text element
+         *  boundaries are merged into single tokens.
+         *  <p>
+         *  There is no corresponding setter, since this accessor does
+         *  not report the full state of the element.
+         */
+        public CharSequence getFlatText() {
+            if (size == 1) {
+                // Simple cases.
+                if (parts[0] instanceof CharSequence) {
+                    return parts[0].toString();
+                } else {
+                    return new TokenList();
+                }
+            }
+            if (isText()) {
+                return getText();
+            }
+            // Filter and merge.
+            Element result = new Element(size);
+            boolean merge = false;
+            for (int i = 0; i < size; i++) {
+                Object text = parts[i];
+                if (!(text instanceof CharSequence)) {
+                    // Skip, but erase this boundary.
+                    if (text instanceof Element) {
+                        Element te = (Element) text;
+                        if (!te.isEmpty()) {
+                            result.addText(te.getFlatText());
+                        }
+                    }
+                    merge = true;
+                    continue;
+                }
+                if (merge) {
+                    // Merge w/ previous token.
+                    result.addText((CharSequence) text);
+                    merge = false;
+                } else {
+                    result.add(text);
+                }
+            }
+            if (result.size() == 1) {
+                return (CharSequence) result.parts[0];
+            } else {
+                return result.getText();
+            }
+        }
+
+        /** Return true if all sub-elements are of type CharSequence. */
+        public boolean isText() {
+            for (int i = 0; i < size; i++) {
+                if (!(parts[i] instanceof CharSequence)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        /** Return true if at least one sub-element is of type CharSequence. */
+        public boolean hasText() {
+            for (int i = 0; i < size; i++) {
+                if (parts[i] instanceof CharSequence) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /** Raise a ClassCastException if !isText. */
+        public void checkTextOnly() {
+            for (int i = 0; i < size; i++) {
+                ((CharSequence) parts[i]).getClass();
+            }
+        }
+
+        /** Clears out all sub-elements, and replaces them by the given text.
+         *  A ClassCastException is raised if there are non-character sub-elements,
+         *  either before or after the change.
+         */
+        public void setText(CharSequence text) {
+            checkTextOnly();
+            clear();
+            if (text instanceof TokenList) {
+                // TL's contain only strings
+                addAll(0, (TokenList) text);
+            } else {
+                add(text);
+            }
+        }
+
+        /** Add text at the given position, merging with any previous
+         *  text element, but preserving token boundaries where possible.
+         *  <p>
+         *  In all cases, the new value of getText() is the string
+         *  concatenation of the old value of getText() plus the new text.
+         *  <p>
+         *  The total effect is to concatenate the given text to any
+         *  pre-existing text, and to do so efficiently even if there
+         *  are many such concatenations.  Also, getText calls which
+         *  return multiple tokens (in a TokenList) are respected.
+         *  For example, if x is empty, x.addText(y.getText()) puts
+         *  an exact structural copy of y's text into x.
+         *  <p>
+         *  Internal token boundaries in the original text, and in the new
+         *  text (i.e., if it is a TokenList), are preserved.  However,
+         *  at the point where new text joins old text, a StringBuffer
+         *  or new String may be created to join the last old and first
+         *  new token.
+         *  <p>
+         *  If the given text is a TokenList, add the tokens as
+         *  separate sub-elements, possibly merging the first token to
+         *  a previous text item (to avoid making a new token boundary).
+         *  <p>
+         *  If the element preceding position i is a StringBuffer,
+         *  append the first new token to it.
+         *  <p>
+         *  If the preceding element is a CharSequence, replace it by a
+         *  StringBuffer containing both its and the first new token.
+         *  <p>
+         *  If tokens are added after a StringBuffer, freeze it into a String.
+         *  <p>
+         *  Every token not merged into a previous CharSequence is added
+         *  as a new sub-element, starting at position i.
+         *  <p>
+         *  Returns the number of elements added, which is useful
+         *  for further calls to addText.  This number is zero
+         *  if the input string was null, or was successfully
+         *  merged into a StringBuffer at position i-1.
+         *  <p>
+         *  By contrast, calling add(text) always adds a new sub-element.
+         *  In that case, if there is a previous string, a separating
+         *  space is virtually present also, and will be observed if
+         *  getText() is used to return all the text together.
+         */
+        public int addText(int i, CharSequence text) {
+            if (text instanceof String) {
+                return addText(i, (String) text);
+            } else if (text instanceof TokenList) {
+                // Text is a list of tokens.
+                TokenList tl = (TokenList) text;
+                int tlsize = tl.size();
+                if (tlsize == 0) {
+                    return 0;
+                }
+                String token0 = tl.get(0).toString();
+                if (tlsize == 1) {
+                    return addText(i, token0);
+                }
+                if (mergeWithPrev(i, token0, false)) {
+                    // Add the n-1 remaining tokens.
+                    addAll(i, tl.subList(1, tlsize));
+                    return tlsize - 1;
+                } else {
+                    addAll(i, (Collection) tl);
+                    return tlsize;
+                }
+            } else {
+                return addText(i, text.toString());
+            }
+        }
+
+        public int addText(CharSequence text) {
+            return addText(size, text);
+        }
+
+        private // no reason to make this helper public
+                int addText(int i, String text) {
+            if (text.length() == 0) {
+                return 0;  // Trivial success.
+            }
+            if (mergeWithPrev(i, text, true)) {
+                return 0;  // Merged with previous token.
+            }
+            // No previous token.
+            add(i, text);
+            return 1;
+        }
+
+        // Tries to merge token with previous contents.
+        // Returns true if token is successfully disposed of.
+        // If keepSB is false, any previous StringBuffer is frozen.
+        // If keepSB is true, a StringBuffer may be created to hold
+        // the merged token.
+        private boolean mergeWithPrev(int i, String token, boolean keepSB) {
+            if (i == 0) // Trivial success if the token is length zero.
+            {
+                return (token.length() == 0);
+            }
+            Object prev = parts[i - 1];
+            if (prev instanceof StringBuffer) {
+                StringBuffer psb = (StringBuffer) prev;
+                psb.append(token);
+                if (!keepSB) {
+                    parts[i - 1] = psb.toString();
+                }
+                return true;
+            }
+            if (token.length() == 0) {
+                return true;  // Trivial success.
+            }
+            if (prev instanceof CharSequence) {
+                // Must concatenate.
+                StringBuffer psb = new StringBuffer(prev.toString());
+                psb.append(token);
+                if (keepSB) {
+                    parts[i - 1] = psb;
+                } else {
+                    parts[i - 1] = psb.toString();
+                }
+                return true;
+            }
+            return false;
+        }
+
+        /** Trim all strings, using String.trim().
+         *  Remove empty strings.
+         *  Normalize CharSequences to Strings.
+         */
+        public void trimText() {
+            checkNotFrozen();
+            int fillp = 0;
+            int size = this.size;
+            Object[] parts = this.parts;
+            for (int i = 0; i < size; i++) {
+                Object e = parts[i];
+                if (e instanceof CharSequence) {
+                    String tt = e.toString().trim();
+                    if (tt.length() == 0) {
+                        continue;
+                    }
+                    e = tt;
+                }
+                parts[fillp++] = e;
+            }
+            while (size > fillp) {
+                parts[--size] = null;
+            }
+            this.size = fillp;
+        }
+
+        /** Add one or more subelements at the given position.
+         *  If the object reference is null, nothing happens.
+         *  If the object is an anonymous Element, addAll is called.
+         *  If the object is a TokenList, addAll is called (to add the tokens).
+         *  Otherwise, add is called, adding a single subelement or string.
+         *  The net effect is to add zero or more tokens.
+         *  The returned value is the number of added elements.
+         *  <p>
+         *  Note that getText() can return a TokenList which preserves
+         *  token boundaries in the text source.  Such a text will be
+         *  added as multiple text sub-elements.
+         *  <p>
+         *  If a text string is added adjacent to an immediately
+         *  preceding string, there will be a token boundary between
+         *  the strings, which will print as an extra space.
+         */
+        public int addContent(int i, Object e) {
+            if (e == null) {
+                return 0;
+            } else if (e instanceof TokenList) {
+                return addAll(i, (Collection) e);
+            } else if (e instanceof Element
+                    && ((Element) e).isAnonymous()) {
+                return addAll(i, (Element) e);
+            } else {
+                add(i, e);
+                return 1;
+            }
+        }
+
+        public int addContent(Object e) {
+            return addContent(size, e);
+        }
+
+        public Object[] toArray() {
+            Object[] result = new Object[size];
+            System.arraycopy(parts, 0, result, 0, size);
+            return result;
+        }
+
+        public Element copyContentOnly() {
+            Element content = new Element(size);
+            System.arraycopy(parts, 0, content.parts, 0, size);
+            content.size = size;
+            return content;
+        }
+
+        public void sort(Comparator<Object> c) {
+            Arrays.sort(parts, 0, size, c);
+        }
+
+        public void sort() {
+            sort(CONTENT_ORDER);
+        }
+
+        /** Equivalent to Collections.reverse(this.asList()). */
+        public void reverse() {
+            for (int i = 0, mid = size >> 1, j = size - 1; i < mid; i++, j--) {
+                Object p = parts[i];
+                parts[i] = parts[j];
+                parts[j] = p;
+            }
+        }
+
+        /** Equivalent to Collections.shuffle(this.asList() [, rnd]). */
+        public void shuffle() {
+            Collections.shuffle(this.asList());
+        }
+
+        public void shuffle(Random rnd) {
+            Collections.shuffle(this.asList(), rnd);
+        }
+
+        /** Equivalent to Collections.rotate(this.asList(), dist). */
+        public void rotate(int dist) {
+            Collections.rotate(this.asList(), dist);
+        }
+
+        /** Equivalent to Collections.min(this.asList(), c). */
+        public Object min(Comparator<Object> c) {
+            return Collections.min(this.asList(), c);
+        }
+
+        public Object min() {
+            return min(CONTENT_ORDER);
+        }
+
+        /** Equivalent to Collections.max(this.asList(), c). */
+        public Object max(Comparator<Object> c) {
+            return Collections.max(this.asList(), c);
+        }
+
+        public Object max() {
+            return max(CONTENT_ORDER);
+        }
+
+        public int addAll(int i, Collection c) {
+            if (c instanceof LView) {
+                return addAll(i, ((LView) c).asElement());
+            } else {
+                int csize = c.size();
+                if (csize == 0) {
+                    return 0;
+                }
+                openOrExpand(i, csize);
+                int fill = i;
+                for (Object part : c) {
+                    parts[fill++] = part;
+                }
+                return csize;
+            }
+        }
+
+        public int addAll(int i, Element e) {
+            int esize = e.size;
+            if (esize == 0) {
+                return 0;
+            }
+            openOrExpand(i, esize);
+            System.arraycopy(e.parts, 0, parts, i, esize);
+            return esize;
+        }
+
+        public int addAll(Collection c) {
+            return addAll(size, c);
+        }
+
+        public int addAll(Element e) {
+            return addAll(size, e);
+        }
+
+        public int addAllAttrsFrom(Element e) {
+            int added = 0;
+            for (int k = 0; e.containsAttr(k); k++) {
+                String old = setAttr(e.getAttrName(k), e.getAttr(k));
+                if (old == null) {
+                    added += 1;
+                }
+            }
+            // Return number of added (not merely changed) attrs.
+            return added;
+        }
+
+        // Search.
+        public boolean matches(Filter f) {
+            return f.filter(this) != null;
+        }
+
+        public Object find(Filter f) {
+            return findOrRemove(f, 0, false);
+        }
+
+        public Object find(Filter f, int fromIndex) {
+            if (fromIndex < 0) {
+                fromIndex = 0;
+            }
+            return findOrRemove(f, fromIndex, false);
+        }
+
+        /** Find the last element matching the given filter.
+         *  Return the filtered value.
+         */
+        public Object findLast(Filter f) {
+            return findOrRemoveLast(f, size - 1, false);
+        }
+
+        public Object findLast(Filter f, int fromIndex) {
+            if (fromIndex >= size) {
+                fromIndex = size - 1;
+            }
+            return findOrRemoveLast(f, fromIndex, false);
+        }
+
+        /** Find all elements matching the given filter.
+         *  If there is a non-null collection given as a sink,
+         *  transfer matching elements to the given collection.
+         *  The int result is the number of matching elements.
+         *  If there is a null sink given, the matching elements are
+         *  not collected.  If there is no sink given, the matching
+         *  elements are returned in an anonymous container element.
+         *  In no case is the receiver element changed.
+         *  <p>
+         *  Note that a simple count of matching elements can be
+         *  obtained by passing a null collection argument.
+         */
+        public Element findAll(Filter f) {
+            Element result = new Element();
+            findOrRemoveAll(f, false, 0, size, result.asList(), false);
+            return result;
+        }
+
+        public Element findAll(Filter f, int fromIndex, int toIndex) {
+            Element result = new Element(name);
+            findOrRemoveAll(f, false, fromIndex, toIndex, result.asList(), false);
+            return result;
+        }
+
+        public int findAll(Filter f, Collection<Object> sink) {
+            return findOrRemoveAll(f, false, 0, size, sink, false);
+        }
+
+        public int findAll(Filter f, int fromIndex, int toIndex, Collection<Object> sink) {
+            return findOrRemoveAll(f, false, fromIndex, toIndex, sink, false);
+        }
+
+        /// Driver routines.
+        private Object findOrRemove(Filter f, int fromIndex, boolean remove) {
+            for (int i = fromIndex; i < size; i++) {
+                Object x = f.filter(parts[i]);
+                if (x != null) {
+                    if (remove) {
+                        close(i, 1);
+                    }
+                    return x;
+                }
+            }
+            return null;
+        }
+
+        private Object findOrRemoveLast(Filter f, int fromIndex, boolean remove) {
+            for (int i = fromIndex; i >= 0; i--) {
+                Object x = f.filter(parts[i]);
+                if (x != null) {
+                    if (remove) {
+                        close(i, 1);
+                    }
+                    return x;
+                }
+            }
+            return null;
+        }
+
+        private int findOrRemoveAll(Filter f, boolean fInvert,
+                int fromIndex, int toIndex,
+                Collection<Object> sink, boolean remove) {
+            if (fromIndex < 0) {
+                badIndex(fromIndex);
+            }
+            if (toIndex > size) {
+                badIndex(toIndex);
+            }
+            int found = 0;
+            for (int i = fromIndex; i < toIndex; i++) {
+                Object p = parts[i];
+                Object x = f.filter(p);
+                if (fInvert ? (x == null) : (x != null)) {
+                    if (remove) {
+                        close(i--, 1);
+                        toIndex--;
+                    }
+                    found += XMLKit.addContent(fInvert ? p : x, sink);
+                }
+            }
+            return found;
+        }
+
+        public void replaceAll(Filter f) {
+            XMLKit.replaceAll(f, this.asList());
+        }
+
+        public void replaceAll(Filter f, int fromIndex, int toIndex) {
+            XMLKit.replaceAll(f, this.asList().subList(fromIndex, toIndex));
+        }
+
+        /// Recursive walks.
+        // findAllInTree(f)     == findAll(findInTree(f,S)), S.toElement
+        // findAllInTree(f,S)   == findAll(findInTree(content(f,S)))
+        // removeAllInTree(f)   == replaceAll(replaceInTree(and(f,emptyF)))
+        // removeAllInTree(f,S) == replaceAll(replaceInTree(and(content(f,S),emptyF)))
+        // retainAllInTree(f)   == removeAllInTree(not(f))
+        // replaceAllInTree(f)  == replaceAll(replaceInTree(f))
+        public Element findAllInTree(Filter f) {
+            Element result = new Element();
+            findAllInTree(f, result.asList());
+            return result;
+        }
+
+        public int findAllInTree(Filter f, Collection<Object> sink) {
+            int found = 0;
+            int size = this.size;  // cache copy
+            for (int i = 0; i < size; i++) {
+                Object p = parts[i];
+                Object x = f.filter(p);
+                if (x != null) {
+                    found += XMLKit.addContent(x, sink);
+                } else if (p instanceof Element) {
+                    found += ((Element) p).findAllInTree(f, sink);
+                }
+            }
+            return found;
+        }
+
+        public int countAllInTree(Filter f) {
+            return findAllInTree(f, null);
+        }
+
+        public int removeAllInTree(Filter f, Collection<Object> sink) {
+            if (sink == null) {
+                sink = newCounterColl();
+            }
+            replaceAll(replaceInTree(and(content(f, sink), emptyFilter())));
+            return sink.size();
+        }
+
+        public Element removeAllInTree(Filter f) {
+            Element result = new Element();
+            removeAllInTree(f, result.asList());
+            return result;
+        }
+
+        public int retainAllInTree(Filter f, Collection<Object> sink) {
+            return removeAllInTree(not(f), sink);
+        }
+
+        public Element retainAllInTree(Filter f) {
+            Element result = new Element();
+            retainAllInTree(f, result.asList());
+            return result;
+        }
+
+        public void replaceAllInTree(Filter f) {
+            replaceAll(replaceInTree(f));
+        }
+
+        /** Raise a ClassCastException if any subelements are the wrong type. */
+        public void checkPartsOnly(Class<?> elementClass) {
+            for (int i = 0; i < size; i++) {
+                elementClass.cast(parts[i]).getClass();
+            }
+        }
+
+        /** Return true if all sub-elements are of the given type. */
+        public boolean isPartsOnly(Class<?> elementClass) {
+            for (int i = 0; i < size; i++) {
+                if (!elementClass.isInstance(parts[i])) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        /** Provides an iterable view of this object as a series of elements.
+         *  All sub-elements of this Element must be of type Element.
+         *  A ClassCastException is raised if there are non-Element sub-elements.
+         */
+        public <T> Iterable<T> partsOnly(Class<T> elementClass) {
+            checkPartsOnly(elementClass);
+            return (Iterable<T>) (Iterable) this;
+        }
+
+        public Iterable<Element> elements() {
+            return partsOnly(Element.class);
+        }
+
+        /// Useful shorthands.
+        // Finding or removing elements w/o regard to their type or content.
+        public Element findElement() {
+            return (Element) find(elementFilter());
+        }
+
+        public Element findAllElements() {
+            return findAll(elementFilter());
+        }
+
+        public Element removeElement() {
+            return (Element) remove(elementFilter());
+        }
+
+        public Element removeAllElements() {
+            return (Element) removeAll(elementFilter());
+        }
+
+        // Finding or removing by element tag or selected attribute,
+        // as if by elementFilter(name) or attrFilter(name, value).
+        // Roughly akin to Common Lisp ASSOC.
+        public Element findElement(String name) {
+            return (Element) find(elementFilter(name));
+        }
+
+        public Element removeElement(String name) {
+            return (Element) remove(elementFilter(name));
+        }
+
+        public Element findWithAttr(String key) {
+            return (Element) find(attrFilter(name));
+        }
+
+        public Element findWithAttr(String key, String value) {
+            return (Element) find(attrFilter(name, value));
+        }
+
+        public Element removeWithAttr(String key) {
+            return (Element) remove(attrFilter(name));
+        }
+
+        public Element removeWithAttr(String key, String value) {
+            return (Element) remove(attrFilter(name, value));
+        }
+
+        public Element findAllElements(String name) {
+            return findAll(elementFilter(name));
+        }
+
+        public Element removeAllElements(String name) {
+            return removeAll(elementFilter(name));
+        }
+
+        public Element retainAllElements(String name) {
+            return retainAll(elementFilter(name));
+        }
+
+        public Element findAllWithAttr(String key) {
+            return findAll(attrFilter(key));
+        }
+
+        public Element removeAllWithAttr(String key) {
+            return removeAll(attrFilter(key));
+        }
+
+        public Element retainAllWithAttr(String key) {
+            return retainAll(attrFilter(key));
+        }
+
+        public Element findAllWithAttr(String key, String value) {
+            return findAll(attrFilter(key, value));
+        }
+
+        public Element removeAllWithAttr(String key, String value) {
+            return removeAll(attrFilter(key, value));
+        }
+
+        public Element retainAllWithAttr(String key, String value) {
+            return retainAll(attrFilter(key, value));
+        }
+
+        public int countAll(Filter f) {
+            return findAll(f, null);
+        }
+
+        public int countAllElements() {
+            return countAll(elementFilter());
+        }
+
+        public int countAllElements(String name) {
+            return countAll(elementFilter(name));
+        }
+
+        public int countAllWithAttr(String key) {
+            return countAll(attrFilter(name));
+        }
+
+        public int countAllWithAttr(String key, String value) {
+            return countAll(attrFilter(key, value));
+        }
+
+        public int indexOf(Object e) {
+            for (int i = 0; i < size; i++) {
+                if (e.equals(parts[i])) {
+                    return i;
+                }
+            }
+            return -1;
+        }
+
+        public int lastIndexOf(Object e) {
+            for (int i = size - 1; i >= 0; i--) {
+                if (e.equals(parts[i])) {
+                    return i;
+                }
+            }
+            return -1;
+        }
+
+        /** Remove the first element matching the given filter.
+         *  Return the filtered value.
+         */
+        public int indexOf(Filter f) {
+            return indexOf(f, 0);
+        }
+
+        public int indexOf(Filter f, int fromIndex) {
+            if (fromIndex < 0) {
+                fromIndex = 0;
+            }
+            for (int i = fromIndex; i < size; i++) {
+                Object x = f.filter(parts[i]);
+                if (x != null) {
+                    return i;
+                }
+            }
+            return -1;
+        }
+
+        /** Remove the last element matching the given filter.
+         *  Return the filtered value.
+         */
+        public int lastIndexOf(Filter f) {
+            return lastIndexOf(f, size - 1);
+        }
+
+        public int lastIndexOf(Filter f, int fromIndex) {
+            if (fromIndex >= size) {
+                fromIndex = size - 1;
+            }
+            for (int i = fromIndex; i >= 0; i--) {
+                Object x = f.filter(parts[i]);
+                if (x != null) {
+                    return i;
+                }
+            }
+            return -1;
+        }
+
+        public boolean contains(Object e) {
+            return indexOf(e) >= 0;
+        }
+
+        // attributes
+        private int findOrCreateAttr(String key, boolean create) {
+            key.toString();  // null check
+            int attrBase = parts.length;
+            for (int i = parts.length - 2; i >= size; i -= 2) {
+                String akey = (String) parts[i + 0];
+                if (akey == null) {
+                    if (!create) {
+                        return -1;
+                    }
+                    if (i == size) {
+                        break;  // NEED_SLOP
+                    }
+                    parts[i + 0] = key;
+                    //parts[i+1] = ""; //caller responsibility
+                    return i;
+                }
+                attrBase = i;
+                if (akey.equals(key)) {
+                    return i;
+                }
+            }
+            // If we fell through, we ran into an element part.
+            // Therefore we have run out of empty slots.
+            if (!create) {
+                return -1;
+            }
+            assert (!isFrozen());
+            int alen = parts.length - attrBase;
+            expand(size, 2); // generally expands by more than 2
+            // since there was a reallocation, the garbage slots are really null
+            assert (parts[size + 0] == null && parts[size + 1] == null);
+            alen += 2;
+            int i = parts.length - alen;
+            parts[i + 0] = key;
+            //parts[i+1] = ""; //caller responsibility
+            return i;
+        }
+
+        public int attrSize() {
+            return attrLength() >>> 1;
+        }
+
+        public int indexOfAttr(String key) {
+            return findOrCreateAttr(key, false);
+        }
+
+        public boolean containsAttr(String key) {
+            return indexOfAttr(key) >= 0;
+        }
+
+        public String getAttr(String key) {
+            return getAttr(key, null);
+        }
+
+        public String getAttr(String key, String dflt) {
+            int i = findOrCreateAttr(key, false);
+            return (i < 0) ? dflt : (String) parts[i + 1];
+        }
+
+        public TokenList getAttrList(String key) {
+            return convertToList(getAttr(key));
+        }
+
+        public Number getAttrNumber(String key) {
+            return convertToNumber(getAttr(key));
+        }
+
+        public long getAttrLong(String key) {
+            return getAttrLong(key, 0);
+        }
+
+        public double getAttrDouble(String key) {
+            return getAttrDouble(key, 0.0);
+        }
+
+        public long getAttrLong(String key, long dflt) {
+            return convertToLong(getAttr(key), dflt);
+        }
+
+        public double getAttrDouble(String key, double dflt) {
+            return convertToDouble(getAttr(key), dflt);
+        }
+
+        int indexAttr(int k) {
+            int i = parts.length - (k * 2) - 2;
+            if (i < size || parts[i] == null) {
+                return -2;  // always oob
+            }
+            return i;
+        }
+
+        public boolean containsAttr(int k) {
+            return indexAttr(k) >= 0;
+        }
+
+        public String getAttr(int k) {
+            return (String) parts[indexAttr(k) + 1];
+        }
+
+        public String getAttrName(int k) {
+            return (String) parts[indexAttr(k) + 0];
+        }
+
+        public Iterable<String> attrNames() {
+            //return asAttrMap().keySet();
+            return new Iterable<String>() {
+
+                public Iterator<String> iterator() {
+                    return new ANItr();
+                }
+            };
+        }
+
+        // Hand-inlined replacement for asAttrMap().keySet().iterator():
+        class ANItr implements Iterator<String> {
+
+            boolean lastRet;
+            int cursor = -2;  // pointer from end of parts
+
+            public boolean hasNext() {
+                int i = cursor + parts.length;
+                return i >= size && parts[i] == null;
+            }
+
+            public String next() {
+                int i = cursor + parts.length;
+                Object x;
+                if (i < size || (x = parts[i]) == null) {
+                    nsee();
+                    return null;
+                }
+                cursor -= 2;
+                lastRet = true;
+                return (String) x;
+            }
+
+            public void remove() {
+                if (!lastRet) {
+                    throw new IllegalStateException();
+                }
+                Element.this.removeAttr((-4 - cursor) / 2);
+                cursor += 2;
+                lastRet = false;
+            }
+
+            Exception nsee() {
+                throw new NoSuchElementException("attribute " + (-2 - cursor) / 2);
+            }
+        }
+
+        /** Return an anonymous copy of self, but only with attributes.
+         */
+        public Element copyAttrsOnly() {
+            int alen = attrLength();
+            Element attrs = new Element(alen);
+            Object[] attrParts = attrs.parts;
+            assert (attrParts.length == NEED_SLOP + alen);
+            System.arraycopy(parts, parts.length - alen,
+                    attrParts, NEED_SLOP,
+                    alen);
+            return attrs;
+        }
+
+        /** Get all attributes, represented as an element with sub-elements.
+         *  The name of each sub-element is the attribute key, and the text
+         *  This is a fresh copy, and can be updated with affecting the original.
+         *  of each sub-element is the corresponding attribute value.
+         *  See also asAttrMap() for a "live" view of all the attributes as a Map.
+         */
+        public Element getAttrs() {
+            int asize = attrSize();
+            Element attrs = new Element(ANON_NAME, asize, NEED_SLOP + asize);
+            for (int i = 0; i < asize; i++) {
+                Element attr = new Element(getAttrName(i), 1, NEED_SLOP + 1);
+                // %%% normalize attrs to token lists?
+                attr.setRaw(0, getAttr(i));
+                attrs.setRaw(i, attr);
+            }
+            return attrs;
+        }
+
+        public void setAttrs(Element attrs) {
+            int alen = attrLength();
+            clearParts(parts.length - alen, alen);
+            if (!hasNulls(NEED_SLOP + attrs.size * 2)) {
+                expand(size, attrs.size * 2);
+            }
+            addAttrs(attrs);
+        }
+
+        public void addAttrs(Element attrs) {
+            for (int i = 0; i < attrs.size; i++) {
+                Element attr = (Element) attrs.get(i);
+                setAttr(attr.name, attr.getText().toString());
+            }
+        }
+
+        public void removeAttr(int i) {
+            checkNotFrozen();
+            while ((i -= 2) >= size) {
+                Object k = parts[i + 0];
+                Object v = parts[i + 1];
+                if (k == null) {
+                    break;
+                }
+                parts[i + 2] = k;
+                parts[i + 3] = v;
+            }
+            parts[i + 2] = null;
+            parts[i + 3] = null;
+        }
+
+        public void clearAttrs() {
+            if (parts.length == 0 || parts[parts.length - 1] == null) {
+                return;  // no attrs to clear
+            }
+            checkNotFrozen();
+            if (size == 0) {
+                // If no elements, free the parts array.
+                parts = noPartsNotFrozen;
+                return;
+            }
+            for (int i = parts.length - 1; parts[i] != null; i--) {
+                assert (i >= size);
+                parts[i] = null;
+            }
+        }
+
+        public String setAttr(String key, String value) {
+            String old;
+            if (value == null) {
+                int i = findOrCreateAttr(key, false);
+                if (i >= 0) {
+                    old = (String) parts[i + 1];
+                    removeAttr(i);
+                } else {
+                    old = null;
+                }
+            } else {
+                checkNotFrozen();
+                int i = findOrCreateAttr(key, true);
+                old = (String) parts[i + 1];
+                parts[i + 1] = value;
+            }
+            return old;
+        }
+
+        public String setAttrList(String key, List<String> l) {
+            if (l == null) {
+                return setAttr(key, null);
+            }
+            if (!(l instanceof TokenList)) {
+                l = new TokenList(l);
+            }
+            return setAttr(key, l.toString());
+        }
+
+        public String setAttrNumber(String key, Number n) {
+            return setAttr(key, (n == null) ? null : n.toString());
+        }
+
+        public String setAttrLong(String key, long n) {
+            return setAttr(key, (n == 0) ? null : String.valueOf(n));
+        }
+
+        public String setAttrDouble(String key, double n) {
+            return setAttr(key, (n == 0) ? null : String.valueOf(n));
+        }
+
+        public String setAttr(int k, String value) {
+            int i = indexAttr(k);
+            String old = (String) parts[i + 1];
+            if (value == null) {
+                removeAttr(i);
+            } else {
+                checkNotFrozen();
+                parts[i + 1] = value;
+            }
+            return old;
+        }
+
+        int attrLength() {
+            return parts.length - attrBase();
+        }
+
+        /** Are the attributes of the two two elements equal?
+         *  Disregards name, sub-elements, and ordering of attributes.
+         */
+        public boolean equalAttrs(Element that) {
+            int alen = this.attrLength();
+            if (alen != that.attrLength()) {
+                return false;
+            }
+            if (alen == 0) {
+                return true;
+            }
+            return compareAttrs(alen, that, alen, false) == 0;
+        }
+
+        private int compareAttrs(int thisAlen,
+                Element that, int thatAlen,
+                boolean fullCompare) {
+            Object[] thisParts = this.parts;
+            Object[] thatParts = that.parts;
+            int thisBase = thisParts.length - thisAlen;
+            int thatBase = thatParts.length - thatAlen;
+            // search indexes into unmatched parts of this.attrs:
+            int firstI = 0;
+            // search indexes into unmatched parts of that.attrs:
+            int firstJ = 0;
+            int lastJ = thatAlen - 2;
+            // try to find the mismatch with the first key:
+            String firstKey = null;
+            int firstKeyValCmp = 0;
+            int foundKeys = 0;
+            for (int i = 0; i < thisAlen; i += 2) {
+                String key = (String) thisParts[thisBase + i + 0];
+                String val = (String) thisParts[thisBase + i + 1];
+                String otherVal = null;
+                for (int j = firstJ; j <= lastJ; j += 2) {
+                    if (key.equals(thatParts[thatBase + j + 0])) {
+                        foundKeys += 1;
+                        otherVal = (String) thatParts[thatBase + j + 1];
+                        // Optimization:  Narrow subsequent searches when easy.
+                        if (j == lastJ) {
+                            lastJ -= 2;
+                        } else if (j == firstJ) {
+                            firstJ += 2;
+                        }
+                        if (i == firstI) {
+                            firstI += 2;
+                        }
+                        break;
+                    }
+                }
+                int valCmp;
+                if (otherVal != null) {
+                    // The key was found.
+                    if (!fullCompare) {
+                        if (!val.equals(otherVal)) {
+                            return 1 - 0; //arb.
+                        }
+                        continue;
+                    }
+                    valCmp = val.compareTo(otherVal);
+                } else {
+                    // Found the key in this but not that.
+                    // Such a mismatch puts the guy missing the key last.
+                    valCmp = 0 - 1;
+                }
+                if (valCmp != 0) {
+                    // found a mismatch, key present in both elems
+                    if (firstKey == null
+                            || firstKey.compareTo(key) > 0) {
+                        // found a better key
+                        firstKey = key;
+                        firstKeyValCmp = valCmp;
+                    }
+                }
+            }
+            // We have located the first mismatch of all keys in this.attrs.
+            // In general we must also look for keys in that.attrs but missing
+            // from this.attrs; such missing keys, if earlier than firstKey,
+            // rule the comparison.
+
+            // We can sometimes prove quickly there is no missing key.
+            if (foundKeys == thatAlen / 2) {
+                // Exhausted all keys in that.attrs.
+                return firstKeyValCmp;
+            }
+
+            // Search for a missing key in that.attrs earlier than firstKey.
+            findMissingKey:
+            for (int j = firstJ; j <= lastJ; j += 2) {
+                String otherKey = (String) thatParts[thatBase + j + 0];
+                if (firstKey == null
+                        || firstKey.compareTo(otherKey) > 0) {
+                    // Found a better key; is it missing?
+                    for (int i = firstI; i < thisAlen; i += 2) {
+                        if (otherKey.equals(thisParts[thisBase + i + 0])) {
+                            continue findMissingKey;
+                        }
+                    }
+                    // If we get here, there was no match in this.attrs.
+                    return 1 - 0;
+                }
+            }
+
+            // No missing key.  Previous comparison value rules.
+            return firstKeyValCmp;
+        }
+
+        // Binary search looking for first non-null after size.
+        int attrBase() {
+            // Smallest & largest possible attribute indexes:
+            int kmin = 0;
+            int kmax = (parts.length - size) >>> 1;
+            // earlist possible attribute position:
+            int abase = parts.length - (kmax * 2);
+            // binary search using scaled indexes:
+            while (kmin != kmax) {
+                int kmid = kmin + ((kmax - kmin) >>> 1);
+                if (parts[abase + (kmid * 2)] == null) {
+                    kmin = kmid + 1;
+                } else {
+                    kmax = kmid;
+                }
+                assert (kmin <= kmax);
+            }
+            return abase + (kmax * 2);
+        }
+
+        /** Sort attributes by name. */
+        public void sortAttrs() {
+            checkNotFrozen();
+            int abase = attrBase();
+            int alen = parts.length - abase;
+            String[] buf = new String[alen];
+            // collect keys
+            for (int k = 0; k < alen / 2; k++) {
+                String akey = (String) parts[abase + (k * 2) + 0];
+                buf[k] = akey;
+            }
+            Arrays.sort(buf, 0, alen / 2);
+            // collect values
+            for (int k = 0; k < alen / 2; k++) {
+                String akey = buf[k];
+                buf[k + alen / 2] = getAttr(akey);
+            }
+            // reorder keys and values
+            int fillp = parts.length;
+            for (int k = 0; k < alen / 2; k++) {
+                String akey = buf[k];
+                String aval = buf[k + alen / 2];
+                fillp -= 2;
+                parts[fillp + 0] = akey;
+                parts[fillp + 1] = aval;
+            }
+            assert (fillp == abase);
+        }
+
+        /*
+        Notes on whitespace and tokenization.
+        On input, never split CDATA blocks.  They remain single tokens.
+        ?Try to treat encoded characters as CDATA-quoted, also?
+
+        Internally, each String sub-element is logically a token.
+        However, if there was no token-splitting on input,
+        consecutive strings are merged by the parser.
+
+        Internally, we need addToken (intervening blank) and addText
+        (hard concatenation).
+
+        Optionally on input, tokenize unquoted text into words.
+        Between each adjacent word pair, elide either one space
+        or all space.
+
+        On output, we always add spaces between tokens.
+        The Element("a", {"b", "c", Element("d"), "e    f"})
+        outputs as "<a>b c<d/>e    f</a>"
+         */
+        /** Split strings into tokens, using a StringTokenizer. */
+        public void tokenize(String delims, boolean returnDelims) {
+            checkNotFrozen();
+            if (delims == null) {
+                delims = WHITESPACE_CHARS;  // StringTokenizer default
+            }
+            for (int i = 0; i < size; i++) {
+                if (!(parts[i] instanceof CharSequence)) {
+                    continue;
+                }
+                int osize = size;
+                String str = parts[i].toString();
+                StringTokenizer st = new StringTokenizer(str, delims, returnDelims);
+                int nstrs = st.countTokens();
+                switch (nstrs) {
+                    case 0:
+                        close(i--, 1);
+                        break;
+                    case 1:
+                        parts[i] = st.nextToken();
+                        break;
+                    default:
+                        openOrExpand(i + 1, nstrs - 1);
+                        for (int j = 0; j < nstrs; j++) {
+                            parts[i + j] = st.nextToken();
+                        }
+                        i += nstrs - 1;
+                        break;
+                }
+            }
+        }
+
+        public void tokenize(String delims) {
+            tokenize(delims, false);
+        }
+
+        public void tokenize() {
+            tokenize(null, false);
+        }
+
+        // views
+        class LView extends AbstractList<Object> {
+
+            Element asElement() {
+                return Element.this;
+            }
+
+            public int size() {
+                return Element.this.size();
+            }
+
+            public Object get(int i) {
+                return Element.this.get(i);
+            }
+
+            @Override
+            public boolean contains(Object e) {
+                return Element.this.contains(e);
+            }
+
+            @Override
+            public Object[] toArray() {
+                return Element.this.toArray();
+            }
+
+            @Override
+            public int indexOf(Object e) {
+                return Element.this.indexOf(e);
+            }
+
+            @Override
+            public int lastIndexOf(Object e) {
+                return Element.this.lastIndexOf(e);
+            }
+
+            @Override
+            public void add(int i, Object e) {
+                ++modCount;
+                Element.this.add(i, e);
+            }
+
+            @Override
+            public boolean addAll(int i, Collection<? extends Object> c) {
+                ++modCount;
+                return Element.this.addAll(i, c) > 0;
+            }
+
+            @Override
+            public boolean addAll(Collection<? extends Object> c) {
+                ++modCount;
+                return Element.this.addAll(c) > 0;
+            }
+
+            @Override
+            public Object remove(int i) {
+                ++modCount;
+                return Element.this.remove(i);
+            }
+
+            @Override
+            public Object set(int i, Object e) {
+                ++modCount;
+                return Element.this.set(i, e);
+            }
+
+            @Override
+            public void clear() {
+                ++modCount;
+                Element.this.clear();
+            }
+            // Others: toArray(Object[]), containsAll, removeAll, retainAll
+        }
+
+        /** Produce a list view of sub-elements.
+         *  (The list view does not provide access to the element's
+         *  name or attributes.)
+         *  Changes to this view are immediately reflected in the
+         *  element itself.
+         */
+        public List<Object> asList() {
+            return new LView();
+        }
+
+        /** Produce a list iterator on all sub-elements. */
+        public ListIterator<Object> iterator() {
+            //return asList().listIterator();
+            return new Itr();
+        }
+
+        // Hand-inlined replacement for LView.listIterator():
+        class Itr implements ListIterator<Object> {
+
+            int lastRet = -1;
+            int cursor = 0;
+
+            public boolean hasNext() {
+                return cursor < size;
+            }
+
+            public boolean hasPrevious() {
+                return cursor > 0 && cursor <= size;
+            }
+
+            public Object next() {
+                if (!hasNext()) {
+                    nsee();
+                }
+                return parts[lastRet = cursor++];
+            }
+
+            public Object previous() {
+                if (!hasPrevious()) {
+                    nsee();
+                }
+                return parts[--cursor];
+            }
+
+            public int nextIndex() {
+                return cursor;
+            }
+
+            public int previousIndex() {
+                return cursor - 1;
+            }
+
+            public void set(Object x) {
+                parts[lastRet] = x;
+            }
+
+            public void add(Object x) {
+                lastRet = -1;
+                Element.this.add(cursor++, x);
+            }
+
+            public void remove() {
+                if (lastRet < 0) {
+                    throw new IllegalStateException();
+                }
+                Element.this.remove(lastRet);
+                if (lastRet < cursor) {
+                    --cursor;
+                }
+                lastRet = -1;
+            }
+
+            void nsee() {
+                throw new NoSuchElementException("element " + cursor);
+            }
+        }
+
+        /** A PrintWriter which always appends as if by addText.
+         *  Use of this stream may insert a StringBuffer at the end
+         *  of the Element.  The user must not directly modify this
+         *  StringBuffer, or use it in other data structures.
+         *  From time to time, the StringBuffer may be replaced by a
+         *  constant string as a result of using the PrintWriter.
+         */
+        public PrintWriter asWriter() {
+            return new ElemW();
+        }
+
+        class ElemW extends PrintWriter {
+
+            ElemW() {
+                super(new StringWriter());
+            }
+            final StringBuffer buf = ((StringWriter) out).getBuffer();
+
+            {
+                lock = buf;
+            }  // synchronize on this buffer
+
+            @Override
+            public void println() {
+                synchronized (buf) {
+                    ensureCursor();
+                    super.println();
+                }
+            }
+
+            @Override
+            public void write(int ch) {
+                synchronized (buf) {
+                    ensureCursor();
+                    //buf.append(ch);
+                    super.write(ch);
+                }
+            }
+
+            @Override
+            public void write(char buf[], int off, int len) {
+                synchronized (buf) {
+                    ensureCursor();
+                    super.write(buf, off, len);
+                }
+            }
+
+            @Override
+            public void write(String s, int off, int len) {
+                synchronized (buf) {
+                    ensureCursor();
+                    //buf.append(s.substring(off, off+len));
+                    super.write(s, off, len);
+                }
+            }
+
+            @Override
+            public void write(String s) {
+                synchronized (buf) {
+                    ensureCursor();
+                    //buf.append(s);
+                    super.write(s);
+                }
+            }
+
+            private void ensureCursor() {
+                checkNotFrozen();
+                if (getLast() != buf) {
+                    int pos = indexOf(buf);
+                    if (pos >= 0) {
+                        // Freeze the pre-existing use of buf.
+                        setRaw(pos, buf.toString());
+                    }
+                    add(buf);
+                }
+            }
+        }
+
+        /** Produce a map view of attributes, in which the attribute
+         *  name strings are the keys.
+         *  (The map view does not provide access to the element's
+         *  name or sub-elements.)
+         *  Changes to this view are immediately reflected in the
+         *  element itself.
+         */
+        public Map<String, String> asAttrMap() {
+            class Entry implements Map.Entry<String, String> {
+
+                final int k;
+
+                Entry(int k) {
+                    this.k = k;
+                    assert (((String) getKey()).toString() != null);  // check, fail-fast
+                }
+
+                public String getKey() {
+                    return Element.this.getAttrName(k);
+                }
+
+                public String getValue() {
+                    return Element.this.getAttr(k);
+                }
+
+                public String setValue(String v) {
+                    return Element.this.setAttr(k, v.toString());
+                }
+
+                @Override
+                public boolean equals(Object o) {
+                    if (!(o instanceof Map.Entry)) {
+                        return false;
+                    }
+                    Map.Entry that = (Map.Entry) o;
+                    return (this.getKey().equals(that.getKey())
+                            && this.getValue().equals(that.getValue()));
+                }
+
+                @Override
+                public int hashCode() {
+                    return getKey().hashCode() ^ getValue().hashCode();
+                }
+            }
+            class EIter implements Iterator<Map.Entry<String, String>> {
+
+                int k = 0;  // index of pending next() attribute
+
+                public boolean hasNext() {
+                    return Element.this.containsAttr(k);
+                }
+
+                public Map.Entry<String, String> next() {
+                    return new Entry(k++);
+                }
+
+                public void remove() {
+                    Element.this.removeAttr(--k);
+                }
+            }
+            class ESet extends AbstractSet<Map.Entry<String, String>> {
+
+                public int size() {
+                    return Element.this.attrSize();
+                }
+
+                public Iterator<Map.Entry<String, String>> iterator() {
+                    return new EIter();
+                }
+
+                @Override
+                public void clear() {
+                    Element.this.clearAttrs();
+                }
+            }
+            class AView extends AbstractMap<String, String> {
+
+                private transient Set<Map.Entry<String, String>> eSet;
+
+                public Set<Map.Entry<String, String>> entrySet() {
+                    if (eSet == null) {
+                        eSet = new ESet();
+                    }
+                    return eSet;
+                }
+
+                @Override
+                public int size() {
+                    return Element.this.attrSize();
+                }
+
+                public boolean containsKey(String k) {
+                    return Element.this.containsAttr(k);
+                }
+
+                public String get(String k) {
+                    return Element.this.getAttr(k);
+                }
+
+                @Override
+                public String put(String k, String v) {
+                    return Element.this.setAttr(k, v.toString());
+                }
+
+                public String remove(String k) {
+                    return Element.this.setAttr(k, null);
+                }
+            }
+            return new AView();
+        }
+
+        /** Reports number of additional elements this object can accommodate
+         *  without reallocation.
+         */
+        public int getExtraCapacity() {
+            int abase = attrBase();
+            return Math.max(0, abase - size - NEED_SLOP);
+        }
+
+        /** Ensures that at least the given number of additional elements
+         *  can be added to this object without reallocation.
+         */
+        public void ensureExtraCapacity(int cap) {
+            if (cap == 0 || hasNulls(cap + NEED_SLOP)) {
+                return;
+            }
+            setExtraCapacity(cap);
+        }
+
+        /**
+         * Trim excess capacity to zero, or do nothing if frozen.
+         * This minimizes the space occupied by this Element,
+         * at the expense of a reallocation if sub-elements or attributes
+         * are added later.
+         */
+        public void trimToSize() {
+            if (isFrozen()) {
+                return;
+            }
+            setExtraCapacity(0);
+        }
+
+        /** Changes the number of additional elements this object can accommodate
+         *  without reallocation.
+         */
+        public void setExtraCapacity(int cap) {
+            checkNotFrozen();
+            int abase = attrBase();
+            int alen = parts.length - abase;  // slots allocated for attrs
+            int nlen = size + cap + NEED_SLOP + alen;
+            if (nlen != parts.length) {
+                Object[] nparts = new Object[nlen];
+                // copy attributes
+                System.arraycopy(parts, abase, nparts, nlen - alen, alen);
+                // copy sub-elements
+                System.arraycopy(parts, 0, nparts, 0, size);
+                parts = nparts;
+            }
+            assert (cap == getExtraCapacity());
+        }
+
+        // Return true if there are at least len nulls of slop available.
+        boolean hasNulls(int len) {
+            if (len == 0) {
+                return true;
+            }
+            int lastNull = size + len - 1;
+            if (lastNull >= parts.length) {
+                return false;
+            }
+            return (parts[lastNull] == null);
+        }
+
+        // Opens up parts array at pos by len spaces.
+        void open(int pos, int len) {
+            assert (pos < size);
+            assert (hasNulls(len + NEED_SLOP));
+            checkNotFrozen();
+            int nsize = size + len;
+            int tlen = size - pos;
+            System.arraycopy(parts, pos, parts, pos + len, tlen);
+            size = nsize;
+        }
+
+        // Reallocate and open up at parts[pos] to at least len empty places.
+        // Shift anything after pos right by len.  Reallocate if necessary.
+        // If pos < size, caller must fill it in with non-null values.
+        // Returns incremented size; caller is responsible for storing it
+        // down, if desired.
+        int expand(int pos, int len) {
+            assert (pos <= size);
+            // There must be at least len nulls between elems and attrs.
+            assert (!hasNulls(NEED_SLOP + len));  // caller responsibility
+            checkNotFrozen();
+            int nsize = size + len;  // length of all elements
+            int tlen = size - pos;   // length of elements in post-pos tail
+            int abase = attrBase();
+            int alen = parts.length - abase;  // slots allocated for attrs
+            int nlen = nsize + alen + NEED_SLOP;
+            nlen += (nlen >>> 1);  // add new slop!
+            Object[] nparts = new Object[nlen];
+            // copy head of sub-elements
+            System.arraycopy(parts, 0, nparts, 0, pos);
+            // copy tail of sub-elements
+            System.arraycopy(parts, pos, nparts, pos + len, tlen);
+            // copy attributes
+            System.arraycopy(parts, abase, nparts, nlen - alen, alen);
+            // update self
+            parts = nparts;
+            //assert(hasNulls(len));  <- not yet true, since size != nsize
+            return nsize;
+        }
+
+        // Open or expand at the given position, as appropriate.
+        boolean openOrExpand(int pos, int len) {
+            if (pos < 0 || pos > size) {
+                badIndex(pos);
+            }
+            if (hasNulls(len + NEED_SLOP)) {
+                if (pos == size) {
+                    size += len;
+                } else {
+                    open(pos, len);
+                }
+                return false;
+            } else {
+                size = expand(pos, len);
+                return true;
+            }
+        }
+
+        // Close up at parts[pos] len old places.
+        // Shift anything after pos left by len.
+        // Fill unused end of parts with null.
+        void close(int pos, int len) {
+            assert (len > 0);
+            assert ((size - pos) >= len);
+            checkNotFrozen();
+            int tlen = (size - pos) - len;   // length of elements in post-pos tail
+            int nsize = size - len;
+            System.arraycopy(parts, pos + len, parts, pos, tlen);
+            // reinitialize the unoccupied slots to null
+            clearParts(nsize, nsize + len);
+            // update self
+            size = nsize;
+            assert (hasNulls(len));
+        }
+
+        public void writeTo(Writer w) throws IOException {
+            new Printer(w).print(this);
+        }
+
+        public void writePrettyTo(Writer w) throws IOException {
+            prettyPrintTo(w, this);
+        }
+
+        public String prettyString() {
+            StringWriter sw = new StringWriter();
+            try {
+                writePrettyTo(sw);
+            } catch (IOException ee) {
+                throw new Error(ee);  // should not happen
+            }
+            return sw.toString();
+        }
+
+        @Override
+        public String toString() {
+            StringWriter sw = new StringWriter();
+            try {
+                writeTo(sw);
+            } catch (IOException ee) {
+                throw new Error(ee);  // should not happen
+            }
+            return sw.toString();
+        }
+
+        public String dump() {
+            // For debugging only.  Reveals internal layout.
+            StringBuilder buf = new StringBuilder();
+            buf.append("<").append(name).append("[").append(size).append("]");
+            for (int i = 0; i < parts.length; i++) {
+                Object p = parts[i];
+                if (p == null) {
+                    buf.append(" null");
+                } else {
+                    buf.append(" {");
+                    String cname = p.getClass().getName();
+                    cname = cname.substring(1 + cname.indexOf('/'));
+                    cname = cname.substring(1 + cname.indexOf('$'));
+                    cname = cname.substring(1 + cname.indexOf('#'));
+                    if (!cname.equals("String")) {
+                        buf.append(cname).append(":");
+                    }
+                    buf.append(p);
+                    buf.append("}");
+                }
+            }
+            return buf.append(">").toString();
+        }
+
+        public static java.lang.reflect.Method method(String name) {
+            HashMap allM = allMethods;
+            if (allM == null) {
+                allM = makeAllMethods();
+            }
+            java.lang.reflect.Method res = (java.lang.reflect.Method) allMethods.get(name);
+            if (res == null) {
+                throw new IllegalArgumentException(name);
+            }
+            return res;
+        }
+        private static HashMap allMethods;
+
+        private static synchronized HashMap makeAllMethods() {
+            if (allMethods != null) {
+                return allMethods;
+            }
+            java.lang.reflect.Method[] methods = Element.class.getMethods();
+            HashMap<String, java.lang.reflect.Method> allM = new HashMap<String, java.lang.reflect.Method>(),
+                    ambig = new HashMap<String, java.lang.reflect.Method>();
+            for (int i = 0; i < methods.length; i++) {
+                java.lang.reflect.Method m = methods[i];
+                Class[] args = m.getParameterTypes();
+                String name = m.getName();
+                assert (java.lang.reflect.Modifier.isPublic(m.getModifiers()));
+                if (name.startsWith("notify")) {
+                    continue;
+                }
+                if (name.endsWith("Attr")
+                        && args.length > 0 && args[0] == int.class) // ignore getAttr(int), etc.
+                {
+                    continue;
+                }
+                if (name.endsWith("All")
+                        && args.length > 1 && args[0] == Filter.class) // ignore findAll(Filter, int...), etc.
+                {
+                    continue;
+                }
+                java.lang.reflect.Method pm = allM.put(name, m);
+                if (pm != null) {
+                    Class[] pargs = pm.getParameterTypes();
+                    if (pargs.length > args.length) {
+                        allM.put(name, pm);   // put it back
+                    } else if (pargs.length == args.length) {
+                        ambig.put(name, pm);  // make a note of it
+                    }
+                }
+            }
+            // Delete ambiguous methods.
+            for (Map.Entry<String, java.lang.reflect.Method> e : ambig.entrySet()) {
+                String name = e.getKey();
+                java.lang.reflect.Method pm = e.getValue();
+                java.lang.reflect.Method m = allM.get(name);
+                Class[] args = m.getParameterTypes();
+                Class[] pargs = pm.getParameterTypes();
+                if (pargs.length == args.length) {
+                    //System.out.println("ambig: "+pm);
+                    //System.out.println(" with: "+m);
+                    //ambig: int addAll(int,Element)
+                    // with: int addAll(int,Collection)
+                    allM.put(name, null);  // get rid of
+                }
+            }
+            //System.out.println("allM: "+allM);
+            return allMethods = allM;
+        }
+    }
+
+    static Object fixupString(Object part) {
+        if (part instanceof CharSequence && !(part instanceof String)) {
+            return part.toString();
+        } else {
+            return part;
+        }
+    }
+
+    public static final class Special implements Comparable<Special> {
+
+        String kind;
+        Object value;
+
+        public Special(String kind, Object value) {
+            this.kind = kind;
+            this.value = value;
+        }
+
+        public String getKind() {
+            return kind;
+        }
+
+        public Object getValue() {
+            return value;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof Special)) {
+                return false;
+            }
+            Special that = (Special) o;
+            return this.kind.equals(that.kind) && this.value.equals(that.value);
+        }
+
+        @Override
+        public int hashCode() {
+            return kind.hashCode() * 65 + value.hashCode();
+        }
+
+        public int compareTo(Special that) {
+            int r = this.kind.compareTo(that.kind);
+            if (r != 0) {
+                return r;
+            }
+            return ((Comparable) this.value).compareTo(that.value);
+        }
+
+        @Override
+        public String toString() {
+            int split = kind.indexOf(' ');
+            String pref = kind.substring(0, split < 0 ? 0 : split);
+            String post = kind.substring(split + 1);
+            return pref + value + post;
+        }
+    }
+
+    /** Supports sorting of mixed content.  Sorts strings first,
+     *  then Elements, then everything else (as Comparable).
+     */
+    public static Comparator<Object> contentOrder() {
+        return CONTENT_ORDER;
+    }
+    private static Comparator<Object> CONTENT_ORDER = new ContentComparator();
+
+    private static class ContentComparator implements Comparator<Object> {
+
+        public int compare(Object o1, Object o2) {
+            boolean cs1 = (o1 instanceof CharSequence);
+            boolean cs2 = (o2 instanceof CharSequence);
+            if (cs1 && cs2) {
+                String s1 = (String) fixupString(o1);
+                String s2 = (String) fixupString(o2);
+                return s1.compareTo(s2);
+            }
+            if (cs1) {
+                return 0 - 1;
+            }
+            if (cs2) {
+                return 1 - 0;
+            }
+            boolean el1 = (o1 instanceof Element);
+            boolean el2 = (o2 instanceof Element);
+            if (el1 && el2) {
+                return ((Element) o1).compareTo((Element) o2);
+            }
+            if (el1) {
+                return 0 - 1;
+            }
+            if (el2) {
+                return 1 - 0;
+            }
+            return ((Comparable) o1).compareTo(o2);
+        }
+    }
+
+    /** Used to find, filter, or transform sub-elements.
+     *  When used as a predicate, the filter returns a null
+     *  value for false, and the original object value for true.
+     *  When used as a transformer, the filter may return
+     *  null, for no values, the original object, a new object,
+     *  or an anonymous Element (meaning multiple results).
+     */
+    public interface Filter {
+
+        Object filter(Object value);
+    }
+
+    /** Use this to find an element, perhaps with a given name. */
+    public static class ElementFilter implements Filter {
+
+        /** Subclasses may override this to implement better value tests.
+         *  By default, it returns the element itself, thus recognizing
+         *  all elements, regardless of name.
+         */
+        public Element filter(Element elem) {
+            return elem;  // override this
+        }
+
+        public final Object filter(Object value) {
+            if (!(value instanceof Element)) {
+                return null;
+            }
+            return filter((Element) value);
+        }
+
+        @Override
+        public String toString() {
+            return "<ElementFilter name='*'/>";
+        }
+    }
+    private static Filter elementFilter;
+
+    public static Filter elementFilter() {
+        return (elementFilter != null) ? elementFilter : (elementFilter = new ElementFilter());
+    }
+
+    public static Filter elementFilter(final String name) {
+        name.toString();  // null check
+        return new ElementFilter() {
+
+            @Override
+            public Element filter(Element elem) {
+                return name.equals(elem.name) ? elem : null;
+            }
+
+            @Override
+            public String toString() {
+                return "<ElementFilter name='" + name + "'/>";
+            }
+        };
+    }
+
+    public static Filter elementFilter(final Collection nameSet) {
+        nameSet.getClass();  // null check
+        return new ElementFilter() {
+
+            @Override
+            public Element filter(Element elem) {
+                return nameSet.contains(elem.name) ? elem : null;
+            }
+
+            @Override
+            public String toString() {
+                return "<ElementFilter name='" + nameSet + "'/>";
+            }
+        };
+    }
+
+    public static Filter elementFilter(String... nameSet) {
+        Collection<String> ncoll = Arrays.asList(nameSet);
+        if (nameSet.length > 10) {
+            ncoll = new HashSet<String>(ncoll);
+        }
+        return elementFilter(ncoll);
+    }
+
+    /** Use this to find an element with a named attribute,
+     *  possibly with a particular value.
+     *  (Note that an attribute is missing if and only if its value is null.)
+     */
+    public static class AttrFilter extends ElementFilter {
+
+        protected final String attrName;
+
+        public AttrFilter(String attrName) {
+            this.attrName = attrName.toString();
+        }
+
+        /** Subclasses may override this to implement better value tests.
+         *  By default, it returns true for any non-null value, thus
+         *  recognizing any attribute of the given name, regardless of value.
+         */
+        public boolean test(String attrVal) {
+            return attrVal != null;  // override this
+        }
+
+        @Override
+        public final Element filter(Element elem) {
+            return test(elem.getAttr(attrName)) ? elem : null;
+        }
+
+        @Override
+        public String toString() {
+            return "<AttrFilter name='" + attrName + "' value='*'/>";
+        }
+    }
+
+    public static Filter attrFilter(String attrName) {
+        return new AttrFilter(attrName);
+    }
+
+    public static Filter attrFilter(String attrName, final String attrVal) {
+        if (attrVal == null) {
+            return not(attrFilter(attrName));
+        }
+        return new AttrFilter(attrName) {
+
+            @Override
+            public boolean test(String attrVal2) {
+                return attrVal.equals(attrVal2);
+            }
+
+            @Override
+            public String toString() {
+                return "<AttrFilter name='" + attrName + "' value='" + attrVal + "'/>";
+            }
+        };
+    }
+
+    public static Filter attrFilter(Element matchThis, String attrName) {
+        return attrFilter(attrName, matchThis.getAttr(attrName));
+    }
+
+    /** Use this to find a sub-element of a given class. */
+    public static Filter classFilter(final Class clazz) {
+        return new Filter() {
+
+            public Object filter(Object value) {
+                return clazz.isInstance(value) ? value : null;
+            }
+
+            @Override
+            public String toString() {
+                return "<ClassFilter class='" + clazz.getName() + "'/>";
+            }
+        };
+    }
+    private static Filter textFilter;
+
+    public static Filter textFilter() {
+        return (textFilter != null) ? textFilter : (textFilter = classFilter(CharSequence.class));
+    }
+    private static Filter specialFilter;
+
+    public static Filter specialFilter() {
+        return (specialFilter != null) ? specialFilter : (specialFilter = classFilter(Special.class));
+    }
+    private static Filter selfFilter;
+
+    /** This filter always returns its own argument. */
+    public static Filter selfFilter() {
+        if (selfFilter != null) {
+            return selfFilter;
+        }
+        return selfFilter = new Filter() {
+
+            public Object filter(Object value) {
+                return value;
+            }
+
+            @Override
+            public String toString() {
+                return "<Self/>";
+            }
+        };
+    }
+
+    /** This filter always returns a fixed value, regardless of argument. */
+    public static Filter constantFilter(final Object value) {
+        return new Filter() {
+
+            public Object filter(Object ignore) {
+                return value;
+            }
+
+            @Override
+            public String toString() {
+                return "<Constant>" + value + "</Constant>";
+            }
+        };
+    }
+    private static Filter nullFilter;
+
+    public static Filter nullFilter() {
+        return (nullFilter != null) ? nullFilter : (nullFilter = constantFilter(null));
+    }
+    private static Filter emptyFilter;
+
+    public static Filter emptyFilter() {
+        return (emptyFilter != null) ? emptyFilter : (emptyFilter = constantFilter(Element.EMPTY));
+    }
+
+    /** Use this to invert the logical sense of the given filter. */
+    public static Filter not(final Filter f) {
+        return new Filter() {
+
+            public Object filter(Object value) {
+                return f.filter(value) == null ? value : null;
+            }
+
+            @Override
+            public String toString() {
+                return "<Not>" + f + "</Not>";
+            }
+        };
+    }
+
+    /** Use this to combine several filters with logical AND.
+     *  Returns either the first null or the last non-null value.
+     */
+    public static Filter and(final Filter f0, final Filter f1) {
+        return and(new Filter[]{f0, f1});
+    }
+
+    public static Filter and(final Filter... fs) {
+        switch (fs.length) {
+            case 0:
+                return selfFilter();  // always true (on non-null inputs)
+            case 1:
+                return fs[0];
+        }
+        return new Filter() {
+
+            public Object filter(Object value) {
+                Object res = fs[0].filter(value);
+                if (res != null) {
+                    res = fs[1].filter(value);
+                    for (int i = 2; res != null && i < fs.length; i++) {
+                        res = fs[i].filter(value);
+                    }
+                }
+                return res;
+            }
+
+            @Override
+            public String toString() {
+                return opToString("<And>", fs, "</And>");
+            }
+        };
+    }
+
+    /** Use this to combine several filters with logical OR.
+     *  Returns either the first non-null or the last null value.
+     */
+    public static Filter or(final Filter f0, final Filter f1) {
+        return or(new Filter[]{f0, f1});
+    }
+
+    public static Filter or(final Filter... fs) {
+        switch (fs.length) {
+            case 0:
+                return nullFilter();
+            case 1:
+                return fs[0];
+        }
+        return new Filter() {
+
+            public Object filter(Object value) {
+                Object res = fs[0].filter(value);
+                if (res == null) {
+                    res = fs[1].filter(value);
+                    for (int i = 2; res == null && i < fs.length; i++) {
+                        res = fs[i].filter(value);
+                    }
+                }
+                return res;
+            }
+
+            @Override
+            public String toString() {
+                return opToString("<Or>", fs, "</Or>");
+            }
+        };
+    }
+
+    /** Use this to combine several filters with logical AND,
+     *  and where each non-null result is passed as the argument
+     *  to the next filter.
+     *  Returns either the first null or the last non-null value.
+     */
+    public static Filter stack(final Filter f0, final Filter f1) {
+        return stack(new Filter[]{f0, f1});
+    }
+
+    public static Filter stack(final Filter... fs) {
+        switch (fs.length) {
+            case 0:
+                return nullFilter();
+            case 1:
+                return fs[0];
+        }
+        return new Filter() {
+
+            public Object filter(Object value) {
+                Object res = fs[0].filter(value);
+                if (res != null) {
+                    res = fs[1].filter(res);
+                    for (int i = 2; res != null && i < fs.length; i++) {
+                        res = fs[i].filter(res);
+                    }
+                }
+                return res;
+            }
+
+            @Override
+            public String toString() {
+                return opToString("<Stack>", fs, "</Stack>");
+            }
+        };
+    }
+
+    /** Copy everything produced by f to sink, using addContent. */
+    public static Filter content(final Filter f, final Collection<Object> sink) {
+        return new Filter() {
+
+            public Object filter(Object value) {
+                Object res = f.filter(value);
+                addContent(res, sink);
+                return res;
+            }
+
+            @Override
+            public String toString() {
+                return opToString("<addContent>", new Object[]{f, sink},
+                        "</addContent>");
+            }
+        };
+    }
+
+    /** Look down the tree using f, collecting fx, else recursing into x.
+     *  Identities:
+     *  <code>
+     *     findInTree(f, s) == findInTree(content(f, s))
+     *     findInTree(f)    == replaceInTree(and(f, selfFilter())).
+     *  </code>
+     */
+    public static Filter findInTree(Filter f, Collection<Object> sink) {
+        if (sink != null) {
+            f = content(f, sink);
+        }
+        return findInTree(f);
+    }
+
+    /** Look down the tree using f, recursing into x unless fx. */
+    public static Filter findInTree(final Filter f) {
+        return new Filter() {
+
+            public Object filter(Object value) {
+                Object res = f.filter(value);
+                if (res != null) {
+                    return res;
+                }
+                if (value instanceof Element) {
+                    // recurse
+                    return ((Element) value).find(this);
+                }
+                return null;
+            }
+
+            @Override
+            public String toString() {
+                return opToString("<FindInTree>", new Object[]{f},
+                        "</FindInTree>");
+            }
+        };
+    }
+
+    /** Look down the tree using f.  Replace each x with fx, else recurse.
+     *  If post filter g is given, optionally replace with gx after recursion.
+     */
+    public static Filter replaceInTree(final Filter f, final Filter g) {
+        return new Filter() {
+
+            public Object filter(Object value) {
+                Object res = (f == null) ? null : f.filter(value);
+                if (res != null) {
+                    return res;
+                }
+                if (value instanceof Element) {
+                    // recurse
+                    ((Element) value).replaceAll(this);
+                    // Optional postorder traversal:
+                    if (g != null) {
+                        res = g.filter(value);
+                    }
+                }
+                return res;  // usually null, meaning no replacement
+            }
+
+            @Override
+            public String toString() {
+                return opToString("<ReplaceInTree>",
+                        new Object[]{f, g},
+                        "</ReplaceInTree>");
+            }
+        };
+    }
+
+    public static Filter replaceInTree(Filter f) {
+        f.getClass(); // null check
+        return replaceInTree(f, null);
+    }
+
+    /** Make a filter which calls this method on the given element.
+     *  If the method is static, the first argument is passed the
+     *  the subtree value being filtered.
+     *  If the method is non-static, the receiver is the subtree value itself.
+     *  <p>
+     *  Optionally, additional arguments may be specified.
+     *  <p>
+     *  If the filtered value does not match the receiver class
+     *  (or else the first argument type, if the method is static),
+     *  the filter returns null without invoking the method.
+     *  <p>
+     *  The returned filter value is the result returned from the method.
+     *  Optionally, a non-null special false result value may be specified.
+     *  If the result returned from the method is equal to that false value,
+     *  the filter will return null.
+     */
+    public static Filter methodFilter(java.lang.reflect.Method m, Object[] extraArgs,
+            Object falseResult) {
+        return methodFilter(m, false, extraArgs, falseResult);
+    }
+
+    public static Filter methodFilter(java.lang.reflect.Method m,
+            Object[] args) {
+        return methodFilter(m, args, null);
+    }
+
+    public static Filter methodFilter(java.lang.reflect.Method m) {
+        return methodFilter(m, null, null);
+    }
+
+    public static Filter testMethodFilter(java.lang.reflect.Method m, Object[] extraArgs,
+            Object falseResult) {
+        return methodFilter(m, true, extraArgs, falseResult);
+    }
+
+    public static Filter testMethodFilter(java.lang.reflect.Method m, Object[] extraArgs) {
+        return methodFilter(m, true, extraArgs, zeroArgs.get(m.getReturnType()));
+    }
+
+    public static Filter testMethodFilter(java.lang.reflect.Method m) {
+        return methodFilter(m, true, null, zeroArgs.get(m.getReturnType()));
+    }
+
+    private static Filter methodFilter(final java.lang.reflect.Method m,
+            final boolean isTest,
+            Object[] extraArgs, final Object falseResult) {
+        Class[] params = m.getParameterTypes();
+        final boolean isStatic = java.lang.reflect.Modifier.isStatic(m.getModifiers());
+        int insertLen = (isStatic ? 1 : 0);
+        if (insertLen + (extraArgs == null ? 0 : extraArgs.length) > params.length) {
+            throw new IllegalArgumentException("too many arguments");
+        }
+        final Object[] args = (params.length == insertLen) ? null
+                : new Object[params.length];
+        final Class valueType = !isStatic ? m.getDeclaringClass() : params[0];
+        if (valueType.isPrimitive()) {
+            throw new IllegalArgumentException("filtered value must be reference type");
+        }
+        int fillp = insertLen;
+        if (extraArgs != null) {
+            for (int i = 0; i < extraArgs.length; i++) {
+                args[fillp++] = extraArgs[i];
+            }
+        }
+        if (args != null) {
+            while (fillp < args.length) {
+                Class param = params[fillp];
+                args[fillp++] = param.isPrimitive() ? zeroArgs.get(param) : null;
+            }
+        }
+        final Thread curt = Thread.currentThread();
+        class MFilt implements Filter {
+
+            public Object filter(Object value) {
+                if (!valueType.isInstance(value)) {
+                    return null;  // filter fails quickly
+                }
+                Object[] args1 = args;
+                if (isStatic) {
+                    if (args1 == null) {
+                        args1 = new Object[1];
+                    } else if (curt != Thread.currentThread()) // Dirty hack to curtail array copying in common case.
+                    {
+                        args1 = (Object[]) args1.clone();
+                    }
+                    args1[0] = value;
+                }
+                Object res;
+                try {
+                    res = m.invoke(value, args1);
+                } catch (java.lang.reflect.InvocationTargetException te) {
+                    Throwable ee = te.getCause();
+                    if (ee instanceof RuntimeException) {
+                        throw (RuntimeException) ee;
+                    }
+                    if (ee instanceof Error) {
+                        throw (Error) ee;
+                    }
+                    throw new RuntimeException("throw in filter", ee);
+                } catch (IllegalAccessException ee) {
+                    throw new RuntimeException("access error in filter", ee);
+                }
+                if (res == null) {
+                    if (!isTest && m.getReturnType() == Void.TYPE) {
+                        // Void methods return self by convention.
+                        // (But void "tests" always return false.)
+                        res = value;
+                    }
+                } else {
+                    if (falseResult != null && falseResult.equals(res)) {
+                        res = null;
+                    } else if (isTest) {
+                        // Tests return self by convention.
+                        res = value;
+                    }
+                }
+                return res;
+            }
+
+            @Override
+            public String toString() {
+                return "<Method>" + m + "</Method>";
+            }
+        }
+        return new MFilt();
+    }
+    private static HashMap<Class, Object> zeroArgs = new HashMap<Class, Object>();
+
+    static {
+        zeroArgs.put(Boolean.TYPE, Boolean.FALSE);
+        zeroArgs.put(Character.TYPE, new Character((char) 0));
+        zeroArgs.put(Byte.TYPE, new Byte((byte) 0));
+        zeroArgs.put(Short.TYPE, new Short((short) 0));
+        zeroArgs.put(Integer.TYPE, new Integer(0));
+        zeroArgs.put(Float.TYPE, new Float(0));
+        zeroArgs.put(Long.TYPE, new Long(0));
+        zeroArgs.put(Double.TYPE, new Double(0));
+    }
+
+    private static String opToString(String s1, Object[] s2, String s3) {
+        StringBuilder buf = new StringBuilder(s1);
+        for (int i = 0; i < s2.length; i++) {
+            if (s2[i] != null) {
+                buf.append(s2[i]);
+            }
+        }
+        buf.append(s3);
+        return buf.toString();
+    }
+
+    /** Call the filter on each list element x, and replace x with the
+     *  resulting filter value e, or its parts.
+     *  If e is null, keep x.  (This eases use of partial-domain filters.)
+     *  If e is a TokenList or an anonymous Element, add e's parts
+     *  to the list instead of x.
+     *  Otherwise, replace x by e.
+     *  <p>
+     *  The effect at each list position <code>n</code> may be expressed
+     *  in terms of XMLKit.addContent as follows:
+     *  <pre>
+     *     Object e = f.filter(target.get(n));
+     *     if (e != null) {
+     *         target.remove(n);
+     *         addContent(e, target.subList(n,n));
+     *     }
+     *  </pre>
+     *  <p>
+     *  Note:  To force deletion of x, simply have the filter return
+     *  Element.EMPTY or TokenList.EMPTY.
+     *  To force null filter values to have this effect,
+     *  use the expression: <code>or(f, emptyFilter())</code>.
+     */
+    public static void replaceAll(Filter f, List<Object> target) {
+        for (ListIterator<Object> i = target.listIterator(); i.hasNext();) {
+            Object x = i.next();
+            Object fx = f.filter(x);
+            if (fx == null) {
+                // Unliked addContent, a null is a no-op here.
+                // i.remove();
+            } else if (fx instanceof TokenList) {
+                TokenList tl = (TokenList) fx;
+                if (tl.size() == 1) {
+                    i.set(tl);
+                } else {
+                    i.remove();
+                    for (String part : tl) {
+                        i.add(part);
+                    }
+                }
+            } else if (fx instanceof Element
+                    && ((Element) fx).isAnonymous()) {
+                Element anon = (Element) fx;
+                if (anon.size() == 1) {
+                    i.set(anon);
+                } else {
+                    i.remove();
+                    for (Object part : anon) {
+                        i.add(part);
+                    }
+                }
+            } else if (x != fx) {
+                i.set(fx);
+            }
+        }
+    }
+
+    /** If e is null, return zero.
+     *  If e is a TokenList or an anonymous Element, add e's parts
+     *  to the collection, and return the number of parts.
+     *  Otherwise, add e to the collection, and return one.
+     *  If the collection reference is null, the result is as if
+     *  a throwaway collection were used.
+     */
+    public static int addContent(Object e, Collection<Object> sink) {
+        if (e == null) {
+            return 0;
+        } else if (e instanceof TokenList) {
+            TokenList tl = (TokenList) e;
+            if (sink != null) {
+                sink.addAll(tl);
+            }
+            return tl.size();
+        } else if (e instanceof Element
+                && ((Element) e).isAnonymous()) {
+            Element anon = (Element) e;
+            if (sink != null) {
+                sink.addAll(anon.asList());
+            }
+            return anon.size();
+        } else {
+            if (sink != null) {
+                sink.add(e);
+            }
+            return 1;
+        }
+    }
+
+    static Collection<Object> newCounterColl() {
+        return new AbstractCollection<Object>() {
+
+            int size;
+
+            public int size() {
+                return size;
+            }
+
+            @Override
+            public boolean add(Object o) {
+                ++size;
+                return true;
+            }
+
+            public Iterator<Object> iterator() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    /** SAX2 document handler for building Element trees. */
+    private static class Builder implements ContentHandler, LexicalHandler {
+        /*, EntityResolver, DTDHandler, ErrorHandler*/
+
+        Collection<Object> sink;
+        boolean makeFrozen;
+        boolean tokenizing;
+
+        Builder(Collection<Object> sink, boolean tokenizing, boolean makeFrozen) {
+            this.sink = sink;
+            this.tokenizing = tokenizing;
+            this.makeFrozen = makeFrozen;
+        }
+        Object[] parts = new Object[30];
+        int nparts = 0;
+        int[] attrBases = new int[10];  // index into parts
+        int[] elemBases = new int[10];  // index into parts
+        int depth = -1;  // index into attrBases, elemBases
+        // Parts is organized this way:
+        // | name0 | akey aval ... | subelem ... | name1 | ... |
+        // The position of the first "akey" after name0 is attrBases[0].
+        // The position of the first "subelem" after name0 is elemBases[0].
+        // The position after the last part is always nparts.
+        int mergeableToken = -1;  // index into parts of recent CharSequence
+        boolean inCData = false;
+
+        void addPart(Object x) {
+            //System.out.println("addPart "+x);
+            if (nparts == parts.length) {
+                Object[] newParts = new Object[parts.length * 2];
+                System.arraycopy(parts, 0, newParts, 0, parts.length);
+                parts = newParts;
+            }
+            parts[nparts++] = x;
+        }
+
+        Object getMergeableToken() {
+            if (mergeableToken == nparts - 1) {
+                assert (parts[mergeableToken] instanceof CharSequence);
+                return parts[nparts - 1];
+            } else {
+                return null;
+            }
+        }
+
+        void clearMergeableToken() {
+            if (mergeableToken >= 0) {
+                // Freeze temporary StringBuffers into strings.
+                assert (parts[mergeableToken] instanceof CharSequence);
+                parts[mergeableToken] = parts[mergeableToken].toString();
+                mergeableToken = -1;
+            }
+        }
+
+        void setMergeableToken() {
+            if (mergeableToken != nparts - 1) {
+                clearMergeableToken();
+                mergeableToken = nparts - 1;
+                assert (parts[mergeableToken] instanceof CharSequence);
+            }
+        }
+
+        // ContentHandler callbacks
+        public void startElement(String ns, String localName, String name, Attributes atts) {
+            clearMergeableToken();
+            addPart(name.intern());
+            ++depth;
+            if (depth == attrBases.length) {
+                int oldlen = depth;
+                int newlen = depth * 2;
+                int[] newAB = new int[newlen];
+                int[] newEB = new int[newlen];
+                System.arraycopy(attrBases, 0, newAB, 0, oldlen);
+                System.arraycopy(elemBases, 0, newEB, 0, oldlen);
+                attrBases = newAB;
+                elemBases = newEB;
+            }
+            attrBases[depth] = nparts;
+            // Collect attributes.
+            int na = atts.getLength();
+            for (int k = 0; k < na; k++) {
+                addPart(atts.getQName(k).intern());
+                addPart(atts.getValue(k));
+            }
+            // Get ready to collect elements.
+            elemBases[depth] = nparts;
+        }
+
+        public void endElement(String ns, String localName, String name) {
+            assert (depth >= 0);
+            clearMergeableToken();
+            int ebase = elemBases[depth];
+            int elen = nparts - ebase;
+            int abase = attrBases[depth];
+            int alen = ebase - abase;
+            int nbase = abase - 1;
+            int cap = alen + (makeFrozen ? 0 : NEED_SLOP) + elen;
+            Element e = new Element((String) parts[nbase], elen, cap);
+            // Set up attributes.
+            for (int k = 0; k < alen; k += 2) {
+                e.parts[cap - k - 2] = parts[abase + k + 0];
+                e.parts[cap - k - 1] = parts[abase + k + 1];
+            }
+            // Set up sub-elements.
+            System.arraycopy(parts, ebase, e.parts, 0, elen);
+            // Back out of this level.
+            --depth;
+            nparts = nbase;
+            assert (e.isFrozen() == makeFrozen);
+            assert (e.size() == elen);
+            assert (e.attrSize() * 2 == alen);
+            if (depth >= 0) {
+                addPart(e);
+            } else {
+                sink.add(e);
+            }
+        }
+
+        public void startCDATA() {
+            inCData = true;
+        }
+
+        public void endCDATA() {
+            inCData = false;
+        }
+
+        public void characters(char[] buf, int off, int len) {
+            boolean headSpace = false;
+            boolean tailSpace = false;
+            int firstLen;
+            if (tokenizing && !inCData) {
+                // Strip unquoted blanks.
+                while (len > 0 && isWhitespace(buf[off])) {
+                    headSpace = true;
+                    ++off;
+                    --len;
+                }
+                if (len == 0) {
+                    tailSpace = true;  // it is all space
+                }
+                while (len > 0 && isWhitespace(buf[off + len - 1])) {
+                    tailSpace = true;
+                    --len;
+                }
+                firstLen = 0;
+                while (firstLen < len && !isWhitespace(buf[off + firstLen])) {
+                    ++firstLen;
+                }
+            } else {
+                firstLen = len;
+            }
+            if (headSpace) {
+                clearMergeableToken();
+            }
+            boolean mergeAtEnd = !tailSpace;
+            // If buffer was empty, or had only ignorable blanks, do nothing.
+            if (len == 0) {
+                return;
+            }
+            // Decide whether to merge some of these chars into a previous token.
+            Object prev = getMergeableToken();
+            if (prev instanceof StringBuffer) {
+                ((StringBuffer) prev).append(buf, off, firstLen);
+            } else if (prev == null) {
+                addPart(new String(buf, off, firstLen));
+            } else {
+                // Merge two strings.
+                String prevStr = prev.toString();
+                StringBuffer prevBuf = new StringBuffer(prevStr.length() + firstLen);
+                prevBuf.append(prevStr);
+                prevBuf.append(buf, off, firstLen);
+                if (mergeAtEnd && len == firstLen) {
+                    // Replace previous string with new StringBuffer.
+                    parts[nparts - 1] = prevBuf;
+                } else {
+                    // Freeze it now.
+                    parts[nparts - 1] = prevBuf.toString();
+                }
+            }
+            off += firstLen;
+            len -= firstLen;
+            if (len > 0) {
+                // Appended only the first token.
+                clearMergeableToken();
+                // Add the rest as separate parts.
+                while (len > 0) {
+                    while (len > 0 && isWhitespace(buf[off])) {
+                        ++off;
+                        --len;
+                    }
+                    int nextLen = 0;
+                    while (nextLen < len && !isWhitespace(buf[off + nextLen])) {
+                        ++nextLen;
+                    }
+                    assert (nextLen > 0);
+                    addPart(new String(buf, off, nextLen));
+                    off += nextLen;
+                    len -= nextLen;
+                }
+            }
+            if (mergeAtEnd) {
+                setMergeableToken();
+            }
+        }
+
+        public void ignorableWhitespace(char[] buf, int off, int len) {
+            clearMergeableToken();
+            if (false) {
+                characters(buf, off, len);
+                clearMergeableToken();
+            }
+        }
+
+        public void comment(char[] buf, int off, int len) {
+            addPart(new Special("<!-- -->", new String(buf, off, len)));
+        }
+
+        public void processingInstruction(String name, String instruction) {
+            Element pi = new Element(name);
+            pi.add(instruction);
+            addPart(new Special("<? ?>", pi));
+        }
+
+        public void skippedEntity(String name) {
+        }
+
+        public void startDTD(String name, String publicId, String systemId) {
+        }
+
+        public void endDTD() {
+        }
+
+        public void startEntity(String name) {
+        }
+
+        public void endEntity(String name) {
+        }
+
+        public void setDocumentLocator(org.xml.sax.Locator locator) {
+        }
+
+        public void startDocument() {
+        }
+
+        public void endDocument() {
+        }
+
+        public void startPrefixMapping(String prefix, String uri) {
+        }
+
+        public void endPrefixMapping(String prefix) {
+        }
+    }
+
+    /** Produce a ContentHandler for use with an XML parser.
+     *  The object is <em>also</em> a LexicalHandler.
+     *  Every top-level Element produced will get added to sink.
+     *  All elements will be frozen iff makeFrozen is true.
+     */
+    public static ContentHandler makeBuilder(Collection<Object> sink, boolean tokenizing, boolean makeFrozen) {
+        return new Builder(sink, tokenizing, makeFrozen);
+    }
+
+    public static ContentHandler makeBuilder(Collection<Object> sink, boolean tokenizing) {
+        return new Builder(sink, tokenizing, false);
+    }
+
+    public static ContentHandler makeBuilder(Collection<Object> sink) {
+        return makeBuilder(sink, false, false);
+    }
+
+    public static Element readFrom(Reader in, boolean tokenizing, boolean makeFrozen) throws IOException {
+        Element sink = new Element();
+        ContentHandler b = makeBuilder(sink.asList(), tokenizing, makeFrozen);
+        XMLReader parser;
+        try {
+            parser = org.xml.sax.helpers.XMLReaderFactory.createXMLReader();
+        } catch (SAXException ee) {
+            throw new Error(ee);
+        }
+        //parser.setFastStandalone(true);
+        parser.setContentHandler(b);
+        try {
+            parser.setProperty("http://xml.org/sax/properties/lexical-handler",
+                    (LexicalHandler) b);
+        } catch (SAXException ee) {
+            // Ignore.  We will miss the comments and whitespace.
+        }
+        try {
+            parser.parse(new InputSource(in));
+        } catch (SAXParseException ee) {
+            throw new RuntimeException("line " + ee.getLineNumber() + " col " + ee.getColumnNumber() + ": ", ee);
+        } catch (SAXException ee) {
+            throw new RuntimeException(ee);
+        }
+        switch (sink.size()) {
+            case 0:
+                return null;
+            case 1:
+                if (sink.get(0) instanceof Element) {
+                    return (Element) sink.get(0);
+                }
+            // fall through
+            default:
+                if (makeFrozen) {
+                    sink.shallowFreeze();
+                }
+                return sink;
+        }
+    }
+
+    public static Element readFrom(Reader in, boolean tokenizing) throws IOException {
+        return readFrom(in, tokenizing, false);
+    }
+
+    public static Element readFrom(Reader in) throws IOException {
+        return readFrom(in, false, false);
+    }
+
+    public static void prettyPrintTo(OutputStream out, Element e) throws IOException {
+        prettyPrintTo(new OutputStreamWriter(out), e);
+    }
+
+    public static void prettyPrintTo(Writer out, Element e) throws IOException {
+        Printer pr = new Printer(out);
+        pr.pretty = true;
+        pr.print(e);
+    }
+
+    static class Outputter {
+
+        ContentHandler ch;
+        LexicalHandler lh;
+
+        Outputter(ContentHandler ch, LexicalHandler lh) {
+            this.ch = ch;
+            this.lh = lh;
+        }
+        AttributesImpl atts = new AttributesImpl();  // handy
+
+        void output(Object x) throws SAXException {
+            // Cf. jdom.org/jdom-b8/src/java/org/jdom/output/SAXOutputter.java
+            if (x instanceof Element) {
+                Element e = (Element) x;
+                atts.clear();
+                for (int asize = e.attrSize(), k = 0; k < asize; k++) {
+                    String key = e.getAttrName(k);
+                    String val = e.getAttr(k);
+                    atts.addAttribute("", "", key, "CDATA", val);
+                }
+                ch.startElement("", "", e.getName(), atts);
+                for (int i = 0; i < e.size(); i++) {
+                    output(e.get(i));
+                }
+                ch.endElement("", "", e.getName());
+            } else if (x instanceof Special) {
+                Special sp = (Special) x;
+                if (sp.kind.startsWith("<!--")) {
+                    char[] chars = sp.value.toString().toCharArray();
+                    lh.comment(chars, 0, chars.length);
+                } else if (sp.kind.startsWith("<?")) {
+                    Element nameInstr = (Element) sp.value;
+                    ch.processingInstruction(nameInstr.name,
+                            nameInstr.get(0).toString());
+                } else {
+                    // drop silently
+                }
+            } else {
+                char[] chars = x.toString().toCharArray();
+                ch.characters(chars, 0, chars.length);
+            }
+        }
+    }
+
+    public static class Printer {
+
+        public Writer w;
+        public boolean tokenizing;
+        public boolean pretty;
+        public boolean abbreviated;  // nonstandard format cuts down on noise
+        int depth = 0;
+        boolean prevStr;
+        int tabStop = 2;
+
+        public Printer(Writer w) {
+            this.w = w;
+        }
+
+        public Printer() {
+            StringWriter sw = new StringWriter();
+            this.w = sw;
+
+        }
+
+        public String nextString() {
+            StringBuffer sb = ((StringWriter) w).getBuffer();
+            String next = sb.toString();
+            sb.setLength(0);  // reset
+            return next;
+        }
+
+        void indent(int depth) throws IOException {
+            if (depth > 0) {
+                w.write("\n");
+            }
+            int nsp = tabStop * depth;
+            while (nsp > 0) {
+                String s = "                ";
+                String t = s.substring(0, nsp < s.length() ? nsp : s.length());
+                w.write(t);
+                nsp -= t.length();
+            }
+        }
+
+        public void print(Element e) throws IOException {
+            if (e.isAnonymous()) {
+                printParts(e);
+                return;
+            }
+            printRecursive(e);
+        }
+
+        public void println(Element e) throws IOException {
+            print(e);
+            w.write("\n");
+            w.flush();
+        }
+
+        public void printRecursive(Element e) throws IOException {
+            boolean indented = false;
+            if (pretty && !prevStr && e.size() + e.attrSize() > 0) {
+                indent(depth);
+                indented = true;
+            }
+            w.write("<");
+            w.write(e.name);
+            for (int asize = e.attrSize(), k = 0; k < asize; k++) {
+                String key = e.getAttrName(k);
+                String val = e.getAttr(k);
+                w.write(" ");
+                w.write(key);
+                w.write("=");
+                if (val == null) {
+                    w.write("null");  // Should not happen....
+                } else if (val.indexOf("\"") < 0) {
+                    w.write("\"");
+                    writeToken(val, '"', w);
+                    w.write("\"");
+                } else {
+                    w.write("'");
+                    writeToken(val, '\'', w);
+                    w.write("'");
+                }
+            }
+            if (e.size() == 0) {
+                w.write("/>");
+            } else {
+                ++depth;
+                if (abbreviated) {
+                    w.write("/");
+                } else {
+                    w.write(">");
+                }
+                prevStr = false;
+                printParts(e);
+                if (abbreviated) {
+                    w.write(">");
+                } else {
+                    if (indented && !prevStr) {
+                        indent(depth - 1);
+                    }
+                    w.write("</");
+                    w.write(e.name);
+                    w.write(">");
+                }
+                prevStr = false;
+                --depth;
+            }
+        }
+
+        private void printParts(Element e) throws IOException {
+            for (int i = 0; i < e.size(); i++) {
+                Object x = e.get(i);
+                if (x instanceof Element) {
+                    printRecursive((Element) x);
+                    prevStr = false;
+                } else if (x instanceof Special) {
+                    w.write(((Special) x).toString());
+                    prevStr = false;
+                } else {
+                    String s = String.valueOf(x);
+                    if (pretty) {
+                        s = s.trim();
+                        if (s.length() == 0) {
+                            continue;
+                        }
+                    }
+                    if (prevStr) {
+                        w.write(' ');
+                    }
+                    writeToken(s, tokenizing ? ' ' : (char) -1, w);
+                    prevStr = true;
+                }
+                if (pretty && depth == 0) {
+                    w.write("\n");
+                    prevStr = false;
+                }
+            }
+        }
+    }
+
+    public static void output(Object e, ContentHandler ch, LexicalHandler lh) throws SAXException {
+        new Outputter(ch, lh).output(e);
+    }
+
+    public static void output(Object e, ContentHandler ch) throws SAXException {
+        if (ch instanceof LexicalHandler) {
+            output(e, ch, (LexicalHandler) ch);
+        } else {
+            output(e, ch, null);
+        }
+    }
+
+    public static void writeToken(String val, char quote, Writer w) throws IOException {
+        int len = val.length();
+        boolean canUseCData = (quote != '"' && quote != '\'');
+        int vpos = 0;
+        for (int i = 0; i < len; i++) {
+            char ch = val.charAt(i);
+            if ((ch == '<' || ch == '&' || ch == '>' || ch == quote)
+                    || (quote == ' ' && isWhitespace(ch))) {
+                if (canUseCData) {
+                    assert (vpos == 0);
+                    writeCData(val, w);
+                    return;
+                } else {
+                    if (vpos < i) {
+                        w.write(val, vpos, i - vpos);
+                    }
+                    String esc;
+                    switch (ch) {
+                        case '&':
+                            esc = "&amp;";
+                            break;
+                        case '<':
+                            esc = "&lt;";
+                            break;
+                        case '\'':
+                            esc = "&apos;";
+                            break;
+                        case '"':
+                            esc = "&quot;";
+                            break;
+                        case '>':
+                            esc = "&gt;";
+                            break;
+                        default:
+                            esc = "&#" + (int) ch + ";";
+                            break;
+                    }
+                    w.write(esc);
+                    vpos = i + 1;  // skip escaped char
+                }
+            }
+        }
+        // write the unquoted tail
+        w.write(val, vpos, val.length() - vpos);
+    }
+
+    public static void writeCData(String val, Writer w) throws IOException {
+        String begCData = "<![CDATA[";
+        String endCData = "]]>";
+        w.write(begCData);
+        for (int vpos = 0, split;; vpos = split) {
+            split = val.indexOf(endCData, vpos);
+            if (split < 0) {
+                w.write(val, vpos, val.length() - vpos);
+                w.write(endCData);
+                return;
+            }
+            split += 2; // bisect the "]]>" goo
+            w.write(val, vpos, split - vpos);
+            w.write(endCData);
+            w.write(begCData);
+        }
+    }
+
+    public static TokenList convertToList(String str) {
+        if (str == null) {
+            return null;
+        }
+        return new TokenList(str);
+    }
+
+    /** If str is null, empty, or blank, returns null.
+     *  Otherwise, return a Double if str spells a double value and contains '.' or 'e'.
+     *  Otherwise, return an Integer if str spells an int value.
+     *  Otherwise, return a Long if str spells a long value.
+     *  Otherwise, return a BigInteger for the string.
+     *  Otherwise, throw NumberFormatException.
+     */
+    public static Number convertToNumber(String str) {
+        if (str == null) {
+            return null;
+        }
+        str = str.trim();
+        if (str.length() == 0) {
+            return null;
+        }
+        if (str.indexOf('.') >= 0
+                || str.indexOf('e') >= 0
+                || str.indexOf('E') >= 0) {
+            return Double.valueOf(str);
+        }
+        try {
+            long lval = Long.parseLong(str);
+            if (lval == (int) lval) {
+                // Narrow to Integer, if possible.
+                return new Integer((int) lval);
+            }
+            return new Long(lval);
+        } catch (NumberFormatException ee) {
+            // Could not represent it as a long.
+            return new java.math.BigInteger(str, 10);
+        }
+    }
+
+    public static Number convertToNumber(String str, Number dflt) {
+        Number n = convertToNumber(str);
+        return (n == null) ? dflt : n;
+    }
+
+    public static long convertToLong(String str) {
+        return convertToLong(str, 0);
+    }
+
+    public static long convertToLong(String str, long dflt) {
+        Number n = convertToNumber(str);
+        return (n == null) ? dflt : n.longValue();
+    }
+
+    public static double convertToDouble(String str) {
+        return convertToDouble(str, 0);
+    }
+
+    public static double convertToDouble(String str, double dflt) {
+        Number n = convertToNumber(str);
+        return (n == null) ? dflt : n.doubleValue();
+    }
+
+    // Testing:
+    public static void main(String... av) throws Exception {
+        Element.method("getAttr");
+        //new org.jdom.input.SAXBuilder().build(file).getRootElement();
+        //jdom.org/jdom-b8/src/java/org/jdom/input/SAXBuilder.java
+        //Document build(InputSource in) throws JDOMException
+
+        int reps = 0;
+
+        boolean tokenizing = false;
+        boolean makeFrozen = false;
+        if (av.length > 0) {
+            tokenizing = true;
+            try {
+                reps = Integer.parseInt(av[0]);
+            } catch (NumberFormatException ee) {
+            }
+        }
+        Reader inR = new BufferedReader(new InputStreamReader(System.in));
+        String inS = null;
+        if (reps > 1) {
+            StringWriter inBufR = new StringWriter(1 << 14);
+            char[] cbuf = new char[1024];
+            for (int nr; (nr = inR.read(cbuf)) >= 0;) {
+                inBufR.write(cbuf, 0, nr);
+            }
+            inS = inBufR.toString();
+            inR = new StringReader(inS);
+        }
+        Element e = XMLKit.readFrom(inR, tokenizing, makeFrozen);
+        System.out.println("transform = " + e.findAll(methodFilter(Element.method("prettyString"))));
+        System.out.println("transform = " + e.findAll(testMethodFilter(Element.method("hasText"))));
+        long tm0 = 0;
+        int warmup = 10;
+        for (int i = 1; i < reps; i++) {
+            inR = new StringReader(inS);
+            readFrom(inR, tokenizing, makeFrozen);
+            if (i == warmup) {
+                System.out.println("Start timing...");
+                tm0 = System.currentTimeMillis();
+            }
+        }
+        if (tm0 != 0) {
+            long tm1 = System.currentTimeMillis();
+            System.out.println((reps - warmup) + " in " + (tm1 - tm0) + " ms");
+        }
+        System.out.println("hashCode = " + e.hashCode());
+        String eStr = e.toString();
+        System.out.println(eStr);
+        Element e2 = readFrom(new StringReader(eStr), tokenizing, !makeFrozen);
+        System.out.println("hashCode = " + e2.hashCode());
+        if (!e.equals(e2)) {
+            System.out.println("**** NOT EQUAL 1\n" + e2);
+        }
+        e = e.deepCopy();
+        System.out.println("hashCode = " + e.hashCode());
+        if (!e.equals(e2)) {
+            System.out.println("**** NOT EQUAL 2");
+        }
+        e2.shallowFreeze();
+        System.out.println("hashCode = " + e2.hashCode());
+        if (!e.equals(e2)) {
+            System.out.println("**** NOT EQUAL 3");
+        }
+        if (false) {
+            System.out.println(e);
+        } else {
+            prettyPrintTo(new OutputStreamWriter(System.out), e);
+        }
+        System.out.println("Flat text:|" + e.getFlatText() + "|");
+        {
+            System.out.println("<!--- Sorted: --->");
+            Element ce = e.copyContentOnly();
+            ce.sort();
+            prettyPrintTo(new OutputStreamWriter(System.out), ce);
+        }
+        {
+            System.out.println("<!--- Trimmed: --->");
+            Element tr = e.deepCopy();
+            findInTree(testMethodFilter(Element.method("trimText"))).filter(tr);
+            System.out.println(tr);
+        }
+        {
+            System.out.println("<!--- Unstrung: --->");
+            Element us = e.deepCopy();
+            int nr = us.retainAllInTree(elementFilter(), null);
+            System.out.println("nr=" + nr);
+            System.out.println(us);
+        }
+        {
+            System.out.println("<!--- Rollup: --->");
+            Element ru = e.deepCopy();
+            Filter makeAnonF =
+                    methodFilter(Element.method("setName"),
+                    new Object[]{ANON_NAME});
+            Filter testSizeF =
+                    testMethodFilter(Element.method("size"));
+            Filter walk =
+                    replaceInTree(and(not(elementFilter()), emptyFilter()),
+                    and(testSizeF, makeAnonF));
+            ru = (Element) walk.filter(ru);
+            //System.out.println(ru);
+            prettyPrintTo(new OutputStreamWriter(System.out), ru);
+        }
+    }
+
+    static boolean isWhitespace(char c) {
+        switch (c) {
+            case 0x20:
+            case 0x09:
+            case 0x0D:
+            case 0x0A:
+                return true;
+        }
+        return false;
+    }
+}
--- a/langtools/.hgtags	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/.hgtags	Wed Jul 05 17:21:32 2017 +0200
@@ -82,3 +82,4 @@
 aaecac256d39c7cb536e70d20ddd833fc118e43a jdk7-b105
 112fcc00659dda1a356ec75d964584e4dae0228f jdk7-b106
 2c1c657f69a4ff608a43e1ac61baf3294cd55797 jdk7-b107
+a408ebb8b3d427dbb3d8ce153dfaeb060564a0a4 jdk7-b108
--- a/langtools/make/jprt.properties	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/make/jprt.properties	Wed Jul 05 17:21:32 2017 +0200
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2006, 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
@@ -24,31 +24,13 @@
 #
 
 # Properties for jprt
-jprt.tools.default.release=jdk1.7.0
 
-# Specific platform list
-jprt.build.platforms=\
-solaris_sparc_5.10,\
-solaris_sparcv9_5.10,\
-solaris_i586_5.10,\
-solaris_x64_5.10,\
-linux_i586_2.6,\
-linux_x64_2.6,\
-windows_i586,\
-windows_x64
+# Use whatever release that the submitted job requests
+jprt.tools.default.release=${jprt.submit.release}
 
-# The different build flavors we want
+# The different build flavors we want, we override here so we just get these 2
 jprt.build.flavors=product,fastdebug
 
-# Explicitly designate what the 32bit match is for the 64bit build
-jprt.solaris_sparcv9.build.platform.match32=solaris_sparc_5.10
-jprt.solaris_sparcv9_5.10.build.platform.match32=solaris_sparc_5.10
-jprt.solaris_x64.build.platform.match32=solaris_i586_5.10
-jprt.solaris_x64_5.10.build.platform.match32=solaris_i586_5.10
+# Directories to be excluded from the source bundles
+jprt.bundle.exclude.src.dirs=build dist webrev
 
-# Standard list of jprt test targets for this workspace
-jprt.test.targets=
-
-# Directories needed to build
-jprt.bundle.exclude.src.dirs=build dist
-
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Type.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Type.java	Wed Jul 05 17:21:32 2017 +0200
@@ -328,6 +328,10 @@
         return (tsym.flags() & INTERFACE) != 0;
     }
 
+    public boolean isFinal() {
+        return (tsym.flags() & FINAL) != 0;
+    }
+
     public boolean isPrimitive() {
         return tag < VOID;
     }
@@ -347,11 +351,17 @@
         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<Type> ts) {
-        for (List<Type> l = ts; l.nonEmpty(); l = l.tail)
-            if (this.contains(ts.head)) return true;
+    public boolean containsAny(List<Type> ts) {
+        for (Type t : ts)
+            if (this.contains(t)) return true;
+        return false;
+    }
+
+    public static boolean containsAny(List<Type> ts1, List<Type> ts2) {
+        for (Type t : ts1)
+            if (t.containsAny(ts2)) return true;
         return false;
     }
 
@@ -431,6 +441,10 @@
             this.bound = bound;
         }
 
+        public boolean contains(Type t) {
+            return kind != UNBOUND && type.contains(t);
+        }
+
         public boolean isSuperBound() {
             return kind == SUPER ||
                 kind == UNBOUND;
@@ -681,7 +695,9 @@
             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() {
--- a/langtools/src/share/classes/com/sun/tools/javac/code/Types.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/src/share/classes/com/sun/tools/javac/code/Types.java	Wed Jul 05 17:21:32 2017 +0200
@@ -960,7 +960,7 @@
                     return true;
 
                 if (s.tag == TYPEVAR) {
-                    if (isCastable(s.getUpperBound(), t, Warner.noWarnings)) {
+                    if (isCastable(t, s.getUpperBound(), Warner.noWarnings)) {
                         warnStack.head.warnUnchecked();
                         return true;
                     } else {
@@ -1030,7 +1030,12 @@
                                 && !disjointTypes(aHigh.allparams(), lowSub.allparams())
                                 && !disjointTypes(aLow.allparams(), highSub.allparams())
                                 && !disjointTypes(aLow.allparams(), lowSub.allparams())) {
-                                if (upcast ? giveWarning(a, b) :
+                                if (s.isInterface() &&
+                                        !t.isInterface() &&
+                                        t.isFinal() &&
+                                        !isSubtype(t, s)) {
+                                    return false;
+                                } else if (upcast ? giveWarning(a, b) :
                                     giveWarning(b, a))
                                     warnStack.head.warnUnchecked();
                                 return true;
@@ -1230,18 +1235,23 @@
         if (t == s) return false;
         if (t.tag == TYPEVAR) {
             TypeVar tv = (TypeVar) t;
-            if (s.tag == TYPEVAR)
-                s = s.getUpperBound();
             return !isCastable(tv.bound,
-                               s,
+                               relaxBound(s),
                                Warner.noWarnings);
         }
         if (s.tag != WILDCARD)
             s = upperBound(s);
-        if (s.tag == TYPEVAR)
-            s = s.getUpperBound();
-
-        return !isSubtype(t, s);
+
+        return !isSubtype(t, relaxBound(s));
+    }
+
+    private Type relaxBound(Type t) {
+        if (t.tag == TYPEVAR) {
+            while (t.tag == TYPEVAR)
+                t = t.getUpperBound();
+            t = rewriteQuantifiers(t, true, true);
+        }
+        return t;
     }
     // </editor-fold>
 
@@ -2945,6 +2955,13 @@
     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;
@@ -3273,7 +3290,7 @@
      * quantifiers) only
      */
     private Type rewriteQuantifiers(Type t, boolean high, boolean rewriteTypeVars) {
-        return new Rewriter(high, rewriteTypeVars).rewrite(t);
+        return new Rewriter(high, rewriteTypeVars).visit(t);
     }
 
     class Rewriter extends UnaryVisitor<Type> {
@@ -3286,25 +3303,21 @@
             this.rewriteTypeVars = rewriteTypeVars;
         }
 
-        Type rewrite(Type t) {
-            ListBuffer<Type> from = new ListBuffer<Type>();
-            ListBuffer<Type> to = new ListBuffer<Type>();
-            adaptSelf(t, from, to);
+        @Override
+        public Type visitClassType(ClassType t, Void s) {
             ListBuffer<Type> rewritten = new ListBuffer<Type>();
-            List<Type> formals = from.toList();
             boolean changed = false;
-            for (Type arg : to.toList()) {
+            for (Type arg : t.allparams()) {
                 Type bound = visit(arg);
                 if (arg != bound) {
                     changed = true;
-                    bound = high ? makeExtendsWildcard(bound, (TypeVar)formals.head)
-                              : makeSuperWildcard(bound, (TypeVar)formals.head);
                 }
                 rewritten.append(bound);
-                formals = formals.tail;
             }
             if (changed)
-                return subst(t.tsym.type, from.toList(), rewritten.toList());
+                return subst(t.tsym.type,
+                        t.tsym.type.allparams(),
+                        rewritten.toList());
             else
                 return t;
         }
@@ -3315,13 +3328,22 @@
 
         @Override
         public Type visitCapturedType(CapturedType t, Void s) {
-            return visitWildcardType(t.wildcard, null);
+            Type bound = visitWildcardType(t.wildcard, null);
+            return (bound.contains(t)) ?
+                    (high ? syms.objectType : syms.botType) :
+                        bound;
         }
 
         @Override
         public Type visitTypeVar(TypeVar t, Void s) {
-            if (rewriteTypeVars)
-                return high ? t.bound : syms.botType;
+            if (rewriteTypeVars) {
+                Type bound = high ?
+                    (t.bound.contains(t) ?
+                        syms.objectType :
+                        visit(t.bound)) :
+                    syms.botType;
+                return rewriteAsWildcardType(bound, t);
+            }
             else
                 return t;
         }
@@ -3331,11 +3353,31 @@
             Type bound = high ? t.getExtendsBound() :
                                 t.getSuperBound();
             if (bound == null)
-                bound = high ? syms.objectType : syms.botType;
-            return bound;
+            bound = high ? syms.objectType : syms.botType;
+            return rewriteAsWildcardType(visit(bound), t.bound);
+        }
+
+        private Type rewriteAsWildcardType(Type bound, TypeVar formal) {
+            return high ?
+                makeExtendsWildcard(B(bound), formal) :
+                makeSuperWildcard(B(bound), formal);
+        }
+
+        Type B(Type t) {
+            while (t.tag == WILDCARD) {
+                WildcardType w = (WildcardType)t;
+                t = high ?
+                    w.getExtendsBound() :
+                    w.getSuperBound();
+                if (t == null) {
+                    t = high ? syms.objectType : syms.botType;
+                }
+            }
+            return t;
         }
     }
 
+
     /**
      * Create a wildcard with the given upper (extends) bound; create
      * an unbounded wildcard if bound is Object.
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Attr.java	Wed Jul 05 17:21:32 2017 +0200
@@ -675,24 +675,34 @@
 
             // 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)
+
+            // 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);
-
-            if ((owner.flags() & ANNOTATION) != 0)
+                // 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 (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<JCExpression> l = tree.thrown; l.nonEmpty(); l = l.tail)
                 chk.checkType(l.head.pos(), l.head.type, syms.throwableType);
 
@@ -1546,7 +1556,7 @@
         List<Type> 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;
         }
 
@@ -1586,13 +1596,15 @@
                 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;
             }
@@ -1682,8 +1694,7 @@
                         Type clazztype,
                         Pair<Scope, Scope> mapping,
                         List<Type> argtypes,
-                        List<Type> typeargtypes,
-                        boolean reportErrors) {
+                        List<Type> 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
@@ -1721,7 +1732,7 @@
                         env,
                         clazztype.tsym.type,
                         argtypes,
-                        typeargtypes, reportErrors);
+                        typeargtypes);
             } finally {
                 ((ClassSymbol) clazztype.tsym).members_field = mapping.fst;
             }
@@ -1750,42 +1761,37 @@
                         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<Type> 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<Type> 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;
@@ -2002,7 +2008,7 @@
 
     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)
@@ -2015,7 +2021,7 @@
             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);
     }
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Check.java	Wed Jul 05 17:21:32 2017 +0200
@@ -25,6 +25,7 @@
 
 package com.sun.tools.javac.comp;
 
+import com.sun.source.tree.AssignmentTree;
 import java.util.*;
 import java.util.Set;
 
@@ -329,7 +330,7 @@
             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);
@@ -905,33 +906,15 @@
      *
      *  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<AttrContext> 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<AttrContext> 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<AttrContext> env, boolean checkRaw) {
+        new Validator(env).validateTree(tree, checkRaw, true);
     }
 
     /** Visitor method: Validate a list of type expressions.
@@ -945,9 +928,16 @@
      */
     class Validator extends JCTree.Visitor {
 
+        boolean isOuter;
+        Env<AttrContext> env;
+
+        Validator(Env<AttrContext> env) {
+            this.env = env;
+        }
+
         @Override
         public void visitTypeArray(JCArrayTypeTree tree) {
-            validate(tree.elemtype, env);
+            tree.elemtype.accept(this);
         }
 
         @Override
@@ -959,10 +949,14 @@
                 List<Type> forms = tree.type.tsym.type.getTypeArguments();
                 ListBuffer<Type> tvars_buf = new ListBuffer<Type>();
 
+                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
@@ -1014,14 +1008,14 @@
 
         @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
@@ -1059,7 +1053,34 @@
         public void visitTree(JCTree tree) {
         }
 
-        Env<AttrContext> 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<? extends JCTree> trees, boolean checkRaw, boolean isOuter) {
+            for (List<? extends JCTree> l = trees; l.nonEmpty(); l = l.tail)
+                validateTree(l.head, checkRaw, isOuter);
+        }
+
+        void checkRaw(JCTree tree, Env<AttrContext> 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);
+            }
+        }
     }
 
 /* *************************************************************************
@@ -1929,6 +1950,20 @@
  * Check annotations
  **************************************************************************/
 
+    /**
+     * Recursively validate annotations values
+     */
+    void validateAnnotationTree(JCTree tree) {
+        class AnnotationValidator extends TreeScanner {
+            @Override
+            public void visitAnnotation(JCAnnotation tree) {
+                super.visitAnnotation(tree);
+                validateAnnotation(tree);
+            }
+        }
+        tree.accept(new AnnotationValidator());
+    }
+
     /** Annotation types are restricted to primitives, String, an
      *  enum, an annotation, Class, Class<?>, Class<? extends
      *  Anything>, arrays of the preceding.
@@ -1992,7 +2027,7 @@
     /** 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");
@@ -2006,7 +2041,7 @@
     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");
@@ -2103,8 +2138,12 @@
     public void validateAnnotation(JCAnnotation a) {
         if (a.type.isErroneous()) return;
 
-        // collect an inventory of the members
-        Set<MethodSymbol> members = new HashSet<MethodSymbol>();
+        // collect an inventory of the members (sorted alphabetically)
+        Set<MethodSymbol> members = new TreeSet<MethodSymbol>(new Comparator<Symbol>() {
+            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)
@@ -2120,15 +2159,21 @@
             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
-        for (MethodSymbol m : members)
-            if (m.defaultValue == null && !m.type.isErroneous())
-                log.error(a.pos(), "annotation.missing.default.value",
-                          a.type, m.name);
+        ListBuffer<Name> 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
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Infer.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Infer.java	Wed Jul 05 17:21:32 2017 +0200
@@ -138,24 +138,73 @@
     /** 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<Boolean> constraintScanner =
+                        new Types.UnaryVisitor<Boolean>() {
+
+                    List<Type> seen = List.nil();
+
+                    Boolean visit(List<Type> 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 @@
             TypeVar tv = (TypeVar)uv.qtype;
             ListBuffer<Type> hibounds = new ListBuffer<Type>();
             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<Type> inst = that.getConstraints(tv, ConstraintKind.EQUAL);
             if (inst.nonEmpty() && inst.head.tag != BOT) {
                 uv.inst = inst.head;
@@ -279,9 +327,32 @@
 
         // check bounds
         List<Type> 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<Type> instaniateAsUninferredVars(List<Type> undetvars, List<Type> tvars) {
+        ListBuffer<Type> 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'.
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java	Wed Jul 05 17:21:32 2017 +0200
@@ -1079,14 +1079,21 @@
 
 
     private Env<AttrContext> baseEnv(JCClassDecl tree, Env<AttrContext> 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<JCTypeParameter> typarams = tree.typarams;
                  typarams.nonEmpty();
                  typarams = typarams.tail)
-                typaramScope.enter(typarams.head.type.tsym);
+                baseScope.enter(typarams.head.type.tsym);
         Env<AttrContext> outer = env.outer; // the base clause can't see members of this class
-        Env<AttrContext> localEnv = outer.dup(tree, outer.info.dup(typaramScope));
+        Env<AttrContext> localEnv = outer.dup(tree, outer.info.dup(baseScope));
         localEnv.baseClause = true;
         localEnv.outer = outer;
         localEnv.info.isSelfCall = false;
--- a/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java	Wed Jul 05 17:21:32 2017 +0200
@@ -40,6 +40,8 @@
 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.DiagnosticFlag;
+import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType;
 import javax.lang.model.element.ElementVisitor;
 
 import java.util.Map;
@@ -1447,7 +1449,7 @@
                               Env<AttrContext> env,
                               Type site,
                               List<Type> argtypes,
-                              List<Type> typeargtypes, boolean reportErrors) {
+                              List<Type> typeargtypes) {
         Symbol sym = methodNotFound;
         JCDiagnostic explanation = null;
         List<MethodResolutionPhase> steps = methodResolutionSteps;
@@ -1466,11 +1468,20 @@
             }
             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<Type> argtypes, List<Type> 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;
     }
@@ -1655,8 +1666,10 @@
             List<Type> typeargtypes) {
         JCDiagnostic d = error.getDiagnostic(JCDiagnostic.DiagnosticType.ERROR,
                 pos, site, name, argtypes, typeargtypes);
-        if (d != null)
+        if (d != null) {
+            d.setFlag(DiagnosticFlag.RESOLVE_ERROR);
             log.report(d);
+        }
     }
 
     private final LocalizedString noArgs = new LocalizedString("compiler.misc.no.args");
--- a/langtools/src/share/classes/com/sun/tools/javac/jvm/Gen.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/Gen.java	Wed Jul 05 17:21:32 2017 +0200
@@ -1455,24 +1455,27 @@
                       List<Integer> gaps) {
             if (startpc != endpc) {
                 List<JCExpression> subClauses = TreeInfo.isMultiCatch(tree) ?
-                    ((JCTypeDisjoint)tree.param.vartype).components :
-                    List.of(tree.param.vartype);
-                for (JCExpression subCatch : subClauses) {
-                    int catchType = makeRef(tree.pos(), subCatch.type);
-                    List<Integer> lGaps = gaps;
-                    while (lGaps.nonEmpty()) {
-                        int end = lGaps.head.intValue();
+                        ((JCTypeDisjoint)tree.param.vartype).components :
+                        List.of(tree.param.vartype);
+                while (gaps.nonEmpty()) {
+                    for (JCExpression subCatch : subClauses) {
+                        int catchType = makeRef(tree.pos(), subCatch.type);
+                        int end = gaps.head.intValue();
                         registerCatch(tree.pos(),
                                       startpc,  end, code.curPc(),
                                       catchType);
-                        lGaps = lGaps.tail;
-                        startpc = lGaps.head.intValue();
-                        lGaps = lGaps.tail;
                     }
-                    if (startpc < endpc)
+                    gaps = gaps.tail;
+                    startpc = gaps.head.intValue();
+                    gaps = gaps.tail;
+                }
+                if (startpc < endpc) {
+                    for (JCExpression subCatch : subClauses) {
+                        int catchType = makeRef(tree.pos(), subCatch.type);
                         registerCatch(tree.pos(),
                                       startpc, endpc, code.curPc(),
                                       catchType);
+                    }
                 }
                 VarSymbol exparam = tree.param.sym;
                 code.statBegin(tree.pos);
--- a/langtools/src/share/classes/com/sun/tools/javac/main/JavaCompiler.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/src/share/classes/com/sun/tools/javac/main/JavaCompiler.java	Wed Jul 05 17:21:32 2017 +0200
@@ -529,7 +529,7 @@
                 log.error("warnings.and.werror");
             }
         }
-            return log.nerrors;
+        return log.nerrors;
     }
 
     protected final <T> Queue<T> stopIfError(CompileState cs, Queue<T> queue) {
@@ -868,7 +868,7 @@
     /**
      * Parses a list of files.
      */
-   public List<JCCompilationUnit> parseFiles(List<JavaFileObject> fileObjects) throws IOException {
+   public List<JCCompilationUnit> parseFiles(Iterable<JavaFileObject> fileObjects) throws IOException {
        if (shouldStop(CompileState.PARSE))
            return List.nil();
 
@@ -981,14 +981,13 @@
      */
     public JavaCompiler processAnnotations(List<JCCompilationUnit> roots,
                                            List<String> classnames)
-        throws IOException  { // TODO: see TEMP note in JavacProcessingEnvironment
+            throws IOException  { // TODO: see TEMP note in JavacProcessingEnvironment
         if (shouldStop(CompileState.PROCESS)) {
-            // Errors were encountered.  If todo is empty, then the
-            // encountered errors were parse errors.  Otherwise, the
-            // errors were found during the enter phase which should
-            // be ignored when processing annotations.
-
-            if (todo.isEmpty())
+            // Errors were encountered.
+            // If log.unrecoverableError is set, the errors were parse errors
+            // or other errors during enter which cannot be fixed by running
+            // any annotation processors.
+            if (log.unrecoverableError)
                 return this;
         }
 
--- a/langtools/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/src/share/classes/com/sun/tools/javac/processing/JavacProcessingEnvironment.java	Wed Jul 05 17:21:32 2017 +0200
@@ -795,6 +795,13 @@
         final JavaCompiler compiler;
         /** The log for the round. */
         final Log log;
+        /** The number of warnings in the previous round. */
+        final int priorWarnings;
+
+        /** The ASTs to be compiled. */
+        List<JCCompilationUnit> roots;
+        /** The classes to be compiler that have were generated. */
+        Map<String, JavaFileObject> genClassFiles;
 
         /** The set of annotations to be processed this round. */
         Set<TypeElement> annotationsPresent;
@@ -803,10 +810,12 @@
         /** The set of package-info files to be processed this round. */
         List<PackageSymbol> packageInfoFiles;
 
-        /** Create a round. */
-        Round(Context context, int number) {
+        /** Create a round (common code). */
+        private Round(Context context, int number, int priorWarnings) {
             this.context = context;
             this.number = number;
+            this.priorWarnings = priorWarnings;
+
             compiler = JavaCompiler.instance(context);
             log = Log.instance(context);
 
@@ -814,15 +823,86 @@
             JavacProcessingEnvironment.this.context = context;
 
             // the following will be populated as needed
-            annotationsPresent = new LinkedHashSet<TypeElement>();
             topLevelClasses  = List.nil();
             packageInfoFiles = List.nil();
         }
 
+        /** Create the first round. */
+        Round(Context context, List<JCCompilationUnit> roots, List<ClassSymbol> classSymbols) {
+            this(context, 1, 0);
+            this.roots = roots;
+            genClassFiles = new HashMap<String,JavaFileObject>();
+
+            compiler.todo.clear(); // free the compiler's resources
+
+            // The reverse() in the following line is to maintain behavioural
+            // compatibility with the previous revision of the code. Strictly speaking,
+            // it should not be necessary, but a javah golden file test fails without it.
+            topLevelClasses =
+                getTopLevelClasses(roots).prependList(classSymbols.reverse());
+
+            packageInfoFiles = getPackageInfoFiles(roots);
+
+            findAnnotationsPresent();
+        }
+
+        /** Create a new round. */
+        private Round(Round prev,
+                Set<JavaFileObject> newSourceFiles, Map<String,JavaFileObject> newClassFiles)
+                throws IOException {
+            this(prev.nextContext(), prev.number+1, prev.compiler.log.nwarnings);
+            this.genClassFiles = prev.genClassFiles;
+
+            updateProcessingState();
+
+            List<JCCompilationUnit> parsedFiles = compiler.parseFiles(newSourceFiles);
+            roots = cleanTrees(prev.roots).appendList(parsedFiles);
+
+            // Check for errors after parsing
+            if (unrecoverableError())
+                return;
+
+            enterClassFiles(genClassFiles);
+            List<ClassSymbol> newClasses = enterClassFiles(newClassFiles);
+            genClassFiles.putAll(newClassFiles);
+            enterTrees(roots);
+
+            if (unrecoverableError())
+                return;
+
+            topLevelClasses = join(
+                    getTopLevelClasses(parsedFiles),
+                    getTopLevelClassesFromClasses(newClasses));
+
+            packageInfoFiles = join(
+                    getPackageInfoFiles(parsedFiles),
+                    getPackageInfoFilesFromClasses(newClasses));
+
+            findAnnotationsPresent();
+        }
+
         /** Create the next round to be used. */
-        Round next() {
-            compiler.close(false);
-            return new Round(contextForNextRound(), number + 1);
+        Round next(Set<JavaFileObject> newSourceFiles, Map<String, JavaFileObject> newClassFiles)
+                throws IOException {
+            try {
+                return new Round(this, newSourceFiles, newClassFiles);
+            } finally {
+                compiler.close(false);
+            }
+        }
+
+        /** Create the compiler to be used for the final compilation. */
+        JavaCompiler finalCompiler(boolean errorStatus) {
+            try {
+                JavaCompiler c = JavaCompiler.instance(nextContext());
+                if (errorStatus) {
+                    c.log.nwarnings += priorWarnings + compiler.log.nwarnings;
+                    c.log.nerrors += compiler.log.nerrors;
+                }
+                return c;
+            } finally {
+                compiler.close(false);
+            }
         }
 
         /** Return the number of errors found so far in this round.
@@ -839,12 +919,16 @@
 
         /** Return whether or not an unrecoverable error has occurred. */
         boolean unrecoverableError() {
-            return log.unrecoverableError;
+            return log.unrecoverableError
+                    || messager.errorRaised()
+                    || (werror && log.nwarnings > 0)
+                    || (fatalErrors && log.nerrors > 0);
         }
 
         /** Find the set of annotations present in the set of top level
-         * classes and package info files to be processed this round. */
-        void findAnnotationsPresent(ComputeAnnotationSet annotationComputer) {
+         *  classes and package info files to be processed this round. */
+        void findAnnotationsPresent() {
+            ComputeAnnotationSet annotationComputer = new ComputeAnnotationSet(elementUtils);
             // Use annotation processing to compute the set of annotations present
             annotationsPresent = new LinkedHashSet<TypeElement>();
             for (ClassSymbol classSym : topLevelClasses)
@@ -853,26 +937,13 @@
                 annotationComputer.scan(pkgSym, annotationsPresent);
         }
 
-        /**
-         * Parse the latest set of generated source files created by the filer.
-         */
-        List<JCCompilationUnit> parseNewSourceFiles()
-            throws IOException {
-            List<JavaFileObject> fileObjects = List.nil();
-            for (JavaFileObject jfo : filer.getGeneratedSourceFileObjects() ) {
-                fileObjects = fileObjects.prepend(jfo);
-            }
-
-           return compiler.parseFiles(fileObjects);
-        }
-
-        /** Enter the latest set of generated class files created by the filer. */
-        List<ClassSymbol> enterNewClassFiles() {
+        /** Enter a set of generated class files. */
+        List<ClassSymbol> enterClassFiles(Map<String, JavaFileObject> classFiles) {
             ClassReader reader = ClassReader.instance(context);
             Names names = Names.instance(context);
             List<ClassSymbol> list = List.nil();
 
-            for (Map.Entry<String,JavaFileObject> entry : filer.getGeneratedClasses().entrySet()) {
+            for (Map.Entry<String,JavaFileObject> entry : classFiles.entrySet()) {
                 Name name = names.fromString(entry.getKey());
                 JavaFileObject file = entry.getValue();
                 if (file.getKind() != JavaFileObject.Kind.CLASS)
@@ -900,11 +971,7 @@
 
         /** Run a processing round. */
         void run(boolean lastRound, boolean errorStatus) {
-//            assert lastRound
-//                ? (topLevelClasses.size() == 0 && annotationsPresent.size() == 0)
-//                : (errorStatus == false);
-//
-//            printRoundInfo(topLevelClasses, annotationsPresent, lastRound);
+            printRoundInfo(lastRound);
 
             TaskListener taskListener = context.get(TaskListener.class);
             if (taskListener != null)
@@ -912,7 +979,6 @@
 
             try {
                 if (lastRound) {
-                    printRoundInfo(List.<ClassSymbol>nil(), Collections.<TypeElement>emptySet(), lastRound);
                     filer.setLastRound(true);
                     Set<Element> emptyRootElements = Collections.emptySet(); // immutable
                     RoundEnvironment renv = new JavacRoundEnvironment(true,
@@ -921,7 +987,6 @@
                             JavacProcessingEnvironment.this);
                     discoveredProcs.iterator().runContributingProcs(renv);
                 } else {
-                    printRoundInfo(topLevelClasses, annotationsPresent, lastRound);
                     discoverAndRunProcs(context, annotationsPresent, topLevelClasses, packageInfoFiles);
                 }
             } finally {
@@ -931,11 +996,7 @@
         }
 
         /** Update the processing state for the current context. */
-        // Question: should this not be part of next()?
-        // Note: Calling it from next() breaks some tests. There is an issue
-        // whether the annotationComputer is using elementUtils with the
-        // correct context.
-        void updateProcessingState() {
+        private void updateProcessingState() {
             filer.newRound(context);
             messager.newRound(context);
 
@@ -944,14 +1005,14 @@
         }
 
         /** Print info about this round. */
-        private void printRoundInfo(List<ClassSymbol> topLevelClasses,
-                Set<TypeElement> annotationsPresent,
-                boolean lastRound) {
+        private void printRoundInfo(boolean lastRound) {
             if (printRounds || verbose) {
+                List<ClassSymbol> tlc = lastRound ? List.<ClassSymbol>nil() : topLevelClasses;
+                Set<TypeElement> ap = lastRound ? Collections.<TypeElement>emptySet() : annotationsPresent;
                 log.printNoteLines("x.print.rounds",
-                        (!lastRound ? number : number + 1),
-                        "{" + topLevelClasses.toString(", ") + "}",
-                        annotationsPresent,
+                        number,
+                        "{" + tlc.toString(", ") + "}",
+                        ap,
                         lastRound);
             }
         }
@@ -960,7 +1021,7 @@
          * Important values are propogated from round to round;
          * other values are implicitly reset.
          */
-        private Context contextForNextRound() {
+        private Context nextContext() {
             Context next = new Context();
 
             Options options = Options.instance(context);
@@ -1025,138 +1086,90 @@
                                      Iterable<? extends PackageSymbol> pckSymbols)
         throws IOException {
 
+        TaskListener taskListener = context.get(TaskListener.class);
         log = Log.instance(context);
 
-        Round round = new Round(context, 1);
-        round.compiler.todo.clear(); // free the compiler's resources
-
-        // The reverse() in the following line is to maintain behavioural
-        // compatibility with the previous revision of the code. Strictly speaking,
-        // it should not be necessary, but a javah golden file test fails without it.
-        round.topLevelClasses =
-                getTopLevelClasses(roots).prependList(classSymbols.reverse());
-
-        round.packageInfoFiles = getPackageInfoFiles(roots);
-
         Set<PackageSymbol> specifiedPackages = new LinkedHashSet<PackageSymbol>();
         for (PackageSymbol psym : pckSymbols)
             specifiedPackages.add(psym);
         this.specifiedPackages = Collections.unmodifiableSet(specifiedPackages);
 
-        ComputeAnnotationSet annotationComputer = new ComputeAnnotationSet(elementUtils);
-        round.findAnnotationsPresent(annotationComputer);
-
-        boolean errorStatus = false;
+        Round round = new Round(context, roots, classSymbols);
 
-        runAround:
-        while (true) {
-            if ((fatalErrors && round.errorCount() != 0)
-                    || (werror && round.warningCount() != 0)) {
-                errorStatus = true;
-                break runAround;
-            }
-
+        boolean errorStatus;
+        boolean moreToDo;
+        do {
+            // Run processors for round n
             round.run(false, false);
 
-            /*
-             * Processors for round n have run to completion.  Prepare
-             * for round (n+1) by checked for errors raised by
-             * annotation processors and then checking for syntax
-             * errors on any generated source files.
-             */
-            if (messager.errorRaised()) {
-                errorStatus = true;
-                break runAround;
-            }
-
-            if (!moreToDo())
-                break runAround; // No new files
-
-            round = round.next();
-
-            List<JCCompilationUnit> parsedFiles = round.parseNewSourceFiles();
-            roots = cleanTrees(roots).appendList(parsedFiles);
+            // Processors for round n have run to completion.
+            // Check for errors and whether there is more work to do.
+            errorStatus = round.unrecoverableError();
+            moreToDo = moreToDo();
 
-            // Check for errors after parsing
-            if (round.unrecoverableError()) {
-                errorStatus = true;
-                break runAround;
-            }
-
-            List<ClassSymbol> newClasses = round.enterNewClassFiles();
-            round.enterTrees(roots);
+            // Set up next round.
+            // Copy mutable collections returned from filer.
+            round = round.next(
+                    new LinkedHashSet<JavaFileObject>(filer.getGeneratedSourceFileObjects()),
+                    new LinkedHashMap<String,JavaFileObject>(filer.getGeneratedClasses()));
 
-            round.topLevelClasses = join(
-                    getTopLevelClasses(parsedFiles),
-                    getTopLevelClassesFromClasses(newClasses));
+             // Check for errors during setup.
+            if (round.unrecoverableError())
+                errorStatus = true;
 
-            round.packageInfoFiles = join(
-                    getPackageInfoFiles(parsedFiles),
-                    getPackageInfoFilesFromClasses(newClasses));
-
-            round.findAnnotationsPresent(annotationComputer);
-            round.updateProcessingState();
-        }
+        } while (moreToDo && !errorStatus);
 
         // run last round
         round.run(true, errorStatus);
 
-        // Add any sources generated during the last round to the set
-        // of files to be compiled.
-        if (moreToDo()) {
-            List<JCCompilationUnit> parsedFiles = round.parseNewSourceFiles();
-            roots = cleanTrees(roots).appendList(parsedFiles);
-        }
-
-        // Set error status for any files compiled and generated in
-        // the last round
-        if (round.unrecoverableError() || (werror && round.warningCount() != 0))
-            errorStatus = true;
-
-        round = round.next();
-
         filer.warnIfUnclosedFiles();
         warnIfUnmatchedOptions();
 
-       /*
-        * If an annotation processor raises an error in a round,
-        * that round runs to completion and one last round occurs.
-        * The last round may also occur because no more source or
-        * class files have been generated.  Therefore, if an error
-        * was raised on either of the last *two* rounds, the compile
-        * should exit with a nonzero exit code.  The current value of
-        * errorStatus holds whether or not an error was raised on the
-        * second to last round; errorRaised() gives the error status
-        * of the last round.
-        */
-        errorStatus = errorStatus || messager.errorRaised();
+        /*
+         * If an annotation processor raises an error in a round,
+         * that round runs to completion and one last round occurs.
+         * The last round may also occur because no more source or
+         * class files have been generated.  Therefore, if an error
+         * was raised on either of the last *two* rounds, the compile
+         * should exit with a nonzero exit code.  The current value of
+         * errorStatus holds whether or not an error was raised on the
+         * second to last round; errorRaised() gives the error status
+         * of the last round.
+         */
+        if (messager.errorRaised()
+                || werror && round.warningCount() > 0 && round.errorCount() > 0)
+            errorStatus = true;
+
+        Set<JavaFileObject> newSourceFiles =
+                new LinkedHashSet<JavaFileObject>(filer.getGeneratedSourceFileObjects());
+        roots = cleanTrees(round.roots);
+
+        JavaCompiler compiler = round.finalCompiler(errorStatus);
+
+        if (newSourceFiles.size() > 0)
+            roots = roots.appendList(compiler.parseFiles(newSourceFiles));
+
+        errorStatus = errorStatus || (compiler.errorCount() > 0);
 
         // Free resources
         this.close();
 
-        TaskListener taskListener = this.context.get(TaskListener.class);
         if (taskListener != null)
             taskListener.finished(new TaskEvent(TaskEvent.Kind.ANNOTATION_PROCESSING));
 
-        JavaCompiler compiler;
-
         if (errorStatus) {
-            compiler = round.compiler;
-            compiler.log.nwarnings += messager.warningCount();
-            compiler.log.nerrors += messager.errorCount();
             if (compiler.errorCount() == 0)
                 compiler.log.nerrors++;
-        } else if (procOnly && !foundTypeProcessors) {
-            compiler = round.compiler;
+            return compiler;
+        }
+
+        if (procOnly && !foundTypeProcessors) {
             compiler.todo.clear();
-        } else { // Final compilation
-            round = round.next();
-            round.updateProcessingState();
-            compiler = round.compiler;
+        } else {
             if (procOnly && foundTypeProcessors)
                 compiler.shouldStopPolicy = CompileState.FLOW;
 
-            compiler.enterTrees(cleanTrees(roots));
+            compiler.enterTrees(roots);
         }
 
         return compiler;
@@ -1185,7 +1198,9 @@
         for (JCCompilationUnit unit : units) {
             for (JCTree node : unit.defs) {
                 if (node.getTag() == JCTree.CLASSDEF) {
-                    classes = classes.prepend(((JCClassDecl) node).sym);
+                    ClassSymbol sym = ((JCClassDecl) node).sym;
+                    assert sym != null;
+                    classes = classes.prepend(sym);
                 }
             }
         }
--- a/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/src/share/classes/com/sun/tools/javac/resources/compiler.properties	Wed Jul 05 17:21:32 2017 +0200
@@ -42,7 +42,9 @@
 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=\
--- a/langtools/src/share/classes/com/sun/tools/javac/util/JCDiagnostic.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/src/share/classes/com/sun/tools/javac/util/JCDiagnostic.java	Wed Jul 05 17:21:32 2017 +0200
@@ -25,8 +25,10 @@
 
 package com.sun.tools.javac.util;
 
+import java.util.EnumSet;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
 
 import javax.tools.Diagnostic;
 import javax.tools.JavaFileObject;
@@ -83,7 +85,7 @@
          */
         public JCDiagnostic error(
                 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
-            return create(ERROR, null, true, source, pos, key, args);
+            return create(ERROR, null, EnumSet.of(DiagnosticFlag.MANDATORY), source, pos, key, args);
         }
 
         /**
@@ -96,7 +98,7 @@
          */
         public JCDiagnostic mandatoryWarning(
                 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
-            return create(WARNING, null, true, source, pos, key, args);
+            return create(WARNING, null, EnumSet.of(DiagnosticFlag.MANDATORY), source, pos, key, args);
         }
 
         /**
@@ -111,7 +113,7 @@
         public JCDiagnostic mandatoryWarning(
                 LintCategory lc,
                 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
-            return create(WARNING, lc, true, source, pos, key, args);
+            return create(WARNING, lc, EnumSet.of(DiagnosticFlag.MANDATORY), source, pos, key, args);
         }
 
         /**
@@ -123,7 +125,7 @@
          */
         public JCDiagnostic warning(
                  LintCategory lc, String key, Object... args) {
-            return create(WARNING, lc, false, null, null, key, args);
+            return create(WARNING, lc, EnumSet.noneOf(DiagnosticFlag.class), null, null, key, args);
         }
 
         /**
@@ -135,7 +137,7 @@
          */
         public JCDiagnostic warning(
                 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
-            return create(WARNING, null, false, source, pos, key, args);
+            return create(WARNING, null, EnumSet.noneOf(DiagnosticFlag.class), source, pos, key, args);
         }
 
         /**
@@ -149,7 +151,7 @@
          */
         public JCDiagnostic warning(
                  LintCategory lc, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
-            return create(WARNING, lc, false, source, pos, key, args);
+            return create(WARNING, lc, EnumSet.noneOf(DiagnosticFlag.class), source, pos, key, args);
         }
 
         /**
@@ -159,7 +161,7 @@
          *  @see MandatoryWarningHandler
          */
         public JCDiagnostic mandatoryNote(DiagnosticSource source, String key, Object... args) {
-            return create(NOTE, null, true, source, null, key, args);
+            return create(NOTE, null, EnumSet.of(DiagnosticFlag.MANDATORY), source, null, key, args);
         }
 
         /**
@@ -168,7 +170,7 @@
          *  @param args   Fields of the message.
          */
         public JCDiagnostic note(String key, Object... args) {
-            return create(NOTE, null, false, null, null, key, args);
+            return create(NOTE, null, EnumSet.noneOf(DiagnosticFlag.class), null, null, key, args);
         }
 
         /**
@@ -180,7 +182,7 @@
          */
         public JCDiagnostic note(
                 DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
-            return create(NOTE, null, false, source, pos, key, args);
+            return create(NOTE, null, EnumSet.noneOf(DiagnosticFlag.class), source, pos, key, args);
         }
 
         /**
@@ -189,7 +191,7 @@
          *  @param args   Fields of the message.
          */
         public JCDiagnostic fragment(String key, Object... args) {
-            return create(FRAGMENT, null, false, null, null, key, args);
+            return create(FRAGMENT, null, EnumSet.noneOf(DiagnosticFlag.class), null, null, key, args);
         }
 
         /**
@@ -204,7 +206,7 @@
          */
         public JCDiagnostic create(
                 DiagnosticType kind, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
-            return create(kind, null, false, source, pos, key, args);
+            return create(kind, null, EnumSet.noneOf(DiagnosticFlag.class), source, pos, key, args);
         }
 
         /**
@@ -218,8 +220,8 @@
          *  @param args        Fields of the message.
          */
         public JCDiagnostic create(
-                DiagnosticType kind, LintCategory lc, boolean isMandatory, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
-            return new JCDiagnostic(formatter, kind, lc, isMandatory, source, pos, qualify(kind, key), args);
+                DiagnosticType kind, LintCategory lc, Set<DiagnosticFlag> flags, DiagnosticSource source, DiagnosticPosition pos, String key, Object... args) {
+            return new JCDiagnostic(formatter, kind, lc, flags, source, pos, qualify(kind, key), args);
         }
 
         protected String qualify(DiagnosticType t, String key) {
@@ -240,7 +242,7 @@
         return new JCDiagnostic(getFragmentFormatter(),
                               FRAGMENT,
                               null,
-                              false,
+                              EnumSet.noneOf(DiagnosticFlag.class),
                               null,
                               null,
                               "compiler." + FRAGMENT.key + "." + key,
@@ -327,6 +329,11 @@
         private final int pos;
     }
 
+    public enum DiagnosticFlag {
+        MANDATORY,
+        RESOLVE_ERROR
+    }
+
     private final DiagnosticType type;
     private final DiagnosticSource source;
     private final DiagnosticPosition position;
@@ -334,7 +341,7 @@
     private final int column;
     private final String key;
     protected final Object[] args;
-    private final boolean mandatory;
+    private final Set<DiagnosticFlag> flags;
     private final LintCategory lintCategory;
 
     /**
@@ -350,7 +357,7 @@
     protected JCDiagnostic(DiagnosticFormatter<JCDiagnostic> formatter,
                        DiagnosticType dt,
                        LintCategory lc,
-                       boolean mandatory,
+                       Set<DiagnosticFlag> flags,
                        DiagnosticSource source,
                        DiagnosticPosition pos,
                        String key,
@@ -361,7 +368,7 @@
         this.defaultFormatter = formatter;
         this.type = dt;
         this.lintCategory = lc;
-        this.mandatory = mandatory;
+        this.flags = flags;
         this.source = source;
         this.position = pos;
         this.key = key;
@@ -401,7 +408,7 @@
      * @return true if this diagnostic is required to be shown.
      */
     public boolean isMandatory() {
-        return mandatory;
+        return flags.contains(DiagnosticFlag.MANDATORY);
     }
 
     /**
@@ -520,8 +527,9 @@
         case NOTE:
             return Diagnostic.Kind.NOTE;
         case WARNING:
-            return mandatory ? Diagnostic.Kind.MANDATORY_WARNING
-                             : Diagnostic.Kind.WARNING;
+            return flags.contains(DiagnosticFlag.MANDATORY)
+                    ? Diagnostic.Kind.MANDATORY_WARNING
+                    : Diagnostic.Kind.WARNING;
         case ERROR:
             return Diagnostic.Kind.ERROR;
         default:
@@ -537,6 +545,14 @@
         return defaultFormatter.formatMessage(this, locale);
     }
 
+    public void setFlag(DiagnosticFlag flag) {
+        flags.add(flag);
+    }
+
+    public boolean isFlagSet(DiagnosticFlag flag) {
+        return flags.contains(flag);
+    }
+
     public static class MultilineDiagnostic extends JCDiagnostic {
 
         private final List<JCDiagnostic> subdiagnostics;
@@ -545,7 +561,7 @@
             super(other.defaultFormatter,
                   other.getType(),
                   other.getLintCategory(),
-                  other.isMandatory(),
+                  other.flags,
                   other.getDiagnosticSource(),
                   other.position,
                   other.getCode(),
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/6857948/T6857948.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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()) {};
+   }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/6857948/T6857948.out	Wed Jul 05 17:21:32 2017 +0200
@@ -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
--- a/langtools/test/tools/javac/Diagnostics/6862608/T6862608a.out	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/test/tools/javac/Diagnostics/6862608/T6862608a.out	Wed Jul 05 17:21:32 2017 +0200
@@ -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<? extends java.util.Comparator<? super java.lang.String>>, java.util.List<java.util.Comparator<?>>)
+T6862608a.java:19:41: compiler.err.prob.found.req: (compiler.misc.incompatible.types.1: (compiler.misc.no.conforming.instance.exists: T, java.util.Comparator<T>, java.util.Comparator<java.lang.String>)), <T>java.util.Comparator<T>, java.util.Comparator<java.lang.String>
 - compiler.misc.where.description.typevar: T,{(compiler.misc.where.typevar: T, java.lang.Object, kindname.method, <T>compound(java.lang.Iterable<? extends java.util.Comparator<? super T>>))}
 1 error
--- a/langtools/test/tools/javac/T6358024.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/test/tools/javac/T6358024.java	Wed Jul 05 17:21:32 2017 +0200
@@ -60,7 +60,7 @@
              new Option[] { new XOption("-XprintRounds"),
                             new Option("-processorpath", "."),
                             new Option("-processor", self) },
-             11);
+             12);
     }
 
     static void test(JavacFileManager fm, JavaFileObject f, Option[] opts, int expect) throws Throwable {
--- a/langtools/test/tools/javac/T6403466.out	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/test/tools/javac/T6403466.out	Wed Jul 05 17:21:32 2017 +0200
@@ -13,6 +13,10 @@
 Finished TaskEvent[ENTER,T6403466Wrapper.java,null]
 Started TaskEvent[ANNOTATION_PROCESSING_ROUND,null,null]
 Finished TaskEvent[ANNOTATION_PROCESSING_ROUND,null,null]
+Started TaskEvent[ENTER,T6403466.java,null]
+Started TaskEvent[ENTER,T6403466Wrapper.java,null]
+Finished TaskEvent[ENTER,T6403466.java,null]
+Finished TaskEvent[ENTER,T6403466Wrapper.java,null]
 Started TaskEvent[ANNOTATION_PROCESSING_ROUND,null,null]
 Finished TaskEvent[ANNOTATION_PROCESSING_ROUND,null,null]
 Finished TaskEvent[ANNOTATION_PROCESSING,null,null]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/T6977800.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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 {}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/annotations/6881115/T6881115.java	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,20 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug     6881115 6976649
+ * @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(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> {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/annotations/6881115/T6881115.out	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,16 @@
+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.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
+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
--- a/langtools/test/tools/javac/annotations/pos/TrailingComma.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/test/tools/javac/annotations/pos/TrailingComma.java	Wed Jul 05 17:21:32 2017 +0200
@@ -36,7 +36,7 @@
 }
 
 
-@TestAnnotation({@SuppressWarnings(),
+@TestAnnotation({@SuppressWarnings({}),
                  @SuppressWarnings({"Beware the ides of March.",}),
                  @SuppressWarnings({"Look both ways", "Before Crossing",}), })
 public class TrailingComma {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/cast/6270087/T6270087.java	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,42 @@
+/*
+ * 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     6270087 6932571
+ * @summary Javac rejects legal cast
+ * @compile T6270087.java
+ */
+
+class T6270087 {
+
+    static class Foo<X> {}
+
+    <S extends Comparable<S>> void test1(Comparable<Integer> c) {
+        Object o = (Comparable<S>)c;
+    }
+
+   <U extends Throwable, V extends Runnable> void test2(Foo<V> lv) {
+        Object o = (Foo<U>) lv;
+   }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/cast/6270087/T6270087neg.java	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,38 @@
+/*
+ * 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     6270087 6932571
+ * @summary Javac rejects legal cast
+ * @compile/fail/ref=T6270087neg.out -XDrawDiagnostics T6270087neg.java
+ */
+
+class T6270087neg {
+
+    static class Foo<X> {}
+
+   <U extends Integer, V extends String> void test2(Foo<V> lv) {
+        Object o = (Foo<U>) lv;
+   }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/cast/6270087/T6270087neg.out	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,2 @@
+T6270087neg.java:36:29: compiler.err.prob.found.req: (compiler.misc.inconvertible.types), T6270087neg.Foo<V>, T6270087neg.Foo<U>
+1 error
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/cast/6507317/T6507317.java	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,37 @@
+/*
+ * 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     6507317 6932571
+ * @summary Problem when casting from parametrized type to concrete class
+ * @compile T6507317.java
+ */
+
+import java.util.Comparator;
+
+abstract class T6507317<T extends Comparable<T>> implements Comparator<T> {
+    void test(T t) {
+        String s = (String)t;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/cast/6569057/T6569057.java	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,43 @@
+/*
+ * 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     6569057 6932571
+ * @summary Generics regression on cast
+ * @compile T6569057.java
+ */
+
+class T6569057 {
+    static class A<X extends B<?>> {  }
+
+    static class B<X extends A<?>> {
+        D<? extends B<X>> get() { return null; }
+    }
+
+    static class D<Y extends B<?>> {}
+
+    <E extends B<?>> void test(E x, D<B<A<?>>> d) {
+        boolean b = x.get() == d;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/cast/6932571/T6932571a.java	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,43 @@
+/*
+ * 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     6932571
+ * @summary Compiling Generics causing Inconvertible types
+ * @compile T6932571a.java
+ */
+
+class T6932571a {
+    static class A<T extends Comparable<? super T>> {
+        public void test(T v) {
+            Object obj = (Integer)v;
+        }
+    }
+
+    static class B<T extends Comparable<? extends T>> {
+        public void test(T v) {
+            Object obj = (Integer)v;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/cast/6932571/T6932571b.java	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,52 @@
+/*
+ * 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     6932571
+ * @summary Compiling Generics causing Inconvertible types
+ * @compile T6932571b.java
+ */
+
+class T6932571b {
+
+    interface A1<T extends B<? super T>> {
+        public T getT();
+    }
+
+    interface A2<T extends B<? extends T>> {
+        public T getT();
+    }
+
+    class B<T extends B<T>> {}
+
+    class C extends B<C> {}
+
+    void test1(A1<?> a) {
+        Object o = (C)a.getT();
+    }
+
+    void test2(A2<?> a) {
+        Object o = (C)a.getT();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/cast/6932571/T6932571neg.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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     6932571
+ * @summary Compiling Generics causing Inconvertible types
+ * @compile/fail/ref=T6932571neg.out -XDrawDiagnostics T6932571neg.java
+ */
+
+class T6932571neg {
+    interface I<T>{ }
+    interface I1 extends I<String> {}
+    static class Y implements I<String> {}
+    final static class S implements I<String> {}
+
+    <G extends I<G>> void test() {
+        S s = new S();
+        G g = (G) s;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/cast/6932571/T6932571neg.out	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,2 @@
+T6932571neg.java:39:19: compiler.err.prob.found.req: (compiler.misc.inconvertible.types), T6932571neg.S, G
+1 error
--- a/langtools/test/tools/javac/diags/Example.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/test/tools/javac/diags/Example.java	Wed Jul 05 17:21:32 2017 +0200
@@ -421,7 +421,7 @@
             if (verbose)
                 System.err.println("run_simple: " + opts + " " + files);
 
-            List<String> args = new ArrayList<String>(opts);
+            List<String> args = new ArrayList<String>();
 
             if (keys != null || raw)
                 args.add("-XDrawDiagnostics");
--- a/langtools/test/tools/javac/diags/examples.not-yet.txt	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/test/tools/javac/diags/examples.not-yet.txt	Wed Jul 05 17:21:32 2017 +0200
@@ -64,6 +64,7 @@
 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/diags/examples/AnnotationMissingValues1.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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 { }
--- a/langtools/test/tools/javac/diags/examples/InvalidInferredTypes.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/test/tools/javac/diags/examples/InvalidInferredTypes.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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 {
 
-    <T> Comparator<T> compound(Iterable<? extends Comparator<? super T>> it) {
+    <T extends List<? super T>> T makeList() {
         return null;
     }
 
-    public void test(List<Comparator<?>> x) {
-        Comparator<String> c3 = compound(x);
+    public void test() {
+        List<? super String> l = makeList();
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/generics/inference/6369605/T6369605a.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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 extends List<T>> T m1() {
+        return null;
+    }
+
+    static <T extends List<U>, U extends List<T>> T m2() {
+        return null;
+    }
+
+    static <T extends List<U>, U extends List<V>, V extends List<T>> T m3() {
+        return null;
+    }
+
+    List<?> l1 = m1();
+    List<?> l2 = m2();
+    List<?> l3 = m3();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/generics/inference/6369605/T6369605b.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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 <T extends List<X>, X> List<T> m1() {
+        return null;
+    }
+
+    static <T extends List<U>, U extends List<X>, X> List<T> m2() {
+        return null;
+    }
+
+    static <T extends List<U>, U extends List<V>, V extends List<X>, X> List<T> m3() {
+        return null;
+    }
+
+    List<?> l1 = m1();
+    List<?> l2 = m2();
+    List<?> l3 = m3();
+}
--- a/langtools/test/tools/javac/generics/inference/6638712/T6638712a.out	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/test/tools/javac/generics/inference/6638712/T6638712a.out	Wed Jul 05 17:21:32 2017 +0200
@@ -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<? extends java.util.Comparator<? super java.lang.String>>, java.util.List<java.util.Comparator<?>>)
+T6638712a.java:16:41: compiler.err.prob.found.req: (compiler.misc.incompatible.types.1: (compiler.misc.no.conforming.instance.exists: T, java.util.Comparator<T>, java.util.Comparator<java.lang.String>)), <T>java.util.Comparator<T>, java.util.Comparator<java.lang.String>
 1 error
--- a/langtools/test/tools/javac/generics/typevars/5060485/Compatibility.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/test/tools/javac/generics/typevars/5060485/Compatibility.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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<T extends Number> {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/generics/typevars/5060485/Compatibility.out	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,2 @@
+Compatibility.java:36:35: compiler.err.not.within.bounds: Test.Y
+1 error
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/generics/typevars/5060485/Compatibility02.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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<T extends Number> {}
+
+class Test {
+    <Y extends Number> void m() {
+        static class Y {}
+        class Y1<S extends NumberList<Y>> {}
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/generics/typevars/5060485/Compatibility02.out	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,2 @@
+Compatibility.java:36:35: compiler.err.not.within.bounds: Test.Y
+1 error
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/generics/wildcards/6886247/T6886247_1.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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<E> {
+
+   public void method(Outer<? extends E>.Inner inner) {
+       E entry = inner.getE();
+   }
+
+   class Inner {
+       E getE() {return null;}
+   }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/generics/wildcards/6886247/T6886247_2.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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<E> {
+
+   public void method(Outer<?>.Inner inner) {
+       E entry = inner.getE();
+   }
+
+   class Inner {
+       E getE() {return null;}
+   }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/generics/wildcards/6886247/T6886247_2.out	Wed Jul 05 17:21:32 2017 +0200
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/multicatch/T6978574.java	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,54 @@
+/*
+ * 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 6978574
+ * @summary  return statement in try block with multi-catch causes ClassFormatError
+ */
+
+public class T6978574  {
+    static class A extends Exception { }
+    static class B extends Exception { }
+
+    static void foo() throws A { throw new A(); }
+    static void bar() throws B { throw new B(); }
+
+    static void test(boolean b) {
+        try {
+            if (b) foo(); else bar();
+            return; // This should *not* cause ClassFormatError
+        } catch (final A | B e ) { caught = true; }
+        return;
+    }
+
+    static boolean caught = false;
+
+    public static void main(String[] args) {
+        test(true);
+        if (!caught) throw new AssertionError();
+        caught = false;
+        test(false);
+        if (!caught) throw new AssertionError();
+    }
+}
--- a/langtools/test/tools/javac/processing/filer/TestLastRound.out	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/test/tools/javac/processing/filer/TestLastRound.out	Wed Jul 05 17:21:32 2017 +0200
@@ -1,3 +1,4 @@
 - compiler.warn.proc.file.create.last.round: LastRound.java
 - compiler.err.warnings.and.werror
 1 error
+1 warning
--- a/langtools/test/tools/javac/warnings/6747671/T6747671.java	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/test/tools/javac/warnings/6747671/T6747671.java	Wed Jul 05 17:21:32 2017 +0200
@@ -27,8 +27,8 @@
     A<B>.Z<A<B>> 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
     }
--- a/langtools/test/tools/javac/warnings/6747671/T6747671.out	Wed Jul 05 17:20:50 2017 +0200
+++ b/langtools/test/tools/javac/warnings/6747671/T6747671.out	Wed Jul 05 17:21:32 2017 +0200
@@ -4,9 +4,7 @@
 T6747671.java:27:14: compiler.warn.raw.class.use: T6747671.B, T6747671.B<X>
 T6747671.java:27:7: compiler.warn.raw.class.use: T6747671.B, T6747671.B<X>
 T6747671.java:29:28: compiler.warn.raw.class.use: T6747671.B, T6747671.B<X>
-T6747671.java:30:37: compiler.warn.raw.class.use: T6747671.A, T6747671<E>.A<X>
-T6747671.java:31:21: compiler.warn.raw.class.use: T6747671.A, T6747671<E>.A<X>
 T6747671.java:32:9: compiler.warn.raw.class.use: T6747671.A, T6747671<E>.A<X>
 T6747671.java:32:20: compiler.warn.raw.class.use: T6747671.A, T6747671<E>.A<X>
 T6747671.java:33:16: compiler.warn.raw.class.use: T6747671.A.Z, T6747671<E>.A<X>.Z<Y>
-11 warnings
+9 warnings
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/warnings/6885255/T6885255.java	Wed Jul 05 17:21:32 2017 +0200
@@ -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<X, Y> {}
+
+    Class<Test> ct; //no warn - outer Class w/ raw param
+    Class<Test<Test, Test>> ctt; //warn - outer Class w/o raw param (2)
+
+    Class<Class<Test>> cct; //warn - outer Class w/o raw param
+    Class<Class<Test<Test, Test>>> cctt; //warn - outer Class w/o raw param (2)
+
+    Object o1 = (Test)null; //no warn - outer raw and cast
+    Object o2 = (Test<Test, Test>)null; //warn - inner raw (2)
+
+    Object o3 = (Class)null; //no warn - outer raw and cast
+    Object o4 = (Class<Test>)null; //no warn - outer Class w/ raw param
+
+    Object o5 = (Class<Test<Test, Test>>)null; //warn - outer Class w/ non raw param (2)
+    Object o6 = (Class<Class<Test<Test, Test>>>)null; //warn - outer Class w/ non raw param (2)
+
+    Object o7 = (Test<Class, Class>)null; //warn - inner raw (2)
+    Object o8 = (Test<Class<Test>, Class<Test>>)null; //warn - inner Class (2)
+
+    boolean b = null instanceof Test; //no warn - raw and instanceof
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/warnings/6885255/T6885255.out	Wed Jul 05 17:21:32 2017 +0200
@@ -0,0 +1,16 @@
+T6885255.java:13:16: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test<X,Y>
+T6885255.java:13:22: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test<X,Y>
+T6885255.java:15:17: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test<X,Y>
+T6885255.java:16:22: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test<X,Y>
+T6885255.java:16:28: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test<X,Y>
+T6885255.java:19:23: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test<X,Y>
+T6885255.java:19:29: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test<X,Y>
+T6885255.java:24:29: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test<X,Y>
+T6885255.java:24:35: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test<X,Y>
+T6885255.java:25:35: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test<X,Y>
+T6885255.java:25:41: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test<X,Y>
+T6885255.java:27:23: compiler.warn.raw.class.use: java.lang.Class, java.lang.Class<T>
+T6885255.java:27:30: compiler.warn.raw.class.use: java.lang.Class, java.lang.Class<T>
+T6885255.java:28:29: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test<X,Y>
+T6885255.java:28:42: compiler.warn.raw.class.use: T6885255.Test, T6885255.Test<X,Y>
+15 warnings
--- a/make/jprt.properties	Wed Jul 05 17:20:50 2017 +0200
+++ b/make/jprt.properties	Wed Jul 05 17:21:32 2017 +0200
@@ -24,32 +24,12 @@
 #
 
 # Properties for jprt
-jprt.tools.default.release=jdk1.7.0
-
-# Specific platform list
-jprt.build.platforms=   \
-  solaris_sparc_5.10,   \
-  solaris_sparcv9_5.10, \
-  solaris_i586_5.10,    \
-  solaris_x64_5.10,     \
-  linux_i586_2.6,       \
-  linux_x64_2.6,        \
-  windows_i586_5.0,     \
-  windows_x64_5.2
 
-# The different build flavors we want
+# Use whatever release that the submitted job requests
+jprt.tools.default.release=${jprt.submit.release}
+
+# The different build flavors we want, we override here so we just get these 2
 jprt.build.flavors=product,fastdebug
-jprt.run.flavors=c1,c2
-jprt.solaris_sparcv9.run.flavors=c2
-jprt.solaris_x64.run.flavors=c2
-jprt.windows_x64.run.flavors=c2
-jprt.linux_x64.run.flavors=c2
-jprt.run.flavor.c1.option=-client
-jprt.run.flavor.c2.option=-server
-
-# Explicitly designate what the 32bit match is for the 64bit build
-jprt.solaris_sparcv9.build.platform.match32=solaris_sparc_5.10
-jprt.solaris_x64.build.platform.match32=solaris_i586_5.10
 
 # Shortened list of vm tests
 jprt.test.targets=              \
@@ -89,6 +69,7 @@
 #   *-product-*-jdk_rmi
 #   *-product-*-jdk_swing
 
-# Directories to be excluded from source bundles
+# Directories to be excluded from the source bundles
 jprt.bundle.exclude.src.dirs=build dist webrev
 
+