changeset 14425:bbf917638fd3

Merge
author lana
date Thu, 12 May 2016 18:47:08 +0000
parents 647431dccd71 64706740c26b
children b14b89e259ac
files make/non-build-utils/src/build/tools/makeclasslist/makeClasslist.js make/src/classes/build/tools/addjsum/AddJsum.java src/java.base/share/classes/java/util/regex/UnicodeProp.java src/java.httpclient/share/classes/java/net/http/CharsetToolkit.java src/java.rmi/share/classes/sun/rmi/transport/proxy/CGIHandler.java src/java.rmi/share/classes/sun/rmi/transport/proxy/HttpAwareServerSocket.java src/java.rmi/share/classes/sun/rmi/transport/proxy/HttpInputStream.java src/java.rmi/share/classes/sun/rmi/transport/proxy/HttpOutputStream.java src/java.rmi/share/classes/sun/rmi/transport/proxy/HttpReceiveSocket.java src/java.rmi/share/classes/sun/rmi/transport/proxy/HttpSendInputStream.java src/java.rmi/share/classes/sun/rmi/transport/proxy/HttpSendOutputStream.java src/java.rmi/share/classes/sun/rmi/transport/proxy/HttpSendSocket.java src/java.rmi/share/classes/sun/rmi/transport/proxy/RMIDirectSocketFactory.java src/java.rmi/share/classes/sun/rmi/transport/proxy/RMIHttpToCGISocketFactory.java src/java.rmi/share/classes/sun/rmi/transport/proxy/RMIHttpToPortSocketFactory.java src/java.rmi/share/classes/sun/rmi/transport/proxy/RMIMasterSocketFactory.java src/java.rmi/share/classes/sun/rmi/transport/proxy/RMISocketInfo.java src/java.rmi/share/classes/sun/rmi/transport/proxy/WrappedSocket.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SortResourcesPlugin.java test/java/rmi/transport/httpSocket/HttpSocketTest.java test/java/rmi/transport/httpSocket/HttpSocketTest_Stub.java test/java/rmi/transport/httpSocket/security.policy test/java/util/jar/JarFile/MultiReleaseJarAPI.java test/java/util/jar/JarFile/MultiReleaseJarHttpProperties.java test/java/util/jar/JarFile/MultiReleaseJarIterators.java test/java/util/jar/JarFile/MultiReleaseJarProperties.java test/java/util/jar/JarFile/MultiReleaseJarSecurity.java test/sun/rmi/transport/proxy/DisableHttpDefaultValue.java test/sun/rmi/transport/proxy/EagerHttpFallback.java test/sun/rmi/transport/tcp/blockAccept/BlockAcceptTest.java test/sun/rmi/transport/tcp/blockAccept/TestIface.java test/sun/rmi/transport/tcp/blockAccept/TestImpl.java test/sun/rmi/transport/tcp/blockAccept/TestImpl_Stub.java test/sun/rmi/transport/tcp/blockAccept/security.policy test/tools/jlink/plugins/SorterPluginTest.java
diffstat 180 files changed, 9424 insertions(+), 6758 deletions(-) [+]
line wrap: on
line diff
--- a/make/Tools.gmk	Thu May 12 15:15:04 2016 +0000
+++ b/make/Tools.gmk	Thu May 12 18:47:08 2016 +0000
@@ -37,10 +37,6 @@
 
 ################################################################################
 
-# Add a checksum ("jsum") to the end of a text file. Prevents trivial tampering with class lists.
-TOOL_ADDJSUM = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \
-    build.tools.addjsum.AddJsum
-
 ifeq ($(BOOT_JDK_MODULAR), true)
   COMPILEFONTCONFIG_ADD_EXPORTS := -XaddExports:java.desktop/sun.awt=ALL-UNNAMED
 endif
--- a/make/mapfiles/libjava/mapfile-vers	Thu May 12 15:15:04 2016 +0000
+++ b/make/mapfiles/libjava/mapfile-vers	Thu May 12 18:47:08 2016 +0000
@@ -262,7 +262,7 @@
 		Java_jdk_internal_reflect_Reflection_getCallerClass__;
 		Java_jdk_internal_reflect_Reflection_getCallerClass__I;
 		Java_jdk_internal_reflect_Reflection_getClassAccessFlags;
-		Java_jdk_internal_misc_VM_latestUserDefinedLoader;
+		Java_jdk_internal_misc_VM_latestUserDefinedLoader0;
                 Java_jdk_internal_misc_VM_getuid;
                 Java_jdk_internal_misc_VM_geteuid;
                 Java_jdk_internal_misc_VM_getgid;
--- a/make/non-build-utils/src/build/tools/makeclasslist/makeClasslist.js	Thu May 12 15:15:04 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 2015, 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.
- */
-
-/**
- * This tool is used to help create the class list for class data sharing.
- *
- * The classlist is produced internally by first running a select number of
- * startup benchmarks with the -XX:DumpLoadedClassList=<file> option, then
- * running this tool in the following fashion to produce a complete classlist:
- *
- * jjs -scripting makeClasslist.js -- list1 list2 list3 > classlist.platform
- *
- * The lists should be listed in roughly smallest to largest order based on
- * application size.
- *
- * After generating the classlist it's necessary to add a checksum (using
- * AddJsum.java) before checking it into the workspace as the corresponding
- * platform-specific classlist, such as make/data/classlist/classlist.linux 
- */
-"use strict";
-var classlist = [];
-var seenClasses = {};
-
-for (var a in $ARG) {
-  var arg = $ARG[a];
-
-  var classes = readFully(arg).replace(/[\r\n]+/g, "\n").split("\n");
-
-  for (var c in classes) {
-    var clazz = classes[c];
-    if (clazz !== "" && seenClasses[clazz] === undefined) {
-      seenClasses[clazz] = clazz;
-      classlist.push(clazz);
-    }
-  }
-}
-
-for (c in classlist) {
-  print(classlist[c]);
-}
--- a/make/src/classes/build/tools/addjsum/AddJsum.java	Thu May 12 15:15:04 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-/*
- * Copyright (c) 2003, 2013, 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 build.tools.addjsum;
-
-import java.io.*;
-import java.util.regex.*;
-
-/** Adds a checksum ("jsum") to the end of a text file. The algorithm
-    used is known to the JVM and prevents trivial tampering with the
-    class list used for class data sharing.
-*/
-
-public class AddJsum {
-  private static final long JSUM_SEED = 0xCAFEBABEBABECAFEL;
-
-  public static void main(String[] args) throws Exception {
-    if (args.length != 2) {
-      System.err.println("Usage: java AddJsum [input file name] [output file name]");
-      System.exit(1);
-    }
-
-    try {
-      File inFile  = new File(args[0]);
-      File outFile = new File(args[1]);
-      BufferedReader reader = new BufferedReader(new FileReader(inFile));
-      BufferedWriter writer = new BufferedWriter(new FileWriter(outFile));
-      Pattern p = Pattern.compile("# [0-9A-Fa-f]*");
-      long computedJsum = JSUM_SEED;
-
-      String line = null;
-      while ((line = reader.readLine()) != null) {
-        if (line.length() > 0 && line.charAt(0) == '#') {
-          Matcher m = p.matcher(line);
-          if (!m.matches()) {
-            writer.write(line);
-            writer.newLine();
-          }
-        } else {
-          computedJsum = jsum(computedJsum, line);
-          writer.write(line);
-          writer.newLine();
-        }
-      }
-      String hex = Long.toHexString(computedJsum);
-      int diff = 16 - hex.length();
-      for (int i = 0; i < diff; i++) {
-        hex = "0" + hex;
-      }
-      writer.write("# " + hex);
-      writer.newLine();
-      reader.close();
-      writer.close();
-    } catch (IOException e) {
-      System.err.println("Error reading or writing file");
-      throw(e);
-    }
-  }
-
-  private static long jsum(long start, String str) {
-    long h = start;
-    int len = str.length();
-    for (int i = 0; i < len; i++) {
-      char c = str.charAt(i);
-      if (c <= ' ') {
-        /* Skip spaces and control characters */
-        continue;
-      }
-      h = 31 * h + c;
-    }
-    return h;
-  }
-}
--- a/src/java.base/share/classes/java/io/ObjectInputStream.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/io/ObjectInputStream.java	Thu May 12 18:47:08 2016 +0000
@@ -603,12 +603,12 @@
      *     Class.forName(desc.getName(), false, loader)
      * </pre>
      * where <code>loader</code> is determined as follows: if there is a
-     * method on the current thread's stack whose declaring class was
-     * defined by a user-defined class loader (and was not a generated to
-     * implement reflective invocations), then <code>loader</code> is class
-     * loader corresponding to the closest such method to the currently
-     * executing frame; otherwise, <code>loader</code> is
-     * <code>null</code>. If this call results in a
+     * method on the current thread's stack whose declaring class is not a
+     * <a href="../lang/ClassLoader.html#builtinLoaders">
+     * <em>platform class</em></a>, then <code>loader</code> is
+     * the class loader of such class; otherwise, <code>loader</code>
+     * is the {@linkplain ClassLoader#getPlatformClassLoader()
+     * platform class loader}.  If this call results in a
      * <code>ClassNotFoundException</code> and the name of the passed
      * <code>ObjectStreamClass</code> instance is the Java language keyword
      * for a primitive type or void, then the <code>Class</code> object
@@ -666,12 +666,15 @@
      * <pre>
      *     Class.forName(i, false, loader)
      * </pre>
-     * where <code>loader</code> is that of the first non-<code>null</code>
-     * class loader up the execution stack, or <code>null</code> if no
-     * non-<code>null</code> class loaders are on the stack (the same class
-     * loader choice used by the <code>resolveClass</code> method).  Unless any
-     * of the resolved interfaces are non-public, this same value of
-     * <code>loader</code> is also the class loader passed to
+     * where <code>loader</code> is determined as follows: if there is a
+     * method on the current thread's stack whose declaring class is not a
+     * <a href="../lang/ClassLoader.html#builtinLoaders">
+     * <em>platform class</em></a>, then <code>loader</code> is
+     * the class loader of such class; otherwise, <code>loader</code>
+     * is the {@linkplain ClassLoader#getPlatformClassLoader()
+     * platform class loader}.
+     * Unless any of the resolved interfaces are non-public, this same value
+     * of <code>loader</code> is also the class loader passed to
      * <code>Proxy.getProxyClass</code>; if non-public interfaces are present,
      * their class loader is passed instead (if more than one non-public
      * interface class loader is encountered, an
@@ -2154,10 +2157,11 @@
                                               int ndoubles);
 
     /**
-     * Returns the first non-null class loader (not counting class loaders of
-     * generated reflection implementation classes) up the execution stack, or
-     * null if only code from the null class loader is on the stack.  This
-     * method is also called via reflection by the following RMI-IIOP class:
+     * Returns the first non-null and non-platform class loader
+     * (not counting class loaders of generated reflection implementation classes)
+     * up the execution stack, or null if only code from the bootstrap and
+     * platform class loader is on the stack.
+     * This method is also called via reflection by the following RMI-IIOP class:
      *
      *     com.sun.corba.se.internal.util.JDKClassLoader
      *
--- a/src/java.base/share/classes/java/lang/Integer.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/lang/Integer.java	Thu May 12 18:47:08 2016 +0000
@@ -1221,13 +1221,13 @@
     }
 
     /**
-     * Returns a hash code for a {@code int} value; compatible with
+     * Returns a hash code for an {@code int} value; compatible with
      * {@code Integer.hashCode()}.
      *
      * @param value the value to hash
      * @since 1.8
      *
-     * @return a hash code value for a {@code int} value.
+     * @return a hash code value for an {@code int} value.
      */
     public static int hashCode(int value) {
         return value;
@@ -1596,7 +1596,7 @@
     @Native public static final int SIZE = 32;
 
     /**
-     * The number of bytes used to represent a {@code int} value in two's
+     * The number of bytes used to represent an {@code int} value in two's
      * complement binary form.
      *
      * @since 1.8
@@ -1790,9 +1790,8 @@
         i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;
         i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333;
         i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f;
-        i = (i << 24) | ((i & 0xff00) << 8) |
-            ((i >>> 8) & 0xff00) | (i >>> 24);
-        return i;
+
+        return reverseBytes(i);
     }
 
     /**
@@ -1820,10 +1819,10 @@
      */
     @HotSpotIntrinsicCandidate
     public static int reverseBytes(int i) {
-        return ((i >>> 24)           ) |
-               ((i >>   8) &   0xFF00) |
-               ((i <<   8) & 0xFF0000) |
-               ((i << 24));
+        return (i << 24)            |
+               ((i & 0xff00) << 8)  |
+               ((i >>> 8) & 0xff00) |
+               (i >>> 24);
     }
 
     /**
--- a/src/java.base/share/classes/java/lang/Long.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/lang/Long.java	Thu May 12 18:47:08 2016 +0000
@@ -1952,10 +1952,8 @@
         i = (i & 0x5555555555555555L) << 1 | (i >>> 1) & 0x5555555555555555L;
         i = (i & 0x3333333333333333L) << 2 | (i >>> 2) & 0x3333333333333333L;
         i = (i & 0x0f0f0f0f0f0f0f0fL) << 4 | (i >>> 4) & 0x0f0f0f0f0f0f0f0fL;
-        i = (i & 0x00ff00ff00ff00ffL) << 8 | (i >>> 8) & 0x00ff00ff00ff00ffL;
-        i = (i << 48) | ((i & 0xffff0000L) << 16) |
-            ((i >>> 16) & 0xffff0000L) | (i >>> 48);
-        return i;
+
+        return reverseBytes(i);
     }
 
     /**
--- a/src/java.base/share/classes/java/lang/SecurityManager.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/lang/SecurityManager.java	Thu May 12 18:47:08 2016 +0000
@@ -1627,8 +1627,7 @@
      * @deprecated This method relies on the caller being at a stack depth
      *             of 4 which is error-prone and cannot be enforced by the runtime.
      *             Users of this method should instead invoke {@link #checkPermission}
-     *             directly.  This method will be changed in a future release
-     *             to check the permission {@code java.security.AllPermission}.
+     *             directly.
      *             This method is subject to removal in a future version of Java SE.
      *
      * @see java.lang.reflect.Member
--- a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java	Thu May 12 18:47:08 2016 +0000
@@ -318,7 +318,7 @@
         /**
          * Tests this module export for equality with the given object.
          *
-         * <p> If the given object is not a {@code Exports} then this method
+         * <p> If the given object is not an {@code Exports} then this method
          * returns {@code false}. Two module exports objects are equal if the
          * package names are equal and the set of target module names is equal.
          * </p>
--- a/src/java.base/share/classes/java/lang/reflect/Field.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/lang/reflect/Field.java	Thu May 12 18:47:08 2016 +0000
@@ -903,7 +903,7 @@
      * Sets the value of a field as an {@code int} on the specified object.
      * This method is equivalent to
      * {@code set(obj, iObj)},
-     * where {@code iObj} is a {@code Integer} object and
+     * where {@code iObj} is an {@code Integer} object and
      * {@code iObj.intValue() == i}.
      *
      * @param obj the object whose field should be modified
--- a/src/java.base/share/classes/java/lang/reflect/InaccessibleObjectException.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/lang/reflect/InaccessibleObjectException.java	Thu May 12 18:47:08 2016 +0000
@@ -36,13 +36,13 @@
     private static final long serialVersionUID = 4158786093378140901L;
 
     /**
-     * Constructs a {@code InaccessibleObjectException} with no detail message.
+     * Constructs an {@code InaccessibleObjectException} with no detail message.
      */
     public InaccessibleObjectException() {
     }
 
     /**
-     * Constructs a {@code InaccessibleObjectException} with the given detail
+     * Constructs an {@code InaccessibleObjectException} with the given detail
      * message.
      *
      * @param msg
--- a/src/java.base/share/classes/java/math/BigInteger.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/math/BigInteger.java	Thu May 12 18:47:08 2016 +0000
@@ -4676,7 +4676,7 @@
      *
      * @return this {@code BigInteger} converted to an {@code int}.
      * @throws ArithmeticException if the value of {@code this} will
-     * not exactly fit in a {@code int}.
+     * not exactly fit in an {@code int}.
      * @see BigInteger#intValue
      * @since  1.8
      */
--- a/src/java.base/share/classes/java/net/InetSocketAddress.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/net/InetSocketAddress.java	Thu May 12 18:47:08 2016 +0000
@@ -246,7 +246,7 @@
      *                  the range of valid port values, or if the hostname
      *                  parameter is {@code null}.
      * @see     #isUnresolved()
-     * @return  a {@code InetSocketAddress} representing the unresolved
+     * @return  an {@code InetSocketAddress} representing the unresolved
      *          socket address
      * @since 1.5
      */
--- a/src/java.base/share/classes/java/net/URLDecoder.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/net/URLDecoder.java	Thu May 12 18:47:08 2016 +0000
@@ -106,8 +106,8 @@
     }
 
     /**
-     * Decodes a {@code application/x-www-form-urlencoded} string using a specific
-     * encoding scheme.
+     * Decodes an {@code application/x-www-form-urlencoded} string using
+     * a specific encoding scheme.
      * The supplied encoding is used to determine
      * what characters are represented by any consecutive sequences of the
      * form "<i>{@code %xy}</i>".
--- a/src/java.base/share/classes/java/net/URLEncoder.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/net/URLEncoder.java	Thu May 12 18:47:08 2016 +0000
@@ -225,7 +225,7 @@
                     /*
                      * If this character represents the start of a Unicode
                      * surrogate pair, then pass in two characters. It's not
-                     * clear what should be done if a bytes reserved in the
+                     * clear what should be done if a byte reserved in the
                      * surrogate pairs range occurs outside of a legal
                      * surrogate pair. For now, just treat it as if it were
                      * any other character.
--- a/src/java.base/share/classes/java/security/DrbgParameters.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/security/DrbgParameters.java	Thu May 12 18:47:08 2016 +0000
@@ -196,10 +196,9 @@
  * of the JDK reference implementation.
  * <p>
  * This implementation supports the Hash_DRBG and HMAC_DRBG mechanisms with
- * DRBG algorithm SHA-1, SHA-224, SHA-512/224, SHA-256, SHA-512/256,
- * SHA-384 and SHA-512, and CTR_DRBG (both using derivation function and
- * not using derivation function) with DRBG algorithm 3KeyTDEA
- * (also known as DESede in JCE), AES-128, AES-192 and AES-256.
+ * DRBG algorithm SHA-224, SHA-512/224, SHA-256, SHA-512/256, SHA-384 and
+ * SHA-512, and CTR_DRBG (both using derivation function and not using
+ * derivation function) with DRBG algorithm AES-128, AES-192 and AES-256.
  * <p>
  * The mechanism name and DRBG algorithm name are determined by the
  * {@linkplain Security#getProperty(String) security property}
--- a/src/java.base/share/classes/java/security/InvalidAlgorithmParameterException.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/security/InvalidAlgorithmParameterException.java	Thu May 12 18:47:08 2016 +0000
@@ -65,7 +65,7 @@
     }
 
     /**
-     * Creates a {@code InvalidAlgorithmParameterException} with the
+     * Creates an {@code InvalidAlgorithmParameterException} with the
      * specified detail message and cause.
      *
      * @param message the detail message (which is saved for later retrieval
@@ -80,7 +80,7 @@
     }
 
     /**
-     * Creates a {@code InvalidAlgorithmParameterException} with the
+     * Creates an {@code InvalidAlgorithmParameterException} with the
      * specified cause and a detail message of
      * {@code (cause==null ? null : cause.toString())}
      * (which typically contains the class and detail message of
--- a/src/java.base/share/classes/java/security/InvalidKeyException.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/security/InvalidKeyException.java	Thu May 12 18:47:08 2016 +0000
@@ -58,7 +58,7 @@
     }
 
     /**
-     * Creates a {@code InvalidKeyException} with the specified
+     * Creates an {@code InvalidKeyException} with the specified
      * detail message and cause.
      *
      * @param message the detail message (which is saved for later retrieval
@@ -73,7 +73,7 @@
     }
 
     /**
-     * Creates a {@code InvalidKeyException} with the specified cause
+     * Creates an {@code InvalidKeyException} with the specified cause
      * and a detail message of {@code (cause==null ? null : cause.toString())}
      * (which typically contains the class and detail message of
      * {@code cause}).
--- a/src/java.base/share/classes/java/security/ProtectionDomain.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/security/ProtectionDomain.java	Thu May 12 18:47:08 2016 +0000
@@ -139,12 +139,10 @@
      */
     final Key key = new Key();
 
-    private static final Debug debug = Debug.getInstance("domain");
-
     /**
      * Creates a new ProtectionDomain with the given CodeSource and
      * Permissions. If the permissions object is not null, then
-     *  {@code setReadOnly())} will be called on the passed in
+     *  {@code setReadOnly()} will be called on the passed in
      * Permissions object. The only permissions granted to this domain
      * are the ones specified; the current Policy will not be consulted.
      *
@@ -338,6 +336,13 @@
             " "+pc+"\n";
     }
 
+    /*
+     * holder class for the static field "debug" to delay its initialization
+     */
+    private static class DebugHolder {
+        private static final Debug debug = Debug.getInstance("domain");
+    }
+
     /**
      * Return true (merge policy permissions) in the following cases:
      *
@@ -359,7 +364,7 @@
         if (sm == null) {
             return true;
         } else {
-            if (debug != null) {
+            if (DebugHolder.debug != null) {
                 if (sm.getClass().getClassLoader() == null &&
                     Policy.getPolicyNoCheck().getClass().getClassLoader()
                                                                 == null) {
--- a/src/java.base/share/classes/java/security/Provider.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/security/Provider.java	Thu May 12 18:47:08 2016 +0000
@@ -1809,7 +1809,7 @@
         }
 
         /**
-         * Return whether this service has its Supported* properties for
+         * Return whether this service has its supported properties for
          * keys defined. Parses the attributes if not yet initialized.
          */
         private boolean hasKeyAttributes() {
--- a/src/java.base/share/classes/java/security/SecureClassLoader.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/security/SecureClassLoader.java	Thu May 12 18:47:08 2016 +0000
@@ -62,8 +62,6 @@
     private final Map<CodeSourceKey, ProtectionDomain> pdcache
             = new ConcurrentHashMap<>(11);
 
-    private static final Debug debug = Debug.getInstance("scl");
-
     static {
         ClassLoader.registerAsParallelCapable();
     }
@@ -203,6 +201,13 @@
     }
 
     /*
+     * holder class for the static field "debug" to delay its initialization
+     */
+    private static class DebugHolder {
+        private static final Debug debug = Debug.getInstance("scl");
+    }
+
+    /*
      * Returned cached ProtectionDomain for the specified CodeSource.
      */
     private ProtectionDomain getProtectionDomain(CodeSource cs) {
@@ -222,9 +227,9 @@
                         = SecureClassLoader.this.getPermissions(cs);
                 ProtectionDomain pd = new ProtectionDomain(
                         cs, perms, SecureClassLoader.this, null);
-                if (debug != null) {
-                    debug.println(" getPermissions " + pd);
-                    debug.println("");
+                if (DebugHolder.debug != null) {
+                    DebugHolder.debug.println(" getPermissions " + pd);
+                    DebugHolder.debug.println("");
                 }
                 return pd;
             }
--- a/src/java.base/share/classes/java/security/Security.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/security/Security.java	Thu May 12 18:47:08 2016 +0000
@@ -549,7 +549,7 @@
 
     /**
      * Returns an array containing all installed providers that satisfy the
-     * specified* selection criteria, or null if no such providers have been
+     * specified selection criteria, or null if no such providers have been
      * installed. The returned providers are ordered
      * according to their
      * {@linkplain #insertProviderAt(java.security.Provider, int)
--- a/src/java.base/share/classes/java/security/spec/InvalidKeySpecException.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/security/spec/InvalidKeySpecException.java	Thu May 12 18:47:08 2016 +0000
@@ -63,7 +63,7 @@
     }
 
     /**
-     * Creates a {@code InvalidKeySpecException} with the specified
+     * Creates an {@code InvalidKeySpecException} with the specified
      * detail message and cause.
      *
      * @param message the detail message (which is saved for later retrieval
@@ -78,7 +78,7 @@
     }
 
     /**
-     * Creates a {@code InvalidKeySpecException} with the specified cause
+     * Creates an {@code InvalidKeySpecException} with the specified cause
      * and a detail message of {@code (cause==null ? null : cause.toString())}
      * (which typically contains the class and detail message of
      * {@code cause}).
--- a/src/java.base/share/classes/java/time/Instant.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/time/Instant.java	Thu May 12 18:47:08 2016 +0000
@@ -799,33 +799,33 @@
      * The supported fields behave as follows:
      * <ul>
      * <li>{@code NANOS} -
-     *  Returns a {@code Instant} with the specified number of nanoseconds added.
+     *  Returns an {@code Instant} with the specified number of nanoseconds added.
      *  This is equivalent to {@link #plusNanos(long)}.
      * <li>{@code MICROS} -
-     *  Returns a {@code Instant} with the specified number of microseconds added.
+     *  Returns an {@code Instant} with the specified number of microseconds added.
      *  This is equivalent to {@link #plusNanos(long)} with the amount
      *  multiplied by 1,000.
      * <li>{@code MILLIS} -
-     *  Returns a {@code Instant} with the specified number of milliseconds added.
+     *  Returns an {@code Instant} with the specified number of milliseconds added.
      *  This is equivalent to {@link #plusNanos(long)} with the amount
      *  multiplied by 1,000,000.
      * <li>{@code SECONDS} -
-     *  Returns a {@code Instant} with the specified number of seconds added.
+     *  Returns an {@code Instant} with the specified number of seconds added.
      *  This is equivalent to {@link #plusSeconds(long)}.
      * <li>{@code MINUTES} -
-     *  Returns a {@code Instant} with the specified number of minutes added.
+     *  Returns an {@code Instant} with the specified number of minutes added.
      *  This is equivalent to {@link #plusSeconds(long)} with the amount
      *  multiplied by 60.
      * <li>{@code HOURS} -
-     *  Returns a {@code Instant} with the specified number of hours added.
+     *  Returns an {@code Instant} with the specified number of hours added.
      *  This is equivalent to {@link #plusSeconds(long)} with the amount
      *  multiplied by 3,600.
      * <li>{@code HALF_DAYS} -
-     *  Returns a {@code Instant} with the specified number of half-days added.
+     *  Returns an {@code Instant} with the specified number of half-days added.
      *  This is equivalent to {@link #plusSeconds(long)} with the amount
      *  multiplied by 43,200 (12 hours).
      * <li>{@code DAYS} -
-     *  Returns a {@code Instant} with the specified number of days added.
+     *  Returns an {@code Instant} with the specified number of days added.
      *  This is equivalent to {@link #plusSeconds(long)} with the amount
      *  multiplied by 86,400 (24 hours).
      * </ul>
@@ -958,7 +958,7 @@
     /**
      * Returns a copy of this instant with the specified amount subtracted.
      * <p>
-     * This returns a {@code Instant}, based on this one, with the amount
+     * This returns an {@code Instant}, based on this one, with the amount
      * in terms of the unit subtracted. If it is not possible to subtract the amount,
      * because the unit is not supported or for some other reason, an exception is thrown.
      * <p>
--- a/src/java.base/share/classes/java/time/LocalDateTime.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/time/LocalDateTime.java	Thu May 12 18:47:08 2016 +0000
@@ -665,7 +665,7 @@
      * The {@link #isSupported(TemporalField) supported fields} will return valid
      * values based on this date-time, except {@code NANO_OF_DAY}, {@code MICRO_OF_DAY},
      * {@code EPOCH_DAY} and {@code PROLEPTIC_MONTH} which are too large to fit in
-     * an {@code int} and throw a {@code UnsupportedTemporalTypeException}.
+     * an {@code int} and throw an {@code UnsupportedTemporalTypeException}.
      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
      * <p>
      * If the field is not a {@code ChronoField}, then the result of this method
--- a/src/java.base/share/classes/java/time/LocalTime.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/time/LocalTime.java	Thu May 12 18:47:08 2016 +0000
@@ -619,7 +619,7 @@
      * If the field is a {@link ChronoField} then the query is implemented here.
      * The {@link #isSupported(TemporalField) supported fields} will return valid
      * values based on this time, except {@code NANO_OF_DAY} and {@code MICRO_OF_DAY}
-     * which are too large to fit in an {@code int} and throw a {@code UnsupportedTemporalTypeException}.
+     * which are too large to fit in an {@code int} and throw an {@code UnsupportedTemporalTypeException}.
      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
      * <p>
      * If the field is not a {@code ChronoField}, then the result of this method
--- a/src/java.base/share/classes/java/time/OffsetDateTime.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/time/OffsetDateTime.java	Thu May 12 18:47:08 2016 +0000
@@ -576,7 +576,7 @@
      * The {@link #isSupported(TemporalField) supported fields} will return valid
      * values based on this date-time, except {@code NANO_OF_DAY}, {@code MICRO_OF_DAY},
      * {@code EPOCH_DAY}, {@code PROLEPTIC_MONTH} and {@code INSTANT_SECONDS} which are too
-     * large to fit in an {@code int} and throw a {@code UnsupportedTemporalTypeException}.
+     * large to fit in an {@code int} and throw an {@code UnsupportedTemporalTypeException}.
      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
      * <p>
      * If the field is not a {@code ChronoField}, then the result of this method
--- a/src/java.base/share/classes/java/time/OffsetTime.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/time/OffsetTime.java	Thu May 12 18:47:08 2016 +0000
@@ -481,7 +481,7 @@
      * If the field is a {@link ChronoField} then the query is implemented here.
      * The {@link #isSupported(TemporalField) supported fields} will return valid
      * values based on this time, except {@code NANO_OF_DAY} and {@code MICRO_OF_DAY}
-     * which are too large to fit in an {@code int} and throw a {@code UnsupportedTemporalTypeException}.
+     * which are too large to fit in an {@code int} and throw an {@code UnsupportedTemporalTypeException}.
      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
      * <p>
      * If the field is not a {@code ChronoField}, then the result of this method
--- a/src/java.base/share/classes/java/time/ZonedDateTime.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/time/ZonedDateTime.java	Thu May 12 18:47:08 2016 +0000
@@ -793,7 +793,7 @@
      * The {@link #isSupported(TemporalField) supported fields} will return valid
      * values based on this date-time, except {@code NANO_OF_DAY}, {@code MICRO_OF_DAY},
      * {@code EPOCH_DAY}, {@code PROLEPTIC_MONTH} and {@code INSTANT_SECONDS} which are too
-     * large to fit in an {@code int} and throw a {@code UnsupportedTemporalTypeException}.
+     * large to fit in an {@code int} and throw an {@code UnsupportedTemporalTypeException}.
      * All other {@code ChronoField} instances will throw an {@code UnsupportedTemporalTypeException}.
      * <p>
      * If the field is not a {@code ChronoField}, then the result of this method
--- a/src/java.base/share/classes/java/time/temporal/IsoFields.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/time/temporal/IsoFields.java	Thu May 12 18:47:08 2016 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016, 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,12 @@
                 long moy = temporal.getLong(MONTH_OF_YEAR);
                 return ((moy + 2) / 3);
             }
+            public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
+                if (isSupportedBy(temporal) == false) {
+                    throw new UnsupportedTemporalTypeException("Unsupported field: QuarterOfYear");
+                }
+                return super.rangeRefinedBy(temporal);
+            }
             @SuppressWarnings("unchecked")
             @Override
             public <R extends Temporal> R adjustInto(R temporal, long newValue) {
@@ -529,6 +535,12 @@
                 }
                 return getWeekBasedYear(LocalDate.from(temporal));
             }
+            public ValueRange rangeRefinedBy(TemporalAccessor temporal) {
+                if (isSupportedBy(temporal) == false) {
+                    throw new UnsupportedTemporalTypeException("Unsupported field: WeekBasedYear");
+                }
+                return super.rangeRefinedBy(temporal);
+            }
             @SuppressWarnings("unchecked")
             @Override
             public <R extends Temporal> R adjustInto(R temporal, long newValue) {
--- a/src/java.base/share/classes/java/util/Comparator.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/util/Comparator.java	Thu May 12 18:47:08 2016 +0000
@@ -267,7 +267,7 @@
 
     /**
      * Returns a lexicographic-order comparator with a function that
-     * extracts a {@code int} sort key.
+     * extracts an {@code int} sort key.
      *
      * @implSpec This default implementation behaves as if {@code
      *           thenComparing(comparingInt(keyExtractor))}.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/util/regex/CharPredicates.java	Thu May 12 18:47:08 2016 +0000
@@ -0,0 +1,375 @@
+/*
+ * Copyright (c) 2011, 2016, 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 java.util.regex;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.regex.Pattern.CharPredicate;
+import java.util.regex.Pattern.BmpCharPredicate;
+
+class CharPredicates {
+
+    static final CharPredicate ALPHABETIC  = Character::isAlphabetic;
+
+    // \p{gc=Decimal_Number}
+    static final CharPredicate DIGIT       = Character::isDigit;
+
+    static final CharPredicate LETTER      = Character::isLetter;
+
+    static final CharPredicate IDEOGRAPHIC = Character::isIdeographic;
+
+    static final CharPredicate LOWERCASE   = Character::isLowerCase;
+
+    static final CharPredicate UPPERCASE   = Character::isUpperCase;
+
+    static final CharPredicate TITLECASE   = Character::isTitleCase;
+
+    // \p{Whitespace}
+    static final CharPredicate WHITE_SPACE = ch ->
+        ((((1 << Character.SPACE_SEPARATOR) |
+           (1 << Character.LINE_SEPARATOR) |
+           (1 << Character.PARAGRAPH_SEPARATOR)) >> Character.getType(ch)) & 1)
+        != 0 || (ch >= 0x9 && ch <= 0xd) || (ch == 0x85);
+
+    // \p{gc=Control}
+    static final CharPredicate CONTROL     = ch ->
+        Character.getType(ch) == Character.CONTROL;
+
+    // \p{gc=Punctuation}
+    static final CharPredicate PUNCTUATION = ch ->
+        ((((1 << Character.CONNECTOR_PUNCTUATION) |
+           (1 << Character.DASH_PUNCTUATION) |
+           (1 << Character.START_PUNCTUATION) |
+           (1 << Character.END_PUNCTUATION) |
+           (1 << Character.OTHER_PUNCTUATION) |
+           (1 << Character.INITIAL_QUOTE_PUNCTUATION) |
+           (1 << Character.FINAL_QUOTE_PUNCTUATION)) >> Character.getType(ch)) & 1)
+        != 0;
+
+    // \p{gc=Decimal_Number}
+    // \p{Hex_Digit}    -> PropList.txt: Hex_Digit
+    static final CharPredicate HEX_DIGIT = DIGIT.union(
+        ch -> (ch >= 0x0030 && ch <= 0x0039) ||
+              (ch >= 0x0041 && ch <= 0x0046) ||
+              (ch >= 0x0061 && ch <= 0x0066) ||
+              (ch >= 0xFF10 && ch <= 0xFF19) ||
+              (ch >= 0xFF21 && ch <= 0xFF26) ||
+              (ch >= 0xFF41 && ch <= 0xFF46));
+
+    static final CharPredicate ASSIGNED = ch ->
+        Character.getType(ch) != Character.UNASSIGNED;
+
+    // PropList.txt:Noncharacter_Code_Point
+    static final CharPredicate NONCHARACTER_CODE_POINT = ch ->
+        (ch & 0xfffe) == 0xfffe || (ch >= 0xfdd0 && ch <= 0xfdef);
+
+    // \p{alpha}
+    // \p{digit}
+    static final CharPredicate ALNUM = ALPHABETIC.union(DIGIT);
+
+    // \p{Whitespace} --
+    // [\N{LF} \N{VT} \N{FF} \N{CR} \N{NEL}  -> 0xa, 0xb, 0xc, 0xd, 0x85
+    //  \p{gc=Line_Separator}
+    //  \p{gc=Paragraph_Separator}]
+    static final CharPredicate BLANK = ch ->
+        Character.getType(ch) == Character.SPACE_SEPARATOR ||
+        ch == 0x9; // \N{HT}
+
+    // [^
+    //  \p{space}
+    //  \p{gc=Control}
+    //  \p{gc=Surrogate}
+    //  \p{gc=Unassigned}]
+    static final CharPredicate GRAPH = ch ->
+        ((((1 << Character.SPACE_SEPARATOR) |
+           (1 << Character.LINE_SEPARATOR) |
+           (1 << Character.PARAGRAPH_SEPARATOR) |
+           (1 << Character.CONTROL) |
+           (1 << Character.SURROGATE) |
+           (1 << Character.UNASSIGNED)) >> Character.getType(ch)) & 1)
+        == 0;
+
+    // \p{graph}
+    // \p{blank}
+    // -- \p{cntrl}
+    static final CharPredicate PRINT = GRAPH.union(BLANK).and(CONTROL.negate());
+
+    //  200C..200D    PropList.txt:Join_Control
+    static final CharPredicate JOIN_CONTROL = ch -> ch == 0x200C || ch == 0x200D;
+
+    //  \p{alpha}
+    //  \p{gc=Mark}
+    //  \p{digit}
+    //  \p{gc=Connector_Punctuation}
+    //  \p{Join_Control}    200C..200D
+    static final CharPredicate WORD =
+        ALPHABETIC.union(ch -> ((((1 << Character.NON_SPACING_MARK) |
+                                  (1 << Character.ENCLOSING_MARK) |
+                                  (1 << Character.COMBINING_SPACING_MARK) |
+                                  (1 << Character.DECIMAL_DIGIT_NUMBER) |
+                                  (1 << Character.CONNECTOR_PUNCTUATION))
+                                 >> Character.getType(ch)) & 1) != 0,
+                         JOIN_CONTROL);
+
+    /////////////////////////////////////////////////////////////////////////////
+
+    private static final HashMap<String, CharPredicate> posix = new HashMap<>(12);
+    private static final HashMap<String, CharPredicate> uprops = new HashMap<>(18);
+
+    private static void defPosix(String name, CharPredicate p) {
+        posix.put(name, p);
+    }
+    private static void defUProp(String name, CharPredicate p) {
+        uprops.put(name, p);
+    }
+
+    static {
+        defPosix("ALPHA", ALPHABETIC);
+        defPosix("LOWER", LOWERCASE);
+        defPosix("UPPER", UPPERCASE);
+        defPosix("SPACE", WHITE_SPACE);
+        defPosix("PUNCT", PUNCTUATION);
+        defPosix("XDIGIT",HEX_DIGIT);
+        defPosix("ALNUM", ALNUM);
+        defPosix("CNTRL", CONTROL);
+        defPosix("DIGIT", DIGIT);
+        defPosix("BLANK", BLANK);
+        defPosix("GRAPH", GRAPH);
+        defPosix("PRINT", PRINT);
+
+        defUProp("ALPHABETIC", ALPHABETIC);
+        defUProp("ASSIGNED", ASSIGNED);
+        defUProp("CONTROL", CONTROL);
+        defUProp("HEXDIGIT", HEX_DIGIT);
+        defUProp("IDEOGRAPHIC", IDEOGRAPHIC);
+        defUProp("JOINCONTROL", JOIN_CONTROL);
+        defUProp("LETTER", LETTER);
+        defUProp("LOWERCASE", LOWERCASE);
+        defUProp("NONCHARACTERCODEPOINT", NONCHARACTER_CODE_POINT);
+        defUProp("TITLECASE", TITLECASE);
+        defUProp("PUNCTUATION", PUNCTUATION);
+        defUProp("UPPERCASE", UPPERCASE);
+        defUProp("WHITESPACE", WHITE_SPACE);
+        defUProp("WORD", WORD);
+        defUProp("WHITE_SPACE", WHITE_SPACE);
+        defUProp("HEX_DIGIT", HEX_DIGIT);
+        defUProp("NONCHARACTER_CODE_POINT", NONCHARACTER_CODE_POINT);
+        defUProp("JOIN_CONTROL", JOIN_CONTROL);
+    }
+
+    public static CharPredicate forUnicodeProperty(String propName) {
+        propName = propName.toUpperCase(Locale.ROOT);
+        CharPredicate p = uprops.get(propName);
+        if (p != null)
+            return p;
+        return posix.get(propName);
+    }
+
+    public static CharPredicate forPOSIXName(String propName) {
+        return posix.get(propName.toUpperCase(Locale.ENGLISH));
+    }
+
+    /////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Returns a predicate matching all characters belong to a named
+     * UnicodeScript.
+     */
+    static CharPredicate forUnicodeScript(String name) {
+        final Character.UnicodeScript script;
+        try {
+            script = Character.UnicodeScript.forName(name);
+            return ch -> script == Character.UnicodeScript.of(ch);
+        } catch (IllegalArgumentException iae) {}
+        return null;
+    }
+
+    /**
+     * Returns a predicate matching all characters in a UnicodeBlock.
+     */
+    static CharPredicate forUnicodeBlock(String name) {
+        final Character.UnicodeBlock block;
+        try {
+            block = Character.UnicodeBlock.forName(name);
+            return ch -> block == Character.UnicodeBlock.of(ch);
+        } catch (IllegalArgumentException iae) {}
+         return null;
+    }
+
+    /////////////////////////////////////////////////////////////////////////////
+
+    // unicode categories, aliases, properties, java methods ...
+
+    private static final HashMap<String, CharPredicate> props = new HashMap<>(128);
+
+    /**
+     * Returns a predicate matching all characters in a named property.
+     */
+    static CharPredicate forProperty(String name) {
+        return props.get(name);
+    }
+
+    private static void defProp(String name, CharPredicate p) {
+        props.put(name, p);
+    }
+
+    private static void defCategory(String name, final int typeMask) {
+        CharPredicate p = ch -> (typeMask & (1 << Character.getType(ch))) != 0;
+        props.put(name, p);
+    }
+
+    private static void defRange(String name, final int lower, final int upper) {
+        BmpCharPredicate p = ch -> lower <= ch && ch <= upper;
+        props.put(name, p);
+    }
+
+    private static void defCtype(String name, final int ctype) {
+        BmpCharPredicate p = ch -> ch < 128 && ASCII.isType(ch, ctype);
+        // PrintPattern.pmap.put(p, name);
+        props.put(name, p);
+    }
+
+    static {
+        // Unicode character property aliases, defined in
+        // http://www.unicode.org/Public/UNIDATA/PropertyValueAliases.txt
+        defCategory("Cn", 1<<Character.UNASSIGNED);
+        defCategory("Lu", 1<<Character.UPPERCASE_LETTER);
+        defCategory("Ll", 1<<Character.LOWERCASE_LETTER);
+        defCategory("Lt", 1<<Character.TITLECASE_LETTER);
+        defCategory("Lm", 1<<Character.MODIFIER_LETTER);
+        defCategory("Lo", 1<<Character.OTHER_LETTER);
+        defCategory("Mn", 1<<Character.NON_SPACING_MARK);
+        defCategory("Me", 1<<Character.ENCLOSING_MARK);
+        defCategory("Mc", 1<<Character.COMBINING_SPACING_MARK);
+        defCategory("Nd", 1<<Character.DECIMAL_DIGIT_NUMBER);
+        defCategory("Nl", 1<<Character.LETTER_NUMBER);
+        defCategory("No", 1<<Character.OTHER_NUMBER);
+        defCategory("Zs", 1<<Character.SPACE_SEPARATOR);
+        defCategory("Zl", 1<<Character.LINE_SEPARATOR);
+        defCategory("Zp", 1<<Character.PARAGRAPH_SEPARATOR);
+        defCategory("Cc", 1<<Character.CONTROL);
+        defCategory("Cf", 1<<Character.FORMAT);
+        defCategory("Co", 1<<Character.PRIVATE_USE);
+        defCategory("Cs", 1<<Character.SURROGATE);
+        defCategory("Pd", 1<<Character.DASH_PUNCTUATION);
+        defCategory("Ps", 1<<Character.START_PUNCTUATION);
+        defCategory("Pe", 1<<Character.END_PUNCTUATION);
+        defCategory("Pc", 1<<Character.CONNECTOR_PUNCTUATION);
+        defCategory("Po", 1<<Character.OTHER_PUNCTUATION);
+        defCategory("Sm", 1<<Character.MATH_SYMBOL);
+        defCategory("Sc", 1<<Character.CURRENCY_SYMBOL);
+        defCategory("Sk", 1<<Character.MODIFIER_SYMBOL);
+        defCategory("So", 1<<Character.OTHER_SYMBOL);
+        defCategory("Pi", 1<<Character.INITIAL_QUOTE_PUNCTUATION);
+        defCategory("Pf", 1<<Character.FINAL_QUOTE_PUNCTUATION);
+        defCategory("L", ((1<<Character.UPPERCASE_LETTER) |
+                          (1<<Character.LOWERCASE_LETTER) |
+                          (1<<Character.TITLECASE_LETTER) |
+                          (1<<Character.MODIFIER_LETTER)  |
+                          (1<<Character.OTHER_LETTER)));
+        defCategory("M", ((1<<Character.NON_SPACING_MARK) |
+                          (1<<Character.ENCLOSING_MARK)   |
+                          (1<<Character.COMBINING_SPACING_MARK)));
+        defCategory("N", ((1<<Character.DECIMAL_DIGIT_NUMBER) |
+                          (1<<Character.LETTER_NUMBER)        |
+                          (1<<Character.OTHER_NUMBER)));
+        defCategory("Z", ((1<<Character.SPACE_SEPARATOR) |
+                          (1<<Character.LINE_SEPARATOR)  |
+                          (1<<Character.PARAGRAPH_SEPARATOR)));
+        defCategory("C", ((1<<Character.CONTROL)     |
+                          (1<<Character.FORMAT)      |
+                          (1<<Character.PRIVATE_USE) |
+                          (1<<Character.SURROGATE))); // Other
+        defCategory("P", ((1<<Character.DASH_PUNCTUATION)      |
+                          (1<<Character.START_PUNCTUATION)     |
+                          (1<<Character.END_PUNCTUATION)       |
+                          (1<<Character.CONNECTOR_PUNCTUATION) |
+                          (1<<Character.OTHER_PUNCTUATION)     |
+                          (1<<Character.INITIAL_QUOTE_PUNCTUATION) |
+                          (1<<Character.FINAL_QUOTE_PUNCTUATION)));
+        defCategory("S", ((1<<Character.MATH_SYMBOL)     |
+                          (1<<Character.CURRENCY_SYMBOL) |
+                          (1<<Character.MODIFIER_SYMBOL) |
+                          (1<<Character.OTHER_SYMBOL)));
+        defCategory("LC", ((1<<Character.UPPERCASE_LETTER) |
+                           (1<<Character.LOWERCASE_LETTER) |
+                           (1<<Character.TITLECASE_LETTER)));
+        defCategory("LD", ((1<<Character.UPPERCASE_LETTER) |
+                           (1<<Character.LOWERCASE_LETTER) |
+                           (1<<Character.TITLECASE_LETTER) |
+                           (1<<Character.MODIFIER_LETTER)  |
+                           (1<<Character.OTHER_LETTER)     |
+                           (1<<Character.DECIMAL_DIGIT_NUMBER)));
+        defRange("L1", 0x00, 0xFF); // Latin-1
+        props.put("all", ch -> true);
+
+        // Posix regular expression character classes, defined in
+        // http://www.unix.org/onlinepubs/009695399/basedefs/xbd_chap09.html
+        defRange("ASCII", 0x00, 0x7F);   // ASCII
+        defCtype("Alnum", ASCII.ALNUM);  // Alphanumeric characters
+        defCtype("Alpha", ASCII.ALPHA);  // Alphabetic characters
+        defCtype("Blank", ASCII.BLANK);  // Space and tab characters
+        defCtype("Cntrl", ASCII.CNTRL);  // Control characters
+        defRange("Digit", '0', '9');     // Numeric characters
+        defCtype("Graph", ASCII.GRAPH);  // printable and visible
+        defRange("Lower", 'a', 'z');     // Lower-case alphabetic
+        defRange("Print", 0x20, 0x7E);   // Printable characters
+        defCtype("Punct", ASCII.PUNCT);  // Punctuation characters
+        defCtype("Space", ASCII.SPACE);  // Space characters
+        defRange("Upper", 'A', 'Z');     // Upper-case alphabetic
+        defCtype("XDigit",ASCII.XDIGIT); // hexadecimal digits
+
+        // Java character properties, defined by methods in Character.java
+        defProp("javaLowerCase", java.lang.Character::isLowerCase);
+        defProp("javaUpperCase",  Character::isUpperCase);
+        defProp("javaAlphabetic", java.lang.Character::isAlphabetic);
+        defProp("javaIdeographic", java.lang.Character::isIdeographic);
+        defProp("javaTitleCase", java.lang.Character::isTitleCase);
+        defProp("javaDigit", java.lang.Character::isDigit);
+        defProp("javaDefined", java.lang.Character::isDefined);
+        defProp("javaLetter", java.lang.Character::isLetter);
+        defProp("javaLetterOrDigit", java.lang.Character::isLetterOrDigit);
+        defProp("javaJavaIdentifierStart", java.lang.Character::isJavaIdentifierStart);
+        defProp("javaJavaIdentifierPart", java.lang.Character::isJavaIdentifierPart);
+        defProp("javaUnicodeIdentifierStart", java.lang.Character::isUnicodeIdentifierStart);
+        defProp("javaUnicodeIdentifierPart", java.lang.Character::isUnicodeIdentifierPart);
+        defProp("javaIdentifierIgnorable", java.lang.Character::isIdentifierIgnorable);
+        defProp("javaSpaceChar", java.lang.Character::isSpaceChar);
+        defProp("javaWhitespace", java.lang.Character::isWhitespace);
+        defProp("javaISOControl", java.lang.Character::isISOControl);
+        defProp("javaMirrored", java.lang.Character::isMirrored);
+    }
+
+    /////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Posix ASCII variants, not in the lookup map
+     */
+    static final BmpCharPredicate ASCII_DIGIT = ch -> ch < 128 && ASCII.isDigit(ch);
+    static final BmpCharPredicate ASCII_WORD  = ch -> ch < 128 && ASCII.isWord(ch);
+    static final BmpCharPredicate ASCII_SPACE = ch -> ch < 128 && ASCII.isSpace(ch);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/util/regex/IntHashSet.java	Thu May 12 18:47:08 2016 +0000
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2016, 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 java.util.regex;
+
+import java.util.Arrays;
+
+/**
+ * A lightweight hashset implementation for positive 'int'. Not safe for
+ * concurrent access.
+ */
+class IntHashSet {
+    private int[] entries;
+    private int[] hashes;
+    private int pos = 0;
+
+    public IntHashSet() {
+        this.entries = new int[16 << 1];      // initCapacity = 16;
+        this.hashes = new int[(16 / 2) | 1];  // odd -> fewer collisions
+        Arrays.fill(this.entries, -1);
+        Arrays.fill(this.hashes, -1);
+    }
+
+    public boolean contains(int i) {
+        int h = hashes[i % hashes.length];
+        while (h != -1) {
+            if (entries[h] == i)
+                return true;
+            h = entries[h + 1];
+        }
+        return false;
+    }
+
+    public void add(int i) {
+        int h0 = i % hashes.length;
+        int next = hashes[h0];
+        //  if invoker guarantees contains(i) checked before add(i)
+        //  the following check is not needed.
+        int next0 = next;
+        while (next0 != -1) {
+            if (entries[next0 ] == i)
+                return;
+            next0 = entries[next0 + 1];
+        }
+        hashes[h0] = pos;
+        entries[pos++] = i;
+        entries[pos++] = next;
+        if (pos == entries.length)
+            expand();
+    }
+
+    public void clear() {
+        Arrays.fill(this.entries, -1);
+        Arrays.fill(this.hashes, -1);
+        pos = 0;
+    }
+
+    private void expand() {
+        int[] old = entries;
+        int[] es = new int[old.length << 1];
+        int hlen = (old.length / 2) | 1;
+        int[] hs = new int[hlen];
+        Arrays.fill(es, -1);
+        Arrays.fill(hs, -1);
+        for (int n = 0; n < pos;) {  // re-hashing
+            int i = old[n];
+            int hsh = i % hlen;
+            int next = hs[hsh];
+            hs[hsh] = n;
+            es[n++] = i;
+            es[n++] = next;
+        }
+        this.entries = es;
+        this.hashes = hs;
+    }
+}
--- a/src/java.base/share/classes/java/util/regex/Matcher.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/util/regex/Matcher.java	Thu May 12 18:47:08 2016 +0000
@@ -178,6 +178,14 @@
     int[] locals;
 
     /**
+     * Storage used by top greedy Loop node to store a specific hash set to
+     * keep the beginning index of the failed repetition match. The nodes
+     * themselves are stateless, so they rely on this field to hold state
+     * during a match.
+     */
+    IntHashSet[] localsPos;
+
+    /**
      * Boolean indicating whether or not more input could change
      * the results of the last match.
      *
@@ -239,6 +247,7 @@
         int parentGroupCount = Math.max(parent.capturingGroupCount, 10);
         groups = new int[parentGroupCount * 2];
         locals = new int[parent.localCount];
+        localsPos = new IntHashSet[parent.localTCNCount];
 
         // Put fields into initial states
         reset();
@@ -375,6 +384,7 @@
             groups[i] = -1;
         for (int i = 0; i < locals.length; i++)
             locals[i] = -1;
+        localsPos = new IntHashSet[parentPattern.localTCNCount];
         modCount++;
         return this;
     }
@@ -397,6 +407,10 @@
             groups[i] = -1;
         for(int i=0; i<locals.length; i++)
             locals[i] = -1;
+        for (int i = 0; i < localsPos.length; i++) {
+            if (localsPos[i] != null)
+                localsPos[i].clear();
+        }
         lastAppendPosition = 0;
         from = 0;
         to = getTextLength();
@@ -1706,6 +1720,10 @@
         this.oldLast = oldLast < 0 ? from : oldLast;
         for (int i = 0; i < groups.length; i++)
             groups[i] = -1;
+        for (int i = 0; i < localsPos.length; i++) {
+            if (localsPos[i] != null)
+                localsPos[i].clear();
+        }
         acceptMode = NOANCHOR;
         boolean result = parentPattern.root.match(this, from, text);
         if (!result)
@@ -1729,6 +1747,10 @@
         this.oldLast = oldLast < 0 ? from : oldLast;
         for (int i = 0; i < groups.length; i++)
             groups[i] = -1;
+        for (int i = 0; i < localsPos.length; i++) {
+            if (localsPos[i] != null)
+                localsPos[i].clear();
+        }
         acceptMode = anchor;
         boolean result = parentPattern.matchRoot.match(this, from, text);
         if (!result)
--- a/src/java.base/share/classes/java/util/regex/Pattern.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/java/util/regex/Pattern.java	Thu May 12 18:47:08 2016 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,11 +26,15 @@
 package java.util.regex;
 
 import java.text.Normalizer;
+import java.text.Normalizer.Form;
 import java.util.Locale;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
 import java.util.Arrays;
 import java.util.NoSuchElementException;
 import java.util.Spliterator;
@@ -469,7 +473,7 @@
  *
  *   <li> A line-separator character&nbsp;(<code>'&#92;u2028'</code>), or
  *
- *   <li> A paragraph-separator character&nbsp;(<code>'&#92;u2029</code>).
+ *   <li> A paragraph-separator character&nbsp;(<code>'&#92;u2029'</code>).
  *
  * </ul>
  * <p>If {@link #UNIX_LINES} mode is activated, then the only line terminators
@@ -984,6 +988,11 @@
     transient int[] buffer;
 
     /**
+     * A temporary storage used for predicate for double return.
+     */
+    transient CharPredicate predicate;
+
+    /**
      * Map the "name" of the "named capturing group" to its group id
      * node.
      */
@@ -995,6 +1004,24 @@
     transient GroupHead[] groupNodes;
 
     /**
+     * Temporary storage used to store the top level closure nodes.
+     */
+    transient List<Node> topClosureNodes;
+
+    /**
+     * The number of top greedy closure nodes in this Pattern. Used by
+     * matchers to allocate storage needed for a IntHashSet to keep the
+     * beginning pos {@code i} of all failed match.
+     */
+    transient int localTCNCount;
+
+    /*
+     * Turn off the stop-exponential-backtracking optimization if there
+     * is a group ref in the pattern.
+     */
+    transient boolean hasGroupRef;
+
+    /**
      * Temporary null terminated code point array used by pattern compiling.
      */
     private transient int[] temp;
@@ -1026,7 +1053,7 @@
      * If the Start node might possibly match supplementary characters.
      * It is set to true during compiling if
      * (1) There is supplementary char in pattern, or
-     * (2) There is complement node of Category or Block
+     * (2) There is complement node of a "family" CharProperty
      */
     private transient boolean hasSupplementary;
 
@@ -1338,6 +1365,7 @@
         // Initialize counts
         capturingGroupCount = 1;
         localCount = 0;
+        localTCNCount = 0;
 
         // if length > 0, the Pattern is lazily compiled
         if (pattern.length() == 0) {
@@ -1368,6 +1396,7 @@
         // Reset group index count
         capturingGroupCount = 1;
         localCount = 0;
+        localTCNCount = 0;
 
         if (pattern.length() > 0) {
             compile();
@@ -1378,105 +1407,114 @@
     }
 
     /**
-     * The pattern is converted to normalized form ({@linkplain
-     * java.text.Normalizer.Form.NFD NFD}, canonical decomposition)
-     * and then a pure group is constructed to match canonical
-     * equivalences of the characters.
+     * The pattern is converted to normalized form ({@link
+     * java.text.Normalizer.Form.NFC NFC}, canonical decomposition,
+     * followed by canonical composition for the character class
+     * part, and {@link java.text.Normalizer.Form.NFD NFD},
+     * canonical decomposition) for the rest), and then a pure
+     * group is constructed to match canonical equivalences of the
+     * characters.
      */
-    private void normalize() {
-        int lastCodePoint = -1;
-
-        // Convert pattern into normalized form
-        normalizedPattern = Normalizer.normalize(pattern, Normalizer.Form.NFD);
-        patternLength = normalizedPattern.length();
-
-        // Modify pattern to match canonical equivalences
-        StringBuilder newPattern = new StringBuilder(patternLength);
-        for(int i=0; i<patternLength; ) {
-            int c = normalizedPattern.codePointAt(i);
-            StringBuilder sequenceBuffer;
-            if ((Character.getType(c) == Character.NON_SPACING_MARK)
-                && (lastCodePoint != -1)) {
-                sequenceBuffer = new StringBuilder();
-                sequenceBuffer.appendCodePoint(lastCodePoint);
-                sequenceBuffer.appendCodePoint(c);
-                while(Character.getType(c) == Character.NON_SPACING_MARK) {
-                    i += Character.charCount(c);
-                    if (i >= patternLength)
-                        break;
-                    c = normalizedPattern.codePointAt(i);
-                    sequenceBuffer.appendCodePoint(c);
+    private static String normalize(String pattern) {
+        int plen = pattern.length();
+        StringBuilder pbuf = new StringBuilder(plen);
+        char last = 0;
+        int lastStart = 0;
+        char cc = 0;
+        for (int i = 0; i < plen;) {
+            char c = pattern.charAt(i);
+            if (cc == 0 &&    // top level
+                c == '\\' && i + 1 < plen && pattern.charAt(i + 1) == '\\') {
+                i += 2; last = 0;
+                continue;
+            }
+            if (c == '[' && last != '\\') {
+                if (cc == 0) {
+                    if (lastStart < i)
+                        normalizeSlice(pattern, lastStart, i, pbuf);
+                    lastStart = i;
                 }
-                String ea = produceEquivalentAlternation(
-                                               sequenceBuffer.toString());
-                newPattern.setLength(newPattern.length()-Character.charCount(lastCodePoint));
-                newPattern.append("(?:").append(ea).append(")");
-            } else if (c == '[' && lastCodePoint != '\\') {
-                i = normalizeCharClass(newPattern, i);
-            } else {
-                newPattern.appendCodePoint(c);
+                cc++;
+            } else if (c == ']' && last != '\\') {
+                cc--;
+                if (cc == 0) {
+                    normalizeClazz(pattern, lastStart, i + 1, pbuf);
+                    lastStart = i + 1;
+                }
             }
-            lastCodePoint = c;
-            i += Character.charCount(c);
-        }
-        normalizedPattern = newPattern.toString();
+            last = c;
+            i++;
+        }
+        assert (cc == 0);
+        if (lastStart < plen)
+            normalizeSlice(pattern, lastStart, plen, pbuf);
+        return pbuf.toString();
     }
 
-    /**
-     * Complete the character class being parsed and add a set
-     * of alternations to it that will match the canonical equivalences
-     * of the characters within the class.
-     */
-    private int normalizeCharClass(StringBuilder newPattern, int i) {
-        StringBuilder charClass = new StringBuilder();
-        StringBuilder eq = null;
-        int lastCodePoint = -1;
-        String result;
-
-        i++;
-        charClass.append("[");
-        while(true) {
-            int c = normalizedPattern.codePointAt(i);
-            StringBuilder sequenceBuffer;
-
-            if (c == ']' && lastCodePoint != '\\') {
-                charClass.append((char)c);
-                break;
-            } else if (Character.getType(c) == Character.NON_SPACING_MARK) {
-                sequenceBuffer = new StringBuilder();
-                sequenceBuffer.appendCodePoint(lastCodePoint);
-                while(Character.getType(c) == Character.NON_SPACING_MARK) {
-                    sequenceBuffer.appendCodePoint(c);
-                    i += Character.charCount(c);
-                    if (i >= normalizedPattern.length())
-                        break;
-                    c = normalizedPattern.codePointAt(i);
+    private static void normalizeSlice(String src, int off, int limit,
+                                       StringBuilder dst)
+    {
+        int len = src.length();
+        int off0 = off;
+        while (off < limit && ASCII.isAscii(src.charAt(off))) {
+            off++;
+        }
+        if (off == limit) {
+            dst.append(src, off0, limit);
+            return;
+        }
+        off--;
+        if (off < off0)
+            off = off0;
+        else
+            dst.append(src, off0, off);
+        while (off < limit) {
+            int ch0 = src.codePointAt(off);
+            if (".$|()[]{}^?*+\\".indexOf(ch0) != -1) {
+                dst.append((char)ch0);
+                off++;
+                continue;
+            }
+            int j = off + Character.charCount(ch0);
+            int ch1;
+            while (j < limit) {
+                ch1 = src.codePointAt(j);
+                if (Grapheme.isBoundary(ch0, ch1))
+                    break;
+                ch0 = ch1;
+                j += Character.charCount(ch1);
+            }
+            String seq = src.substring(off, j);
+            String nfd = Normalizer.normalize(seq, Normalizer.Form.NFD);
+            off = j;
+            if (nfd.length() > 1) {
+                ch0 = nfd.codePointAt(0);
+                ch1 = nfd.codePointAt(Character.charCount(ch0));
+                if (Character.getType(ch1) == Character.NON_SPACING_MARK) {
+                    Set<String> altns = new LinkedHashSet<>();
+                    altns.add(seq);
+                    produceEquivalentAlternation(nfd, altns);
+                    dst.append("(?:");
+                    altns.forEach( s -> dst.append(s + "|"));
+                    dst.delete(dst.length() - 1, dst.length());
+                    dst.append(")");
+                    continue;
                 }
-                String ea = produceEquivalentAlternation(
-                                                  sequenceBuffer.toString());
-
-                charClass.setLength(charClass.length()-Character.charCount(lastCodePoint));
-                if (eq == null)
-                    eq = new StringBuilder();
-                eq.append('|');
-                eq.append(ea);
-            } else {
-                charClass.appendCodePoint(c);
-                i++;
             }
-            if (i == normalizedPattern.length())
-                throw error("Unclosed character class");
-            lastCodePoint = c;
-        }
-
-        if (eq != null) {
-            result = "(?:"+charClass.toString()+eq.toString()+")";
-        } else {
-            result = charClass.toString();
-        }
-
-        newPattern.append(result);
-        return i;
+            String nfc = Normalizer.normalize(seq, Normalizer.Form.NFC);
+            if (!seq.equals(nfc) && !nfd.equals(nfc))
+                dst.append("(?:" + seq + "|" + nfd  + "|" + nfc + ")");
+            else if (!seq.equals(nfd))
+                dst.append("(?:" + seq + "|" + nfd + ")");
+            else
+                dst.append(seq);
+        }
+    }
+
+    private static void normalizeClazz(String src, int off, int limit,
+                                       StringBuilder dst)
+    {
+        dst.append(Normalizer.normalize(src.substring(off, limit), Form.NFC));
     }
 
     /**
@@ -1484,28 +1522,26 @@
      * combining marks that follow it, produce the alternation that will
      * match all canonical equivalences of that sequence.
      */
-    private String produceEquivalentAlternation(String source) {
-        int len = countChars(source, 0, 1);
-        if (source.length() == len)
-            // source has one character.
-            return source;
-
-        String base = source.substring(0,len);
-        String combiningMarks = source.substring(len);
-
+    private static void produceEquivalentAlternation(String src,
+                                                     Set<String> dst)
+    {
+        int len = countChars(src, 0, 1);
+        if (src.length() == len) {
+            dst.add(src);  // source has one character.
+            return;
+        }
+        String base = src.substring(0,len);
+        String combiningMarks = src.substring(len);
         String[] perms = producePermutations(combiningMarks);
-        StringBuilder result = new StringBuilder(source);
-
         // Add combined permutations
-        for(int x=0; x<perms.length; x++) {
+        for(int x = 0; x < perms.length; x++) {
             String next = base + perms[x];
-            if (x>0)
-                result.append("|"+next);
+            dst.add(next);
             next = composeOneStep(next);
-            if (next != null)
-                result.append("|"+produceEquivalentAlternation(next));
-        }
-        return result.toString();
+            if (next != null) {
+                produceEquivalentAlternation(next, dst);
+            }
+        }
     }
 
     /**
@@ -1517,7 +1553,7 @@
      * possibilities must be removed because they are not canonically
      * equivalent.
      */
-    private String[] producePermutations(String input) {
+    private static String[] producePermutations(String input) {
         if (input.length() == countChars(input, 0, 1))
             return new String[] {input};
 
@@ -1575,7 +1611,7 @@
         return result;
     }
 
-    private int getClass(int c) {
+    private static int getClass(int c) {
         return sun.text.Normalizer.getCombiningClass(c);
     }
 
@@ -1586,11 +1622,10 @@
      * combining mark followed by the remaining combining marks. Returns
      * null if the first two characters cannot be further composed.
      */
-    private String composeOneStep(String input) {
+    private static String composeOneStep(String input) {
         int len = countChars(input, 0, 2);
         String firstTwoCharacters = input.substring(0, len);
         String result = Normalizer.normalize(firstTwoCharacters, Normalizer.Form.NFC);
-
         if (result.equals(firstTwoCharacters))
             return null;
         else {
@@ -1677,7 +1712,7 @@
     private void compile() {
         // Handle canonical equivalences
         if (has(CANON_EQ) && !has(LITERAL)) {
-            normalize();
+            normalizedPattern = normalize(pattern);
         } else {
             normalizedPattern = pattern;
         }
@@ -1707,6 +1742,7 @@
         buffer = new int[32];
         groupNodes = new GroupHead[10];
         namedGroups = null;
+        topClosureNodes = new ArrayList<>(10);
 
         if (has(LITERAL)) {
             // Literal pattern handling
@@ -1737,12 +1773,26 @@
             root = hasSupplementary ? new StartS(matchRoot) : new Start(matchRoot);
         }
 
+        // Optimize the greedy Loop to prevent exponential backtracking, IF there
+        // is no group ref in this pattern. With a non-negative localTCNCount value,
+        // the greedy type Loop, Curly will skip the backtracking for any starting
+        // position "i" that failed in the past.
+        if (!hasGroupRef) {
+            for (Node node : topClosureNodes) {
+                if (node instanceof Loop) {
+                    // non-deterministic-greedy-group
+                    ((Loop)node).posIndex = localTCNCount++;
+                }
+            }
+        }
+
         // Release temporary storage
         temp = null;
         buffer = null;
         groupNodes = null;
         patternLength = 0;
         compiled = true;
+        topClosureNodes = null;
     }
 
     Map<String, Integer> namedGroups() {
@@ -1754,44 +1804,6 @@
     }
 
     /**
-     * Used to print out a subtree of the Pattern to help with debugging.
-     */
-    private static void printObjectTree(Node node) {
-        while(node != null) {
-            if (node instanceof Prolog) {
-                System.out.println(node);
-                printObjectTree(((Prolog)node).loop);
-                System.out.println("**** end contents prolog loop");
-            } else if (node instanceof Loop) {
-                System.out.println(node);
-                printObjectTree(((Loop)node).body);
-                System.out.println("**** end contents Loop body");
-            } else if (node instanceof Curly) {
-                System.out.println(node);
-                printObjectTree(((Curly)node).atom);
-                System.out.println("**** end contents Curly body");
-            } else if (node instanceof GroupCurly) {
-                System.out.println(node);
-                printObjectTree(((GroupCurly)node).atom);
-                System.out.println("**** end contents GroupCurly body");
-            } else if (node instanceof GroupTail) {
-                System.out.println(node);
-                System.out.println("Tail next is "+node.next);
-                return;
-            } else {
-                System.out.println(node);
-            }
-            node = node.next;
-            if (node != null)
-                System.out.println("->next:");
-            if (node == Pattern.accept) {
-                System.out.println("Accept Node");
-                node = null;
-            }
-       }
-    }
-
-    /**
      * Used to accumulate information about a subtree of the object graph
      * so that optimizations can be applied to the subtree.
      */
@@ -2083,7 +2095,10 @@
                 tail = root;
                 continue;
             case '[':
-                node = clazz(true);
+                if (has(CANON_EQ) && !has(LITERAL))
+                    node = new NFCCharProperty(clazz(true));
+                else
+                    node = newCharProperty(clazz(true));
                 break;
             case '\\':
                 ch = nextEscaped();
@@ -2096,7 +2111,11 @@
                     } else {
                         oneLetter = false;
                     }
-                    node = family(oneLetter, comp);
+                    // node = newCharProperty(family(oneLetter, comp));
+                    if (has(CANON_EQ) && !has(LITERAL))
+                        node = new NFCCharProperty(family(oneLetter, comp));
+                    else
+                        node = newCharProperty(family(oneLetter, comp));
                 } else {
                     unread();
                     node = atom();
@@ -2123,12 +2142,12 @@
             case '.':
                 next();
                 if (has(DOTALL)) {
-                    node = new All();
+                    node = new CharProperty(ALL);
                 } else {
-                    if (has(UNIX_LINES))
-                        node = new UnixDot();
-                    else {
-                        node = new Dot();
+                    if (has(UNIX_LINES)) {
+                        node = new CharProperty(UNIXDOT);
+                    } else {
+                        node = new CharProperty(DOT);
                     }
                 }
                 break;
@@ -2155,7 +2174,12 @@
             }
 
             node = closure(node);
-
+            /* save the top dot-greedy nodes (.*, .+) as well
+            if (node instanceof GreedyCharProperty &&
+                ((GreedyCharProperty)node).cp instanceof Dot) {
+                topClosureNodes.add(node);
+            }
+            */
             if (head == null) {
                 head = tail = node;
             } else {
@@ -2213,7 +2237,10 @@
                             unread();
                         else
                             oneLetter = false;
-                        return family(oneLetter, comp);
+                        if (has(CANON_EQ) && !has(LITERAL))
+                            return new NFCCharProperty(family(oneLetter, comp));
+                        else
+                            return newCharProperty(family(oneLetter, comp));
                     }
                 }
                 unread();
@@ -2251,7 +2278,7 @@
             break;
         }
         if (first == 1) {
-            return newSingle(buffer[0]);
+            return newCharProperty(single(buffer[0]));
         } else {
             return newSlice(buffer, first, hasSupplementary);
         }
@@ -2302,6 +2329,7 @@
                 break;
             }
         }
+        hasGroupRef = true;
         if (has(CASE_INSENSITIVE))
             return new CIBackRef(refNum, has(UNICODE_CASE));
         else
@@ -2346,9 +2374,13 @@
         case 'C':
             break;
         case 'D':
-            if (create) root = has(UNICODE_CHARACTER_CLASS)
-                               ? new Utype(UnicodeProp.DIGIT).complement()
-                               : new Ctype(ASCII.DIGIT).complement();
+            if (create) {
+                predicate = has(UNICODE_CHARACTER_CLASS) ?
+                            CharPredicates.DIGIT : CharPredicates.ASCII_DIGIT;
+                predicate = predicate.negate();
+                if (!inclass)
+                    root = newCharProperty(predicate);
+            }
             return -1;
         case 'E':
         case 'F':
@@ -2358,7 +2390,11 @@
             if (create) root = new LastMatch();
             return -1;
         case 'H':
-            if (create) root = new HorizWS().complement();
+            if (create) {
+                predicate = HorizWS.negate();
+                if (!inclass)
+                    root = newCharProperty(predicate);
+            }
             return -1;
         case 'I':
         case 'J':
@@ -2377,20 +2413,32 @@
             if (create) root = new LineEnding();
             return -1;
         case 'S':
-            if (create) root = has(UNICODE_CHARACTER_CLASS)
-                               ? new Utype(UnicodeProp.WHITE_SPACE).complement()
-                               : new Ctype(ASCII.SPACE).complement();
+            if (create) {
+                predicate = has(UNICODE_CHARACTER_CLASS) ?
+                            CharPredicates.WHITE_SPACE : CharPredicates.ASCII_SPACE;
+                predicate = predicate.negate();
+                if (!inclass)
+                    root = newCharProperty(predicate);
+            }
             return -1;
         case 'T':
         case 'U':
             break;
         case 'V':
-            if (create) root = new VertWS().complement();
+            if (create) {
+                predicate = VertWS.negate();
+                if (!inclass)
+                    root = newCharProperty(predicate);
+            }
             return -1;
         case 'W':
-            if (create) root = has(UNICODE_CHARACTER_CLASS)
-                               ? new Utype(UnicodeProp.WORD).complement()
-                               : new Ctype(ASCII.WORD).complement();
+            if (create) {
+                predicate = has(UNICODE_CHARACTER_CLASS) ?
+                            CharPredicates.WORD : CharPredicates.ASCII_WORD;
+                predicate = predicate.negate();
+                if (!inclass)
+                    root = newCharProperty(predicate);
+            }
             return -1;
         case 'X':
             if (inclass) break;
@@ -2430,9 +2478,12 @@
         case 'c':
             return c();
         case 'd':
-            if (create) root = has(UNICODE_CHARACTER_CLASS)
-                               ? new Utype(UnicodeProp.DIGIT)
-                               : new Ctype(ASCII.DIGIT);
+            if (create) {
+                predicate = has(UNICODE_CHARACTER_CLASS) ?
+                            CharPredicates.DIGIT : CharPredicates.ASCII_DIGIT;
+                if (!inclass)
+                    root = newCharProperty(predicate);
+            }
             return -1;
         case 'e':
             return '\033';
@@ -2441,7 +2492,11 @@
         case 'g':
             break;
         case 'h':
-            if (create) root = new HorizWS();
+            if (create) {
+                predicate = HorizWS;
+                if (!inclass)
+                    root = newCharProperty(predicate);
+            }
             return -1;
         case 'i':
         case 'j':
@@ -2455,6 +2510,7 @@
             if (!namedGroups().containsKey(name))
                 throw error("(named capturing group <"+ name+"> does not exit");
             if (create) {
+                hasGroupRef = true;
                 if (has(CASE_INSENSITIVE))
                     root = new CIBackRef(namedGroups().get(name), has(UNICODE_CASE));
                 else
@@ -2473,9 +2529,12 @@
         case 'r':
             return '\r';
         case 's':
-            if (create) root = has(UNICODE_CHARACTER_CLASS)
-                               ? new Utype(UnicodeProp.WHITE_SPACE)
-                               : new Ctype(ASCII.SPACE);
+            if (create) {
+                predicate = has(UNICODE_CHARACTER_CLASS) ?
+                            CharPredicates.WHITE_SPACE : CharPredicates.ASCII_SPACE;
+                if (!inclass)
+                    root = newCharProperty(predicate);
+            }
             return -1;
         case 't':
             return '\t';
@@ -2492,12 +2551,19 @@
             // compatibility concern '\013'/0x0B is returned if isrange.
             if (isrange)
                 return '\013';
-            if (create) root = new VertWS();
+            if (create) {
+                predicate = VertWS;
+                if (!inclass)
+                    root = newCharProperty(predicate);
+            }
             return -1;
         case 'w':
-            if (create) root = has(UNICODE_CHARACTER_CLASS)
-                               ? new Utype(UnicodeProp.WORD)
-                               : new Ctype(ASCII.WORD);
+            if (create) {
+                predicate = has(UNICODE_CHARACTER_CLASS) ?
+                            CharPredicates.WORD : CharPredicates.ASCII_WORD;
+                if (!inclass)
+                    root = newCharProperty(predicate);
+            }
             return -1;
         case 'x':
             return x();
@@ -2520,63 +2586,66 @@
      * is true except for the case of [abc&&def] where def is a separate
      * right hand node with "understood" brackets.
      */
-    private CharProperty clazz(boolean consume) {
-        CharProperty prev = null;
-        CharProperty node = null;
+    private CharPredicate clazz(boolean consume) {
+        CharPredicate prev = null;
+        CharPredicate curr = null;
         BitClass bits = new BitClass();
-        boolean include = true;
-        boolean firstInClass = true;
+        BmpCharPredicate bitsP = ch -> ch < 256 && bits.bits[ch];
+
+        boolean isNeg = false;
+        boolean hasBits = false;
         int ch = next();
+
+        // Negates if first char in a class, otherwise literal
+        if (ch == '^' && temp[cursor-1] == '[') {
+            ch = next();
+            isNeg = true;
+        }
         for (;;) {
             switch (ch) {
-                case '^':
-                    // Negates if first char in a class, otherwise literal
-                    if (firstInClass) {
-                        if (temp[cursor-1] != '[')
-                            break;
-                        ch = next();
-                        include = !include;
-                        continue;
-                    } else {
-                        // ^ not first in class, treat as literal
-                        break;
-                    }
                 case '[':
-                    firstInClass = false;
-                    node = clazz(true);
+                    curr = clazz(true);
                     if (prev == null)
-                        prev = node;
+                        prev = curr;
                     else
-                        prev = union(prev, node);
+                        prev = prev.union(curr);
                     ch = peek();
                     continue;
                 case '&':
-                    firstInClass = false;
                     ch = next();
                     if (ch == '&') {
                         ch = next();
-                        CharProperty rightNode = null;
+                        CharPredicate right = null;
                         while (ch != ']' && ch != '&') {
                             if (ch == '[') {
-                                if (rightNode == null)
-                                    rightNode = clazz(true);
+                                if (right == null)
+                                    right = clazz(true);
                                 else
-                                    rightNode = union(rightNode, clazz(true));
+                                    right = right.union(clazz(true));
                             } else { // abc&&def
                                 unread();
-                                rightNode = clazz(false);
+                                right = clazz(false);
                             }
                             ch = peek();
                         }
-                        if (rightNode != null)
-                            node = rightNode;
+                        if (hasBits) {
+                            // bits used, union has high precedence
+                            if (prev == null) {
+                                prev = curr = bitsP;
+                            } else {
+                                prev = prev.union(bitsP);
+                            }
+                            hasBits = false;
+                        }
+                        if (right != null)
+                            curr = right;
                         if (prev == null) {
-                            if (rightNode == null)
+                            if (right == null)
                                 throw error("Bad class syntax");
                             else
-                                prev = rightNode;
+                                prev = right;
                         } else {
-                            prev = intersection(prev, node);
+                            prev = prev.and(curr);
                         }
                     } else {
                         // treat as a literal &
@@ -2585,43 +2654,39 @@
                     }
                     continue;
                 case 0:
-                    firstInClass = false;
                     if (cursor >= patternLength)
                         throw error("Unclosed character class");
                     break;
                 case ']':
-                    firstInClass = false;
-                    if (prev != null) {
+                    if (prev != null || hasBits) {
                         if (consume)
                             next();
+                        if (prev == null)
+                            prev = bitsP;
+                        else if (hasBits)
+                            prev = prev.union(bitsP);
+                        if (isNeg)
+                            return prev.negate();
                         return prev;
                     }
                     break;
                 default:
-                    firstInClass = false;
                     break;
             }
-            node = range(bits);
-            if (include) {
-                if (prev == null) {
-                    prev = node;
-                } else {
-                    if (prev != node)
-                        prev = union(prev, node);
-                }
+            curr = range(bits);
+            if (curr == null) {    // the bits used
+                hasBits = true;
             } else {
-                if (prev == null) {
-                    prev = node.complement();
-                } else {
-                    if (prev != node)
-                        prev = setDifference(prev, node);
-                }
+                if (prev == null)
+                    prev = curr;
+                else if (prev != curr)
+                    prev = prev.union(curr);
             }
             ch = peek();
         }
     }
 
-    private CharProperty bitsOrSingle(BitClass bits, int ch) {
+    private CharPredicate bitsOrSingle(BitClass bits, int ch) {
         /* Bits can only handle codepoints in [u+0000-u+00ff] range.
            Use "single" node instead of bits when dealing with unicode
            case folding for codepoints listed below.
@@ -2643,19 +2708,46 @@
         if (ch < 256 &&
             !(has(CASE_INSENSITIVE) && has(UNICODE_CASE) &&
               (ch == 0xff || ch == 0xb5 ||
-               ch == 0x49 || ch == 0x69 ||  //I and i
-               ch == 0x53 || ch == 0x73 ||  //S and s
-               ch == 0x4b || ch == 0x6b ||  //K and k
-               ch == 0xc5 || ch == 0xe5)))  //A+ring
-            return bits.add(ch, flags());
-        return newSingle(ch);
+               ch == 0x49 || ch == 0x69 ||    //I and i
+               ch == 0x53 || ch == 0x73 ||    //S and s
+               ch == 0x4b || ch == 0x6b ||    //K and k
+               ch == 0xc5 || ch == 0xe5))) {  //A+ring
+            bits.add(ch, flags());
+            return null;
+        }
+        return single(ch);
+    }
+
+    /**
+     *  Returns a suitably optimized, single character predicate
+     */
+    private CharPredicate single(final int ch) {
+        if (has(CASE_INSENSITIVE)) {
+            int lower, upper;
+            if (has(UNICODE_CASE)) {
+                upper = Character.toUpperCase(ch);
+                lower = Character.toLowerCase(upper);
+                // Unicode case insensitive matches
+                if (upper != lower)
+                    return SingleU(lower);
+            } else if (ASCII.isAscii(ch)) {
+                lower = ASCII.toLower(ch);
+                upper = ASCII.toUpper(ch);
+                // Case insensitive matches a given BMP character
+                if (lower != upper)
+                    return SingleI(lower, upper);
+            }
+        }
+        if (isSupplementary(ch))
+            return SingleS(ch);
+        return Single(ch);  // Match a given BMP character
     }
 
     /**
      * Parse a single character or a character range in a character class
      * and return its representative node.
      */
-    private CharProperty range(BitClass bits) {
+    private CharPredicate range(BitClass bits) {
         int ch = peek();
         if (ch == '\\') {
             ch = nextEscaped();
@@ -2674,7 +2766,7 @@
                 unread();
                 ch = escape(true, true, isrange);
                 if (ch == -1)
-                    return (CharProperty) root;
+                    return predicate;
             }
         } else {
             next();
@@ -2696,10 +2788,13 @@
                     if (m < ch) {
                         throw error("Illegal character range");
                     }
-                    if (has(CASE_INSENSITIVE))
-                        return caseInsensitiveRangeFor(ch, m);
-                    else
-                        return rangeFor(ch, m);
+                    if (has(CASE_INSENSITIVE)) {
+                        if (has(UNICODE_CASE))
+                            return CIRangeU(ch, m);
+                        return CIRange(ch, m);
+                    } else {
+                        return Range(ch, m);
+                    }
                 }
             }
             return bitsOrSingle(bits, ch);
@@ -2710,12 +2805,10 @@
     /**
      * Parses a Unicode character family and returns its representative node.
      */
-    private CharProperty family(boolean singleLetter,
-                                boolean maybeComplement)
-    {
+    private CharPredicate family(boolean singleLetter, boolean isComplement) {
         next();
         String name;
-        CharProperty node = null;
+        CharPredicate p = null;
 
         if (singleLetter) {
             int c = temp[cursor];
@@ -2747,88 +2840,62 @@
             switch (name) {
                 case "sc":
                 case "script":
-                    node = unicodeScriptPropertyFor(value);
+                    p = CharPredicates.forUnicodeScript(value);
                     break;
                 case "blk":
                 case "block":
-                    node = unicodeBlockPropertyFor(value);
+                    p = CharPredicates.forUnicodeBlock(value);
                     break;
                 case "gc":
                 case "general_category":
-                    node = charPropertyNodeFor(value);
+                    p = CharPredicates.forProperty(value);
                     break;
                 default:
-                    throw error("Unknown Unicode property {name=<" + name + ">, "
-                                + "value=<" + value + ">}");
+                    break;
             }
+            if (p == null)
+                throw error("Unknown Unicode property {name=<" + name + ">, "
+                             + "value=<" + value + ">}");
+
         } else {
             if (name.startsWith("In")) {
-                // \p{inBlockName}
-                node = unicodeBlockPropertyFor(name.substring(2));
+                // \p{InBlockName}
+                p = CharPredicates.forUnicodeBlock(name.substring(2));
             } else if (name.startsWith("Is")) {
-                // \p{isGeneralCategory} and \p{isScriptName}
+                // \p{IsGeneralCategory} and \p{IsScriptName}
                 name = name.substring(2);
-                UnicodeProp uprop = UnicodeProp.forName(name);
-                if (uprop != null)
-                    node = new Utype(uprop);
-                if (node == null)
-                    node = CharPropertyNames.charPropertyFor(name);
-                if (node == null)
-                    node = unicodeScriptPropertyFor(name);
+                p = CharPredicates.forUnicodeProperty(name);
+                if (p == null)
+                    p = CharPredicates.forProperty(name);
+                if (p == null)
+                    p = CharPredicates.forUnicodeScript(name);
             } else {
                 if (has(UNICODE_CHARACTER_CLASS)) {
-                    UnicodeProp uprop = UnicodeProp.forPOSIXName(name);
-                    if (uprop != null)
-                        node = new Utype(uprop);
+                    p = CharPredicates.forPOSIXName(name);
                 }
-                if (node == null)
-                    node = charPropertyNodeFor(name);
+                if (p == null)
+                    p = CharPredicates.forProperty(name);
             }
-        }
-        if (maybeComplement) {
-            if (node instanceof Category || node instanceof Block)
-                hasSupplementary = true;
-            node = node.complement();
-        }
-        return node;
+            if (p == null)
+                throw error("Unknown character property name {In/Is" + name + "}");
+        }
+        if (isComplement) {
+            // it might be too expensive to detect if a complement of
+            // CharProperty can match "certain" supplementary. So just
+            // go with StartS.
+            hasSupplementary = true;
+            p = p.negate();
+        }
+        return p;
     }
 
-
-    /**
-     * Returns a CharProperty matching all characters belong to
-     * a UnicodeScript.
-     */
-    private CharProperty unicodeScriptPropertyFor(String name) {
-        final Character.UnicodeScript script;
-        try {
-            script = Character.UnicodeScript.forName(name);
-        } catch (IllegalArgumentException iae) {
-            throw error("Unknown character script name {" + name + "}");
-        }
-        return new Script(script);
-    }
-
-    /**
-     * Returns a CharProperty matching all characters in a UnicodeBlock.
-     */
-    private CharProperty unicodeBlockPropertyFor(String name) {
-        final Character.UnicodeBlock block;
-        try {
-            block = Character.UnicodeBlock.forName(name);
-        } catch (IllegalArgumentException iae) {
-            throw error("Unknown character block name {" + name + "}");
-        }
-        return new Block(block);
-    }
-
-    /**
-     * Returns a CharProperty matching all characters in a named property.
-     */
-    private CharProperty charPropertyNodeFor(String name) {
-        CharProperty p = CharPropertyNames.charPropertyFor(name);
+    private CharProperty newCharProperty(CharPredicate p) {
         if (p == null)
-            throw error("Unknown character property name {" + name + "}");
-        return p;
+            return null;
+        if (p instanceof BmpCharPredicate)
+            return new BmpCharProperty((BmpCharPredicate)p);
+        else
+            return new CharProperty(p);
     }
 
     /**
@@ -2859,6 +2926,7 @@
         Node head = null;
         Node tail = null;
         int save = flags;
+        int saveTCNCount = topClosureNodes.size();
         root = null;
         int ch = next();
         if (ch == '?') {
@@ -2884,7 +2952,7 @@
                 head = createGroup(true);
                 tail = root;
                 head.next = expr(tail);
-                head = tail = new Ques(head, INDEPENDENT);
+                head = tail = new Ques(head, Qtype.INDEPENDENT);
                 break;
             case '<':   // (?<xxx)  look behind
                 ch = read();
@@ -2928,6 +2996,9 @@
                 } else {
                     throw error("Unknown look-behind group");
                 }
+                // clear all top-closure-nodes inside lookbehind
+                if (saveTCNCount < topClosureNodes.size())
+                    topClosureNodes.subList(saveTCNCount, topClosureNodes.size()).clear();
                 break;
             case '$':
             case '@':
@@ -2968,15 +3039,20 @@
             return node;    // Dual return
         }
 
+        // have group closure, clear all inner closure nodes from the
+        // top list (no backtracking stopper optimization for inner
+        if (saveTCNCount < topClosureNodes.size())
+            topClosureNodes.subList(saveTCNCount, topClosureNodes.size()).clear();
+
         if (node instanceof Ques) {
             Ques ques = (Ques) node;
-            if (ques.type == POSSESSIVE) {
+            if (ques.type == Qtype.POSSESSIVE) {
                 root = node;
                 return node;
             }
             tail.next = new BranchConn();
             tail = tail.next;
-            if (ques.type == GREEDY) {
+            if (ques.type == Qtype.GREEDY) {
                 head = new Branch(head, null, tail);
             } else { // Reluctant quantifier
                 head = new Branch(null, head, tail);
@@ -2985,7 +3061,7 @@
             return head;
         } else if (node instanceof Curly) {
             Curly curly = (Curly) node;
-            if (curly.type == POSSESSIVE) {
+            if (curly.type == Qtype.POSSESSIVE) {
                 root = node;
                 return node;
             }
@@ -3002,10 +3078,14 @@
             } else { // Non-deterministic
                 int temp = ((GroupHead) head).localIndex;
                 Loop loop;
-                if (curly.type == GREEDY)
+                if (curly.type == Qtype.GREEDY) {
                     loop = new Loop(this.localCount, temp);
-                else  // Reluctant Curly
+                    // add the max_reps greedy to the top-closure-node list
+                    if (curly.cmax == MAX_REPS)
+                        topClosureNodes.add(loop);
+                } else {  // Reluctant Curly
                     loop = new LazyLoop(this.localCount, temp);
+                }
                 Prolog prolog = new Prolog(loop);
                 this.localCount += 1;
                 loop.cmin = curly.cmin;
@@ -3031,6 +3111,10 @@
             groupIndex = capturingGroupCount++;
         GroupHead head = new GroupHead(localIndex);
         root = new GroupTail(localIndex, groupIndex);
+
+        // for debug/print only, head.match does NOT need the "tail" info
+        head.tail = (GroupTail)root;
+
         if (!anonymous && groupIndex < 10)
             groupNodes[groupIndex] = head;
         return head;
@@ -3119,13 +3203,26 @@
 
     static final int MAX_REPS   = 0x7FFFFFFF;
 
-    static final int GREEDY     = 0;
-
-    static final int LAZY       = 1;
-
-    static final int POSSESSIVE = 2;
-
-    static final int INDEPENDENT = 3;
+    static enum Qtype {
+        GREEDY, LAZY, POSSESSIVE, INDEPENDENT
+    }
+
+    private Node curly(Node prev, int cmin) {
+        int ch = next();
+        if (ch == '?') {
+            next();
+            return new Curly(prev, cmin, MAX_REPS, Qtype.LAZY);
+        } else if (ch == '+') {
+            next();
+            return new Curly(prev, cmin, MAX_REPS, Qtype.POSSESSIVE);
+        }
+        if (prev instanceof BmpCharProperty) {
+            return new BmpCharPropertyGreedy((BmpCharProperty)prev, cmin);
+        } else if (prev instanceof CharProperty) {
+            return new CharPropertyGreedy((CharProperty)prev, cmin);
+        }
+        return new Curly(prev, cmin, MAX_REPS, Qtype.GREEDY);
+    }
 
     /**
      * Processes repetition. If the next character peeked is a quantifier
@@ -3140,32 +3237,16 @@
             ch = next();
             if (ch == '?') {
                 next();
-                return new Ques(prev, LAZY);
+                return new Ques(prev, Qtype.LAZY);
             } else if (ch == '+') {
                 next();
-                return new Ques(prev, POSSESSIVE);
+                return new Ques(prev, Qtype.POSSESSIVE);
             }
-            return new Ques(prev, GREEDY);
+            return new Ques(prev, Qtype.GREEDY);
         case '*':
-            ch = next();
-            if (ch == '?') {
-                next();
-                return new Curly(prev, 0, MAX_REPS, LAZY);
-            } else if (ch == '+') {
-                next();
-                return new Curly(prev, 0, MAX_REPS, POSSESSIVE);
-            }
-            return new Curly(prev, 0, MAX_REPS, GREEDY);
+            return curly(prev, 0);
         case '+':
-            ch = next();
-            if (ch == '?') {
-                next();
-                return new Curly(prev, 1, MAX_REPS, LAZY);
-            } else if (ch == '+') {
-                next();
-                return new Curly(prev, 1, MAX_REPS, POSSESSIVE);
-            }
-            return new Curly(prev, 1, MAX_REPS, GREEDY);
+            return curly(prev, 1);
         case '{':
             ch = temp[cursor+1];
             if (ASCII.isDigit(ch)) {
@@ -3194,12 +3275,12 @@
                 ch = peek();
                 if (ch == '?') {
                     next();
-                    curly = new Curly(prev, cmin, cmax, LAZY);
+                    curly = new Curly(prev, cmin, cmax, Qtype.LAZY);
                 } else if (ch == '+') {
                     next();
-                    curly = new Curly(prev, cmin, cmax, POSSESSIVE);
+                    curly = new Curly(prev, cmin, cmax, Qtype.POSSESSIVE);
                 } else {
-                    curly = new Curly(prev, cmin, cmax, GREEDY);
+                    curly = new Curly(prev, cmin, cmax, Qtype.GREEDY);
                 }
                 return curly;
             } else {
@@ -3376,10 +3457,15 @@
      *  never matches values above Latin-1, and a complemented BitClass always
      *  matches values above Latin-1.
      */
-    private static final class BitClass extends BmpCharProperty {
+    static final class BitClass extends BmpCharProperty {
         final boolean[] bits;
-        BitClass() { bits = new boolean[256]; }
-        private BitClass(boolean[] bits) { this.bits = bits; }
+        BitClass() {
+            this(new boolean[256]);
+        }
+        private BitClass(boolean[] bits) {
+            super( ch -> ch < 256 && bits[ch]);
+            this.bits = bits;
+        }
         BitClass add(int c, int flags) {
             assert c >= 0 && c <= 255;
             if ((flags & CASE_INSENSITIVE) != 0) {
@@ -3394,32 +3480,6 @@
             bits[c] = true;
             return this;
         }
-        boolean isSatisfiedBy(int ch) {
-            return ch < 256 && bits[ch];
-        }
-    }
-
-    /**
-     *  Returns a suitably optimized, single character matcher.
-     */
-    private CharProperty newSingle(final int ch) {
-        if (has(CASE_INSENSITIVE)) {
-            int lower, upper;
-            if (has(UNICODE_CASE)) {
-                upper = Character.toUpperCase(ch);
-                lower = Character.toLowerCase(upper);
-                if (upper != lower)
-                    return new SingleU(lower);
-            } else if (ASCII.isAscii(ch)) {
-                lower = ASCII.toLower(ch);
-                upper = ASCII.toUpper(ch);
-                if (lower != upper)
-                    return new SingleI(lower, upper);
-            }
-        }
-        if (isSupplementary(ch))
-            return new SingleS(ch);    // Match a given Unicode character
-        return new Single(ch);         // Match a given BMP character
     }
 
     /**
@@ -3827,18 +3887,17 @@
      * Abstract node class to match one character satisfying some
      * boolean property.
      */
-    private abstract static class CharProperty extends Node {
-        abstract boolean isSatisfiedBy(int ch);
-        CharProperty complement() {
-            return new CharProperty() {
-                    boolean isSatisfiedBy(int ch) {
-                        return ! CharProperty.this.isSatisfiedBy(ch);}};
+    static class CharProperty extends Node {
+        CharPredicate predicate;
+
+        CharProperty (CharPredicate predicate) {
+            this.predicate = predicate;
         }
         boolean match(Matcher matcher, int i, CharSequence seq) {
             if (i < matcher.to) {
                 int ch = Character.codePointAt(seq, i);
-                return isSatisfiedBy(ch)
-                    && next.match(matcher, i+Character.charCount(ch), seq);
+                return predicate.is(ch) &&
+                       next.match(matcher, i + Character.charCount(ch), seq);
             } else {
                 matcher.hitEnd = true;
                 return false;
@@ -3855,11 +3914,14 @@
      * Optimized version of CharProperty that works only for
      * properties never satisfied by Supplementary characters.
      */
-    private abstract static class BmpCharProperty extends CharProperty {
+    private static class BmpCharProperty extends CharProperty {
+        BmpCharProperty (BmpCharPredicate predicate) {
+            super(predicate);
+        }
         boolean match(Matcher matcher, int i, CharSequence seq) {
             if (i < matcher.to) {
-                return isSatisfiedBy(seq.charAt(i))
-                    && next.match(matcher, i+1, seq);
+                return predicate.is(seq.charAt(i)) &&
+                       next.match(matcher, i + 1, seq);
             } else {
                 matcher.hitEnd = true;
                 return false;
@@ -3867,135 +3929,53 @@
         }
     }
 
-    /**
-     * Node class that matches a Supplementary Unicode character
-     */
-    static final class SingleS extends CharProperty {
-        final int c;
-        SingleS(int c) { this.c = c; }
-        boolean isSatisfiedBy(int ch) {
-            return ch == c;
-        }
-    }
-
-    /**
-     * Optimization -- matches a given BMP character
-     */
-    static final class Single extends BmpCharProperty {
-        final int c;
-        Single(int c) { this.c = c; }
-        boolean isSatisfiedBy(int ch) {
-            return ch == c;
-        }
-    }
-
-    /**
-     * Case insensitive matches a given BMP character
-     */
-    static final class SingleI extends BmpCharProperty {
-        final int lower;
-        final int upper;
-        SingleI(int lower, int upper) {
-            this.lower = lower;
-            this.upper = upper;
-        }
-        boolean isSatisfiedBy(int ch) {
-            return ch == lower || ch == upper;
-        }
-    }
-
-    /**
-     * Unicode case insensitive matches a given Unicode character
-     */
-    static final class SingleU extends CharProperty {
-        final int lower;
-        SingleU(int lower) {
-            this.lower = lower;
-        }
-        boolean isSatisfiedBy(int ch) {
-            return lower == ch ||
-                lower == Character.toLowerCase(Character.toUpperCase(ch));
-        }
-    }
-
-    /**
-     * Node class that matches a Unicode block.
-     */
-    static final class Block extends CharProperty {
-        final Character.UnicodeBlock block;
-        Block(Character.UnicodeBlock block) {
-            this.block = block;
-        }
-        boolean isSatisfiedBy(int ch) {
-            return block == Character.UnicodeBlock.of(ch);
-        }
-    }
-
-    /**
-     * Node class that matches a Unicode script
-     */
-    static final class Script extends CharProperty {
-        final Character.UnicodeScript script;
-        Script(Character.UnicodeScript script) {
-            this.script = script;
-        }
-        boolean isSatisfiedBy(int ch) {
-            return script == Character.UnicodeScript.of(ch);
-        }
-    }
-
-    /**
-     * Node class that matches a Unicode category.
-     */
-    static final class Category extends CharProperty {
-        final int typeMask;
-        Category(int typeMask) { this.typeMask = typeMask; }
-        boolean isSatisfiedBy(int ch) {
-            return (typeMask & (1 << Character.getType(ch))) != 0;
-        }
-    }
-
-    /**
-     * Node class that matches a Unicode "type"
-     */
-    static final class Utype extends CharProperty {
-        final UnicodeProp uprop;
-        Utype(UnicodeProp uprop) { this.uprop = uprop; }
-        boolean isSatisfiedBy(int ch) {
-            return uprop.is(ch);
-        }
-    }
-
-    /**
-     * Node class that matches a POSIX type.
-     */
-    static final class Ctype extends BmpCharProperty {
-        final int ctype;
-        Ctype(int ctype) { this.ctype = ctype; }
-        boolean isSatisfiedBy(int ch) {
-            return ch < 128 && ASCII.isType(ch, ctype);
-        }
-    }
-
-    /**
-     * Node class that matches a Perl vertical whitespace
-     */
-    static final class VertWS extends BmpCharProperty {
-        boolean isSatisfiedBy(int cp) {
-            return (cp >= 0x0A && cp <= 0x0D) ||
-                   cp == 0x85 || cp == 0x2028 || cp == 0x2029;
-        }
-    }
-
-    /**
-     * Node class that matches a Perl horizontal whitespace
-     */
-    static final class HorizWS extends BmpCharProperty {
-        boolean isSatisfiedBy(int cp) {
-            return cp == 0x09 || cp == 0x20 || cp == 0xa0 ||
-                   cp == 0x1680 || cp == 0x180e ||
-                   cp >= 0x2000 && cp <= 0x200a ||
-                   cp == 0x202f || cp == 0x205f || cp == 0x3000;
+    private static class NFCCharProperty extends Node {
+        CharPredicate predicate;
+        NFCCharProperty (CharPredicate predicate) {
+            this.predicate = predicate;
+        }
+
+        boolean match(Matcher matcher, int i, CharSequence seq) {
+            if (i < matcher.to) {
+                int ch0 = Character.codePointAt(seq, i);
+                int n = Character.charCount(ch0);
+                int j = i + n;
+                while (j < matcher.to) {
+                    int ch1 = Character.codePointAt(seq, j);
+                    if (Grapheme.isBoundary(ch0, ch1))
+                        break;
+                    ch0 = ch1;
+                    j += Character.charCount(ch1);
+                }
+                if (i + n == j) {    // single, assume nfc cp
+                    if (predicate.is(ch0))
+                        return next.match(matcher, j, seq);
+                } else {
+                    while (i + n < j) {
+                        String nfc = Normalizer.normalize(
+                            seq.toString().substring(i, j), Normalizer.Form.NFC);
+                        if (nfc.codePointCount(0, nfc.length()) == 1) {
+                            if (predicate.is(nfc.codePointAt(0)) &&
+                                next.match(matcher, j, seq)) {
+                                return true;
+                            }
+                        }
+
+                        ch0 = Character.codePointBefore(seq, j);
+                        j -= Character.charCount(ch0);
+                    }
+                }
+                if (j < matcher.to)
+                    return false;
+            }
+            matcher.hitEnd = true;
+            return false;
+        }
+
+        boolean study(TreeInfo info) {
+            info.minLength++;
+            info.deterministic = false;
+            return next.study(info);
         }
     }
 
@@ -4217,81 +4197,13 @@
         }
     }
 
-    private static boolean inRange(int lower, int ch, int upper) {
-        return lower <= ch && ch <= upper;
-    }
-
-    /**
-     * Returns node for matching characters within an explicit value range.
-     */
-    private static CharProperty rangeFor(final int lower,
-                                         final int upper) {
-        return new CharProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return inRange(lower, ch, upper);}};
-    }
-
-    /**
-     * Returns node for matching characters within an explicit value
-     * range in a case insensitive manner.
-     */
-    private CharProperty caseInsensitiveRangeFor(final int lower,
-                                                 final int upper) {
-        if (has(UNICODE_CASE))
-            return new CharProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    if (inRange(lower, ch, upper))
-                        return true;
-                    int up = Character.toUpperCase(ch);
-                    return inRange(lower, up, upper) ||
-                           inRange(lower, Character.toLowerCase(up), upper);}};
-        return new CharProperty() {
-            boolean isSatisfiedBy(int ch) {
-                return inRange(lower, ch, upper) ||
-                    ASCII.isAscii(ch) &&
-                        (inRange(lower, ASCII.toUpper(ch), upper) ||
-                         inRange(lower, ASCII.toLower(ch), upper));
-            }};
-    }
-
-    /**
-     * Implements the Unicode category ALL and the dot metacharacter when
-     * in dotall mode.
-     */
-    static final class All extends CharProperty {
-        boolean isSatisfiedBy(int ch) {
-            return true;
-        }
-    }
-
-    /**
-     * Node class for the dot metacharacter when dotall is not enabled.
-     */
-    static final class Dot extends CharProperty {
-        boolean isSatisfiedBy(int ch) {
-            return (ch != '\n' && ch != '\r'
-                    && (ch|1) != '\u2029'
-                    && ch != '\u0085');
-        }
-    }
-
-    /**
-     * Node class for the dot metacharacter when dotall is not enabled
-     * but UNIX_LINES is enabled.
-     */
-    static final class UnixDot extends CharProperty {
-        boolean isSatisfiedBy(int ch) {
-            return ch != '\n';
-        }
-    }
-
     /**
      * The 0 or 1 quantifier. This one class implements all three types.
      */
     static final class Ques extends Node {
         Node atom;
-        int type;
-        Ques(Node node, int type) {
+        Qtype type;
+        Ques(Node node, Qtype type) {
             this.atom = node;
             this.type = type;
         }
@@ -4311,7 +4223,7 @@
             }
         }
         boolean study(TreeInfo info) {
-            if (type != INDEPENDENT) {
+            if (type != Qtype.INDEPENDENT) {
                 int minL = info.minLength;
                 atom.study(info);
                 info.minLength = minL;
@@ -4325,17 +4237,90 @@
     }
 
     /**
+     * Handles the greedy style repetition with the minimum either be
+     * 0 or 1 and the maximum be MAX_REPS, for * and + quantifier.
+     */
+    static class CharPropertyGreedy extends Node {
+        final CharPredicate predicate;
+        final int cmin;
+
+        CharPropertyGreedy(CharProperty cp, int cmin) {
+            this.predicate = cp.predicate;
+            this.cmin = cmin;
+        }
+        boolean match(Matcher matcher, int i,  CharSequence seq) {
+            int n = 0;
+            int to = matcher.to;
+            // greedy, all the way down
+            while (i < to) {
+                int ch = Character.codePointAt(seq, i);
+                if (!predicate.is(ch))
+                   break;
+                i += Character.charCount(ch);
+                n++;
+            }
+            if (i >= to) {
+                matcher.hitEnd = true;
+            }
+            while (n >= cmin) {
+                if (next.match(matcher, i, seq))
+                    return true;
+                if (n == cmin)
+                    return false;
+                 // backing off if match fails
+                int ch = Character.codePointBefore(seq, i);
+                i -= Character.charCount(ch);
+                n--;
+            }
+            return false;
+        }
+
+        boolean study(TreeInfo info) {
+            info.minLength += cmin;
+            if (info.maxValid) {
+                info.maxLength += MAX_REPS;
+            }
+            info.deterministic = false;
+            return next.study(info);
+        }
+    }
+
+    static final class BmpCharPropertyGreedy extends CharPropertyGreedy {
+
+        BmpCharPropertyGreedy(BmpCharProperty bcp, int cmin) {
+            super(bcp, cmin);
+        }
+
+        boolean match(Matcher matcher, int i,  CharSequence seq) {
+            int n = 0;
+            int to = matcher.to;
+            while (i < to && predicate.is(seq.charAt(i))) {
+                i++; n++;
+            }
+            if (i >= to) {
+                matcher.hitEnd = true;
+            }
+            while (n >= cmin) {
+                if (next.match(matcher, i, seq))
+                    return true;
+                i--; n--;  // backing off if match fails
+            }
+            return false;
+        }
+    }
+
+    /**
      * Handles the curly-brace style repetition with a specified minimum and
      * maximum occurrences. The * quantifier is handled as a special case.
      * This class handles the three types.
      */
     static final class Curly extends Node {
         Node atom;
-        int type;
+        Qtype type;
         int cmin;
         int cmax;
 
-        Curly(Node node, int cmin, int cmax, int type) {
+        Curly(Node node, int cmin, int cmax, Qtype type) {
             this.atom = node;
             this.type = type;
             this.cmin = cmin;
@@ -4350,9 +4335,9 @@
                 }
                 return false;
             }
-            if (type == GREEDY)
+            if (type == Qtype.GREEDY)
                 return match0(matcher, i, j, seq);
-            else if (type == LAZY)
+            else if (type == Qtype.LAZY)
                 return match1(matcher, i, j, seq);
             else
                 return match2(matcher, i, j, seq);
@@ -4474,14 +4459,14 @@
      */
     static final class GroupCurly extends Node {
         Node atom;
-        int type;
+        Qtype type;
         int cmin;
         int cmax;
         int localIndex;
         int groupIndex;
         boolean capture;
 
-        GroupCurly(Node node, int cmin, int cmax, int type, int local,
+        GroupCurly(Node node, int cmin, int cmax, Qtype type, int local,
                    int group, boolean capture) {
             this.atom = node;
             this.type = type;
@@ -4521,9 +4506,9 @@
                 }
             }
             if (ret) {
-                if (type == GREEDY) {
+                if (type == Qtype.GREEDY) {
                     ret = match0(matcher, i, cmin, seq);
-                } else if (type == LAZY) {
+                } else if (type == Qtype.LAZY) {
                     ret = match1(matcher, i, cmin, seq);
                 } else {
                     ret = match2(matcher, i, cmin, seq);
@@ -4769,6 +4754,7 @@
      */
     static final class GroupHead extends Node {
         int localIndex;
+        GroupTail tail;    // for debug/print only, match does not need to know
         GroupHead(int localCount) {
             localIndex = localCount;
         }
@@ -4876,9 +4862,11 @@
         int countIndex; // local count index in matcher locals
         int beginIndex; // group beginning index
         int cmin, cmax;
+        int posIndex;
         Loop(int countIndex, int beginIndex) {
             this.countIndex = countIndex;
             this.beginIndex = beginIndex;
+            this.posIndex = -1;
         }
         boolean match(Matcher matcher, int i, CharSequence seq) {
             // Avoid infinite loop in zero-length case.
@@ -4901,14 +4889,25 @@
                 // This block is for after we have the minimum
                 // iterations required for the loop to match
                 if (count < cmax) {
+                    // Let's check if we have already tried and failed
+                    // at this starting position "i" in the past.
+                    // If yes, then just return false wihtout trying
+                    // again, to stop the exponential backtracking.
+                    if (posIndex != -1 &&
+                        matcher.localsPos[posIndex].contains(i)) {
+                        return next.match(matcher, i, seq);
+                    }
                     matcher.locals[countIndex] = count + 1;
                     boolean b = body.match(matcher, i, seq);
                     // If match failed we must backtrack, so
                     // the loop count should NOT be incremented
-                    if (!b)
-                        matcher.locals[countIndex] = count;
-                    else
+                    if (b)
                         return true;
+                    matcher.locals[countIndex] = count;
+                    // save the failed position
+                    if (posIndex != -1) {
+                        matcher.localsPos[posIndex].add(i);
+                    }
                 }
             }
             return next.match(matcher, i, seq);
@@ -4916,6 +4915,9 @@
         boolean matchInit(Matcher matcher, int i, CharSequence seq) {
             int save = matcher.locals[countIndex];
             boolean ret = false;
+            if (posIndex != -1 && matcher.localsPos[posIndex] == null) {
+                matcher.localsPos[posIndex] = new IntHashSet();
+            }
             if (0 < cmin) {
                 matcher.locals[countIndex] = 1;
                 ret = body.match(matcher, i, seq);
@@ -5362,36 +5364,6 @@
     }
 
     /**
-     * Returns the set union of two CharProperty nodes.
-     */
-    private static CharProperty union(final CharProperty lhs,
-                                      final CharProperty rhs) {
-        return new CharProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return lhs.isSatisfiedBy(ch) || rhs.isSatisfiedBy(ch);}};
-    }
-
-    /**
-     * Returns the set intersection of two CharProperty nodes.
-     */
-    private static CharProperty intersection(final CharProperty lhs,
-                                             final CharProperty rhs) {
-        return new CharProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return lhs.isSatisfiedBy(ch) && rhs.isSatisfiedBy(ch);}};
-    }
-
-    /**
-     * Returns the set difference of two CharProperty nodes.
-     */
-    private static CharProperty setDifference(final CharProperty lhs,
-                                              final CharProperty rhs) {
-        return new CharProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return ! rhs.isSatisfiedBy(ch) && lhs.isSatisfiedBy(ch);}};
-    }
-
-    /**
      * Handles word boundaries. Includes a field to allow this one class to
      * deal with the different types of word boundaries we can match. The word
      * characters include underscores, letters, and digits. Non spacing marks
@@ -5411,7 +5383,7 @@
         }
 
         boolean isWord(int ch) {
-            return useUWORD ? UnicodeProp.WORD.is(ch)
+            return useUWORD ? CharPredicates.WORD.is(ch)
                             : (ch == '_' || Character.isLetterOrDigit(ch));
         }
 
@@ -5657,214 +5629,154 @@
         }
     }
 
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
+    @FunctionalInterface
+    static interface CharPredicate {
+        boolean is(int ch);
+
+        default CharPredicate and(CharPredicate p) {
+            return ch -> is(ch) && p.is(ch);
+        }
+        default CharPredicate union(CharPredicate p) {
+            return ch -> is(ch) || p.is(ch);
+        }
+        default CharPredicate union(CharPredicate p1,
+                                    CharPredicate p2 ) {
+            return ch -> is(ch) || p1.is(ch) || p2.is(ch);
+        }
+        default CharPredicate negate() {
+            return ch -> !is(ch);
+        }
+    }
+
+    static interface BmpCharPredicate extends CharPredicate {
+
+        default CharPredicate and(CharPredicate p) {
+            if(p instanceof BmpCharPredicate)
+                return (BmpCharPredicate)(ch -> is(ch) && p.is(ch));
+            return ch -> is(ch) && p.is(ch);
+        }
+        default CharPredicate union(CharPredicate p) {
+            if (p instanceof BmpCharPredicate)
+                return (BmpCharPredicate)(ch -> is(ch) || p.is(ch));
+            return ch -> is(ch) || p.is(ch);
+        }
+        static CharPredicate union(CharPredicate... predicates) {
+            CharPredicate cp = ch -> {
+                for (CharPredicate p : predicates) {
+                    if (!p.is(ch))
+                        return false;
+                }
+                return true;
+            };
+            for (CharPredicate p : predicates) {
+                if (! (p instanceof BmpCharPredicate))
+                    return cp;
+            }
+            return (BmpCharPredicate)cp;
+        }
+    }
+
+    /**
+     * matches a Perl vertical whitespace
+     */
+    static BmpCharPredicate VertWS = cp ->
+        (cp >= 0x0A && cp <= 0x0D) || cp == 0x85 || cp == 0x2028 || cp == 0x2029;
+
+    /**
+     * matches a Perl horizontal whitespace
+     */
+    static BmpCharPredicate HorizWS = cp ->
+        cp == 0x09 || cp == 0x20 || cp == 0xa0 || cp == 0x1680 ||
+        cp == 0x180e || cp >= 0x2000 && cp <= 0x200a ||  cp == 0x202f ||
+        cp == 0x205f || cp == 0x3000;
+
+    /**
+     *  for the Unicode category ALL and the dot metacharacter when
+     *  in dotall mode.
+     */
+    static CharPredicate ALL = ch -> true;
+
+    /**
+     * for the dot metacharacter when dotall is not enabled.
+     */
+    static CharPredicate DOT = ch -> (ch != '\n' && ch != '\r'
+                                          && (ch|1) != '\u2029'
+                                          && ch != '\u0085');
+    /**
+     *  the dot metacharacter when dotall is not enabled but UNIX_LINES is enabled.
+     */
+    static CharPredicate UNIXDOT = ch ->  ch != '\n';
+
+    /**
+     * Indicate that matches a Supplementary Unicode character
+     */
+    static CharPredicate SingleS(int c) {
+        return ch -> ch == c;
+    }
+
+    /**
+     * A bmp/optimized predicate of single
+     */
+    static BmpCharPredicate Single(int c) {
+        return ch -> ch == c;
+    }
+
+    /**
+     * Case insensitive matches a given BMP character
+     */
+    static BmpCharPredicate SingleI(int lower, int upper) {
+        return ch -> ch == lower || ch == upper;
+    }
+
+    /**
+     * Unicode case insensitive matches a given Unicode character
+     */
+    static CharPredicate SingleU(int lower) {
+        return ch -> lower == ch ||
+                     lower == Character.toLowerCase(Character.toUpperCase(ch));
+    }
+
+    private static boolean inRange(int lower, int ch, int upper) {
+        return lower <= ch && ch <= upper;
+    }
+
+    /**
+     * Charactrs within a explicit value range
+     */
+    static CharPredicate Range(int lower, int upper) {
+        if (upper < Character.MIN_HIGH_SURROGATE ||
+            lower > Character.MAX_HIGH_SURROGATE &&
+            upper < Character.MIN_SUPPLEMENTARY_CODE_POINT)
+            return (BmpCharPredicate)(ch -> inRange(lower, ch, upper));
+        return ch -> inRange(lower, ch, upper);
+    }
+
+   /**
+    * Charactrs within a explicit value range in a case insensitive manner.
+    */
+    static CharPredicate CIRange(int lower, int upper) {
+        return ch -> inRange(lower, ch, upper) ||
+                     ASCII.isAscii(ch) &&
+                     (inRange(lower, ASCII.toUpper(ch), upper) ||
+                      inRange(lower, ASCII.toLower(ch), upper));
+    }
+
+    static CharPredicate CIRangeU(int lower, int upper) {
+        return ch -> {
+            if (inRange(lower, ch, upper))
+                return true;
+            int up = Character.toUpperCase(ch);
+            return inRange(lower, up, upper) ||
+                   inRange(lower, Character.toLowerCase(up), upper);
+        };
+    }
 
     /**
      *  This must be the very first initializer.
      */
-    static Node accept = new Node();
-
-    static Node lastAccept = new LastNode();
-
-    private static class CharPropertyNames {
-
-        static CharProperty charPropertyFor(String name) {
-            CharPropertyFactory m = map.get(name);
-            return m == null ? null : m.make();
-        }
-
-        private abstract static class CharPropertyFactory {
-            abstract CharProperty make();
-        }
-
-        private static void defCategory(String name,
-                                        final int typeMask) {
-            map.put(name, new CharPropertyFactory() {
-                    CharProperty make() { return new Category(typeMask);}});
-        }
-
-        private static void defRange(String name,
-                                     final int lower, final int upper) {
-            map.put(name, new CharPropertyFactory() {
-                    CharProperty make() { return rangeFor(lower, upper);}});
-        }
-
-        private static void defCtype(String name,
-                                     final int ctype) {
-            map.put(name, new CharPropertyFactory() {
-                    CharProperty make() { return new Ctype(ctype);}});
-        }
-
-        private abstract static class CloneableProperty
-            extends CharProperty implements Cloneable
-        {
-            public CloneableProperty clone() {
-                try {
-                    return (CloneableProperty) super.clone();
-                } catch (CloneNotSupportedException e) {
-                    throw new AssertionError(e);
-                }
-            }
-        }
-
-        private static void defClone(String name,
-                                     final CloneableProperty p) {
-            map.put(name, new CharPropertyFactory() {
-                    CharProperty make() { return p.clone();}});
-        }
-
-        private static final HashMap<String, CharPropertyFactory> map
-            = new HashMap<>();
-
-        static {
-            // Unicode character property aliases, defined in
-            // http://www.unicode.org/Public/UNIDATA/PropertyValueAliases.txt
-            defCategory("Cn", 1<<Character.UNASSIGNED);
-            defCategory("Lu", 1<<Character.UPPERCASE_LETTER);
-            defCategory("Ll", 1<<Character.LOWERCASE_LETTER);
-            defCategory("Lt", 1<<Character.TITLECASE_LETTER);
-            defCategory("Lm", 1<<Character.MODIFIER_LETTER);
-            defCategory("Lo", 1<<Character.OTHER_LETTER);
-            defCategory("Mn", 1<<Character.NON_SPACING_MARK);
-            defCategory("Me", 1<<Character.ENCLOSING_MARK);
-            defCategory("Mc", 1<<Character.COMBINING_SPACING_MARK);
-            defCategory("Nd", 1<<Character.DECIMAL_DIGIT_NUMBER);
-            defCategory("Nl", 1<<Character.LETTER_NUMBER);
-            defCategory("No", 1<<Character.OTHER_NUMBER);
-            defCategory("Zs", 1<<Character.SPACE_SEPARATOR);
-            defCategory("Zl", 1<<Character.LINE_SEPARATOR);
-            defCategory("Zp", 1<<Character.PARAGRAPH_SEPARATOR);
-            defCategory("Cc", 1<<Character.CONTROL);
-            defCategory("Cf", 1<<Character.FORMAT);
-            defCategory("Co", 1<<Character.PRIVATE_USE);
-            defCategory("Cs", 1<<Character.SURROGATE);
-            defCategory("Pd", 1<<Character.DASH_PUNCTUATION);
-            defCategory("Ps", 1<<Character.START_PUNCTUATION);
-            defCategory("Pe", 1<<Character.END_PUNCTUATION);
-            defCategory("Pc", 1<<Character.CONNECTOR_PUNCTUATION);
-            defCategory("Po", 1<<Character.OTHER_PUNCTUATION);
-            defCategory("Sm", 1<<Character.MATH_SYMBOL);
-            defCategory("Sc", 1<<Character.CURRENCY_SYMBOL);
-            defCategory("Sk", 1<<Character.MODIFIER_SYMBOL);
-            defCategory("So", 1<<Character.OTHER_SYMBOL);
-            defCategory("Pi", 1<<Character.INITIAL_QUOTE_PUNCTUATION);
-            defCategory("Pf", 1<<Character.FINAL_QUOTE_PUNCTUATION);
-            defCategory("L", ((1<<Character.UPPERCASE_LETTER) |
-                              (1<<Character.LOWERCASE_LETTER) |
-                              (1<<Character.TITLECASE_LETTER) |
-                              (1<<Character.MODIFIER_LETTER)  |
-                              (1<<Character.OTHER_LETTER)));
-            defCategory("M", ((1<<Character.NON_SPACING_MARK) |
-                              (1<<Character.ENCLOSING_MARK)   |
-                              (1<<Character.COMBINING_SPACING_MARK)));
-            defCategory("N", ((1<<Character.DECIMAL_DIGIT_NUMBER) |
-                              (1<<Character.LETTER_NUMBER)        |
-                              (1<<Character.OTHER_NUMBER)));
-            defCategory("Z", ((1<<Character.SPACE_SEPARATOR) |
-                              (1<<Character.LINE_SEPARATOR)  |
-                              (1<<Character.PARAGRAPH_SEPARATOR)));
-            defCategory("C", ((1<<Character.CONTROL)     |
-                              (1<<Character.FORMAT)      |
-                              (1<<Character.PRIVATE_USE) |
-                              (1<<Character.SURROGATE))); // Other
-            defCategory("P", ((1<<Character.DASH_PUNCTUATION)      |
-                              (1<<Character.START_PUNCTUATION)     |
-                              (1<<Character.END_PUNCTUATION)       |
-                              (1<<Character.CONNECTOR_PUNCTUATION) |
-                              (1<<Character.OTHER_PUNCTUATION)     |
-                              (1<<Character.INITIAL_QUOTE_PUNCTUATION) |
-                              (1<<Character.FINAL_QUOTE_PUNCTUATION)));
-            defCategory("S", ((1<<Character.MATH_SYMBOL)     |
-                              (1<<Character.CURRENCY_SYMBOL) |
-                              (1<<Character.MODIFIER_SYMBOL) |
-                              (1<<Character.OTHER_SYMBOL)));
-            defCategory("LC", ((1<<Character.UPPERCASE_LETTER) |
-                               (1<<Character.LOWERCASE_LETTER) |
-                               (1<<Character.TITLECASE_LETTER)));
-            defCategory("LD", ((1<<Character.UPPERCASE_LETTER) |
-                               (1<<Character.LOWERCASE_LETTER) |
-                               (1<<Character.TITLECASE_LETTER) |
-                               (1<<Character.MODIFIER_LETTER)  |
-                               (1<<Character.OTHER_LETTER)     |
-                               (1<<Character.DECIMAL_DIGIT_NUMBER)));
-            defRange("L1", 0x00, 0xFF); // Latin-1
-            map.put("all", new CharPropertyFactory() {
-                    CharProperty make() { return new All(); }});
-
-            // Posix regular expression character classes, defined in
-            // http://www.unix.org/onlinepubs/009695399/basedefs/xbd_chap09.html
-            defRange("ASCII", 0x00, 0x7F);   // ASCII
-            defCtype("Alnum", ASCII.ALNUM);  // Alphanumeric characters
-            defCtype("Alpha", ASCII.ALPHA);  // Alphabetic characters
-            defCtype("Blank", ASCII.BLANK);  // Space and tab characters
-            defCtype("Cntrl", ASCII.CNTRL);  // Control characters
-            defRange("Digit", '0', '9');     // Numeric characters
-            defCtype("Graph", ASCII.GRAPH);  // printable and visible
-            defRange("Lower", 'a', 'z');     // Lower-case alphabetic
-            defRange("Print", 0x20, 0x7E);   // Printable characters
-            defCtype("Punct", ASCII.PUNCT);  // Punctuation characters
-            defCtype("Space", ASCII.SPACE);  // Space characters
-            defRange("Upper", 'A', 'Z');     // Upper-case alphabetic
-            defCtype("XDigit",ASCII.XDIGIT); // hexadecimal digits
-
-            // Java character properties, defined by methods in Character.java
-            defClone("javaLowerCase", new CloneableProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return Character.isLowerCase(ch);}});
-            defClone("javaUpperCase", new CloneableProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return Character.isUpperCase(ch);}});
-            defClone("javaAlphabetic", new CloneableProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return Character.isAlphabetic(ch);}});
-            defClone("javaIdeographic", new CloneableProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return Character.isIdeographic(ch);}});
-            defClone("javaTitleCase", new CloneableProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return Character.isTitleCase(ch);}});
-            defClone("javaDigit", new CloneableProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return Character.isDigit(ch);}});
-            defClone("javaDefined", new CloneableProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return Character.isDefined(ch);}});
-            defClone("javaLetter", new CloneableProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return Character.isLetter(ch);}});
-            defClone("javaLetterOrDigit", new CloneableProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return Character.isLetterOrDigit(ch);}});
-            defClone("javaJavaIdentifierStart", new CloneableProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return Character.isJavaIdentifierStart(ch);}});
-            defClone("javaJavaIdentifierPart", new CloneableProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return Character.isJavaIdentifierPart(ch);}});
-            defClone("javaUnicodeIdentifierStart", new CloneableProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return Character.isUnicodeIdentifierStart(ch);}});
-            defClone("javaUnicodeIdentifierPart", new CloneableProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return Character.isUnicodeIdentifierPart(ch);}});
-            defClone("javaIdentifierIgnorable", new CloneableProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return Character.isIdentifierIgnorable(ch);}});
-            defClone("javaSpaceChar", new CloneableProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return Character.isSpaceChar(ch);}});
-            defClone("javaWhitespace", new CloneableProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return Character.isWhitespace(ch);}});
-            defClone("javaISOControl", new CloneableProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return Character.isISOControl(ch);}});
-            defClone("javaMirrored", new CloneableProperty() {
-                boolean isSatisfiedBy(int ch) {
-                    return Character.isMirrored(ch);}});
-        }
-    }
+    static final Node accept = new Node();
+
+    static final Node lastAccept = new LastNode();
 
     /**
      * Creates a predicate which can be used to match a string.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/util/regex/PrintPattern.java	Thu May 12 18:47:08 2016 +0000
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2016, 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 java.util.regex;
+
+import java.util.HashMap;
+import java.util.regex.Pattern.CharPredicate;
+import java.util.regex.CharPredicates;
+import static java.util.regex.ASCII.*;
+
+/**
+ * A utility class to print out the pattern node tree.
+ */
+
+class PrintPattern {
+
+    private static HashMap<Pattern.Node, Integer> ids = new HashMap<>();
+
+    private static void print(Pattern.Node node, String text, int depth) {
+        if (!ids.containsKey(node))
+            ids.put(node, ids.size());
+        print("%6d:%" + (depth==0? "": depth<<1) + "s<%s>", ids.get(node), "", text);
+        if (ids.containsKey(node.next))
+            print(" (=>%d)", ids.get(node.next));
+        print("%n");
+    }
+
+    private static void print(String s, int depth) {
+        print("       %" + (depth==0?"":depth<<1) + "s<%s>%n", "", s);
+    }
+
+    private static void print(String fmt, Object ... args) {
+        System.err.printf(fmt, args);
+    }
+
+    private static String toStringCPS(int[] cps) {
+        StringBuilder sb = new StringBuilder(cps.length);
+        for (int cp : cps)
+            sb.append(toStringCP(cp));
+        return sb.toString();
+    }
+
+    private static String toStringCP(int cp) {
+        return (isPrint(cp) ? "" + (char)cp
+                            : "\\u" + Integer.toString(cp, 16));
+    }
+
+    private static String toStringRange(int min, int max) {
+       if (max == Pattern.MAX_REPS) {
+           if (min == 0)
+               return " * ";
+           else if (min == 1)
+               return " + ";
+           return "{" + min + ", max}";
+       }
+       return "{" + min + ", " +  max + "}";
+    }
+
+    private static String toStringCtype(int type) {
+        switch(type) {
+        case UPPER:  return "ASCII.UPPER";
+        case LOWER:  return "ASCII.LOWER";
+        case DIGIT:  return "ASCII.DIGIT";
+        case SPACE:  return "ASCII.SPACE";
+        case PUNCT:  return "ASCII.PUNCT";
+        case CNTRL:  return "ASCII.CNTRL";
+        case BLANK:  return "ASCII.BLANK";
+        case UNDER:  return "ASCII.UNDER";
+        case ASCII:  return "ASCII.ASCII";
+        case ALPHA:  return "ASCII.ALPHA";
+        case ALNUM:  return "ASCII.ALNUM";
+        case GRAPH:  return "ASCII.GRAPH";
+        case WORD:   return "ASCII.WORD";
+        case XDIGIT: return "ASCII.XDIGIT";
+        default: return "ASCII ?";
+        }
+    }
+
+    private static String toString(Pattern.Node node) {
+        String name = node.getClass().getName();
+        return name.substring(name.lastIndexOf('$') + 1);
+    }
+
+    static HashMap<CharPredicate, String> pmap;
+    static {
+        pmap = new HashMap<>();
+        pmap.put(Pattern.ALL, "All");
+        pmap.put(Pattern.DOT, "Dot");
+        pmap.put(Pattern.UNIXDOT, "UnixDot");
+        pmap.put(Pattern.VertWS, "VertWS");
+        pmap.put(Pattern.HorizWS, "HorizWS");
+
+        pmap.put(CharPredicates.ASCII_DIGIT, "ASCII.DIGIT");
+        pmap.put(CharPredicates.ASCII_WORD,  "ASCII.WORD");
+        pmap.put(CharPredicates.ASCII_SPACE, "ASCII.SPACE");
+    }
+
+    static void walk(Pattern.Node node, int depth) {
+        depth++;
+        while(node != null) {
+            String name = toString(node);
+            String str;
+            if (node instanceof Pattern.Prolog) {
+                print(node, name, depth);
+                // print the loop here
+                Pattern.Loop loop = ((Pattern.Prolog)node).loop;
+                name = toString(loop);
+                str = name + " " + toStringRange(loop.cmin, loop.cmax);
+                print(loop, str, depth);
+                walk(loop.body, depth);
+                print("/" + name, depth);
+                node = loop;
+            } else if (node instanceof Pattern.Loop) {
+                return;  // stop here, body.next -> loop
+            } else if (node instanceof Pattern.Curly) {
+                Pattern.Curly c = (Pattern.Curly)node;
+                str = "Curly " + c.type + " " + toStringRange(c.cmin, c.cmax);
+                print(node, str, depth);
+                walk(c.atom, depth);
+                print("/Curly", depth);
+            } else if (node instanceof Pattern.GroupCurly) {
+                Pattern.GroupCurly gc = (Pattern.GroupCurly)node;
+                str = "GroupCurly " + gc.groupIndex / 2 +
+                      ", " + gc.type + " " + toStringRange(gc.cmin, gc.cmax);
+                print(node, str, depth);
+                walk(gc.atom, depth);
+                print("/GroupCurly", depth);
+            } else if (node instanceof Pattern.GroupHead) {
+                Pattern.GroupHead head = (Pattern.GroupHead)node;
+                Pattern.GroupTail tail = head.tail;
+                print(head, "Group.head " + (tail.groupIndex / 2), depth);
+                walk(head.next, depth);
+                print(tail, "/Group.tail " + (tail.groupIndex / 2), depth);
+                node = tail;
+            } else if (node instanceof Pattern.GroupTail) {
+                return;  // stopper
+            } else if (node instanceof Pattern.Ques) {
+                print(node, "Ques " + ((Pattern.Ques)node).type, depth);
+                walk(((Pattern.Ques)node).atom, depth);
+                print("/Ques", depth);
+            } else if (node instanceof Pattern.Branch) {
+                Pattern.Branch b = (Pattern.Branch)node;
+                print(b, name, depth);
+                int i = 0;
+                while (true) {
+                    if (b.atoms[i] != null) {
+                        walk(b.atoms[i], depth);
+                    } else {
+                        print("  (accepted)", depth);
+                    }
+                    if (++i == b.size)
+                        break;
+                    print("-branch.separator-", depth);
+                }
+                node = b.conn;
+                print(node, "/Branch", depth);
+            } else if (node instanceof Pattern.BranchConn) {
+                return;
+            } else if (node instanceof Pattern.CharProperty) {
+                str = pmap.get(((Pattern.CharProperty)node).predicate);
+                if (str == null)
+                    str = toString(node);
+                else
+                    str = "Single \"" + str + "\"";
+                print(node, str, depth);
+            } else if (node instanceof Pattern.SliceNode) {
+                str = name + "  \"" +
+                      toStringCPS(((Pattern.SliceNode)node).buffer) + "\"";
+                print(node, str, depth);
+            } else if (node instanceof Pattern.CharPropertyGreedy) {
+                Pattern.CharPropertyGreedy gcp = (Pattern.CharPropertyGreedy)node;
+                String pstr = pmap.get(gcp.predicate);
+                if (pstr == null)
+                    pstr = gcp.predicate.toString();
+                else
+                    pstr = "Single \"" + pstr + "\"";
+                str = name + " " + pstr + ((gcp.cmin == 0) ? "*" : "+");
+                print(node, str, depth);
+            } else if (node instanceof Pattern.BackRef) {
+                str = "GroupBackRef " + ((Pattern.BackRef)node).groupIndex / 2;
+                print(node, str, depth);
+            } else if (node instanceof Pattern.LastNode) {
+                print(node, "END", depth);
+            } else if (node == Pattern.accept) {
+                return;
+            } else {
+                print(node, name, depth);
+            }
+            node = node.next;
+        }
+    }
+
+    public static void main(String[] args) {
+        Pattern p = Pattern.compile(args[0]);
+        System.out.println("   Pattern: " + p);
+        walk(p.root, 0);
+    }
+}
--- a/src/java.base/share/classes/java/util/regex/UnicodeProp.java	Thu May 12 15:15:04 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,246 +0,0 @@
-/*
- * Copyright (c) 2011, 2013, 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 java.util.regex;
-
-import java.util.HashMap;
-import java.util.Locale;
-
-enum UnicodeProp {
-
-    ALPHABETIC {
-        public boolean is(int ch) {
-            return Character.isAlphabetic(ch);
-        }
-    },
-
-    LETTER {
-        public boolean is(int ch) {
-            return Character.isLetter(ch);
-        }
-    },
-
-    IDEOGRAPHIC {
-        public boolean is(int ch) {
-            return Character.isIdeographic(ch);
-        }
-    },
-
-    LOWERCASE {
-        public boolean is(int ch) {
-            return Character.isLowerCase(ch);
-        }
-    },
-
-    UPPERCASE {
-        public boolean is(int ch) {
-            return Character.isUpperCase(ch);
-        }
-    },
-
-    TITLECASE {
-        public boolean is(int ch) {
-            return Character.isTitleCase(ch);
-        }
-    },
-
-    WHITE_SPACE {
-        // \p{Whitespace}
-        public boolean is(int ch) {
-            return ((((1 << Character.SPACE_SEPARATOR) |
-                      (1 << Character.LINE_SEPARATOR) |
-                      (1 << Character.PARAGRAPH_SEPARATOR)) >> Character.getType(ch)) & 1)
-                   != 0 || (ch >= 0x9 && ch <= 0xd) || (ch == 0x85);
-        }
-    },
-
-    CONTROL {
-        // \p{gc=Control}
-        public boolean is(int ch) {
-            return Character.getType(ch) == Character.CONTROL;
-        }
-    },
-
-    PUNCTUATION {
-        // \p{gc=Punctuation}
-        public boolean is(int ch) {
-            return ((((1 << Character.CONNECTOR_PUNCTUATION) |
-                      (1 << Character.DASH_PUNCTUATION) |
-                      (1 << Character.START_PUNCTUATION) |
-                      (1 << Character.END_PUNCTUATION) |
-                      (1 << Character.OTHER_PUNCTUATION) |
-                      (1 << Character.INITIAL_QUOTE_PUNCTUATION) |
-                      (1 << Character.FINAL_QUOTE_PUNCTUATION)) >> Character.getType(ch)) & 1)
-                   != 0;
-        }
-    },
-
-    HEX_DIGIT {
-        // \p{gc=Decimal_Number}
-        // \p{Hex_Digit}    -> PropList.txt: Hex_Digit
-        public boolean is(int ch) {
-            return DIGIT.is(ch) ||
-                   (ch >= 0x0030 && ch <= 0x0039) ||
-                   (ch >= 0x0041 && ch <= 0x0046) ||
-                   (ch >= 0x0061 && ch <= 0x0066) ||
-                   (ch >= 0xFF10 && ch <= 0xFF19) ||
-                   (ch >= 0xFF21 && ch <= 0xFF26) ||
-                   (ch >= 0xFF41 && ch <= 0xFF46);
-        }
-    },
-
-    ASSIGNED {
-        public boolean is(int ch) {
-            return Character.getType(ch) != Character.UNASSIGNED;
-        }
-    },
-
-    NONCHARACTER_CODE_POINT {
-        // PropList.txt:Noncharacter_Code_Point
-        public boolean is(int ch) {
-            return (ch & 0xfffe) == 0xfffe || (ch >= 0xfdd0 && ch <= 0xfdef);
-        }
-    },
-
-    DIGIT {
-        // \p{gc=Decimal_Number}
-        public boolean is(int ch) {
-            return Character.isDigit(ch);
-        }
-    },
-
-    ALNUM {
-        // \p{alpha}
-        // \p{digit}
-        public boolean is(int ch) {
-            return ALPHABETIC.is(ch) || DIGIT.is(ch);
-        }
-    },
-
-    BLANK {
-        // \p{Whitespace} --
-        // [\N{LF} \N{VT} \N{FF} \N{CR} \N{NEL}  -> 0xa, 0xb, 0xc, 0xd, 0x85
-        //  \p{gc=Line_Separator}
-        //  \p{gc=Paragraph_Separator}]
-        public boolean is(int ch) {
-            return Character.getType(ch) == Character.SPACE_SEPARATOR ||
-                   ch == 0x9; // \N{HT}
-        }
-    },
-
-    GRAPH {
-        // [^
-        //  \p{space}
-        //  \p{gc=Control}
-        //  \p{gc=Surrogate}
-        //  \p{gc=Unassigned}]
-        public boolean is(int ch) {
-            return ((((1 << Character.SPACE_SEPARATOR) |
-                      (1 << Character.LINE_SEPARATOR) |
-                      (1 << Character.PARAGRAPH_SEPARATOR) |
-                      (1 << Character.CONTROL) |
-                      (1 << Character.SURROGATE) |
-                      (1 << Character.UNASSIGNED)) >> Character.getType(ch)) & 1)
-                   == 0;
-        }
-    },
-
-    PRINT {
-        // \p{graph}
-        // \p{blank}
-        // -- \p{cntrl}
-        public boolean is(int ch) {
-            return (GRAPH.is(ch) || BLANK.is(ch)) && !CONTROL.is(ch);
-        }
-    },
-
-    WORD {
-        //  \p{alpha}
-        //  \p{gc=Mark}
-        //  \p{digit}
-        //  \p{gc=Connector_Punctuation}
-        //  \p{Join_Control}    200C..200D
-
-        public boolean is(int ch) {
-            return ALPHABETIC.is(ch) ||
-                   ((((1 << Character.NON_SPACING_MARK) |
-                      (1 << Character.ENCLOSING_MARK) |
-                      (1 << Character.COMBINING_SPACING_MARK) |
-                      (1 << Character.DECIMAL_DIGIT_NUMBER) |
-                      (1 << Character.CONNECTOR_PUNCTUATION)) >> Character.getType(ch)) & 1)
-                   != 0 ||
-                   JOIN_CONTROL.is(ch);
-        }
-    },
-
-    JOIN_CONTROL {
-        //  200C..200D    PropList.txt:Join_Control
-        public boolean is(int ch) {
-           return (ch == 0x200C || ch == 0x200D);
-        }
-    };
-
-    private static final HashMap<String, String> posix = new HashMap<>();
-    private static final HashMap<String, String> aliases = new HashMap<>();
-    static {
-        posix.put("ALPHA", "ALPHABETIC");
-        posix.put("LOWER", "LOWERCASE");
-        posix.put("UPPER", "UPPERCASE");
-        posix.put("SPACE", "WHITE_SPACE");
-        posix.put("PUNCT", "PUNCTUATION");
-        posix.put("XDIGIT","HEX_DIGIT");
-        posix.put("ALNUM", "ALNUM");
-        posix.put("CNTRL", "CONTROL");
-        posix.put("DIGIT", "DIGIT");
-        posix.put("BLANK", "BLANK");
-        posix.put("GRAPH", "GRAPH");
-        posix.put("PRINT", "PRINT");
-
-        aliases.put("WHITESPACE", "WHITE_SPACE");
-        aliases.put("HEXDIGIT","HEX_DIGIT");
-        aliases.put("NONCHARACTERCODEPOINT", "NONCHARACTER_CODE_POINT");
-        aliases.put("JOINCONTROL", "JOIN_CONTROL");
-    }
-
-    public static UnicodeProp forName(String propName) {
-        propName = propName.toUpperCase(Locale.ENGLISH);
-        String alias = aliases.get(propName);
-        if (alias != null)
-            propName = alias;
-        try {
-            return valueOf (propName);
-        } catch (IllegalArgumentException x) {}
-        return null;
-    }
-
-    public static UnicodeProp forPOSIXName(String propName) {
-        propName = posix.get(propName.toUpperCase(Locale.ENGLISH));
-        if (propName == null)
-            return null;
-        return valueOf (propName);
-    }
-
-    public abstract boolean is(int ch);
-}
--- a/src/java.base/share/classes/javax/security/auth/callback/UnsupportedCallbackException.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/javax/security/auth/callback/UnsupportedCallbackException.java	Thu May 12 18:47:08 2016 +0000
@@ -40,7 +40,7 @@
     private Callback callback;
 
     /**
-     * Constructs a {@code UnsupportedCallbackException}
+     * Constructs an {@code UnsupportedCallbackException}
      * with no detail message.
      *
      * @param callback the unrecognized {@code Callback}.
--- a/src/java.base/share/classes/jdk/internal/misc/VM.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/jdk/internal/misc/VM.java	Thu May 12 18:47:08 2016 +0000
@@ -390,10 +390,25 @@
     private static final int JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT = 0x0020;
 
     /*
-     * Returns the first non-null class loader up the execution stack,
-     * or null if only code from the null class loader is on the stack.
+     * Returns the first user-defined class loader up the execution stack,
+     * or the platform class loader if only code from the platform or
+     * bootstrap class loader is on the stack.
      */
-    public static native ClassLoader latestUserDefinedLoader();
+    public static ClassLoader latestUserDefinedLoader() {
+        ClassLoader loader = latestUserDefinedLoader0();
+        return loader != null ? loader : ClassLoader.getPlatformClassLoader();
+    }
+
+    /*
+     * Returns the first user-defined class loader up the execution stack,
+     * or null if only code from the platform or bootstrap class loader is
+     * on the stack.  VM does not keep a reference of platform loader and so
+     * it returns null.
+     *
+     * This method should be replaced with StackWalker::walk and then we can
+     * remove the logic in the VM.
+     */
+    private static native ClassLoader latestUserDefinedLoader0();
 
     /**
      * Returns {@code true} if we are in a set UID program.
--- a/src/java.base/share/classes/sun/net/ftp/FtpClient.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/sun/net/ftp/FtpClient.java	Thu May 12 18:47:08 2016 +0000
@@ -784,7 +784,7 @@
      *
      * @param path the pathname of the directory to list or {@code null}
      *        for the current working directoty.
-     * @return a {@code Iterator} of files or {@code null} if the
+     * @return an {@code Iterator} of files or {@code null} if the
      *         command failed.
      * @throws IOException if an error occurred during the transmission
      * @see #setDirParser(FtpDirParser)
--- a/src/java.base/share/classes/sun/security/provider/AbstractDrbg.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/sun/security/provider/AbstractDrbg.java	Thu May 12 18:47:08 2016 +0000
@@ -51,12 +51,9 @@
  * configuration is eagerly called to set up parameters, and instantiation
  * is lazily called only when nextBytes or reseed is called.
  * <p>
- * Synchronized keyword should be added to all externally callable engine
- * methods including {@link #engineReseed}, {@link #engineSetSeed}, and
- * {@link #engineNextBytes} (but not {@link #engineGenerateSeed}).
- * Internal methods like {@link #configure} and {@link #instantiateAlgorithm}
- * are not synchronized. They will either be called in a constructor or
- * in another synchronized method.
+ * SecureRandom methods like reseed and nextBytes are not thread-safe.
+ * An implementation is required to protect shared access to instantiate states
+ * (instantiated, nonce) and DRBG states (v, c, key, reseedCounter).
  */
 public abstract class AbstractDrbg extends SecureRandomSpi {
 
@@ -78,8 +75,10 @@
      * Reseed counter of a DRBG instance. A mechanism should increment it
      * after each random bits generation and reset it in reseed. A mechanism
      * does <em>not</em> need to compare it to {@link #reseedInterval}.
+     *
+     * Volatile, will be used in a double checked locking.
      */
-    protected transient int reseedCounter = 0;
+    protected transient volatile int reseedCounter = 0;
 
     // Mech features. If not same as below, must be redefined in constructor.
 
@@ -268,10 +267,9 @@
      * {@code DEFAULT_STRENGTH} is 128) for HashDRBG:
      * <pre>
      * requested             effective
-     * (SHA-1, -1)           (SHA-1,128)
-     * (SHA-1, 112)          (SHA-1,112)
-     * (SHA-1, 192)          IAE
+     * (SHA-224, 256)        IAE
      * (SHA-256, -1)         (SHA-256,128)
+     * (SHA-256, 112)        (SHA-256,112)
      * (SHA-256, 128)        (SHA-256,128)
      * (SHA-3, -1)           IAE
      * (null, -1)            (SHA-256,128)
@@ -383,9 +381,14 @@
             instantiateIfNecessary(null);
 
             // Step 7: Auto reseed
+            // Double checked locking, safe because reseedCounter is volatile
             if (reseedCounter > reseedInterval || pr) {
-                reseedAlgorithm(getEntropyInput(pr), ai);
-                ai = null;
+                synchronized (this) {
+                    if (reseedCounter > reseedInterval || pr) {
+                        reseedAlgorithm(getEntropyInput(pr), ai);
+                        ai = null;
+                    }
+                }
             }
 
             // Step 8, 10: Generate_algorithm
@@ -615,8 +618,7 @@
      * @throws IllegalArgumentException if {@code params} is
      *         inappropriate for this SecureRandom.
      */
-    protected final synchronized void configure(
-            SecureRandomParameters params) {
+    protected final void configure(SecureRandomParameters params) {
         if (debug != null) {
             debug.println(this, "configure " + this + " with " + params);
         }
--- a/src/java.base/share/classes/sun/security/provider/AbstractHashDrbg.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/sun/security/provider/AbstractHashDrbg.java	Thu May 12 18:47:08 2016 +0000
@@ -39,8 +39,6 @@
 
     private static int alg2strength(String algorithm) {
         switch (algorithm.toUpperCase(Locale.ROOT)) {
-            case "SHA-1":
-                return 128;
             case "SHA-224":
             case "SHA-512/224":
                 return 192;
@@ -82,10 +80,6 @@
             this.securityStrength = tryStrength;
         }
         switch (algorithm.toUpperCase(Locale.ROOT)) {
-            case "SHA-1":
-                this.seedLen = 440 / 8;
-                this.outLen = 160 / 8;
-                break;
             case "SHA-224":
             case "SHA-512/224":
                 this.seedLen = 440 / 8;
--- a/src/java.base/share/classes/sun/security/provider/CtrDrbg.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/sun/security/provider/CtrDrbg.java	Thu May 12 18:47:08 2016 +0000
@@ -27,7 +27,6 @@
 
 import javax.crypto.Cipher;
 import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;
 import java.io.IOException;
 import java.security.*;
@@ -68,11 +67,6 @@
 
     private static int alg2strength(String algorithm) {
         switch (algorithm.toUpperCase(Locale.ROOT)) {
-            case "TDEA":
-            case "3KEYTDEA":
-            case "3 KEY TDEA":
-            case "DESEDE":
-                return 112;
             case "AES-128":
                 return 128;
             case "AES-192":
@@ -120,16 +114,6 @@
             this.securityStrength = tryStrength;
         }
         switch (algorithm.toUpperCase(Locale.ROOT)) {
-            case "TDEA":
-            case "3KEYTDEA":
-            case "3 KEY TDEA":
-            case "DESEDE":
-                algorithm = "DESede";
-                this.keyAlg = "DESede";
-                this.cipherAlg = "DESede/ECB/NoPadding";
-                this.blockLen = 64 / 8;
-                this.keyLen = 168 / 8;
-                break;
             case "AES-128":
             case "AES-192":
             case "AES-256":
@@ -224,7 +208,7 @@
                 // Step 2.1. Increment
                 addOne(v, ctrLen);
                 // Step 2.2. Block_Encrypt
-                cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k));
+                cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg));
                 // Step 2.3. Encrypt into right position, no need to cat
                 cipher.doFinal(v, 0, blockLen, temp, i * blockLen);
             }
@@ -316,7 +300,7 @@
 
         for (int i = 0; i * blockLen < seedLen; i++) {
             try {
-                cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k));
+                cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg));
                 int tailLen = temp.length - blockLen*i;
                 if (tailLen > blockLen) {
                     tailLen = blockLen;
@@ -340,7 +324,7 @@
                 inputBlock[j] ^= chain[j];
             }
             try {
-                cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k));
+                cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg));
                 chain = cipher.doFinal(inputBlock);
             } catch (GeneralSecurityException e) {
                 throw new InternalError(e);
@@ -350,7 +334,7 @@
     }
 
     @Override
-    protected void reseedAlgorithm(
+    protected synchronized void reseedAlgorithm(
             byte[] ei,
             byte[] additionalInput) {
         if (usedf) {
@@ -456,7 +440,7 @@
             addOne(v, ctrLen);
             try {
                 // Step 4.2. Encrypt
-                cipher.init(Cipher.ENCRYPT_MODE, getKey(keyAlg, k));
+                cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg));
                 byte[] out = cipher.doFinal(v);
 
                 // Step 4.3 and 5. Cat bytes and leftmost
@@ -479,43 +463,6 @@
         // Step 8. Return
     }
 
-    private static void des7to8(
-            byte[] key56, int off56, byte[] key64, int off64) {
-        key64[off64 + 0] = (byte)
-                (key56[off56 + 0] & 0xFE); // << 0
-        key64[off64 + 1] = (byte)
-                ((key56[off56 + 0] << 7) | ((key56[off56 + 1] & 0xFF) >>> 1));
-        key64[off64 + 2] = (byte)
-                ((key56[off56 + 1] << 6) | ((key56[off56 + 2] & 0xFF) >>> 2));
-        key64[off64 + 3] = (byte)
-                ((key56[off56 + 2] << 5) | ((key56[off56 + 3] & 0xFF) >>> 3));
-        key64[off64 + 4] = (byte)
-                ((key56[off56 + 3] << 4) | ((key56[off56 + 4] & 0xFF) >>> 4));
-        key64[off64 + 5] = (byte)
-                ((key56[off56 + 4] << 3) | ((key56[off56 + 5] & 0xFF) >>> 5));
-        key64[off64 + 6] = (byte)
-                ((key56[off56 + 5] << 2) | ((key56[off56 + 6] & 0xFF) >>> 6));
-        key64[off64 + 7] = (byte)
-                (key56[off56 + 6] << 1);
-
-        for (int i = 0; i < 8; i++) {
-            // if even # bits, make uneven, XOR with 1 (uneven & 1)
-            // for uneven # bits, make even, XOR with 0 (even & 1)
-            key64[off64 + i] ^= Integer.bitCount(key64[off64 + i] ^ 1) & 1;
-        }
-    }
-
-    private static SecretKey getKey(String keyAlg, byte[] k) {
-        if (keyAlg.equals("DESede")) {
-            byte[] k2 = new byte[24];
-            des7to8(k, 0, k2, 0);
-            des7to8(k, 7, k2, 8);
-            des7to8(k, 14, k2, 16);
-            k = k2;
-        }
-        return new SecretKeySpec(k, keyAlg);
-    }
-
     private void readObject(java.io.ObjectInputStream s)
             throws IOException, ClassNotFoundException {
         s.defaultReadObject ();
--- a/src/java.base/share/classes/sun/security/provider/HashDrbg.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/sun/security/provider/HashDrbg.java	Thu May 12 18:47:08 2016 +0000
@@ -115,7 +115,7 @@
 
     // This method is used by both instantiation and reseeding.
     @Override
-    protected final void hashReseedInternal(byte[] input) {
+    protected final synchronized void hashReseedInternal(byte[] input) {
 
         // 800-90Ar1 10.1.1.2: Instantiate Process.
         // 800-90Ar1 10.1.1.3: Reseed Process.
--- a/src/java.base/share/classes/sun/security/provider/HmacDrbg.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/sun/security/provider/HmacDrbg.java	Thu May 12 18:47:08 2016 +0000
@@ -115,7 +115,7 @@
 
     // This method is used by both instantiation and reseeding.
     @Override
-    protected final void hashReseedInternal(byte[] input) {
+    protected final synchronized void hashReseedInternal(byte[] input) {
 
         // 800-90Ar1 10.1.2.3: Instantiate Process.
         // 800-90Ar1 10.1.2.4: Reseed Process.
--- a/src/java.base/share/classes/sun/security/provider/SecureRandom.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/sun/security/provider/SecureRandom.java	Thu May 12 18:47:08 2016 +0000
@@ -156,6 +156,7 @@
             }
         }
         state = digest.digest(seed);
+        remCount = 0;
     }
 
     private static void updateState(byte[] state, byte[] output) {
--- a/src/java.base/share/classes/sun/security/provider/certpath/OCSPNonceExtension.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/sun/security/provider/certpath/OCSPNonceExtension.java	Thu May 12 18:47:08 2016 +0000
@@ -51,7 +51,7 @@
     private byte[] nonceData = null;
 
     /**
-     * Create a {@code OCSPNonceExtension} by providing the nonce length.
+     * Create an {@code OCSPNonceExtension} by providing the nonce length.
      * The criticality is set to false, and the OID for the extension will
      * be the value defined by "id-pkix-ocsp-nonce" from RFC 6960.
      *
@@ -66,7 +66,7 @@
     }
 
     /**
-     * Create a {@code OCSPNonceExtension} by providing the nonce length and
+     * Create an {@code OCSPNonceExtension} by providing the nonce length and
      * criticality setting.  The OID for the extension will
      * be the value defined by "id-pkix-ocsp-nonce" from RFC 6960.
      *
@@ -96,7 +96,7 @@
     }
 
     /**
-     * Create a {@code OCSPNonceExtension} by providing a nonce value.
+     * Create an {@code OCSPNonceExtension} by providing a nonce value.
      * The criticality is set to false, and the OID for the extension will
      * be the value defined by "id-pkix-ocsp-nonce" from RFC 6960.
      *
@@ -114,7 +114,7 @@
     }
 
     /**
-     * Create a {@code OCSPNonceExtension} by providing a nonce value and
+     * Create an {@code OCSPNonceExtension} by providing a nonce value and
      * criticality setting.  The OID for the extension will
      * be the value defined by "id-pkix-ocsp-nonce" from RFC 6960.
      *
--- a/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/SSLSocketInputRecord.java	Thu May 12 18:47:08 2016 +0000
@@ -123,7 +123,7 @@
                  */
                 //
                 // Short header is using here.  We reverse the code here
-                // in case it it used in the future.
+                // in case it is used in the future.
                 //
                 // int mask = (isShort ? 0x7F : 0x3F);
                 // len = ((byteZero & mask) << 8) +
--- a/src/java.base/share/conf/security/java.security	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/conf/security/java.security	Thu May 12 18:47:08 2016 +0000
@@ -206,16 +206,15 @@
 #     "Hash_DRBG" | "HMAC_DRBG" | "CTR_DRBG"
 #
 #   // The DRBG algorithm name. The "SHA-***" names are for Hash_DRBG and
-#   // HMAC_DRBG, default "SHA-256". "3KeyTDEA" and "AES-***" names are for
-#   // CTR_DRBG, default "AES-128" when using the limited cryptographic
-#   // or "AES-256" when using the unlimited.
+#   // HMAC_DRBG, default "SHA-256". The "AES-***" names are for CTR_DRBG,
+#   // default "AES-128" when using the limited cryptographic or "AES-256"
+#   // when using the unlimited.
 #   algorithm_name:
-#     "SHA-1" | "SHA-224" | "SHA-512/224" | "SHA-256" |
+#     "SHA-224" | "SHA-512/224" | "SHA-256" |
 #     "SHA-512/256" | "SHA-384" | "SHA-512" |
-#     "3KeyTDEA" | "AES-128" | "AES-192" | "AES-256"
+#     "AES-128" | "AES-192" | "AES-256"
 #
-#   // Security strength requested. Default "128", or "112"
-#   // if mech_name is CTR_DRBG and algorithm_name is "3KeyTDEA"
+#   // Security strength requested. Default "128"
 #   strength:
 #     "112" | "128" | "192" | "256"
 #
@@ -234,7 +233,7 @@
 #     "use_df" | "no_df"
 #
 # Examples,
-#   securerandom.drbg.config=Hash_DRBG,SHA-1,112,none
+#   securerandom.drbg.config=Hash_DRBG,SHA-224,112,none
 #   securerandom.drbg.config=CTR_DRBG,AES-256,192,pr_and_reseed,use_df
 #
 # The default value is an empty string, which is equivalent to
--- a/src/java.base/share/native/libjava/VM.c	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/share/native/libjava/VM.c	Thu May 12 18:47:08 2016 +0000
@@ -36,7 +36,7 @@
 };
 
 JNIEXPORT jobject JNICALL
-Java_jdk_internal_misc_VM_latestUserDefinedLoader(JNIEnv *env, jclass cls) {
+Java_jdk_internal_misc_VM_latestUserDefinedLoader0(JNIEnv *env, jclass cls) {
     return JVM_LatestUserDefinedLoader(env);
 }
 
--- a/src/java.base/unix/classes/sun/security/provider/NativePRNG.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/unix/classes/sun/security/provider/NativePRNG.java	Thu May 12 18:47:08 2016 +0000
@@ -469,7 +469,8 @@
                     try {
                         seedOut.write(seed);
                     } catch (IOException e) {
-                        throw new ProviderException("setSeed() failed", e);
+                        // Ignored. On Mac OS X, /dev/urandom can be opened
+                        // for write, but actual write is not permitted.
                     }
                 }
                 getMixRandom().engineSetSeed(seed);
--- a/src/java.base/unix/native/libnet/NetworkInterface.c	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.base/unix/native/libnet/NetworkInterface.c	Thu May 12 18:47:08 2016 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2016, 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
@@ -23,7 +23,6 @@
  * questions.
  */
 
-
 #include <errno.h>
 #include <strings.h>
 #if defined(_ALLBSD_SOURCE) && defined(__OpenBSD__)
@@ -38,14 +37,14 @@
 #include <net/if.h>
 #include <net/if_arp.h>
 
-#ifdef __solaris__
+#if defined(__solaris__)
 #include <sys/dlpi.h>
 #include <fcntl.h>
 #include <stropts.h>
 #include <sys/sockio.h>
 #endif
 
-#ifdef __linux__
+#if defined(__linux__)
 #include <sys/ioctl.h>
 #include <bits/ioctls.h>
 #include <sys/utsname.h>
@@ -59,10 +58,6 @@
 #include <sys/kinfo.h>
 #endif
 
-#ifdef __linux__
-#define _PATH_PROCNET_IFINET6           "/proc/net/if_inet6"
-#endif
-
 #if defined(_ALLBSD_SOURCE)
 #include <sys/param.h>
 #include <sys/ioctl.h>
@@ -80,6 +75,10 @@
 #include "jni_util.h"
 #include "net_util.h"
 
+#if defined(__linux__)
+#define _PATH_PROCNET_IFINET6 "/proc/net/if_inet6"
+#endif
+
 typedef struct _netaddr  {
     struct sockaddr *addr;
     struct sockaddr *brdcast;
@@ -135,29 +134,32 @@
 static netif  *enumIPv6Interfaces(JNIEnv *env, int sock, netif *ifs);
 #endif
 
-static netif  *addif(JNIEnv *env, int sock, const char * if_name, netif *ifs, struct sockaddr* ifr_addrP, int family, short prefix);
+static netif  *addif(JNIEnv *env, int sock, const char *if_name, netif *ifs,
+                     struct sockaddr *ifr_addrP, int family, short prefix);
 static void    freeif(netif *ifs);
 
 static int     openSocket(JNIEnv *env, int proto);
 static int     openSocketWithFallback(JNIEnv *env, const char *ifname);
 
 
-static struct  sockaddr *getBroadcast(JNIEnv *env, int sock, const char *name, struct sockaddr *brdcast_store);
+static struct  sockaddr *getBroadcast(JNIEnv *env, int sock, const char *name,
+                                      struct sockaddr *brdcast_store);
 static short   getSubnet(JNIEnv *env, int sock, const char *ifname);
 static int     getIndex(int sock, const char *ifname);
 
 static int     getFlags(int sock, const char *ifname, int *flags);
-static int     getMacAddress(JNIEnv *env, int sock,  const char* ifname, const struct in_addr* addr, unsigned char *buf);
+static int     getMacAddress(JNIEnv *env, int sock,  const char *ifname,
+                             const struct in_addr *addr, unsigned char *buf);
 static int     getMTU(JNIEnv *env, int sock, const char *ifname);
 
 
-
-#ifdef __solaris__
-static netif *enumIPvXInterfaces(JNIEnv *env, int sock, netif *ifs, int family);
-static int    getMacFromDevice(JNIEnv *env, const char* ifname, unsigned char* retbuf);
+#if defined(__solaris__)
+static netif  *enumIPvXInterfaces(JNIEnv *env, int sock, netif *ifs, int family);
+static int     getMacFromDevice(JNIEnv *env, const char *ifname,
+                                unsigned char *retbuf);
 
 #ifndef SIOCGLIFHWADDR
-#define SIOCGLIFHWADDR  _IOWR('i', 192, struct lifreq)
+#define SIOCGLIFHWADDR _IOWR('i', 192, struct lifreq)
 #endif
 
 #endif
@@ -169,27 +171,33 @@
  * Method:    init
  * Signature: ()V
  */
-JNIEXPORT void JNICALL
-Java_java_net_NetworkInterface_init(JNIEnv *env, jclass cls) {
-    ni_class = (*env)->FindClass(env,"java/net/NetworkInterface");
+JNIEXPORT void JNICALL Java_java_net_NetworkInterface_init
+  (JNIEnv *env, jclass cls)
+{
+    ni_class = (*env)->FindClass(env, "java/net/NetworkInterface");
     CHECK_NULL(ni_class);
     ni_class = (*env)->NewGlobalRef(env, ni_class);
     CHECK_NULL(ni_class);
-    ni_nameID = (*env)->GetFieldID(env, ni_class,"name", "Ljava/lang/String;");
+    ni_nameID = (*env)->GetFieldID(env, ni_class, "name", "Ljava/lang/String;");
     CHECK_NULL(ni_nameID);
     ni_indexID = (*env)->GetFieldID(env, ni_class, "index", "I");
     CHECK_NULL(ni_indexID);
-    ni_addrsID = (*env)->GetFieldID(env, ni_class, "addrs", "[Ljava/net/InetAddress;");
+    ni_addrsID = (*env)->GetFieldID(env, ni_class, "addrs",
+                                    "[Ljava/net/InetAddress;");
     CHECK_NULL(ni_addrsID);
-    ni_bindsID = (*env)->GetFieldID(env, ni_class, "bindings", "[Ljava/net/InterfaceAddress;");
+    ni_bindsID = (*env)->GetFieldID(env, ni_class, "bindings",
+                                    "[Ljava/net/InterfaceAddress;");
     CHECK_NULL(ni_bindsID);
-    ni_descID = (*env)->GetFieldID(env, ni_class, "displayName", "Ljava/lang/String;");
+    ni_descID = (*env)->GetFieldID(env, ni_class, "displayName",
+                                   "Ljava/lang/String;");
     CHECK_NULL(ni_descID);
     ni_virutalID = (*env)->GetFieldID(env, ni_class, "virtual", "Z");
     CHECK_NULL(ni_virutalID);
-    ni_childsID = (*env)->GetFieldID(env, ni_class, "childs", "[Ljava/net/NetworkInterface;");
+    ni_childsID = (*env)->GetFieldID(env, ni_class, "childs",
+                                     "[Ljava/net/NetworkInterface;");
     CHECK_NULL(ni_childsID);
-    ni_parentID = (*env)->GetFieldID(env, ni_class, "parent", "Ljava/net/NetworkInterface;");
+    ni_parentID = (*env)->GetFieldID(env, ni_class, "parent",
+                                     "Ljava/net/NetworkInterface;");
     CHECK_NULL(ni_parentID);
     ni_ctrID = (*env)->GetMethodID(env, ni_class, "<init>", "()V");
     CHECK_NULL(ni_ctrID);
@@ -199,27 +207,28 @@
     CHECK_NULL(ni_ibcls);
     ni_ibctrID = (*env)->GetMethodID(env, ni_ibcls, "<init>", "()V");
     CHECK_NULL(ni_ibctrID);
-    ni_ibaddressID = (*env)->GetFieldID(env, ni_ibcls, "address", "Ljava/net/InetAddress;");
+    ni_ibaddressID = (*env)->GetFieldID(env, ni_ibcls, "address",
+                                        "Ljava/net/InetAddress;");
     CHECK_NULL(ni_ibaddressID);
-    ni_ib4broadcastID = (*env)->GetFieldID(env, ni_ibcls, "broadcast", "Ljava/net/Inet4Address;");
+    ni_ib4broadcastID = (*env)->GetFieldID(env, ni_ibcls, "broadcast",
+                                           "Ljava/net/Inet4Address;");
     CHECK_NULL(ni_ib4broadcastID);
     ni_ib4maskID = (*env)->GetFieldID(env, ni_ibcls, "maskLength", "S");
     CHECK_NULL(ni_ib4maskID);
-    ni_defaultIndexID = (*env)->GetStaticFieldID(env, ni_class, "defaultIndex", "I");
+    ni_defaultIndexID = (*env)->GetStaticFieldID(env, ni_class, "defaultIndex",
+                                                 "I");
     CHECK_NULL(ni_defaultIndexID);
-
     initInetAddressIDs(env);
 }
 
-
 /*
  * Class:     java_net_NetworkInterface
  * Method:    getByName0
  * Signature: (Ljava/lang/String;)Ljava/net/NetworkInterface;
  */
 JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByName0
-    (JNIEnv *env, jclass cls, jstring name) {
-
+  (JNIEnv *env, jclass cls, jstring name)
+{
     netif *ifs, *curr;
     jboolean isCopy;
     const char* name_utf;
@@ -236,9 +245,8 @@
            JNU_ThrowOutOfMemoryError(env, NULL);
        return NULL;
     }
-    /*
-     * Search the list of interface based on name
-     */
+
+    // Search the list of interface based on name
     curr = ifs;
     while (curr != NULL) {
         if (strcmp(name_utf, curr->name) == 0) {
@@ -247,27 +255,26 @@
         curr = curr->next;
     }
 
-    /* if found create a NetworkInterface */
+    // if found create a NetworkInterface
     if (curr != NULL) {;
         obj = createNetworkInterface(env, curr);
     }
 
-    /* release the UTF string and interface list */
+    // release the UTF string and interface list
     (*env)->ReleaseStringUTFChars(env, name, name_utf);
     freeif(ifs);
 
     return obj;
 }
 
-
 /*
  * Class:     java_net_NetworkInterface
  * Method:    getByIndex0
  * Signature: (Ljava/lang/String;)Ljava/net/NetworkInterface;
  */
 JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByIndex0
-    (JNIEnv *env, jclass cls, jint index) {
-
+  (JNIEnv *env, jclass cls, jint index)
+{
     netif *ifs, *curr;
     jobject obj = NULL;
 
@@ -279,9 +286,7 @@
         return NULL;
     }
 
-    /*
-     * Search the list of interface based on index
-     */
+    // Search the list of interface based on index
     curr = ifs;
     while (curr != NULL) {
         if (index == curr->index) {
@@ -290,7 +295,7 @@
         curr = curr->next;
     }
 
-    /* if found create a NetworkInterface */
+    // if found create a NetworkInterface
     if (curr != NULL) {;
         obj = createNetworkInterface(env, curr);
     }
@@ -305,8 +310,8 @@
  * Signature: (Ljava/net/InetAddress;)Ljava/net/NetworkInterface;
  */
 JNIEXPORT jobject JNICALL Java_java_net_NetworkInterface_getByInetAddress0
-    (JNIEnv *env, jclass cls, jobject iaObj) {
-
+  (JNIEnv *env, jclass cls, jobject iaObj)
+{
     netif *ifs, *curr;
 
 #ifdef AF_INET6
@@ -327,14 +332,13 @@
     while (curr != NULL) {
         netaddr *addrP = curr->addr;
 
-        /*
-         * Iterate through each address on the interface
-         */
+        // Iterate through each address on the interface
         while (addrP != NULL) {
 
             if (family == addrP->family) {
                 if (family == AF_INET) {
-                    int address1 = htonl(((struct sockaddr_in*)addrP->addr)->sin_addr.s_addr);
+                    int address1 = htonl(
+                        ((struct sockaddr_in*)addrP->addr)->sin_addr.s_addr);
                     int address2 = getInetAddress_addr(env, iaObj);
 
                     if (address1 == address2) {
@@ -345,7 +349,8 @@
 
 #ifdef AF_INET6
                 if (family == AF_INET6) {
-                    jbyte *bytes = (jbyte *)&(((struct sockaddr_in6*)addrP->addr)->sin6_addr);
+                    jbyte *bytes = (jbyte *)&(
+                        ((struct sockaddr_in6*)addrP->addr)->sin6_addr);
                     jbyte caddr[16];
                     int i;
                     getInet6Address_ipaddress(env, iaObj, (char *)caddr);
@@ -362,7 +367,6 @@
                     }
                 }
 #endif
-
             }
 
             if (match) {
@@ -377,7 +381,7 @@
         curr = curr->next;
     }
 
-    /* if found create a NetworkInterface */
+    // if found create a NetworkInterface
     if (match) {;
         obj = createNetworkInterface(env, curr);
     }
@@ -386,15 +390,14 @@
     return obj;
 }
 
-
 /*
  * Class:     java_net_NetworkInterface
  * Method:    getAll
  * Signature: ()[Ljava/net/NetworkInterface;
  */
 JNIEXPORT jobjectArray JNICALL Java_java_net_NetworkInterface_getAll
-    (JNIEnv *env, jclass cls) {
-
+  (JNIEnv *env, jclass cls)
+{
     netif *ifs, *curr;
     jobjectArray netIFArr;
     jint arr_index, ifCount;
@@ -404,7 +407,7 @@
         return NULL;
     }
 
-    /* count the interface */
+    // count the interface
     ifCount = 0;
     curr = ifs;
     while (curr != NULL) {
@@ -412,17 +415,15 @@
         curr = curr->next;
     }
 
-    /* allocate a NetworkInterface array */
+    // allocate a NetworkInterface array
     netIFArr = (*env)->NewObjectArray(env, ifCount, cls, NULL);
     if (netIFArr == NULL) {
         freeif(ifs);
         return NULL;
     }
 
-    /*
-     * Iterate through the interfaces, create a NetworkInterface instance
-     * for each array element and populate the object.
-     */
+    // Iterate through the interfaces, create a NetworkInterface instance
+    // for each array element and populate the object.
     curr = ifs;
     arr_index = 0;
     while (curr != NULL) {
@@ -434,7 +435,7 @@
             return NULL;
         }
 
-        /* put the NetworkInterface into the array */
+        // put the NetworkInterface into the array
         (*env)->SetObjectArrayElement(env, netIFArr, arr_index++, netifObj);
 
         curr = curr->next;
@@ -444,13 +445,14 @@
     return netIFArr;
 }
 
-
 /*
  * Class:     java_net_NetworkInterface
  * Method:    isUp0
  * Signature: (Ljava/lang/String;I)Z
  */
-JNIEXPORT jboolean JNICALL Java_java_net_NetworkInterface_isUp0(JNIEnv *env, jclass cls, jstring name, jint index) {
+JNIEXPORT jboolean JNICALL Java_java_net_NetworkInterface_isUp0
+  (JNIEnv *env, jclass cls, jstring name, jint index)
+{
     int ret = getFlags0(env, name);
     return ((ret & IFF_UP) && (ret & IFF_RUNNING)) ? JNI_TRUE :  JNI_FALSE;
 }
@@ -460,7 +462,9 @@
  * Method:    isP2P0
  * Signature: (Ljava/lang/String;I)Z
  */
-JNIEXPORT jboolean JNICALL Java_java_net_NetworkInterface_isP2P0(JNIEnv *env, jclass cls, jstring name, jint index) {
+JNIEXPORT jboolean JNICALL Java_java_net_NetworkInterface_isP2P0
+  (JNIEnv *env, jclass cls, jstring name, jint index)
+{
     int ret = getFlags0(env, name);
     return (ret & IFF_POINTOPOINT) ? JNI_TRUE :  JNI_FALSE;
 }
@@ -470,7 +474,9 @@
  * Method:    isLoopback0
  * Signature: (Ljava/lang/String;I)Z
  */
-JNIEXPORT jboolean JNICALL Java_java_net_NetworkInterface_isLoopback0(JNIEnv *env, jclass cls, jstring name, jint index) {
+JNIEXPORT jboolean JNICALL Java_java_net_NetworkInterface_isLoopback0
+  (JNIEnv *env, jclass cls, jstring name, jint index)
+{
     int ret = getFlags0(env, name);
     return (ret & IFF_LOOPBACK) ? JNI_TRUE :  JNI_FALSE;
 }
@@ -480,7 +486,9 @@
  * Method:    supportsMulticast0
  * Signature: (Ljava/lang/String;I)Z
  */
-JNIEXPORT jboolean JNICALL Java_java_net_NetworkInterface_supportsMulticast0(JNIEnv *env, jclass cls, jstring name, jint index) {
+JNIEXPORT jboolean JNICALL Java_java_net_NetworkInterface_supportsMulticast0
+  (JNIEnv *env, jclass cls, jstring name, jint index)
+{
     int ret = getFlags0(env, name);
     return (ret & IFF_MULTICAST) ? JNI_TRUE :  JNI_FALSE;
 }
@@ -490,7 +498,9 @@
  * Method:    getMacAddr0
  * Signature: ([bLjava/lang/String;I)[b
  */
-JNIEXPORT jbyteArray JNICALL Java_java_net_NetworkInterface_getMacAddr0(JNIEnv *env, jclass class, jbyteArray addrArray, jstring name, jint index) {
+JNIEXPORT jbyteArray JNICALL Java_java_net_NetworkInterface_getMacAddr0
+  (JNIEnv *env, jclass cls, jbyteArray addrArray, jstring name, jint index)
+{
     jint addr;
     jbyte caddr[4];
     struct in_addr iaddr;
@@ -507,12 +517,11 @@
            JNU_ThrowOutOfMemoryError(env, NULL);
        return NULL;
     }
-    if ((sock =openSocketWithFallback(env, name_utf)) < 0) {
+    if ((sock = openSocketWithFallback(env, name_utf)) < 0) {
        (*env)->ReleaseStringUTFChars(env, name, name_utf);
        return JNI_FALSE;
     }
 
-
     if (!IS_NULL(addrArray)) {
        (*env)->GetByteArrayRegion(env, addrArray, 0, 4, caddr);
        addr = ((caddr[0]<<24) & 0xff000000);
@@ -522,7 +531,7 @@
        iaddr.s_addr = htonl(addr);
        len = getMacAddress(env, sock, name_utf, &iaddr, mac);
     } else {
-       len = getMacAddress(env, sock, name_utf,NULL, mac);
+       len = getMacAddress(env, sock, name_utf, NULL, mac);
     }
     if (len > 0) {
        ret = (*env)->NewByteArray(env, len);
@@ -530,10 +539,10 @@
           /* we may have memory to free at the end of this */
           goto fexit;
        }
-       (*env)->SetByteArrayRegion(env, ret, 0, len, (jbyte *) (mac));
+       (*env)->SetByteArrayRegion(env, ret, 0, len, (jbyte *)(mac));
     }
  fexit:
-   /* release the UTF string and interface list */
+   // release the UTF string and interface list
    (*env)->ReleaseStringUTFChars(env, name, name_utf);
 
    close(sock);
@@ -545,8 +554,9 @@
  * Method:      getMTU0
  * Signature:   ([bLjava/lang/String;I)I
  */
-
-JNIEXPORT jint JNICALL Java_java_net_NetworkInterface_getMTU0(JNIEnv *env, jclass class, jstring name, jint index) {
+JNIEXPORT jint JNICALL Java_java_net_NetworkInterface_getMTU0
+  (JNIEnv *env, jclass cls, jstring name, jint index)
+{
     jboolean isCopy;
     int ret = -1;
     int sock;
@@ -564,7 +574,7 @@
        return ret;
     }
 
-    if ((sock =openSocketWithFallback(env, name_utf)) < 0) {
+    if ((sock = openSocketWithFallback(env, name_utf)) < 0) {
        (*env)->ReleaseStringUTFChars(env, name, name_utf);
        return JNI_FALSE;
     }
@@ -608,19 +618,17 @@
     (*env)->ReleaseStringUTFChars(env, name, name_utf);
 
     if (ret < 0) {
-        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "IOCTL  SIOCGLIFFLAGS failed");
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "ioctl SIOCGLIFFLAGS failed");
         return -1;
     }
 
     return flags;
 }
 
-
-
-
 /*
- * Create a NetworkInterface object, populate the name and index, and
- * populate the InetAddress array based on the IP addresses for this
+ * Creates a NetworkInterface object, populates the name, the index, and
+ * populates the InetAddress array based on the IP addresses for this
  * interface.
  */
 jobject createNetworkInterface(JNIEnv *env, netif *ifs) {
@@ -636,9 +644,7 @@
     netif *childP;
     jobject tmp;
 
-    /*
-     * Create a NetworkInterface object and populate it
-     */
+    // Create a NetworkInterface object and populate it
     netifObj = (*env)->NewObject(env, ni_class, ni_ctrID);
     CHECK_NULL_RETURN(netifObj, NULL);
     name = (*env)->NewStringUTF(env, ifs->name);
@@ -646,11 +652,10 @@
     (*env)->SetObjectField(env, netifObj, ni_nameID, name);
     (*env)->SetObjectField(env, netifObj, ni_descID, name);
     (*env)->SetIntField(env, netifObj, ni_indexID, ifs->index);
-    (*env)->SetBooleanField(env, netifObj, ni_virutalID, ifs->virtual ? JNI_TRUE : JNI_FALSE);
+    (*env)->SetBooleanField(env, netifObj, ni_virutalID,
+                            ifs->virtual ? JNI_TRUE : JNI_FALSE);
 
-    /*
-     * Count the number of address on this interface
-     */
+    //Count the number of address on this interface
     addr_count = 0;
     addrP = ifs->addr;
     while (addrP != NULL) {
@@ -658,9 +663,7 @@
         addrP = addrP->next;
     }
 
-    /*
-     * Create the array of InetAddresses
-     */
+    // Create the array of InetAddresses
     addrArr = (*env)->NewObjectArray(env, addr_count,  ia_class, NULL);
     if (addrArr == NULL) {
         return NULL;
@@ -680,7 +683,8 @@
         if (addrP->family == AF_INET) {
             iaObj = (*env)->NewObject(env, ia4_class, ia4_ctrID);
             if (iaObj) {
-                 setInetAddress_addr(env, iaObj, htonl(((struct sockaddr_in*)addrP->addr)->sin_addr.s_addr));
+                 setInetAddress_addr(env, iaObj, htonl(
+                     ((struct sockaddr_in*)addrP->addr)->sin_addr.s_addr));
             } else {
                 return NULL;
             }
@@ -691,7 +695,8 @@
                     jobject ia2Obj = NULL;
                     ia2Obj = (*env)->NewObject(env, ia4_class, ia4_ctrID);
                     if (ia2Obj) {
-                       setInetAddress_addr(env, ia2Obj, htonl(((struct sockaddr_in*)addrP->brdcast)->sin_addr.s_addr));
+                       setInetAddress_addr(env, ia2Obj, htonl(
+                           ((struct sockaddr_in*)addrP->brdcast)->sin_addr.s_addr));
                        (*env)->SetObjectField(env, ibObj, ni_ib4broadcastID, ia2Obj);
                     } else {
                         return NULL;
@@ -709,7 +714,8 @@
             int scope=0;
             iaObj = (*env)->NewObject(env, ia6_class, ia6_ctrID);
             if (iaObj) {
-                jboolean ret = setInet6Address_ipaddress(env, iaObj, (char *)&(((struct sockaddr_in6*)addrP->addr)->sin6_addr));
+                jboolean ret = setInet6Address_ipaddress(env, iaObj,
+                    (char *)&(((struct sockaddr_in6*)addrP->addr)->sin6_addr));
                 if (ret == JNI_FALSE) {
                     return NULL;
                 }
@@ -738,9 +744,7 @@
         addrP = addrP->next;
     }
 
-    /*
-     * See if there is any virtual interface attached to this one.
-     */
+    // See if there is any virtual interface attached to this one.
     child_count = 0;
     childP = ifs->childs;
     while (childP) {
@@ -753,10 +757,7 @@
         return NULL;
     }
 
-    /*
-     * Create the NetworkInterface instances for the sub-interfaces as
-     * well.
-     */
+    // Create the NetworkInterface instances for the sub-interfaces as well.
     child_index = 0;
     childP = ifs->childs;
     while(childP) {
@@ -772,7 +773,7 @@
     (*env)->SetObjectField(env, netifObj, ni_bindsID, bindArr);
     (*env)->SetObjectField(env, netifObj, ni_childsID, childArr);
 
-    /* return the NetworkInterface */
+    // return the NetworkInterface
     return netifObj;
 }
 
@@ -783,10 +784,7 @@
     netif *ifs;
     int sock;
 
-    /*
-     * Enumerate IPv4 addresses
-     */
-
+    // Enumerate IPv4 addresses
     sock = openSocket(env, AF_INET);
     if (sock < 0 && (*env)->ExceptionOccurred(env)) {
         return NULL;
@@ -799,19 +797,16 @@
         return NULL;
     }
 
-    /* return partial list if an exception occurs in the middle of process ???*/
+    // return partial list if an exception occurs in the middle of process ???
 
-    /*
-     * If IPv6 is available then enumerate IPv6 addresses.
-     */
+    // If IPv6 is available then enumerate IPv6 addresses.
 #ifdef AF_INET6
 
-        /* User can disable ipv6 explicitly by -Djava.net.preferIPv4Stack=true,
-         * so we have to call ipv6_available()
-         */
+        // User can disable ipv6 explicitly by -Djava.net.preferIPv4Stack=true,
+        // so we have to call ipv6_available()
         if (ipv6_available()) {
 
-           sock =  openSocket(env, AF_INET6);
+           sock = openSocket(env, AF_INET6);
            if (sock < 0 && (*env)->ExceptionOccurred(env)) {
                freeif(ifs);
                return NULL;
@@ -831,18 +826,18 @@
     return ifs;
 }
 
-#define CHECKED_MALLOC3(_pointer,_type,_size) \
-       do{ \
-        _pointer = (_type)malloc( _size ); \
+#define CHECKED_MALLOC3(_pointer, _type, _size) \
+    do { \
+        _pointer = (_type)malloc(_size); \
         if (_pointer == NULL) { \
             JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed"); \
             return ifs; /* return untouched list */ \
         } \
-       } while(0)
+    } while(0)
 
 
 /*
- * Free an interface list (including any attached addresses)
+ * Frees an interface list (including any attached addresses)
  */
 void freeif(netif *ifs) {
     netif *currif = ifs;
@@ -854,24 +849,21 @@
             netaddr *next = addrP->next;
             free(addrP);
             addrP = next;
-         }
+        }
 
-            /*
-            * Don't forget to free the sub-interfaces.
-            */
-          if (currif->childs != NULL) {
-                freeif(currif->childs);
-          }
+        // Don't forget to free the sub-interfaces.
+        if (currif->childs != NULL) {
+            freeif(currif->childs);
+        }
 
-          ifs = currif->next;
-          free(currif);
-          currif = ifs;
+        ifs = currif->next;
+        free(currif);
+        currif = ifs;
     }
 }
 
-netif *addif(JNIEnv *env, int sock, const char * if_name,
-             netif *ifs, struct sockaddr* ifr_addrP, int family,
-             short prefix)
+netif *addif(JNIEnv *env, int sock, const char *if_name, netif *ifs,
+             struct sockaddr *ifr_addrP, int family, short prefix)
 {
     netif *currif = ifs, *parent;
     netaddr *addrP;
@@ -884,37 +876,34 @@
     char name[IFNAMSIZ], vname[IFNAMSIZ];
 #endif
 
-    char  *name_colonP;
+    char *name_colonP;
     int mask;
     int isVirtual = 0;
     int addr_size;
     int flags = 0;
 
-    /*
-     * If the interface name is a logical interface then we
-     * remove the unit number so that we have the physical
-     * interface (eg: hme0:1 -> hme0). NetworkInterface
-     * currently doesn't have any concept of physical vs.
-     * logical interfaces.
-     */
+    // If the interface name is a logical interface then we remove the unit
+    // number so that we have the physical interface (eg: hme0:1 -> hme0).
+    // NetworkInterface currently doesn't have any concept of physical vs.
+    // logical interfaces.
     strncpy(name, if_name, ifnam_size);
     name[ifnam_size - 1] = '\0';
     *vname = 0;
 
-    /*
-     * Create and populate the netaddr node. If allocation fails
-     * return an un-updated list.
-     */
-    /*Allocate for addr and brdcast at once*/
+     // Create and populate the netaddr node. If allocation fails
+     // return an un-updated list.
+
+     // Allocate for addr and brdcast at once
 
 #ifdef AF_INET6
-    addr_size = (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
+    addr_size = (family == AF_INET) ? sizeof(struct sockaddr_in)
+                                    : sizeof(struct sockaddr_in6);
 #else
     addr_size = sizeof(struct sockaddr_in);
 #endif
 
-    CHECKED_MALLOC3(addrP, netaddr *, sizeof(netaddr)+2*addr_size);
-    addrP->addr = (struct sockaddr *)( (char *) addrP+sizeof(netaddr) );
+    CHECKED_MALLOC3(addrP, netaddr *, sizeof(netaddr) + 2 * addr_size);
+    addrP->addr = (struct sockaddr *)((char *)addrP + sizeof(netaddr));
     memcpy(addrP->addr, ifr_addrP, addr_size);
 
     addrP->family = family;
@@ -923,8 +912,9 @@
     addrP->next = 0;
     if (family == AF_INET) {
        // Deal with broadcast addr & subnet mask
-       struct sockaddr * brdcast_to = (struct sockaddr *) ((char *) addrP + sizeof(netaddr) + addr_size);
-       addrP->brdcast = getBroadcast(env, sock, name,  brdcast_to );
+       struct sockaddr *brdcast_to =
+              (struct sockaddr *) ((char *)addrP + sizeof(netaddr) + addr_size);
+       addrP->brdcast = getBroadcast(env, sock, name, brdcast_to);
        if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
            return ifs;
        }
@@ -935,36 +925,28 @@
        }
      }
 
-    /**
-     * Deal with virtual interface with colon notation e.g. eth0:1
-     */
+    // Deal with virtual interface with colon notation e.g. eth0:1
     name_colonP = strchr(name, ':');
     if (name_colonP != NULL) {
-      /**
-       * This is a virtual interface. If we are able to access the parent
-       * we need to create a new entry if it doesn't exist yet *and* update
-       * the 'parent' interface with the new records.
-       */
+        // This is a virtual interface. If we are able to access the parent
+        // we need to create a new entry if it doesn't exist yet *and* update
+        // the 'parent' interface with the new records.
         *name_colonP = 0;
         if (getFlags(sock, name, &flags) < 0 || flags < 0) {
-            // failed to access parent interface do not create parent.
-            // We are a virtual interface with no parent.
-            isVirtual = 1;
-            *name_colonP = ':';
-        }
-        else{
-           // Got access to parent, so create it if necessary.
-           // Save original name to vname and truncate name by ':'
-            memcpy(vname, name, sizeof(vname) );
-            vname[name_colonP - name] = ':';
+             // failed to access parent interface do not create parent.
+             // We are a virtual interface with no parent.
+             isVirtual = 1;
+             *name_colonP = ':';
+        } else {
+             // Got access to parent, so create it if necessary.
+             // Save original name to vname and truncate name by ':'
+             memcpy(vname, name, sizeof(vname) );
+             vname[name_colonP - name] = ':';
         }
     }
 
-    /*
-     * Check if this is a "new" interface. Use the interface
-     * name for matching because index isn't supported on
-     * Solaris 2.6 & 7.
-     */
+    // Check if this is a "new" interface. Use the interface name for
+    // matching because index isn't supported on Solaris 2.6 & 7.
     while (currif != NULL) {
         if (strcmp(name, currif->name) == 0) {
             break;
@@ -972,13 +954,10 @@
         currif = currif->next;
     }
 
-    /*
-     * If "new" then create an netif structure and
-     * insert it onto the list.
-     */
+    // If "new" then create an netif structure and insert it into the list.
     if (currif == NULL) {
          CHECKED_MALLOC3(currif, netif *, sizeof(netif) + ifnam_size);
-         currif->name = (char *) currif+sizeof(netif);
+         currif->name = (char *)currif + sizeof(netif);
          strncpy(currif->name, name, ifnam_size);
          currif->name[ifnam_size - 1] = '\0';
          currif->index = getIndex(sock, name);
@@ -989,17 +968,13 @@
          ifs = currif;
     }
 
-    /*
-     * Finally insert the address on the interface
-     */
+    // Finally insert the address on the interface
     addrP->next = currif->addr;
     currif->addr = addrP;
 
     parent = currif;
 
-    /**
-     * Let's deal with the virtual interface now.
-     */
+    // Deal with the virtual interface now.
     if (vname[0]) {
         netaddr *tmpaddr;
 
@@ -1014,27 +989,29 @@
 
         if (currif == NULL) {
             CHECKED_MALLOC3(currif, netif *, sizeof(netif) + ifnam_size);
-            currif->name = (char *) currif + sizeof(netif);
+            currif->name = (char *)currif + sizeof(netif);
             strncpy(currif->name, vname, ifnam_size);
             currif->name[ifnam_size - 1] = '\0';
             currif->index = getIndex(sock, vname);
             currif->addr = NULL;
-           /* Need to duplicate the addr entry? */
+            // Need to duplicate the addr entry?
             currif->virtual = 1;
             currif->childs = NULL;
             currif->next = parent->childs;
             parent->childs = currif;
         }
 
-        CHECKED_MALLOC3(tmpaddr, netaddr *, sizeof(netaddr)+2*addr_size);
+        CHECKED_MALLOC3(tmpaddr, netaddr *, sizeof(netaddr) + 2 * addr_size);
         memcpy(tmpaddr, addrP, sizeof(netaddr));
         if (addrP->addr != NULL) {
-            tmpaddr->addr = (struct sockaddr *) ( (char*)tmpaddr + sizeof(netaddr) ) ;
+            tmpaddr->addr = (struct sockaddr *)
+                ((char*)tmpaddr + sizeof(netaddr));
             memcpy(tmpaddr->addr, addrP->addr, addr_size);
         }
 
         if (addrP->brdcast != NULL) {
-            tmpaddr->brdcast = (struct sockaddr *) ((char *) tmpaddr + sizeof(netaddr)+addr_size);
+            tmpaddr->brdcast = (struct sockaddr *)
+                ((char *)tmpaddr + sizeof(netaddr) + addr_size);
             memcpy(tmpaddr->brdcast, addrP->brdcast, addr_size);
         }
 
@@ -1045,19 +1022,18 @@
     return ifs;
 }
 
-/* Open socket for further ioct calls
- * proto is AF_INET/AF_INET6
+/*
+ * Opens a socket for further ioct calls. proto is one of AF_INET or AF_INET6.
  */
-static int  openSocket(JNIEnv *env, int proto){
+static int openSocket(JNIEnv *env, int proto) {
     int sock;
 
     if ((sock = socket(proto, SOCK_DGRAM, 0)) < 0) {
-        /*
-         * If EPROTONOSUPPORT is returned it means we don't have
-         * support  for this proto so don't throw an exception.
-         */
+        // If EPROTONOSUPPORT is returned it means we don't have
+        // support for this proto so don't throw an exception.
         if (errno != EPROTONOSUPPORT) {
-            NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException", "Socket creation failed");
+            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                         "Socket creation failed");
         }
         return -1;
     }
@@ -1068,37 +1044,38 @@
 
 /** Linux, AIX **/
 #if defined(__linux__) || defined(_AIX)
-/* Open socket for further ioct calls, try v4 socket first and
- * if it falls return v6 socket
- */
 
 #ifdef AF_INET6
+/*
+ * Opens a socket for further ioct calls. Tries AF_INET socket first and
+ * if it falls return AF_INET6 socket.
+ */
 // unused arg ifname and struct if2
-static int openSocketWithFallback(JNIEnv *env, const char *ifname){
+static int openSocketWithFallback(JNIEnv *env, const char *ifname) {
     int sock;
     struct ifreq if2;
 
-     if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-         if (errno == EPROTONOSUPPORT){
-              if ( (sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0 ){
-                 NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException", "IPV6 Socket creation failed");
-                 return -1;
-              }
-         }
-         else{ // errno is not NOSUPPORT
-             NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException", "IPV4 Socket creation failed");
-             return -1;
-         }
-   }
+    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+        if (errno == EPROTONOSUPPORT) {
+            if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+                NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                             "IPV6 Socket creation failed");
+                return -1;
+            }
+        } else { // errno is not NOSUPPORT
+            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                         "IPV4 Socket creation failed");
+            return -1;
+        }
+    }
 
-     /* Linux starting from 2.6.? kernel allows ioctl call with either IPv4 or IPv6 socket regardless of type
-        of address of an interface */
-
-       return sock;
+    // Linux starting from 2.6.? kernel allows ioctl call with either IPv4 or
+    // IPv6 socket regardless of type of address of an interface.
+    return sock;
 }
 
 #else
-static int openSocketWithFallback(JNIEnv *env, const char *ifname){
+static int openSocketWithFallback(JNIEnv *env, const char *ifname) {
     return openSocket(env,AF_INET);
 }
 #endif
@@ -1111,52 +1088,48 @@
     unsigned i;
     int siocgifconfRequest = SIOCGIFCONF;
 
-
 #if defined(__linux__)
-    /* need to do a dummy SIOCGIFCONF to determine the buffer size.
-     * SIOCGIFCOUNT doesn't work
-     */
+    // need to do a dummy SIOCGIFCONF to determine the buffer size.
+    // SIOCGIFCOUNT doesn't work
     ifc.ifc_buf = NULL;
     if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
-        NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException", "ioctl SIOCGIFCONF failed");
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "ioctl SIOCGIFCONF failed");
         return ifs;
     }
 #elif defined(_AIX)
     ifc.ifc_buf = NULL;
     if (ioctl(sock, SIOCGSIZIFCONF, &(ifc.ifc_len)) < 0) {
-        NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException", "ioctl SIOCGSIZIFCONF failed");
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "ioctl SIOCGSIZIFCONF failed");
         return ifs;
     }
 #endif /* __linux__ */
 
-    CHECKED_MALLOC3(buf,char *, ifc.ifc_len);
+    CHECKED_MALLOC3(buf, char *, ifc.ifc_len);
 
     ifc.ifc_buf = buf;
 #if defined(_AIX)
     siocgifconfRequest = CSIOCGIFCONF;
 #endif
     if (ioctl(sock, siocgifconfRequest, (char *)&ifc) < 0) {
-        NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException", "ioctl SIOCGIFCONF failed");
-        (void) free(buf);
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "ioctl SIOCGIFCONF failed");
+        free(buf);
         return ifs;
     }
 
-    /*
-     * Iterate through each interface
-     */
+    // Iterate through each interface
     ifreqP = ifc.ifc_req;
-    for (i=0; i<ifc.ifc_len/sizeof (struct ifreq); i++, ifreqP++) {
+    for (i = 0; i < ifc.ifc_len / sizeof(struct ifreq); i++, ifreqP++) {
 #if defined(_AIX)
         if (ifreqP->ifr_addr.sa_family != AF_INET) continue;
 #endif
-        /*
-         * Add to the list
-         */
-        ifs = addif(env, sock, ifreqP->ifr_name, ifs, (struct sockaddr *) & (ifreqP->ifr_addr), AF_INET, 0);
+        // Add to the list
+        ifs = addif(env, sock, ifreqP->ifr_name, ifs,
+                    (struct sockaddr *)&(ifreqP->ifr_addr), AF_INET, 0);
 
-        /*
-         * If an exception occurred then free the list
-         */
+        // If an exception occurred then free the list
         if ((*env)->ExceptionOccurred(env)) {
             free(buf);
             freeif(ifs);
@@ -1164,37 +1137,37 @@
         }
     }
 
-    /*
-     * Free socket and buffer
-     */
+    // Free socket and buffer
     free(buf);
     return ifs;
 }
 
 
+#if defined(AF_INET6) && defined(__linux__)
+
 /*
- * Enumerates and returns all IPv6 interfaces on Linux
+ * Enumerates and returns all IPv6 interfaces on Linux.
  */
-
-#if defined(AF_INET6) && defined(__linux__)
 static netif *enumIPv6Interfaces(JNIEnv *env, int sock, netif *ifs) {
     FILE *f;
     char addr6[40], devname[21];
     char addr6p[8][5];
-    int plen, scope, dad_status, if_idx;
+    int prefix, scope, dad_status, if_idx;
     uint8_t ipv6addr[16];
 
     if ((f = fopen(_PATH_PROCNET_IFINET6, "r")) != NULL) {
         while (fscanf(f, "%4s%4s%4s%4s%4s%4s%4s%4s %08x %02x %02x %02x %20s\n",
-                         addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4], addr6p[5], addr6p[6], addr6p[7],
-                         &if_idx, &plen, &scope, &dad_status, devname) != EOF) {
+                      addr6p[0], addr6p[1], addr6p[2], addr6p[3],
+                      addr6p[4], addr6p[5], addr6p[6], addr6p[7],
+                      &if_idx, &prefix, &scope, &dad_status, devname) != EOF) {
 
             struct netif *ifs_ptr = NULL;
             struct netif *last_ptr = NULL;
             struct sockaddr_in6 addr;
 
             sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s",
-                           addr6p[0], addr6p[1], addr6p[2], addr6p[3], addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
+                    addr6p[0], addr6p[1], addr6p[2], addr6p[3],
+                    addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
             inet_pton(AF_INET6, addr6, ipv6addr);
 
             memset(&addr, 0, sizeof(struct sockaddr_in6));
@@ -1202,12 +1175,10 @@
 
             addr.sin6_scope_id = if_idx;
 
-            ifs = addif(env, sock, devname, ifs, (struct sockaddr *)&addr, AF_INET6, plen);
+            ifs = addif(env, sock, devname, ifs, (struct sockaddr *)&addr,
+                        AF_INET6, (short)prefix);
 
-
-            /*
-             * If an exception occurred then return the list as is.
-             */
+            // If an exception occurred then return the list as is.
             if ((*env)->ExceptionOccurred(env)) {
                 fclose(f);
                 return ifs;
@@ -1220,11 +1191,11 @@
 #endif
 
 
+#if defined(AF_INET6) && defined(_AIX)
+
 /*
- * Enumerates and returns all IPv6 interfaces on AIX
+ * Enumerates and returns all IPv6 interfaces on AIX.
  */
-
-#if defined(AF_INET6) && defined(_AIX)
 static netif *enumIPv6Interfaces(JNIEnv *env, int sock, netif *ifs) {
     struct ifconf ifc;
     struct ifreq *ifreqP;
@@ -1234,12 +1205,12 @@
     unsigned bufsize;
     char *cp, *cplimit;
 
-    /* use SIOCGSIZIFCONF to get size for  SIOCGIFCONF */
+    // use SIOCGSIZIFCONF to get size for  SIOCGIFCONF
 
     ifc.ifc_buf = NULL;
     if (ioctl(sock, SIOCGSIZIFCONF, &(ifc.ifc_len)) < 0) {
-        NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException",
-                        "ioctl SIOCGSIZIFCONF failed");
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                    "ioctl SIOCGSIZIFCONF failed");
         return ifs;
     }
     bufsize = ifc.ifc_len;
@@ -1252,29 +1223,27 @@
     ifc.ifc_len = bufsize;
     ifc.ifc_buf = buf;
     if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
-        NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException",
-                       "ioctl CSIOCGIFCONF failed");
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "ioctl CSIOCGIFCONF failed");
         free(buf);
         return ifs;
     }
 
-    /*
-     * Iterate through each interface
-     */
+    // Iterate through each interface
     ifreqP = ifc.ifc_req;
     cp = (char *)ifc.ifc_req;
     cplimit = cp + ifc.ifc_len;
 
-    for ( ; cp < cplimit; cp += (sizeof(ifreqP->ifr_name) + MAX((ifreqP->ifr_addr).sa_len, sizeof(ifreqP->ifr_addr)))) {
+    for (; cp < cplimit;
+        cp += (sizeof(ifreqP->ifr_name) +
+               MAX((ifreqP->ifr_addr).sa_len, sizeof(ifreqP->ifr_addr))))
+    {
         ifreqP = (struct ifreq *)cp;
         struct ifreq if2;
+        memset((char *)&if2, 0, sizeof(if2));
+        strncpy(if2.ifr_name, ifreqP->ifr_name, sizeof(if2.ifr_name) - 1);
 
-        memset((char *)&if2, 0, sizeof(if2));
-        strcpy(if2.ifr_name, ifreqP->ifr_name);
-
-        /*
-         * Skip interface that aren't UP
-         */
+        // Skip interface that aren't UP
         if (ioctl(sock, SIOCGIFFLAGS, (char *)&if2) >= 0) {
             if (!(if2.ifr_flags & IFF_UP)) {
                 continue;
@@ -1289,16 +1258,11 @@
             s6->sin6_scope_id = if2.ifr_site6;
         }
 
-        /*
-         * Add to the list
-         */
+        // Add to the list
         ifs = addif(env, sock, ifreqP->ifr_name, ifs,
-                    (struct sockaddr *)&(ifreqP->ifr_addr),
-                    AF_INET6, 0);
+                    (struct sockaddr *)&(ifreqP->ifr_addr), AF_INET6, 0);
 
-        /*
-         * If an exception occurred then free the list
-         */
+        // If an exception occurred then free the list
         if ((*env)->ExceptionOccurred(env)) {
             free(buf);
             freeif(ifs);
@@ -1306,24 +1270,21 @@
         }
     }
 
-    /*
-     * Free socket and buffer
-     */
+    // Free socket and buffer
     free(buf);
     return ifs;
 }
 #endif
 
 
-static int getIndex(int sock, const char *name){
-     /*
-      * Try to get the interface index
-      */
+static int getIndex(int sock, const char *name) {
+     // Try to get the interface index
 #if defined(_AIX)
     return if_nametoindex(name);
 #else
     struct ifreq if2;
-    strcpy(if2.ifr_name, name);
+    memset((char *)&if2, 0, sizeof(if2));
+    strncpy(if2.ifr_name, name, sizeof(if2.ifr_name) - 1);
 
     if (ioctl(sock, SIOCGIFINDEX, (char *)&if2) < 0) {
         return -1;
@@ -1333,38 +1294,41 @@
 #endif
 }
 
-/**
+/*
  * Returns the IPv4 broadcast address of a named interface, if it exists.
  * Returns 0 if it doesn't have one.
  */
-static struct sockaddr *getBroadcast(JNIEnv *env, int sock, const char *ifname, struct sockaddr *brdcast_store) {
-  struct sockaddr *ret = NULL;
-  struct ifreq if2;
+static struct sockaddr *getBroadcast
+  (JNIEnv *env, int sock, const char *ifname, struct sockaddr *brdcast_store)
+{
+    struct sockaddr *ret = NULL;
+    struct ifreq if2;
+    memset((char *)&if2, 0, sizeof(if2));
+    strncpy(if2.ifr_name, ifname, sizeof(if2.ifr_name) - 1);
 
-  memset((char *) &if2, 0, sizeof(if2));
-  strcpy(if2.ifr_name, ifname);
+    // Let's make sure the interface does have a broadcast address.
+    if (ioctl(sock, SIOCGIFFLAGS, (char *)&if2)  < 0) {
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "ioctl SIOCGIFFLAGS failed");
+        return ret;
+    }
 
-  /* Let's make sure the interface does have a broadcast address */
-  if (ioctl(sock, SIOCGIFFLAGS, (char *)&if2)  < 0) {
-      NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "IOCTL  SIOCGIFFLAGS failed");
-      return ret;
-  }
+    if (if2.ifr_flags & IFF_BROADCAST) {
+        // It does, let's retrieve it
+        if (ioctl(sock, SIOCGIFBRDADDR, (char *)&if2) < 0) {
+            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                         "ioctl SIOCGIFBRDADDR failed");
+            return ret;
+        }
 
-  if (if2.ifr_flags & IFF_BROADCAST) {
-      /* It does, let's retrieve it*/
-      if (ioctl(sock, SIOCGIFBRDADDR, (char *)&if2) < 0) {
-          NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "IOCTL SIOCGIFBRDADDR failed");
-          return ret;
-      }
+        ret = brdcast_store;
+        memcpy(ret, &if2.ifr_broadaddr, sizeof(struct sockaddr));
+    }
 
-      ret = brdcast_store;
-      memcpy(ret, &if2.ifr_broadaddr, sizeof(struct sockaddr));
-  }
-
-  return ret;
+    return ret;
 }
 
-/**
+/*
  * Returns the IPv4 subnet prefix length (aka subnet mask) for the named
  * interface, if it has one, otherwise return -1.
  */
@@ -1372,12 +1336,12 @@
     unsigned int mask;
     short ret;
     struct ifreq if2;
-
-    memset((char *) &if2, 0, sizeof(if2));
-    strcpy(if2.ifr_name, ifname);
+    memset((char *)&if2, 0, sizeof(if2));
+    strncpy(if2.ifr_name, ifname, sizeof(if2.ifr_name) - 1);
 
     if (ioctl(sock, SIOCGIFNETMASK, (char *)&if2) < 0) {
-        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "IOCTL SIOCGIFNETMASK failed");
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "ioctl SIOCGIFNETMASK failed");
         return -1;
     }
 
@@ -1391,12 +1355,15 @@
     return ret;
 }
 
-/**
- * Get the Hardware address (usually MAC address) for the named interface.
- * return puts the data in buf, and returns the length, in byte, of the
+/*
+ * Gets the Hardware address (usually MAC address) for the named interface.
+ * On return puts the data in buf, and returns the length, in byte, of the
  * MAC address. Returns -1 if there is no hardware address on that interface.
  */
-static int getMacAddress(JNIEnv *env, int sock, const char* ifname, const struct in_addr* addr, unsigned char *buf) {
+static int getMacAddress
+  (JNIEnv *env, int sock, const char* ifname, const struct in_addr* addr,
+   unsigned char *buf)
+{
 #if defined (_AIX)
     int size;
     struct kinfo_ndd *nddp;
@@ -1415,7 +1382,8 @@
     nddp = (struct kinfo_ndd *)malloc(size);
 
     if (!nddp) {
-        JNU_ThrowOutOfMemoryError(env, "Network interface getMacAddress native buffer allocation failed");
+        JNU_ThrowOutOfMemoryError(env,
+            "Network interface getMacAddress native buffer allocation failed");
         return -1;
     }
 
@@ -1436,22 +1404,20 @@
     }
 
     return -1;
-
 #elif defined(__linux__)
     static struct ifreq ifr;
     int i;
-
-    strcpy(ifr.ifr_name, ifname);
+    memset((char *)&ifr, 0, sizeof(ifr));
+    strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);
     if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) {
-        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "IOCTL SIOCGIFHWADDR failed");
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "ioctl SIOCGIFHWADDR failed");
         return -1;
     }
 
     memcpy(buf, &ifr.ifr_hwaddr.sa_data, IFHWADDRLEN);
 
-   /*
-    * All bytes to 0 means no hardware address.
-    */
+    // All bytes to 0 means no hardware address.
 
     for (i = 0; i < IFHWADDRLEN; i++) {
         if (buf[i] != 0)
@@ -1464,17 +1430,18 @@
 
 static int getMTU(JNIEnv *env, int sock,  const char *ifname) {
     struct ifreq if2;
-    memset((char *) &if2, 0, sizeof(if2));
+    memset((char *)&if2, 0, sizeof(if2));
 
     if (ifname != NULL) {
-        strcpy(if2.ifr_name, ifname);
+        strncpy(if2.ifr_name, ifname, sizeof(if2.ifr_name) - 1);
     } else {
         JNU_ThrowNullPointerException(env, "network interface name is NULL");
         return -1;
     }
 
     if (ioctl(sock, SIOCGIFMTU, (char *)&if2) < 0) {
-        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "IOCTL SIOCGIFMTU failed");
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "ioctl SIOCGIFMTU failed");
         return -1;
     }
 
@@ -1483,11 +1450,10 @@
 
 static int getFlags(int sock, const char *ifname, int *flags) {
   struct ifreq if2;
+  memset((char *)&if2, 0, sizeof(if2));
+  strncpy(if2.ifr_name, ifname, sizeof(if2.ifr_name) - 1);
 
-  memset((char *) &if2, 0, sizeof(if2));
-  strcpy(if2.ifr_name, ifname);
-
-  if (ioctl(sock, SIOCGIFFLAGS, (char *)&if2) < 0){
+  if (ioctl(sock, SIOCGIFFLAGS, (char *)&if2) < 0) {
       return -1;
   }
 
@@ -1499,53 +1465,54 @@
   return 0;
 }
 
-#endif
+#endif  /* defined(__linux__) || defined(_AIX) */
 
 /** Solaris **/
-#ifdef __solaris__
-/* Open socket for further ioct calls, try v4 socket first and
- * if it falls return v6 socket
+#if defined(__solaris__)
+
+/*
+ * Opens a socket for further ioct calls. Tries AF_INET socket first and
+ * if it falls return AF_INET6 socket.
  */
-
 #ifdef AF_INET6
-static int openSocketWithFallback(JNIEnv *env, const char *ifname){
+static int openSocketWithFallback(JNIEnv *env, const char *ifname) {
     int sock, alreadyV6 = 0;
     struct lifreq if2;
 
-     if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-         if (errno == EPROTONOSUPPORT){
-              if ( (sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0 ){
-                 NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException", "IPV6 Socket creation failed");
-                 return -1;
-              }
+    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+        if (errno == EPROTONOSUPPORT) {
+            if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+                NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                             "IPV6 Socket creation failed");
+                return -1;
+            }
 
-              alreadyV6=1;
-         }
-         else{ // errno is not NOSUPPORT
-             NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException", "IPV4 Socket creation failed");
-             return -1;
-         }
-   }
+            alreadyV6=1;
+        } else { // errno is not NOSUPPORT
+            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                         "IPV4 Socket creation failed");
+            return -1;
+        }
+    }
 
-     /**
-      * Solaris requires that we have an IPv6 socket to query an
-      * interface without an IPv4 address - check it here.
-      * POSIX 1 require the kernel to return ENOTTY if the call is
-      * inappropriate for a device e.g. the NETMASK for a device having IPv6
-      * only address but not all devices follow the standard so
-      * fall back on any error. It's not an ecologically friendly gesture
-      * but more reliable.
-      */
 
-    if (! alreadyV6 ){
-        memset((char *) &if2, 0, sizeof(if2));
-        strcpy(if2.lifr_name, ifname);
+    // Solaris requires that we have an IPv6 socket to query an  interface
+    // without an IPv4 address - check it here. POSIX 1 require the kernel to
+    // return ENOTTY if the call is inappropriate for a device e.g. the NETMASK
+    // for a device having IPv6 only address but not all devices follow the
+    // standard so fall back on any error. It's not an ecologically friendly
+    // gesture but more reliable.
+
+    if (!alreadyV6) {
+        memset((char *)&if2, 0, sizeof(if2));
+        strncpy(if2.lifr_name, ifname, sizeof(if2.lifr_name) - 1);
         if (ioctl(sock, SIOCGLIFNETMASK, (char *)&if2) < 0) {
-                close(sock);
-                if ( (sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0 ){
-                      NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException", "IPV6 Socket creation failed");
-                      return -1;
-                }
+            close(sock);
+            if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+                NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                             "IPV6 Socket creation failed");
+                return -1;
+            }
         }
     }
 
@@ -1553,18 +1520,16 @@
 }
 
 #else
-static int openSocketWithFallback(JNIEnv *env, const char *ifname){
+static int openSocketWithFallback(JNIEnv *env, const char *ifname) {
     return openSocket(env,AF_INET);
 }
 #endif
 
 /*
- * Enumerates and returns all IPv4 interfaces
- * (linux verision)
+ * Enumerates and returns all IPv4 interfaces.
  */
-
 static netif *enumIPv4Interfaces(JNIEnv *env, int sock, netif *ifs) {
-     return enumIPvXInterfaces(env,sock, ifs, AF_INET);
+    return enumIPvXInterfaces(env,sock, ifs, AF_INET);
 }
 
 #ifdef AF_INET6
@@ -1574,8 +1539,8 @@
 #endif
 
 /*
-   Enumerates and returns all interfaces on Solaris
-   use the same code for IPv4 and IPv6
+ * Enumerates and returns all interfaces on Solaris.
+ * Uses the same code for IPv4 and IPv6.
  */
 static netif *enumIPvXInterfaces(JNIEnv *env, int sock, netif *ifs, int family) {
     struct lifconf ifc;
@@ -1585,19 +1550,16 @@
     struct lifnum numifs;
     unsigned bufsize;
 
-    /*
-     * Get the interface count
-     */
+    // Get the interface count
     numifs.lifn_family = family;
     numifs.lifn_flags = 0;
     if (ioctl(sock, SIOCGLIFNUM, (char *)&numifs) < 0) {
-        NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException", "ioctl SIOCGLIFNUM failed");
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "ioctl SIOCGLIFNUM failed");
         return ifs;
     }
 
-    /*
-     *  Enumerate the interface configurations
-     */
+    //  Enumerate the interface configurations
     bufsize = numifs.lifn_count * sizeof (struct lifreq);
     CHECKED_MALLOC3(buf, char *, bufsize);
 
@@ -1606,22 +1568,19 @@
     ifc.lifc_len = bufsize;
     ifc.lifc_buf = buf;
     if (ioctl(sock, SIOCGLIFCONF, (char *)&ifc) < 0) {
-        NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException", "ioctl SIOCGLIFCONF failed");
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "ioctl SIOCGLIFCONF failed");
         free(buf);
         return ifs;
     }
 
-    /*
-     * Iterate through each interface
-     */
+    // Iterate through each interface
     ifr = ifc.lifc_req;
     for (n=0; n<numifs.lifn_count; n++, ifr++) {
         int index = -1;
         struct lifreq if2;
 
-        /*
-        * Ignore either IPv4 or IPv6 addresses
-        */
+        // Ignore either IPv4 or IPv6 addresses
         if (ifr->lifr_addr.ss_family != family) {
             continue;
         }
@@ -1633,12 +1592,12 @@
         }
 #endif
 
-        /* add to the list */
-        ifs = addif(env, sock,ifr->lifr_name, ifs, (struct sockaddr *)&(ifr->lifr_addr),family, (short) ifr->lifr_addrlen);
+        // add to the list
+        ifs = addif(env, sock,ifr->lifr_name, ifs,
+                    (struct sockaddr *)&(ifr->lifr_addr), family,
+                    (short)ifr->lifr_addrlen);
 
-        /*
-        * If an exception occurred we return immediately
-        */
+        // If an exception occurred we return immediately
         if ((*env)->ExceptionOccurred(env)) {
             free(buf);
             return ifs;
@@ -1650,13 +1609,11 @@
     return ifs;
 }
 
-static int getIndex(int sock, const char *name){
-   /*
-    * Try to get the interface index
-    * (Not supported on Solaris 2.6 or 7)
-    */
+static int getIndex(int sock, const char *name) {
+    // Try to get the interface index.  (Not supported on Solaris 2.6 or 7)
     struct lifreq if2;
-    strcpy(if2.lifr_name, name);
+    memset((char *)&if2, 0, sizeof(if2));
+    strncpy(if2.lifr_name, name, sizeof(if2.lifr_name) - 1);
 
     if (ioctl(sock, SIOCGLIFINDEX, (char *)&if2) < 0) {
         return -1;
@@ -1665,27 +1622,30 @@
     return if2.lifr_index;
 }
 
-/**
+/*
  * Returns the IPv4 broadcast address of a named interface, if it exists.
  * Returns 0 if it doesn't have one.
  */
-static struct sockaddr *getBroadcast(JNIEnv *env, int sock, const char *ifname, struct sockaddr *brdcast_store) {
+static struct sockaddr *getBroadcast
+  (JNIEnv *env, int sock, const char *ifname, struct sockaddr *brdcast_store)
+{
     struct sockaddr *ret = NULL;
     struct lifreq if2;
+    memset((char *)&if2, 0, sizeof(if2));
+    strncpy(if2.lifr_name, ifname, sizeof(if2.lifr_name) - 1);
 
-    memset((char *) &if2, 0, sizeof(if2));
-    strcpy(if2.lifr_name, ifname);
-
-    /* Let's make sure the interface does have a broadcast address */
+    // Let's make sure the interface does have a broadcast address
     if (ioctl(sock, SIOCGLIFFLAGS, (char *)&if2)  < 0) {
-        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "IOCTL  SIOCGLIFFLAGS failed");
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "ioctl SIOCGLIFFLAGS failed");
         return ret;
     }
 
     if (if2.lifr_flags & IFF_BROADCAST) {
-        /* It does, let's retrieve it*/
+        // It does, let's retrieve it
         if (ioctl(sock, SIOCGLIFBRDADDR, (char *)&if2) < 0) {
-            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "IOCTL SIOCGLIFBRDADDR failed");
+            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                         "ioctl SIOCGLIFBRDADDR failed");
             return ret;
         }
 
@@ -1696,7 +1656,7 @@
     return ret;
 }
 
-/**
+/*
  * Returns the IPv4 subnet prefix length (aka subnet mask) for the named
  * interface, if it has one, otherwise return -1.
  */
@@ -1704,12 +1664,12 @@
     unsigned int mask;
     short ret;
     struct lifreq if2;
-
-    memset((char *) &if2, 0, sizeof(if2));
-    strcpy(if2.lifr_name, ifname);
+    memset((char *)&if2, 0, sizeof(if2));
+    strncpy(if2.lifr_name, ifname, sizeof(if2.lifr_name) - 1);
 
     if (ioctl(sock, SIOCGLIFNETMASK, (char *)&if2) < 0) {
-        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "IOCTL SIOCGLIFNETMASK failed");
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "ioctl SIOCGLIFNETMASK failed");
         return -1;
     }
 
@@ -1725,15 +1685,16 @@
 }
 
 
-
 #define DEV_PREFIX  "/dev/"
 
-/**
+/*
  * Solaris specific DLPI code to get hardware address from a device.
  * Unfortunately, at least up to Solaris X, you have to have special
  * privileges (i.e. be root).
  */
-static int getMacFromDevice(JNIEnv *env, const char* ifname, unsigned char* retbuf) {
+static int getMacFromDevice
+  (JNIEnv *env, const char* ifname, unsigned char* retbuf)
+{
     char style1dev[MAXPATHLEN];
     int fd;
     dl_phys_addr_req_t dlpareq;
@@ -1742,17 +1703,12 @@
     char buf[128];
     int flags = 0;
 
-   /**
-    * Device is in /dev
-    * e.g.: /dev/bge0
-    */
+    // Device is in /dev.  e.g.: /dev/bge0
     strcpy(style1dev, DEV_PREFIX);
     strcat(style1dev, ifname);
     if ((fd = open(style1dev, O_RDWR)) < 0) {
-        /*
-         * Can't open it. We probably are missing the privilege.
-         * We'll have to try something else
-         */
+         // Can't open it. We probably are missing the privilege.
+         // We'll have to try something else
          return 0;
     }
 
@@ -1763,7 +1719,8 @@
     msg.len = DL_PHYS_ADDR_REQ_SIZE;
 
     if (putmsg(fd, &msg, NULL, 0) < 0) {
-        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "putmsg failed");
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "putmsg failed");
         return -1;
     }
 
@@ -1773,12 +1730,14 @@
     msg.len = 0;
     msg.maxlen = sizeof (buf);
     if (getmsg(fd, &msg, NULL, &flags) < 0) {
-        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "getmsg failed");
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "getmsg failed");
         return -1;
     }
 
     if (msg.len < DL_PHYS_ADDR_ACK_SIZE || dlpaack->dl_primitive != DL_PHYS_ADDR_ACK) {
-        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "Couldn't obtain phys addr\n");
+        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
+                        "Couldn't obtain phys addr\n");
         return -1;
     }
 
@@ -1786,21 +1745,23 @@
     return dlpaack->dl_addr_length;
 }
 
-/**
- * Get the Hardware address (usually MAC address) for the named interface.
- * return puts the data in buf, and returns the length, in byte, of the
+/*
+ * Gets the Hardware address (usually MAC address) for the named interface.
+ * On return puts the data in buf, and returns the length, in byte, of the
  * MAC address. Returns -1 if there is no hardware address on that interface.
  */
-static int getMacAddress(JNIEnv *env, int sock, const char *ifname,  const struct in_addr* addr, unsigned char *buf) {
+static int getMacAddress
+  (JNIEnv *env, int sock, const char *ifname, const struct in_addr* addr,
+   unsigned char *buf)
+{
     struct arpreq arpreq;
     struct sockaddr_in* sin;
     struct sockaddr_in ipAddr;
     int len, i;
     struct lifreq lif;
 
-    /* First, try the new (S11) SIOCGLIFHWADDR ioctl(). If that fails
-     * try the old way.
-     */
+    // First, try the new (S11) SIOCGLIFHWADDR ioctl(). If that fails
+    // try the old way.
     memset(&lif, 0, sizeof(lif));
     strlcpy(lif.lifr_name, ifname, sizeof(lif.lifr_name));
 
@@ -1811,18 +1772,14 @@
         return sp->sdl_alen;
     }
 
-   /**
-    * On Solaris we have to use DLPI, but it will only work if we have
-    * privileged access (i.e. root). If that fails, we try a lookup
-    * in the ARP table, which requires an IPv4 address.
-    */
+    // On Solaris we have to use DLPI, but it will only work if we have
+    // privileged access (i.e. root). If that fails, we try a lookup
+    // in the ARP table, which requires an IPv4 address.
     if ((len = getMacFromDevice(env, ifname, buf))  == 0) {
-        /*DLPI failed - trying to do arp lookup*/
+        // DLPI failed - trying to do arp lookup
 
         if (addr == NULL) {
-            /**
-             * No IPv4 address for that interface, so can't do an ARP lookup.
-             */
+             // No IPv4 address for that interface, so can't do an ARP lookup.
              return -1;
          }
 
@@ -1843,10 +1800,7 @@
          memcpy(buf, &arpreq.arp_ha.sa_data[0], len );
     }
 
-    /*
-     * All bytes to 0 means no hardware address.
-     */
-
+    // All bytes to 0 means no hardware address.
     for (i = 0; i < len; i++) {
       if (buf[i] != 0)
          return len;
@@ -1857,56 +1811,58 @@
 
 static int getMTU(JNIEnv *env, int sock,  const char *ifname) {
     struct lifreq if2;
-
-    memset((char *) &if2, 0, sizeof(if2));
-    strcpy(if2.lifr_name, ifname);
+    memset((char *)&if2, 0, sizeof(if2));
+    strncpy(if2.lifr_name, ifname, sizeof(if2.lifr_name) - 1);
 
     if (ioctl(sock, SIOCGLIFMTU, (char *)&if2) < 0) {
-        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "IOCTL SIOCGLIFMTU failed");
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                    "ioctl SIOCGLIFMTU failed");
         return -1;
     }
 
-    return  if2.lifr_mtu;
+    return if2.lifr_mtu;
 }
 
+static int getFlags(int sock, const char *ifname, int *flags) {
+    struct lifreq if2;
+    memset((char *)&if2, 0, sizeof(if2));
+    strncpy(if2.lifr_name, ifname, sizeof(if2.lifr_name) - 1);
 
-static int getFlags(int sock, const char *ifname, int *flags) {
-     struct   lifreq lifr;
-     memset((caddr_t)&lifr, 0, sizeof(lifr));
-     strcpy((caddr_t)&(lifr.lifr_name), ifname);
+    if (ioctl(sock, SIOCGLIFFLAGS, (char *)&if2) < 0) {
+         return -1;
+    }
 
-     if (ioctl(sock, SIOCGLIFFLAGS, (char *)&lifr) < 0) {
-         return -1;
-     }
-
-     *flags = lifr.lifr_flags;
-     return 0;
+    *flags = if2.lifr_flags;
+    return 0;
 }
 
 
-#endif
+#endif  /* __solaris__ */
 
 
 /** BSD **/
 #ifdef _ALLBSD_SOURCE
-/* Open socket for further ioct calls, try v4 socket first and
- * if it falls return v6 socket
+
+/*
+ * Opens a socket for further ioct calls. Tries AF_INET socket first and
+ * if it falls return AF_INET6 socket.
  */
-
 #ifdef AF_INET6
-static int openSocketWithFallback(JNIEnv *env, const char *ifname){
+static int openSocketWithFallback(JNIEnv *env, const char *ifname) {
     int sock;
     struct ifreq if2;
 
      if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-         if (errno == EPROTONOSUPPORT){
-              if ( (sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0 ){
-                 NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException", "IPV6 Socket creation failed");
+         if (errno == EPROTONOSUPPORT) {
+              if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+                 NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                              "IPV6 Socket creation failed");
                  return -1;
               }
          }
          else{ // errno is not NOSUPPORT
-             NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException", "IPV4 Socket creation failed");
+             NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                          "IPV4 Socket creation failed");
              return -1;
          }
    }
@@ -1915,39 +1871,33 @@
 }
 
 #else
-static int openSocketWithFallback(JNIEnv *env, const char *ifname){
+static int openSocketWithFallback(JNIEnv *env, const char *ifname) {
     return openSocket(env,AF_INET);
 }
 #endif
 
 /*
- * Enumerates and returns all IPv4 interfaces
+ * Enumerates and returns all IPv4 interfaces.
  */
 static netif *enumIPv4Interfaces(JNIEnv *env, int sock, netif *ifs) {
     struct ifaddrs *ifa, *origifa;
 
     if (getifaddrs(&origifa) != 0) {
-        NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException",
-                         "getifaddrs() function failed");
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "getifaddrs() function failed");
         return ifs;
     }
 
     for (ifa = origifa; ifa != NULL; ifa = ifa->ifa_next) {
 
-        /*
-         * Skip non-AF_INET entries.
-         */
+        // Skip non-AF_INET entries.
         if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET)
             continue;
 
-        /*
-         * Add to the list.
-         */
+        // Add to the list.
         ifs = addif(env, sock, ifa->ifa_name, ifs, ifa->ifa_addr, AF_INET, 0);
 
-        /*
-         * If an exception occurred then free the list.
-         */
+        // If an exception occurred then free the list.
         if ((*env)->ExceptionOccurred(env)) {
             freeifaddrs(origifa);
             freeif(ifs);
@@ -1955,33 +1905,25 @@
         }
     }
 
-    /*
-     * Free socket and buffer
-     */
+    // Free socket and buffer
     freeifaddrs(origifa);
     return ifs;
 }
 
-
-/*
- * Enumerates and returns all IPv6 interfaces on Linux
- */
-
 #ifdef AF_INET6
 /*
  * Determines the prefix on BSD for IPv6 interfaces.
  */
-static
-int prefix(void *val, int size) {
+static int prefix(void *val, int size) {
     u_char *name = (u_char *)val;
-    int byte, bit, plen = 0;
+    int byte, bit, prefix = 0;
 
-    for (byte = 0; byte < size; byte++, plen += 8)
+    for (byte = 0; byte < size; byte++, prefix += 8)
         if (name[byte] != 0xff)
             break;
     if (byte == size)
-        return (plen);
-    for (bit = 7; bit != 0; bit--, plen++)
+        return prefix;
+    for (bit = 7; bit != 0; bit--, prefix++)
         if (!(name[byte] & (1 << bit)))
             break;
     for (; bit != 0; bit--)
@@ -1991,11 +1933,11 @@
     for (; byte < size; byte++)
         if (name[byte])
             return (0);
-    return (plen);
+    return prefix;
 }
 
 /*
- * Enumerates and returns all IPv6 interfaces on BSD
+ * Enumerates and returns all IPv6 interfaces on BSD.
  */
 static netif *enumIPv6Interfaces(JNIEnv *env, int sock, netif *ifs) {
     struct ifaddrs *ifa, *origifa;
@@ -2003,37 +1945,36 @@
     struct in6_ifreq ifr6;
 
     if (getifaddrs(&origifa) != 0) {
-        NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException",
-                         "getifaddrs() function failed");
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "getifaddrs() function failed");
         return ifs;
     }
 
     for (ifa = origifa; ifa != NULL; ifa = ifa->ifa_next) {
 
-        /*
-         * Skip non-AF_INET6 entries.
-         */
+        // Skip non-AF_INET6 entries.
         if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET6)
             continue;
 
         memset(&ifr6, 0, sizeof(ifr6));
         strlcpy(ifr6.ifr_name, ifa->ifa_name, sizeof(ifr6.ifr_name));
-        memcpy(&ifr6.ifr_addr, ifa->ifa_addr, MIN(sizeof(ifr6.ifr_addr), ifa->ifa_addr->sa_len));
+        memcpy(&ifr6.ifr_addr, ifa->ifa_addr,
+               MIN(sizeof(ifr6.ifr_addr), ifa->ifa_addr->sa_len));
 
         if (ioctl(sock, SIOCGIFNETMASK_IN6, (caddr_t)&ifr6) < 0) {
-            NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException",
-                             "ioctl SIOCGIFNETMASK_IN6 failed");
+            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                         "ioctl SIOCGIFNETMASK_IN6 failed");
             freeifaddrs(origifa);
             freeif(ifs);
             return NULL;
         }
 
-        /* Add to the list.  */
+        // Add to the list.
         sin6 = (struct sockaddr_in6 *)&ifr6.ifr_addr;
         ifs = addif(env, sock, ifa->ifa_name, ifs, ifa->ifa_addr, AF_INET6,
-                    prefix(&sin6->sin6_addr, sizeof(struct in6_addr)));
+            (short)prefix(&sin6->sin6_addr, sizeof(struct in6_addr)));
 
-        /* If an exception occurred then free the list.  */
+        // If an exception occurred then free the list.
         if ((*env)->ExceptionOccurred(env)) {
             freeifaddrs(origifa);
             freeif(ifs);
@@ -2041,22 +1982,19 @@
         }
     }
 
-    /*
-     * Free socket and ifaddrs buffer
-     */
+    // Free socket and ifaddrs buffer
     freeifaddrs(origifa);
     return ifs;
 }
 #endif
 
-static int getIndex(int sock, const char *name){
+static int getIndex(int sock, const char *name) {
 #ifdef __FreeBSD__
-     /*
-      * Try to get the interface index
-      * (Not supported on Solaris 2.6 or 7)
-      */
+    // Try to get the interface index
+    // (Not supported on Solaris 2.6 or 7)
     struct ifreq if2;
-    strcpy(if2.ifr_name, name);
+    memset((char *)&if2, 0, sizeof(if2));
+    strncpy(if2.ifr_name, name, sizeof(if2.ifr_name) - 1);
 
     if (ioctl(sock, SIOCGIFINDEX, (char *)&if2) < 0) {
         return -1;
@@ -2064,46 +2002,47 @@
 
     return if2.ifr_index;
 #else
-    /*
-     * Try to get the interface index using BSD specific if_nametoindex
-     */
+    // Try to get the interface index using BSD specific if_nametoindex
     int index = if_nametoindex(name);
     return (index == 0) ? -1 : index;
 #endif
 }
 
-/**
+/*
  * Returns the IPv4 broadcast address of a named interface, if it exists.
  * Returns 0 if it doesn't have one.
  */
-static struct sockaddr *getBroadcast(JNIEnv *env, int sock, const char *ifname, struct sockaddr *brdcast_store) {
-  struct sockaddr *ret = NULL;
-  struct ifreq if2;
+static struct sockaddr *getBroadcast
+  (JNIEnv *env, int sock, const char *ifname, struct sockaddr *brdcast_store)
+{
+    struct sockaddr *ret = NULL;
+    struct ifreq if2;
+    memset((char *)&if2, 0, sizeof(if2));
+    strncpy(if2.ifr_name, ifname, sizeof(if2.ifr_name) - 1);
 
-  memset((char *) &if2, 0, sizeof(if2));
-  strcpy(if2.ifr_name, ifname);
+    // Make sure the interface does have a broadcast address
+    if (ioctl(sock, SIOCGIFFLAGS, (char *)&if2) < 0) {
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "ioctl SIOCGIFFLAGS failed");
+        return ret;
+    }
 
-  /* Let's make sure the interface does have a broadcast address */
-  if (ioctl(sock, SIOCGIFFLAGS, (char *)&if2) < 0) {
-      NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "IOCTL SIOCGIFFLAGS failed");
-      return ret;
-  }
+    if (if2.ifr_flags & IFF_BROADCAST) {
+        // It does, let's retrieve it
+        if (ioctl(sock, SIOCGIFBRDADDR, (char *)&if2) < 0) {
+            NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                         "ioctl SIOCGIFBRDADDR failed");
+            return ret;
+        }
 
-  if (if2.ifr_flags & IFF_BROADCAST) {
-      /* It does, let's retrieve it*/
-      if (ioctl(sock, SIOCGIFBRDADDR, (char *)&if2) < 0) {
-          NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "IOCTL SIOCGIFBRDADDR failed");
-          return ret;
-      }
+        ret = brdcast_store;
+        memcpy(ret, &if2.ifr_broadaddr, sizeof(struct sockaddr));
+    }
 
-      ret = brdcast_store;
-      memcpy(ret, &if2.ifr_broadaddr, sizeof(struct sockaddr));
-  }
-
-  return ret;
+    return ret;
 }
 
-/**
+/*
  * Returns the IPv4 subnet prefix length (aka subnet mask) for the named
  * interface, if it has one, otherwise return -1.
  */
@@ -2111,12 +2050,12 @@
     unsigned int mask;
     short ret;
     struct ifreq if2;
-
-    memset((char *) &if2, 0, sizeof(if2));
-    strcpy(if2.ifr_name, ifname);
+    memset((char *)&if2, 0, sizeof(if2));
+    strncpy(if2.ifr_name, ifname, sizeof(if2.ifr_name) - 1);
 
     if (ioctl(sock, SIOCGIFNETMASK, (char *)&if2) < 0) {
-        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "IOCTL SIOCGIFNETMASK failed");
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "ioctl SIOCGIFNETMASK failed");
         return -1;
     }
 
@@ -2130,25 +2069,28 @@
     return ret;
 }
 
-/**
- * Get the Hardware address (usually MAC address) for the named interface.
+/*
+ * Gets the Hardware address (usually MAC address) for the named interface.
  * return puts the data in buf, and returns the length, in byte, of the
  * MAC address. Returns -1 if there is no hardware address on that interface.
  */
-static int getMacAddress(JNIEnv *env, int sock, const char* ifname, const struct in_addr* addr, unsigned char *buf) {
+static int getMacAddress
+  (JNIEnv *env, int sock, const char* ifname, const struct in_addr* addr,
+   unsigned char *buf)
+{
     struct ifaddrs *ifa0, *ifa;
     struct sockaddr *saddr;
     int i;
 
-    /* Grab the interface list */
+    // Grab the interface list
     if (!getifaddrs(&ifa0)) {
-        /* Cycle through the interfaces */
+        // Cycle through the interfaces
         for (i = 0, ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next, i++) {
             saddr = ifa->ifa_addr;
-            /* Link layer contains the MAC address */
+            // Link layer contains the MAC address
             if (saddr->sa_family == AF_LINK && !strcmp(ifname, ifa->ifa_name)) {
                 struct sockaddr_dl *sadl = (struct sockaddr_dl *) saddr;
-                /* Check the address is the correct length */
+                // Check the address is the correct length
                 if (sadl->sdl_alen == ETHER_ADDR_LEN) {
                     memcpy(buf, (sadl->sdl_data + sadl->sdl_nlen), ETHER_ADDR_LEN);
                     freeifaddrs(ifa0);
@@ -2164,12 +2106,12 @@
 
 static int getMTU(JNIEnv *env, int sock,  const char *ifname) {
     struct ifreq if2;
-
-    memset((char *) &if2, 0, sizeof(if2));
-    strcpy(if2.ifr_name, ifname);
+    memset((char *)&if2, 0, sizeof(if2));
+    strncpy(if2.ifr_name, ifname, sizeof(if2.ifr_name) - 1);
 
     if (ioctl(sock, SIOCGIFMTU, (char *)&if2) < 0) {
-        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "IOCTL SIOCGIFMTU failed");
+        NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException",
+                                     "ioctl SIOCGIFMTU failed");
         return -1;
     }
 
@@ -2177,22 +2119,20 @@
 }
 
 static int getFlags(int sock, const char *ifname, int *flags) {
-  struct ifreq if2;
-  int ret = -1;
+    struct ifreq if2;
+    int ret = -1;
+    memset((char *)&if2, 0, sizeof(if2));
+    strncpy(if2.ifr_name, ifname, sizeof(if2.ifr_name) - 1);
 
-  memset((char *) &if2, 0, sizeof(if2));
-  strcpy(if2.ifr_name, ifname);
+    if (ioctl(sock, SIOCGIFFLAGS, (char *)&if2) < 0) {
+        return -1;
+    }
 
-  if (ioctl(sock, SIOCGIFFLAGS, (char *)&if2) < 0){
-      return -1;
-  }
-
-  if (sizeof(if2.ifr_flags) == sizeof(short)) {
-    *flags = (if2.ifr_flags & 0xffff);
-  } else {
-    *flags = if2.ifr_flags;
-  }
-  return 0;
+    if (sizeof(if2.ifr_flags) == sizeof(short)) {
+        *flags = (if2.ifr_flags & 0xffff);
+    } else {
+        *flags = if2.ifr_flags;
+    }
+    return 0;
 }
-
-#endif
+#endif /* __ALLBSD_SOURCE__ */
--- a/src/java.httpclient/share/classes/java/net/http/CharsetToolkit.java	Thu May 12 15:15:04 2016 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,159 +0,0 @@
-/*
- * Copyright (c) 2016, 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
- */
-package java.net.http;
-
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.CharacterCodingException;
-import java.nio.charset.CharsetDecoder;
-import java.nio.charset.CharsetEncoder;
-import java.nio.charset.CoderResult;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-// The purpose of this class is to separate charset-related tasks from the main
-// WebSocket logic, simplifying where possible.
-//
-//     * Coders hide the differences between coding and flushing stages on the
-//       API level
-//     * Verifier abstracts the way the verification is performed
-//       (spoiler: it's a decoding into a throw-away buffer)
-//
-// Coding methods throw exceptions instead of returning coding result denoting
-// errors, since any kind of handling and recovery is not expected.
-final class CharsetToolkit {
-
-    private CharsetToolkit() { }
-
-    static final class Verifier {
-
-        private final CharsetDecoder decoder = UTF_8.newDecoder();
-        // A buffer used to check validity of UTF-8 byte stream by decoding it.
-        // The contents of this buffer are never used.
-        // The size is arbitrary, though it should probably be chosen from the
-        // performance perspective since it affects the total number of calls to
-        // decoder.decode() and amount of work in each of these calls
-        private final CharBuffer blackHole = CharBuffer.allocate(1024);
-
-        void verify(ByteBuffer in, boolean endOfInput)
-                throws CharacterCodingException {
-            while (true) {
-                // Since decoder.flush() cannot produce an error, it's not
-                // helpful for verification. Therefore this step is skipped.
-                CoderResult r = decoder.decode(in, blackHole, endOfInput);
-                if (r.isOverflow()) {
-                    blackHole.clear();
-                } else if (r.isUnderflow()) {
-                    break;
-                } else if (r.isError()) {
-                    r.throwException();
-                } else {
-                    // Should not happen
-                    throw new InternalError();
-                }
-            }
-        }
-
-        Verifier reset() {
-            decoder.reset();
-            return this;
-        }
-    }
-
-    static final class Encoder {
-
-        private final CharsetEncoder encoder = UTF_8.newEncoder();
-        private boolean coding = true;
-
-        CoderResult encode(CharBuffer in, ByteBuffer out, boolean endOfInput)
-                throws CharacterCodingException {
-
-            if (coding) {
-                CoderResult r = encoder.encode(in, out, endOfInput);
-                if (r.isOverflow()) {
-                    return r;
-                } else if (r.isUnderflow()) {
-                    if (endOfInput) {
-                        coding = false;
-                    } else {
-                        return r;
-                    }
-                } else if (r.isError()) {
-                    r.throwException();
-                } else {
-                    // Should not happen
-                    throw new InternalError();
-                }
-            }
-            assert !coding;
-            return encoder.flush(out);
-        }
-
-        Encoder reset() {
-            coding = true;
-            encoder.reset();
-            return this;
-        }
-    }
-
-    static CharBuffer decode(ByteBuffer in) throws CharacterCodingException {
-        return UTF_8.newDecoder().decode(in);
-    }
-
-    static final class Decoder {
-
-        private final CharsetDecoder decoder = UTF_8.newDecoder();
-        private boolean coding = true; // Either coding or flushing
-
-        CoderResult decode(ByteBuffer in, CharBuffer out, boolean endOfInput)
-                throws CharacterCodingException {
-
-            if (coding) {
-                CoderResult r = decoder.decode(in, out, endOfInput);
-                if (r.isOverflow()) {
-                    return r;
-                } else if (r.isUnderflow()) {
-                    if (endOfInput) {
-                        coding = false;
-                    } else {
-                        return r;
-                    }
-                } else if (r.isError()) {
-                    r.throwException();
-                } else {
-                    // Should not happen
-                    throw new InternalError();
-                }
-            }
-            assert !coding;
-            return decoder.flush(out);
-        }
-
-        Decoder reset() {
-            coding = true;
-            decoder.reset();
-            return this;
-        }
-    }
-}
--- a/src/java.httpclient/share/classes/java/net/http/RawChannel.java	Thu May 12 15:15:04 2016 +0000
+++ b/src/java.httpclient/share/classes/java/net/http/RawChannel.java	Thu May 12 18:47:08 2016 +0000
@@ -39,18 +39,22 @@
 
     private final HttpClientImpl client;
     private final HttpConnection connection;
-    private volatile boolean closed;
 
     private interface RawEvent {
 
-        /** must return the selector interest op flags OR'd. */
+        /**
+         * must return the selector interest op flags OR'd.
+         */
         int interestOps();
 
-        /** called when event occurs. */
+        /**
+         * called when event occurs.
+         */
         void handle();
     }
 
-    interface NonBlockingEvent extends RawEvent { }
+    interface NonBlockingEvent extends RawEvent {
+    }
 
     RawChannel(HttpClientImpl client, HttpConnection connection) {
         this.client = client;
@@ -127,12 +131,11 @@
 
     @Override
     public boolean isOpen() {
-        return !closed;
+        return connection.isOpen();
     }
 
     @Override
     public void close() throws IOException {
-        closed = true;
         connection.close();
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.httpclient/share/classes/java/net/http/WS.java	Thu May 12 18:47:08 2016 +0000
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2015, 2016, 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 java.net.http;
+
+import java.io.IOException;
+import java.net.ProtocolException;
+import java.net.http.WSOpeningHandshake.Result;
+import java.nio.ByteBuffer;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+import static java.lang.System.Logger.Level.ERROR;
+import static java.lang.System.Logger.Level.WARNING;
+import static java.net.http.WSUtils.logger;
+import static java.util.Objects.requireNonNull;
+
+/*
+ * A WebSocket client.
+ *
+ * Consists of two independent parts; a transmitter responsible for sending
+ * messages, and a receiver which notifies the listener of incoming messages.
+ */
+final class WS implements WebSocket {
+
+    private final String subprotocol;
+    private final RawChannel channel;
+    private final WSTransmitter transmitter;
+    private final WSReceiver receiver;
+    private final Listener listener;
+    private final Object stateLock = new Object();
+    private volatile State state = State.CONNECTED;
+    private final CompletableFuture<Void> whenClosed = new CompletableFuture<>();
+
+    static CompletableFuture<WebSocket> newInstanceAsync(WSBuilder b) {
+        CompletableFuture<Result> result = new WSOpeningHandshake(b).performAsync();
+        Listener listener = b.getListener();
+        Executor executor = b.getClient().executorService();
+        return result.thenApply(r -> {
+            WS ws = new WS(listener, r.subprotocol, r.channel, executor);
+            ws.start();
+            return ws;
+        });
+    }
+
+    private WS(Listener listener, String subprotocol, RawChannel channel,
+               Executor executor) {
+        this.listener = wrapListener(listener);
+        this.channel = channel;
+        this.subprotocol = subprotocol;
+        Consumer<Throwable> errorHandler = error -> {
+            if (error == null) {
+                throw new InternalError();
+            }
+            // If the channel is closed, we need to update the state, to denote
+            // there's no point in trying to continue using WebSocket
+            if (!channel.isOpen()) {
+                synchronized (stateLock) {
+                    tryChangeState(State.ERROR);
+                }
+            }
+        };
+        transmitter = new WSTransmitter(executor, channel, errorHandler);
+        receiver = new WSReceiver(this.listener, this, executor, channel);
+    }
+
+    private void start() {
+        receiver.start();
+    }
+
+    @Override
+    public CompletableFuture<Void> sendText(ByteBuffer message, boolean isLast) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public CompletableFuture<Void> sendText(CharSequence message, boolean isLast) {
+        requireNonNull(message, "message");
+        synchronized (stateLock) {
+            checkState();
+            return transmitter.sendText(message, isLast);
+        }
+    }
+
+    @Override
+    public CompletableFuture<Void> sendText(Stream<? extends CharSequence> message) {
+        requireNonNull(message, "message");
+        synchronized (stateLock) {
+            checkState();
+            return transmitter.sendText(message);
+        }
+    }
+
+    @Override
+    public CompletableFuture<Void> sendBinary(ByteBuffer message, boolean isLast) {
+        requireNonNull(message, "message");
+        synchronized (stateLock) {
+            checkState();
+            return transmitter.sendBinary(message, isLast);
+        }
+    }
+
+    @Override
+    public CompletableFuture<Void> sendPing(ByteBuffer message) {
+        requireNonNull(message, "message");
+        synchronized (stateLock) {
+            checkState();
+            return transmitter.sendPing(message);
+        }
+    }
+
+    @Override
+    public CompletableFuture<Void> sendPong(ByteBuffer message) {
+        requireNonNull(message, "message");
+        synchronized (stateLock) {
+            checkState();
+            return transmitter.sendPong(message);
+        }
+    }
+
+    @Override
+    public CompletableFuture<Void> sendClose(CloseCode code, CharSequence reason) {
+        requireNonNull(code, "code");
+        requireNonNull(reason, "reason");
+        synchronized (stateLock) {
+            return doSendClose(() -> transmitter.sendClose(code, reason));
+        }
+    }
+
+    @Override
+    public CompletableFuture<Void> sendClose() {
+        synchronized (stateLock) {
+            return doSendClose(() -> transmitter.sendClose());
+        }
+    }
+
+    private CompletableFuture<Void> doSendClose(Supplier<CompletableFuture<Void>> s) {
+        checkState();
+        boolean closeChannel = false;
+        synchronized (stateLock) {
+            if (state == State.CLOSED_REMOTELY) {
+                closeChannel = tryChangeState(State.CLOSED);
+            } else {
+                tryChangeState(State.CLOSED_LOCALLY);
+            }
+        }
+        CompletableFuture<Void> sent = s.get();
+        if (closeChannel) {
+            sent.whenComplete((v, t) -> {
+                try {
+                    channel.close();
+                } catch (IOException e) {
+                    logger.log(ERROR, "Error transitioning to state " + State.CLOSED, e);
+                }
+            });
+        }
+        return sent;
+    }
+
+    @Override
+    public long request(long n) {
+        if (n < 0L) {
+            throw new IllegalArgumentException("The number must not be negative: " + n);
+        }
+        return receiver.request(n);
+    }
+
+    @Override
+    public String getSubprotocol() {
+        return subprotocol;
+    }
+
+    @Override
+    public boolean isClosed() {
+        return state.isTerminal();
+    }
+
+    @Override
+    public void abort() throws IOException {
+        synchronized (stateLock) {
+            tryChangeState(State.ABORTED);
+        }
+        channel.close();
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + "[" + state + "]";
+    }
+
+    private void checkState() {
+        if (state.isTerminal() || state == State.CLOSED_LOCALLY) {
+            throw new IllegalStateException("WebSocket is closed [" + state + "]");
+        }
+    }
+
+    /*
+     * Wraps the user's listener passed to the constructor into own listener to
+     * intercept transitions to terminal states (onClose and onError) and to act
+     * upon exceptions and values from the user's listener.
+     */
+    private Listener wrapListener(Listener listener) {
+        return new Listener() {
+
+            // Listener's method MUST be invoked in a happen-before order
+            private final Object visibilityLock = new Object();
+
+            @Override
+            public void onOpen(WebSocket webSocket) {
+                synchronized (visibilityLock) {
+                    listener.onOpen(webSocket);
+                }
+            }
+
+            @Override
+            public CompletionStage<?> onText(WebSocket webSocket, Text message,
+                                             MessagePart part) {
+                synchronized (visibilityLock) {
+                    return listener.onText(webSocket, message, part);
+                }
+            }
+
+            @Override
+            public CompletionStage<?> onBinary(WebSocket webSocket, ByteBuffer message,
+                                               MessagePart part) {
+                synchronized (visibilityLock) {
+                    return listener.onBinary(webSocket, message, part);
+                }
+            }
+
+            @Override
+            public CompletionStage<?> onPing(WebSocket webSocket, ByteBuffer message) {
+                synchronized (visibilityLock) {
+                    return listener.onPing(webSocket, message);
+                }
+            }
+
+            @Override
+            public CompletionStage<?> onPong(WebSocket webSocket, ByteBuffer message) {
+                synchronized (visibilityLock) {
+                    return listener.onPong(webSocket, message);
+                }
+            }
+
+            @Override
+            public void onClose(WebSocket webSocket, Optional<CloseCode> code, String reason) {
+                synchronized (stateLock) {
+                    if (state == State.CLOSED_REMOTELY || state.isTerminal()) {
+                        throw new InternalError("Unexpected onClose in state " + state);
+                    } else if (state == State.CLOSED_LOCALLY) {
+                        try {
+                            channel.close();
+                        } catch (IOException e) {
+                            logger.log(ERROR, "Error transitioning to state " + State.CLOSED, e);
+                        }
+                        tryChangeState(State.CLOSED);
+                    } else if (state == State.CONNECTED) {
+                        tryChangeState(State.CLOSED_REMOTELY);
+                    }
+                }
+                synchronized (visibilityLock) {
+                    listener.onClose(webSocket, code, reason);
+                }
+            }
+
+            @Override
+            public void onError(WebSocket webSocket, Throwable error) {
+                // An error doesn't necessarily mean the connection must be
+                // closed automatically
+                if (!channel.isOpen()) {
+                    synchronized (stateLock) {
+                        tryChangeState(State.ERROR);
+                    }
+                } else if (error instanceof ProtocolException
+                        && error.getCause() instanceof WSProtocolException) {
+                    WSProtocolException cause = (WSProtocolException) error.getCause();
+                    logger.log(WARNING, "Failing connection {0}, reason: ''{1}''",
+                            webSocket, cause.getMessage());
+                    CloseCode cc = cause.getCloseCode();
+                    transmitter.sendClose(cc, "").whenComplete((v, t) -> {
+                        synchronized (stateLock) {
+                            tryChangeState(State.ERROR);
+                        }
+                        try {
+                            channel.close();
+                        } catch (IOException e) {
+                            logger.log(ERROR, e);
+                        }
+                    });
+                }
+                synchronized (visibilityLock) {
+                    listener.onError(webSocket, error);
+                }
+            }
+        };
+    }
+
+    private boolean tryChangeState(State newState) {
+        assert Thread.holdsLock(stateLock);
+        if (state.isTerminal()) {
+            return false;
+        }
+        state = newState;
+        if (newState.isTerminal()) {
+            whenClosed.complete(null);
+        }
+        return true;
+    }
+
+    CompletionStage<Void> whenClosed() {
+        return whenClosed;
+    }
+
+    /*
+     * WebSocket connection internal state.
+     */
+    private enum State {
+
+        /*
+         * Initial WebSocket state. The WebSocket is connected (i.e. remains in
+         * this state) unless proven otherwise. For example, by reading or
+         * writing operations on the channel.
+         */
+        CONNECTED,
+
+        /*
+         * A Close message has been received by the client. No more messages
+         * will be received.
+         */
+        CLOSED_REMOTELY,
+
+        /*
+         * A Close message has been sent by the client. No more messages can be
+         * sent.
+         */
+        CLOSED_LOCALLY,
+
+        /*
+         * Close messages has been both sent and received (closing handshake)
+         * and TCP connection closed. Closed _cleanly_ in terms of RFC 6455.
+         */
+        CLOSED,
+
+        /*
+         * The connection has been aborted by the client. Closed not _cleanly_
+         * in terms of RFC 6455.
+         */
+        ABORTED,
+
+        /*
+         * The connection has been terminated due to a protocol or I/O error.
+         * Might happen during sending or receiving.
+         */
+        ERROR;
+
+        /*
+         * Returns `true` if this state is terminal. If WebSocket has transited
+         * to such a state, if remains in it forever.
+         */
+        boolean isTerminal() {
+            return this == CLOSED || this == ABORTED || this == ERROR;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.httpclient/share/classes/java/net/http/WSBuilder.java	Thu May 12 18:47:08 2016 +0000
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2015, 2016, 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 java.net.http;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNull;
+
+final class WSBuilder implements WebSocket.Builder {
+
+    private static final Set<String> FORBIDDEN_HEADERS =
+            new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
+
+    static {
+        List<String> headers = List.of("Connection", "Upgrade",
+                "Sec-WebSocket-Accept", "Sec-WebSocket-Extensions",
+                "Sec-WebSocket-Key", "Sec-WebSocket-Protocol",
+                "Sec-WebSocket-Version");
+        FORBIDDEN_HEADERS.addAll(headers);
+    }
+
+    private final URI uri;
+    private final HttpClient client;
+    private final LinkedHashMap<String, List<String>> headers = new LinkedHashMap<>();
+    private final WebSocket.Listener listener;
+    private Collection<String> subprotocols = Collections.emptyList();
+    private long timeout;
+    private TimeUnit timeUnit;
+
+    WSBuilder(URI uri, HttpClient client, WebSocket.Listener listener) {
+        checkURI(requireNonNull(uri, "uri"));
+        requireNonNull(client, "client");
+        requireNonNull(listener, "listener");
+        this.uri = uri;
+        this.listener = listener;
+        this.client = client;
+    }
+
+    @Override
+    public WebSocket.Builder header(String name, String value) {
+        requireNonNull(name, "name");
+        requireNonNull(value, "value");
+        if (FORBIDDEN_HEADERS.contains(name)) {
+            throw new IllegalArgumentException(
+                    format("Header '%s' is used in the WebSocket Protocol", name));
+        }
+        List<String> values = headers.computeIfAbsent(name, n -> new LinkedList<>());
+        values.add(value);
+        return this;
+    }
+
+    @Override
+    public WebSocket.Builder subprotocols(String mostPreferred, String... lesserPreferred) {
+        requireNonNull(mostPreferred, "mostPreferred");
+        requireNonNull(lesserPreferred, "lesserPreferred");
+        this.subprotocols = checkSubprotocols(mostPreferred, lesserPreferred);
+        return this;
+    }
+
+    @Override
+    public WebSocket.Builder connectTimeout(long timeout, TimeUnit unit) {
+        if (timeout < 0) {
+            throw new IllegalArgumentException("Negative timeout: " + timeout);
+        }
+        requireNonNull(unit, "unit");
+        this.timeout = timeout;
+        this.timeUnit = unit;
+        return this;
+    }
+
+    @Override
+    public CompletableFuture<WebSocket> buildAsync() {
+        return WS.newInstanceAsync(this);
+    }
+
+    private static URI checkURI(URI uri) {
+        String s = uri.getScheme();
+        if (!("ws".equalsIgnoreCase(s) || "wss".equalsIgnoreCase(s))) {
+            throw new IllegalArgumentException
+                    ("URI scheme not ws or wss (RFC 6455 3.): " + s);
+        }
+        String fragment = uri.getFragment();
+        if (fragment != null) {
+            throw new IllegalArgumentException(format
+                    ("Fragment not allowed in a WebSocket URI (RFC 6455 3.): '%s'",
+                            fragment));
+        }
+        return uri;
+    }
+
+    URI getUri() { return uri; }
+
+    HttpClient getClient() { return client; }
+
+    Map<String, List<String>> getHeaders() {
+        LinkedHashMap<String, List<String>> copy = new LinkedHashMap<>(headers.size());
+        headers.forEach((name, values) -> copy.put(name, new LinkedList<>(values)));
+        return copy;
+    }
+
+    WebSocket.Listener getListener() { return listener; }
+
+    Collection<String> getSubprotocols() {
+        return new ArrayList<>(subprotocols);
+    }
+
+    long getTimeout() { return timeout; }
+
+    TimeUnit getTimeUnit() { return timeUnit; }
+
+    private static Collection<String> checkSubprotocols(String mostPreferred,
+                                                        String... lesserPreferred) {
+        checkSubprotocolSyntax(mostPreferred, "mostPreferred");
+        LinkedHashSet<String> sp = new LinkedHashSet<>(1 + lesserPreferred.length);
+        sp.add(mostPreferred);
+        for (int i = 0; i < lesserPreferred.length; i++) {
+            String p = lesserPreferred[i];
+            String location = format("lesserPreferred[%s]", i);
+            requireNonNull(p, location);
+            checkSubprotocolSyntax(p, location);
+            if (!sp.add(p)) {
+                throw new IllegalArgumentException(format(
+                        "Duplicate subprotocols (RFC 6455 4.1.): '%s'", p));
+            }
+        }
+        return sp;
+    }
+
+    private static void checkSubprotocolSyntax(String subprotocol, String location) {
+        if (subprotocol.isEmpty()) {
+            throw new IllegalArgumentException
+                    ("Subprotocol name is empty (RFC 6455 4.1.): " + location);
+        }
+        if (!subprotocol.chars().allMatch(c -> 0x21 <= c && c <= 0x7e)) {
+            throw new IllegalArgumentException
+                    ("Subprotocol name contains illegal characters (RFC 6455 4.1.): "
+                            + location);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.httpclient/share/classes/java/net/http/WSCharsetToolkit.java	Thu May 12 18:47:08 2016 +0000
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2016, 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
+ */
+package java.net.http;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.StandardCharsets;
+
+import static java.lang.System.Logger.Level.WARNING;
+import static java.net.http.WSUtils.EMPTY_BYTE_BUFFER;
+import static java.net.http.WSUtils.logger;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/*
+ * A collection of tools for UTF-8 coding.
+ */
+final class WSCharsetToolkit {
+
+    private WSCharsetToolkit() { }
+
+    static final class Encoder {
+
+        private final CharsetEncoder encoder = UTF_8.newEncoder();
+
+        ByteBuffer encode(CharBuffer in) throws CharacterCodingException {
+            return encoder.encode(in);
+        }
+
+        // TODO:
+        // ByteBuffer[] encode(CharBuffer in) throws CharacterCodingException {
+        //     return encoder.encode(in);
+        // }
+    }
+
+    static CharBuffer decode(ByteBuffer in) throws CharacterCodingException {
+        return UTF_8.newDecoder().decode(in);
+    }
+
+    static final class Decoder {
+
+        private final CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
+
+        {
+            decoder.onMalformedInput(CodingErrorAction.REPORT);
+            decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
+        }
+
+        private ByteBuffer leftovers = EMPTY_BYTE_BUFFER;
+
+        WSShared<CharBuffer> decode(WSShared<ByteBuffer> in, boolean endOfInput)
+                throws CharacterCodingException {
+            ByteBuffer b;
+            int rem = leftovers.remaining();
+            if (rem != 0) {
+                // TODO: We won't need this wasteful allocation & copying when
+                // JDK-8155222 has been resolved
+                b = ByteBuffer.allocate(rem + in.remaining());
+                b.put(leftovers).put(in.buffer()).flip();
+            } else {
+                b = in.buffer();
+            }
+            CharBuffer out = CharBuffer.allocate(b.remaining());
+            CoderResult r = decoder.decode(b, out, endOfInput);
+            if (r.isError()) {
+                r.throwException();
+            }
+            if (b.hasRemaining()) {
+                leftovers = ByteBuffer.allocate(b.remaining()).put(b).flip();
+            } else {
+                leftovers = EMPTY_BYTE_BUFFER;
+            }
+            // Since it's UTF-8, the assumption is leftovers.remaining() < 4
+            // (i.e. small). Otherwise a shared buffer should be used
+            if (!(leftovers.remaining() < 4)) {
+                logger.log(WARNING,
+                        "The size of decoding leftovers is greater than expected: {0}",
+                        leftovers.remaining());
+            }
+            b.position(b.limit()); // As if we always read to the end
+            in.dispose();
+            // Decoder promises that in the case of endOfInput == true:
+            // "...any remaining undecoded input will be treated as being
+            // malformed"
+            assert !(endOfInput && leftovers.hasRemaining()) : endOfInput + ", " + leftovers;
+            if (endOfInput) {
+                r = decoder.flush(out);
+                decoder.reset();
+                if (r.isOverflow()) {
+                    // FIXME: for now I know flush() does nothing. But the
+                    // implementation of UTF8 decoder might change. And if now
+                    // flush() is a no-op, it is not guaranteed to remain so in
+                    // the future
+                    throw new InternalError("Not yet implemented");
+                }
+            }
+            out.flip();
+            return WSShared.wrap(out);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.httpclient/share/classes/java/net/http/WSDisposable.java	Thu May 12 18:47:08 2016 +0000
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016, 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  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  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  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 java.net.http;
+
+interface WSDisposable {
+
+    void dispose();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.httpclient/share/classes/java/net/http/WSDisposableText.java	Thu May 12 18:47:08 2016 +0000
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2016, 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  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  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  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 java.net.http;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+
+final class WSDisposableText implements WebSocket.Text, WSDisposable {
+
+    private final WSShared<CharBuffer> text;
+
+    WSDisposableText(WSShared<CharBuffer> text) {
+        this.text = text;
+    }
+
+    @Override
+    public int length() {
+        return text.buffer().length();
+    }
+
+    @Override
+    public char charAt(int index) {
+        return text.buffer().charAt(index);
+    }
+
+    @Override
+    public CharSequence subSequence(int start, int end) {
+        return text.buffer().subSequence(start, end);
+    }
+
+    @Override
+    public ByteBuffer asByteBuffer() {
+        throw new UnsupportedOperationException("To be removed from the API");
+    }
+
+    @Override
+    public String toString() {
+        return text.buffer().toString();
+    }
+
+    @Override
+    public void dispose() {
+        text.dispose();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.httpclient/share/classes/java/net/http/WSFrame.java	Thu May 12 18:47:08 2016 +0000
@@ -0,0 +1,486 @@
+/*
+ * Copyright (c) 2016, 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  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  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  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 java.net.http;
+
+import java.nio.ByteBuffer;
+
+import static java.lang.String.format;
+import static java.net.http.WSFrame.Opcode.ofCode;
+import static java.net.http.WSUtils.dump;
+
+/*
+ * A collection of utilities for reading, writing, and masking frames.
+ */
+final class WSFrame {
+
+    private WSFrame() { }
+
+    static final int MAX_HEADER_SIZE_BYTES = 2 + 8 + 4;
+
+    enum Opcode {
+
+        CONTINUATION   (0x0),
+        TEXT           (0x1),
+        BINARY         (0x2),
+        NON_CONTROL_0x3(0x3),
+        NON_CONTROL_0x4(0x4),
+        NON_CONTROL_0x5(0x5),
+        NON_CONTROL_0x6(0x6),
+        NON_CONTROL_0x7(0x7),
+        CLOSE          (0x8),
+        PING           (0x9),
+        PONG           (0xA),
+        CONTROL_0xB    (0xB),
+        CONTROL_0xC    (0xC),
+        CONTROL_0xD    (0xD),
+        CONTROL_0xE    (0xE),
+        CONTROL_0xF    (0xF);
+
+        private static final Opcode[] opcodes;
+
+        static {
+            Opcode[] values = values();
+            opcodes = new Opcode[values.length];
+            for (Opcode c : values) {
+                assert opcodes[c.code] == null
+                        : WSUtils.dump(c, c.code, opcodes[c.code]);
+                opcodes[c.code] = c;
+            }
+        }
+
+        private final byte code;
+        private final char shiftedCode;
+        private final String description;
+
+        Opcode(int code) {
+            this.code = (byte) code;
+            this.shiftedCode = (char) (code << 8);
+            this.description = format("%x (%s)", code, name());
+        }
+
+        boolean isControl() {
+            return (code & 0x8) != 0;
+        }
+
+        static Opcode ofCode(int code) {
+            return opcodes[code & 0xF];
+        }
+
+        @Override
+        public String toString() {
+            return description;
+        }
+    }
+
+    /*
+     * A utility to mask payload data.
+     */
+    static final class Masker {
+
+        private final ByteBuffer acc = ByteBuffer.allocate(8);
+        private final int[] maskBytes = new int[4];
+        private int offset;
+        private long maskLong;
+
+        /*
+         * Sets up the mask.
+         */
+        Masker mask(int value) {
+            acc.clear().putInt(value).putInt(value).flip();
+            for (int i = 0; i < maskBytes.length; i++) {
+                maskBytes[i] = acc.get(i);
+            }
+            offset = 0;
+            maskLong = acc.getLong(0);
+            return this;
+        }
+
+        /*
+         * Reads as many bytes as possible from the given input buffer, writing
+         * the resulting masked bytes to the given output buffer.
+         *
+         * src.remaining() <= dst.remaining() // TODO: do we need this restriction?
+         * 'src' and 'dst' can be the same ByteBuffer
+         */
+        Masker applyMask(ByteBuffer src, ByteBuffer dst) {
+            if (src.remaining() > dst.remaining()) {
+                throw new IllegalArgumentException(dump(src, dst));
+            }
+            begin(src, dst);
+            loop(src, dst);
+            end(src, dst);
+            return this;
+        }
+
+        // Applying the remaining of the mask (strictly not more than 3 bytes)
+        // byte-wise
+        private void begin(ByteBuffer src, ByteBuffer dst) {
+            if (offset > 0) {
+                for (int i = src.position(), j = dst.position();
+                     offset < 4 && i <= src.limit() - 1 && j <= dst.limit() - 1;
+                     i++, j++, offset++) {
+                    dst.put(j, (byte) (src.get(i) ^ maskBytes[offset]));
+                    dst.position(j + 1);
+                    src.position(i + 1);
+                }
+                offset &= 3;
+            }
+        }
+
+        private void loop(ByteBuffer src, ByteBuffer dst) {
+            int i = src.position();
+            int j = dst.position();
+            final int srcLim = src.limit() - 8;
+            final int dstLim = dst.limit() - 8;
+            for (; i <= srcLim && j <= dstLim; i += 8, j += 8) {
+                dst.putLong(j, (src.getLong(i) ^ maskLong));
+            }
+            if (i > src.limit()) {
+                src.position(i - 8);
+            } else {
+                src.position(i);
+            }
+            if (j > dst.limit()) {
+                dst.position(j - 8);
+            } else {
+                dst.position(j);
+            }
+        }
+
+        // Applying the mask to the remaining bytes byte-wise (don't make any
+        // assumptions on how many, hopefully not more than 7 for 64bit arch)
+        private void end(ByteBuffer src, ByteBuffer dst) {
+            for (int i = src.position(), j = dst.position();
+                 i <= src.limit() - 1 && j <= dst.limit() - 1;
+                 i++, j++, offset = (offset + 1) & 3) { // offset cycle through 0..3
+                dst.put(j, (byte) (src.get(i) ^ maskBytes[offset]));
+                src.position(i + 1);
+                dst.position(j + 1);
+            }
+        }
+    }
+
+    /*
+     * A builder of frame headers, capable of writing to a given buffer.
+     *
+     * The builder does not enforce any protocol-level rules, it simply writes
+     * a header structure to the buffer. The order of calls to intermediate
+     * methods is not significant.
+     */
+    static final class HeaderBuilder {
+
+        private char firstChar;
+        private long payloadLen;
+        private int maskingKey;
+        private boolean mask;
+
+        HeaderBuilder fin(boolean value) {
+            if (value) {
+                firstChar |=  0b10000000_00000000;
+            } else {
+                firstChar &= ~0b10000000_00000000;
+            }
+            return this;
+        }
+
+        HeaderBuilder rsv1(boolean value) {
+            if (value) {
+                firstChar |=  0b01000000_00000000;
+            } else {
+                firstChar &= ~0b01000000_00000000;
+            }
+            return this;
+        }
+
+        HeaderBuilder rsv2(boolean value) {
+            if (value) {
+                firstChar |=  0b00100000_00000000;
+            } else {
+                firstChar &= ~0b00100000_00000000;
+            }
+            return this;
+        }
+
+        HeaderBuilder rsv3(boolean value) {
+            if (value) {
+                firstChar |=  0b00010000_00000000;
+            } else {
+                firstChar &= ~0b00010000_00000000;
+            }
+            return this;
+        }
+
+        HeaderBuilder opcode(Opcode value) {
+            firstChar = (char) ((firstChar & 0xF0FF) | value.shiftedCode);
+            return this;
+        }
+
+        HeaderBuilder payloadLen(long value) {
+            payloadLen = value;
+            firstChar &= 0b11111111_10000000; // Clear previous payload length leftovers
+            if (payloadLen < 126) {
+                firstChar |= payloadLen;
+            } else if (payloadLen < 65535) {
+                firstChar |= 126;
+            } else {
+                firstChar |= 127;
+            }
+            return this;
+        }
+
+        HeaderBuilder mask(int value) {
+            firstChar |= 0b00000000_10000000;
+            maskingKey = value;
+            mask = true;
+            return this;
+        }
+
+        HeaderBuilder noMask() {
+            firstChar &= ~0b00000000_10000000;
+            mask = false;
+            return this;
+        }
+
+        /*
+         * Writes the header to the given buffer.
+         *
+         * The buffer must have at least MAX_HEADER_SIZE_BYTES remaining. The
+         * buffer's position is incremented by the number of bytes written.
+         */
+        void build(ByteBuffer buffer) {
+            buffer.putChar(firstChar);
+            if (payloadLen >= 126) {
+                if (payloadLen < 65535) {
+                    buffer.putChar((char) payloadLen);
+                } else {
+                    buffer.putLong(payloadLen);
+                }
+            }
+            if (mask) {
+                buffer.putInt(maskingKey);
+            }
+        }
+    }
+
+    /*
+     * A consumer of frame parts.
+     *
+     * Guaranteed to be called in the following order by the Frame.Reader:
+     *
+     *     fin rsv1 rsv2 rsv3 opcode mask payloadLength maskingKey? payloadData+ endFrame
+     */
+    interface Consumer {
+
+        void fin(boolean value);
+
+        void rsv1(boolean value);
+
+        void rsv2(boolean value);
+
+        void rsv3(boolean value);
+
+        void opcode(Opcode value);
+
+        void mask(boolean value);
+
+        void payloadLen(long value);
+
+        void maskingKey(int value);
+
+        /*
+         * Called when a part of the payload is ready to be consumed.
+         *
+         * Though may not yield a complete payload in a single invocation, i.e.
+         *
+         *     data.remaining() < payloadLen
+         *
+         * the sum of `data.remaining()` passed to all invocations of this
+         * method will be equal to 'payloadLen', reported in
+         * `void payloadLen(long value)`
+         *
+         * No unmasking is done.
+         */
+        void payloadData(WSShared<ByteBuffer> data, boolean isLast);
+
+        void endFrame(); // TODO: remove (payloadData(isLast=true)) should be enough
+    }
+
+    /*
+     * A Reader of Frames.
+     *
+     * No protocol-level rules are enforced, only frame structure.
+     */
+    static final class Reader {
+
+        private static final int AWAITING_FIRST_BYTE  =  1;
+        private static final int AWAITING_SECOND_BYTE =  2;
+        private static final int READING_16_LENGTH    =  4;
+        private static final int READING_64_LENGTH    =  8;
+        private static final int READING_MASK         = 16;
+        private static final int READING_PAYLOAD      = 32;
+
+        // A private buffer used to simplify multi-byte integers reading
+        private final ByteBuffer accumulator = ByteBuffer.allocate(8);
+        private int state = AWAITING_FIRST_BYTE;
+        private boolean mask;
+        private long payloadLength;
+
+        /*
+         * Reads at most one frame from the given buffer invoking the consumer's
+         * methods corresponding to the frame elements found.
+         *
+         * As much of the frame's payload, if any, is read. The buffers position
+         * is updated to reflect the number of bytes read.
+         *
+         * Throws WSProtocolException if the frame is malformed.
+         */
+        void readFrame(WSShared<ByteBuffer> shared, Consumer consumer) {
+            ByteBuffer input = shared.buffer();
+            loop:
+            while (true) {
+                byte b;
+                switch (state) {
+                    case AWAITING_FIRST_BYTE:
+                        if (!input.hasRemaining()) {
+                            break loop;
+                        }
+                        b = input.get();
+                        consumer.fin( (b & 0b10000000) != 0);
+                        consumer.rsv1((b & 0b01000000) != 0);
+                        consumer.rsv2((b & 0b00100000) != 0);
+                        consumer.rsv3((b & 0b00010000) != 0);
+                        consumer.opcode(ofCode(b));
+                        state = AWAITING_SECOND_BYTE;
+                        continue loop;
+                    case AWAITING_SECOND_BYTE:
+                        if (!input.hasRemaining()) {
+                            break loop;
+                        }
+                        b = input.get();
+                        consumer.mask(mask = (b & 0b10000000) != 0);
+                        byte p1 = (byte) (b & 0b01111111);
+                        if (p1 < 126) {
+                            assert p1 >= 0 : p1;
+                            consumer.payloadLen(payloadLength = p1);
+                            state = mask ? READING_MASK : READING_PAYLOAD;
+                        } else if (p1 < 127) {
+                            state = READING_16_LENGTH;
+                        } else {
+                            state = READING_64_LENGTH;
+                        }
+                        continue loop;
+                    case READING_16_LENGTH:
+                        if (!input.hasRemaining()) {
+                            break loop;
+                        }
+                        b = input.get();
+                        if (accumulator.put(b).position() < 2) {
+                            continue loop;
+                        }
+                        payloadLength = accumulator.flip().getChar();
+                        if (payloadLength < 126) {
+                            throw notMinimalEncoding(payloadLength, 2);
+                        }
+                        consumer.payloadLen(payloadLength);
+                        accumulator.clear();
+                        state = mask ? READING_MASK : READING_PAYLOAD;
+                        continue loop;
+                    case READING_64_LENGTH:
+                        if (!input.hasRemaining()) {
+                            break loop;
+                        }
+                        b = input.get();
+                        if (accumulator.put(b).position() < 8) {
+                            continue loop;
+                        }
+                        payloadLength = accumulator.flip().getLong();
+                        if (payloadLength < 0) {
+                            throw negativePayload(payloadLength);
+                        } else if (payloadLength < 65535) {
+                            throw notMinimalEncoding(payloadLength, 8);
+                        }
+                        consumer.payloadLen(payloadLength);
+                        accumulator.clear();
+                        state = mask ? READING_MASK : READING_PAYLOAD;
+                        continue loop;
+                    case READING_MASK:
+                        if (!input.hasRemaining()) {
+                            break loop;
+                        }
+                        b = input.get();
+                        if (accumulator.put(b).position() != 4) {
+                            continue loop;
+                        }
+                        consumer.maskingKey(accumulator.flip().getInt());
+                        accumulator.clear();
+                        state = READING_PAYLOAD;
+                        continue loop;
+                    case READING_PAYLOAD:
+                        // This state does not require any bytes to be available
+                        // in the input buffer in order to proceed
+                        boolean fullyRead;
+                        int limit;
+                        if (payloadLength <= input.remaining()) {
+                            limit = input.position() + (int) payloadLength;
+                            payloadLength = 0;
+                            fullyRead = true;
+                        } else {
+                            limit = input.limit();
+                            payloadLength -= input.remaining();
+                            fullyRead = false;
+                        }
+                        // FIXME: consider a case where payloadLen != 0,
+                        // but input.remaining() == 0
+                        //
+                        // There shouldn't be an invocation of payloadData with
+                        // an empty buffer, as it would be an artifact of
+                        // reading
+                        consumer.payloadData(shared.share(input.position(), limit), fullyRead);
+                        // Update the position manually, since reading the
+                        // payload doesn't advance buffer's position
+                        input.position(limit);
+                        if (fullyRead) {
+                            consumer.endFrame();
+                            state = AWAITING_FIRST_BYTE;
+                        }
+                        break loop;
+                    default:
+                        throw new InternalError(String.valueOf(state));
+                }
+            }
+        }
+
+        private static WSProtocolException negativePayload(long payloadLength) {
+            return new WSProtocolException
+                    ("5.2.", format("Negative 64-bit payload length %s", payloadLength));
+        }
+
+        private static WSProtocolException notMinimalEncoding(long payloadLength, int numBytes) {
+            return new WSProtocolException
+                    ("5.2.", format("Payload length (%s) is not encoded with minimal number (%s) of bytes",
+                            payloadLength, numBytes));
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.httpclient/share/classes/java/net/http/WSFrameConsumer.java	Thu May 12 18:47:08 2016 +0000
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 2016, 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 java.net.http;
+
+import java.net.http.WSFrame.Opcode;
+import java.net.http.WebSocket.MessagePart;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static java.lang.String.format;
+import static java.lang.System.Logger.Level.TRACE;
+import static java.net.http.WSUtils.dump;
+import static java.net.http.WSUtils.logger;
+import static java.net.http.WebSocket.CloseCode.NOT_CONSISTENT;
+import static java.net.http.WebSocket.CloseCode.of;
+import static java.util.Objects.requireNonNull;
+
+/*
+ * Consumes frame parts and notifies a message consumer, when there is
+ * sufficient data to produce a message, or part thereof.
+ *
+ * Data consumed but not yet translated is accumulated until it's sufficient to
+ * form a message.
+ */
+final class WSFrameConsumer implements WSFrame.Consumer {
+
+    private final AtomicInteger invocationOrder = new AtomicInteger();
+
+    private final WSMessageConsumer output;
+    private final WSCharsetToolkit.Decoder decoder = new WSCharsetToolkit.Decoder();
+    private boolean fin;
+    private Opcode opcode, originatingOpcode;
+    private MessagePart part = MessagePart.WHOLE;
+    private long payloadLen;
+    private WSShared<ByteBuffer> binaryData;
+
+    WSFrameConsumer(WSMessageConsumer output) {
+        this.output = requireNonNull(output);
+    }
+
+    @Override
+    public void fin(boolean value) {
+        assert invocationOrder.compareAndSet(0, 1) : dump(invocationOrder, value);
+        if (logger.isLoggable(TRACE)) {
+            // Checked for being loggable because of autoboxing of 'value'
+            logger.log(TRACE, "Reading fin: {0}", value);
+        }
+        fin = value;
+    }
+
+    @Override
+    public void rsv1(boolean value) {
+        assert invocationOrder.compareAndSet(1, 2) : dump(invocationOrder, value);
+        if (logger.isLoggable(TRACE)) {
+            logger.log(TRACE, "Reading rsv1: {0}", value);
+        }
+        if (value) {
+            throw new WSProtocolException("5.2.", "rsv1 bit is set unexpectedly");
+        }
+    }
+
+    @Override
+    public void rsv2(boolean value) {
+        assert invocationOrder.compareAndSet(2, 3) : dump(invocationOrder, value);
+        if (logger.isLoggable(TRACE)) {
+            logger.log(TRACE, "Reading rsv2: {0}", value);
+        }
+        if (value) {
+            throw new WSProtocolException("5.2.", "rsv2 bit is set unexpectedly");
+        }
+    }
+
+    @Override
+    public void rsv3(boolean value) {
+        assert invocationOrder.compareAndSet(3, 4) : dump(invocationOrder, value);
+        if (logger.isLoggable(TRACE)) {
+            logger.log(TRACE, "Reading rsv3: {0}", value);
+        }
+        if (value) {
+            throw new WSProtocolException("5.2.", "rsv3 bit is set unexpectedly");
+        }
+    }
+
+    @Override
+    public void opcode(Opcode v) {
+        assert invocationOrder.compareAndSet(4, 5) : dump(invocationOrder, v);
+        logger.log(TRACE, "Reading opcode: {0}", v);
+        if (v == Opcode.PING || v == Opcode.PONG || v == Opcode.CLOSE) {
+            if (!fin) {
+                throw new WSProtocolException("5.5.", "A fragmented control frame " + v);
+            }
+            opcode = v;
+        } else if (v == Opcode.TEXT || v == Opcode.BINARY) {
+            if (originatingOpcode != null) {
+                throw new WSProtocolException
+                        ("5.4.", format("An unexpected frame %s (fin=%s)", v, fin));
+            }
+            opcode = v;
+            if (!fin) {
+                originatingOpcode = v;
+            }
+        } else if (v == Opcode.CONTINUATION) {
+            if (originatingOpcode == null) {
+                throw new WSProtocolException
+                        ("5.4.", format("An unexpected frame %s (fin=%s)", v, fin));
+            }
+            opcode = v;
+        } else {
+            throw new WSProtocolException("5.2.", "An unknown opcode " + v);
+        }
+    }
+
+    @Override
+    public void mask(boolean value) {
+        assert invocationOrder.compareAndSet(5, 6) : dump(invocationOrder, value);
+        if (logger.isLoggable(TRACE)) {
+            logger.log(TRACE, "Reading mask: {0}", value);
+        }
+        if (value) {
+            throw new WSProtocolException
+                    ("5.1.", "Received a masked frame from the server");
+        }
+    }
+
+    @Override
+    public void payloadLen(long value) {
+        assert invocationOrder.compareAndSet(6, 7) : dump(invocationOrder, value);
+        if (logger.isLoggable(TRACE)) {
+            logger.log(TRACE, "Reading payloadLen: {0}", value);
+        }
+        if (opcode.isControl()) {
+            if (value > 125) {
+                throw new WSProtocolException
+                        ("5.5.", format("A control frame %s has a payload length of %s",
+                                opcode, value));
+            }
+            assert Opcode.CLOSE.isControl();
+            if (opcode == Opcode.CLOSE && value == 1) {
+                throw new WSProtocolException
+                        ("5.5.1.", "A Close frame's status code is only 1 byte long");
+            }
+        }
+        payloadLen = value;
+    }
+
+    @Override
+    public void maskingKey(int value) {
+        assert false : dump(invocationOrder, value);
+    }
+
+    @Override
+    public void payloadData(WSShared<ByteBuffer> data, boolean isLast) {
+        assert invocationOrder.compareAndSet(7, isLast ? 8 : 7)
+                : dump(invocationOrder, data, isLast);
+        if (logger.isLoggable(TRACE)) {
+            logger.log(TRACE, "Reading payloadData: data={0}, isLast={1}", data, isLast);
+        }
+        if (opcode.isControl()) {
+            if (binaryData != null) {
+                binaryData.put(data);
+                data.dispose();
+            } else if (!isLast) {
+                // The first chunk of the message
+                int remaining = data.remaining();
+                // It shouldn't be 125, otherwise the next chunk will be of size
+                // 0, which is not what Reader promises to deliver (eager
+                // reading)
+                assert remaining < 125 : dump(remaining);
+                WSShared<ByteBuffer> b = WSShared.wrap(ByteBuffer.allocate(125)).put(data);
+                data.dispose();
+                binaryData = b; // Will be disposed by the user
+            } else {
+                // The only chunk; will be disposed by the user
+                binaryData = data.position(data.limit()); // FIXME: remove this hack
+            }
+        } else {
+            part = determinePart(isLast);
+            boolean text = opcode == Opcode.TEXT || originatingOpcode == Opcode.TEXT;
+            if (!text) {
+                output.onBinary(part, data);
+            } else {
+                boolean binaryNonEmpty = data.hasRemaining();
+                WSShared<CharBuffer> textData;
+                try {
+                    textData = decoder.decode(data, part.isLast());
+                } catch (CharacterCodingException e) {
+                    throw new WSProtocolException
+                            ("5.6.", "Invalid UTF-8 sequence in frame " + opcode, NOT_CONSISTENT, e);
+                }
+                if (!(binaryNonEmpty && !textData.hasRemaining())) {
+                    // If there's a binary data, that result in no text, then we
+                    // don't deliver anything
+                    output.onText(part, new WSDisposableText(textData));
+                }
+            }
+        }
+    }
+
+    @Override
+    public void endFrame() {
+        assert invocationOrder.compareAndSet(8, 0) : dump(invocationOrder);
+        if (opcode.isControl()) {
+            binaryData.flip();
+        }
+        switch (opcode) {
+            case CLOSE:
+                WebSocket.CloseCode cc;
+                String reason;
+                if (payloadLen == 0) {
+                    cc = null;
+                    reason = "";
+                } else {
+                    ByteBuffer b = binaryData.buffer();
+                    int len = b.remaining();
+                    assert 2 <= len && len <= 125 : dump(len, payloadLen);
+                    try {
+                        cc = of(b.getChar());
+                        reason = WSCharsetToolkit.decode(b).toString();
+                    } catch (IllegalArgumentException e) {
+                        throw new WSProtocolException
+                                ("5.5.1", "Incorrect status code", e);
+                    } catch (CharacterCodingException e) {
+                        throw new WSProtocolException
+                                ("5.5.1", "Close reason is a malformed UTF-8 sequence", e);
+                    }
+                }
+                binaryData.dispose(); // Manual dispose
+                output.onClose(cc, reason);
+                break;
+            case PING:
+                output.onPing(binaryData);
+                binaryData = null;
+                break;
+            case PONG:
+                output.onPong(binaryData);
+                binaryData = null;
+                break;
+            default:
+                assert opcode == Opcode.TEXT || opcode == Opcode.BINARY
+                        || opcode == Opcode.CONTINUATION : dump(opcode);
+                if (fin) {
+                    // It is always the last chunk:
+                    // either TEXT(FIN=TRUE)/BINARY(FIN=TRUE) or CONT(FIN=TRUE)
+                    originatingOpcode = null;
+                }
+                break;
+        }
+        payloadLen = 0;
+        opcode = null;
+    }
+
+    private MessagePart determinePart(boolean isLast) {
+        boolean lastChunk = fin && isLast;
+        switch (part) {
+            case LAST:
+            case WHOLE:
+                return lastChunk ? MessagePart.WHOLE : MessagePart.FIRST;
+            case FIRST:
+            case PART:
+                return lastChunk ? MessagePart.LAST : MessagePart.PART;
+            default:
+                throw new InternalError(String.valueOf(part));
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.httpclient/share/classes/java/net/http/WSMessageConsumer.java	Thu May 12 18:47:08 2016 +0000
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2016, 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 java.net.http;
+
+import java.net.http.WebSocket.CloseCode;
+import java.net.http.WebSocket.MessagePart;
+import java.nio.ByteBuffer;
+
+interface WSMessageConsumer {
+
+    void onText(MessagePart part, WSDisposableText data);
+
+    void onBinary(MessagePart part, WSShared<ByteBuffer> data);
+
+    void onPing(WSShared<ByteBuffer> data);
+
+    void onPong(WSShared<ByteBuffer> data);
+
+    void onClose(CloseCode code, CharSequence reason);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.httpclient/share/classes/java/net/http/WSMessageSender.java	Thu May 12 18:47:08 2016 +0000
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2016, 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  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  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  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 java.net.http;
+
+import java.net.http.WSFrame.HeaderBuilder;
+import java.net.http.WSFrame.Masker;
+import java.net.http.WSOutgoingMessage.Binary;
+import java.net.http.WSOutgoingMessage.Close;
+import java.net.http.WSOutgoingMessage.Ping;
+import java.net.http.WSOutgoingMessage.Pong;
+import java.net.http.WSOutgoingMessage.StreamedText;
+import java.net.http.WSOutgoingMessage.Text;
+import java.net.http.WSOutgoingMessage.Visitor;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.security.SecureRandom;
+import java.util.function.Consumer;
+
+import static java.net.http.WSFrame.MAX_HEADER_SIZE_BYTES;
+import static java.net.http.WSFrame.Opcode.BINARY;
+import static java.net.http.WSFrame.Opcode.CLOSE;
+import static java.net.http.WSFrame.Opcode.CONTINUATION;
+import static java.net.http.WSFrame.Opcode.PING;
+import static java.net.http.WSFrame.Opcode.PONG;
+import static java.net.http.WSFrame.Opcode.TEXT;
+import static java.util.Objects.requireNonNull;
+
+/*
+ * A Sender of outgoing messages.  Given a message,
+ *
+ *     1) constructs the frame
+ *     2) initiates the channel write
+ *     3) notifies when the message has been sent
+ */
+final class WSMessageSender {
+
+    private final Visitor frameBuilderVisitor;
+    private final Consumer<Throwable> completionEventConsumer;
+    private final WSWriter writer;
+    private final ByteBuffer[] buffers = new ByteBuffer[2];
+
+    WSMessageSender(RawChannel channel, Consumer<Throwable> completionEventConsumer) {
+        // Single reusable buffer that holds a header
+        this.buffers[0] = ByteBuffer.allocateDirect(MAX_HEADER_SIZE_BYTES);
+        this.frameBuilderVisitor = new FrameBuilderVisitor();
+        this.completionEventConsumer = completionEventConsumer;
+        this.writer = new WSWriter(channel, this.completionEventConsumer);
+    }
+
+    /*
+     * Tries to send the given message fully. Invoked once per message.
+     */
+    boolean trySendFully(WSOutgoingMessage m) {
+        requireNonNull(m);
+        synchronized (this) {
+            try {
+                return sendNow(m);
+            } catch (Exception e) {
+                completionEventConsumer.accept(e);
+                return false;
+            }
+        }
+    }
+
+    private boolean sendNow(WSOutgoingMessage m) {
+        buffers[0].clear();
+        m.accept(frameBuilderVisitor);
+        buffers[0].flip();
+        return writer.tryWriteFully(buffers);
+    }
+
+    /*
+     * Builds and initiates a write of a frame, from a given message.
+     */
+    class FrameBuilderVisitor implements Visitor {
+
+        private final SecureRandom random = new SecureRandom();
+        private final WSCharsetToolkit.Encoder encoder = new WSCharsetToolkit.Encoder();
+        private final Masker masker = new Masker();
+        private final HeaderBuilder headerBuilder = new HeaderBuilder();
+        private boolean previousIsLast = true;
+
+        @Override
+        public void visit(Text message) {
+            try {
+                buffers[1] = encoder.encode(CharBuffer.wrap(message.characters));
+            } catch (CharacterCodingException e) {
+                completionEventConsumer.accept(e);
+                return;
+            }
+            int mask = random.nextInt();
+            maskAndRewind(buffers[1], mask);
+            headerBuilder
+                    .fin(message.isLast)
+                    .opcode(previousIsLast ? TEXT : CONTINUATION)
+                    .payloadLen(buffers[1].remaining())
+                    .mask(mask)
+                    .build(buffers[0]);
+            previousIsLast = message.isLast;
+        }
+
+        @Override
+        public void visit(StreamedText streamedText) {
+            throw new IllegalArgumentException("Not yet implemented");
+        }
+
+        @Override
+        public void visit(Binary message) {
+            buffers[1] = message.bytes;
+            int mask = random.nextInt();
+            maskAndRewind(buffers[1], mask);
+            headerBuilder
+                    .fin(message.isLast)
+                    .opcode(previousIsLast ? BINARY : CONTINUATION)
+                    .payloadLen(message.bytes.remaining())
+                    .mask(mask)
+                    .build(buffers[0]);
+            previousIsLast = message.isLast;
+        }
+
+        @Override
+        public void visit(Ping message) {
+            buffers[1] = message.bytes;
+            int mask = random.nextInt();
+            maskAndRewind(buffers[1], mask);
+            headerBuilder
+                    .fin(true)
+                    .opcode(PING)
+                    .payloadLen(message.bytes.remaining())
+                    .mask(mask)
+                    .build(buffers[0]);
+        }
+
+        @Override
+        public void visit(Pong message) {
+            buffers[1] = message.bytes;
+            int mask = random.nextInt();
+            maskAndRewind(buffers[1], mask);
+            headerBuilder
+                    .fin(true)
+                    .opcode(PONG)
+                    .payloadLen(message.bytes.remaining())
+                    .mask(mask)
+                    .build(buffers[0]);
+        }
+
+        @Override
+        public void visit(Close message) {
+            buffers[1] = message.bytes;
+            int mask = random.nextInt();
+            maskAndRewind(buffers[1], mask);
+            headerBuilder
+                    .fin(true)
+                    .opcode(CLOSE)
+                    .payloadLen(buffers[1].remaining())
+                    .mask(mask)
+                    .build(buffers[0]);
+        }
+
+        private void maskAndRewind(ByteBuffer b, int mask) {
+            int oldPos = b.position();
+            masker.mask(mask).applyMask(b, b);
+            b.position(oldPos);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.httpclient/share/classes/java/net/http/WSOpeningHandshake.java	Thu May 12 18:47:08 2016 +0000
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2015, 2016, 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 java.net.http;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import static java.lang.String.format;
+import static java.lang.System.Logger.Level.TRACE;
+import static java.net.http.WSUtils.logger;
+import static java.net.http.WSUtils.webSocketSpecViolation;
+
+final class WSOpeningHandshake {
+
+    private static final String HEADER_CONNECTION = "Connection";
+    private static final String HEADER_UPGRADE = "Upgrade";
+    private static final String HEADER_ACCEPT = "Sec-WebSocket-Accept";
+    private static final String HEADER_EXTENSIONS = "Sec-WebSocket-Extensions";
+    private static final String HEADER_KEY = "Sec-WebSocket-Key";
+    private static final String HEADER_PROTOCOL = "Sec-WebSocket-Protocol";
+    private static final String HEADER_VERSION = "Sec-WebSocket-Version";
+    private static final String VALUE_VERSION = "13"; // WebSocket's lucky number
+
+    private static final SecureRandom srandom = new SecureRandom();
+
+    private final MessageDigest sha1;
+
+    {
+        try {
+            sha1 = MessageDigest.getInstance("SHA-1");
+        } catch (NoSuchAlgorithmException e) {
+            // Shouldn't happen:
+            // SHA-1 must be available in every Java platform implementation
+            throw new InternalError("Minimum platform requirements are not met", e);
+        }
+    }
+
+    private final HttpRequest request;
+    private final Collection<String> subprotocols;
+    private final String nonce;
+
+    WSOpeningHandshake(WSBuilder b) {
+        URI httpURI = createHttpUri(b.getUri());
+        HttpRequest.Builder requestBuilder = b.getClient().request(httpURI);
+        if (b.getTimeUnit() != null) {
+            requestBuilder.timeout(b.getTimeUnit(), b.getTimeout());
+        }
+        Collection<String> s = b.getSubprotocols();
+        if (!s.isEmpty()) {
+            String p = s.stream().collect(Collectors.joining(", "));
+            requestBuilder.header(HEADER_PROTOCOL, p);
+        }
+        requestBuilder.header(HEADER_VERSION, VALUE_VERSION);
+        this.nonce = createNonce();
+        requestBuilder.header(HEADER_KEY, this.nonce);
+        this.request = requestBuilder.GET();
+        HttpRequestImpl r = (HttpRequestImpl) this.request;
+        r.isWebSocket(true);
+        r.setSystemHeader(HEADER_UPGRADE, "websocket");
+        r.setSystemHeader(HEADER_CONNECTION, "Upgrade");
+        this.subprotocols = s;
+    }
+
+    private URI createHttpUri(URI webSocketUri) {
+        // FIXME: check permission for WebSocket URI and translate it into http/https permission
+        logger.log(TRACE, "->createHttpUri(''{0}'')", webSocketUri);
+        String httpScheme = webSocketUri.getScheme().equalsIgnoreCase("ws")
+                ? "http"
+                : "https";
+        try {
+            URI uri = new URI(httpScheme,
+                    webSocketUri.getUserInfo(),
+                    webSocketUri.getHost(),
+                    webSocketUri.getPort(),
+                    webSocketUri.getPath(),
+                    webSocketUri.getQuery(),
+                    null);
+            logger.log(TRACE, "<-createHttpUri: ''{0}''", uri);
+            return uri;
+        } catch (URISyntaxException e) {
+            // Shouldn't happen: URI invariant
+            throw new InternalError("Error translating WebSocket URI to HTTP URI", e);
+        }
+    }
+
+    CompletableFuture<Result> performAsync() {
+        // The whole dancing with thenCompose instead of thenApply is because
+        // WebSocketHandshakeException is a checked exception
+        return request.responseAsync()
+                .thenCompose(response -> {
+                    try {
+                        Result result = handleResponse(response);
+                        return CompletableFuture.completedFuture(result);
+                    } catch (WebSocketHandshakeException e) {
+                        return CompletableFuture.failedFuture(e);
+                    }
+                });
+    }
+
+    private Result handleResponse(HttpResponse response) throws WebSocketHandshakeException {
+        // By this point all redirects, authentications, etc. (if any) must have
+        // been done by the httpClient used by the WebSocket; so only 101 is
+        // expected
+        int statusCode = response.statusCode();
+        if (statusCode != 101) {
+            String m = webSocketSpecViolation("1.3.",
+                    "Unable to complete handshake; HTTP response status code "
+                            + statusCode
+            );
+            throw new WebSocketHandshakeException(m, response);
+        }
+        HttpHeaders h = response.headers();
+        checkHeader(h, response, HEADER_UPGRADE, v -> v.equalsIgnoreCase("websocket"));
+        checkHeader(h, response, HEADER_CONNECTION, v -> v.equalsIgnoreCase("Upgrade"));
+        checkVersion(response, h);
+        checkAccept(response, h);
+        checkExtensions(response, h);
+        String subprotocol = checkAndReturnSubprotocol(response, h);
+        RawChannel channel = ((HttpResponseImpl) response).rawChannel();
+        return new Result(subprotocol, channel);
+    }
+
+    private void checkExtensions(HttpResponse response, HttpHeaders headers)
+            throws WebSocketHandshakeException {
+        List<String> ext = headers.allValues(HEADER_EXTENSIONS);
+        if (!ext.isEmpty()) {
+            String m = webSocketSpecViolation("4.1.",
+                    "Server responded with extension(s) though none were requested "
+                            + Arrays.toString(ext.toArray())
+            );
+            throw new WebSocketHandshakeException(m, response);
+        }
+    }
+
+    private String checkAndReturnSubprotocol(HttpResponse response, HttpHeaders headers)
+            throws WebSocketHandshakeException {
+        assert response.statusCode() == 101 : response.statusCode();
+        List<String> sp = headers.allValues(HEADER_PROTOCOL);
+        int size = sp.size();
+        if (size == 0) {
+            // In this case the subprotocol requested (if any) by the client
+            // doesn't matter. If there is no such header in the response, then
+            // the server doesn't want to use any subprotocol
+            return null;
+        } else if (size > 1) {
+            // We don't know anything about toString implementation of this
+            // list, so let's create an array
+            String m = webSocketSpecViolation("4.1.",
+                    "Server responded with multiple subprotocols: "
+                            + Arrays.toString(sp.toArray())
+            );
+            throw new WebSocketHandshakeException(m, response);
+        } else {
+            String selectedSubprotocol = sp.get(0);
+            if (this.subprotocols.contains(selectedSubprotocol)) {
+                return selectedSubprotocol;
+            } else {
+                String m = webSocketSpecViolation("4.1.",
+                        format("Server responded with a subprotocol " +
+                                        "not among those requested: '%s'",
+                                selectedSubprotocol));
+                throw new WebSocketHandshakeException(m, response);
+            }
+        }
+    }
+
+    private void checkAccept(HttpResponse response, HttpHeaders headers)
+            throws WebSocketHandshakeException {
+        assert response.statusCode() == 101 : response.statusCode();
+        String x = nonce + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+        sha1.update(x.getBytes(StandardCharsets.ISO_8859_1));
+        String expected = Base64.getEncoder().encodeToString(sha1.digest());
+        checkHeader(headers, response, HEADER_ACCEPT, actual -> actual.trim().equals(expected));
+    }
+
+    private void checkVersion(HttpResponse response, HttpHeaders headers)
+            throws WebSocketHandshakeException {
+        assert response.statusCode() == 101 : response.statusCode();
+        List<String> versions = headers.allValues(HEADER_VERSION);
+        if (versions.isEmpty()) { // That's normal and expected
+            return;
+        }
+        String m = webSocketSpecViolation("4.4.",
+                "Server responded with version(s) "
+                        + Arrays.toString(versions.toArray()));
+        throw new WebSocketHandshakeException(m, response);
+    }
+
+    //
+    // Checks whether there's only one value for the header with the given name
+    // and the value satisfies the predicate.
+    //
+    private static void checkHeader(HttpHeaders headers,
+                                    HttpResponse response,
+                                    String headerName,
+                                    Predicate<? super String> valuePredicate)
+            throws WebSocketHandshakeException {
+        assert response.statusCode() == 101 : response.statusCode();
+        List<String> values = headers.allValues(headerName);
+        if (values.isEmpty()) {
+            String m = webSocketSpecViolation("4.1.",
+                    format("Server response field '%s' is missing", headerName)
+            );
+            throw new WebSocketHandshakeException(m, response);
+        } else if (values.size() > 1) {
+            String m = webSocketSpecViolation("4.1.",
+                    format("Server response field '%s' has multiple values", headerName)
+            );
+            throw new WebSocketHandshakeException(m, response);
+        }
+        if (!valuePredicate.test(values.get(0))) {
+            String m = webSocketSpecViolation("4.1.",
+                    format("Server response field '%s' is incorrect", headerName)
+            );
+            throw new WebSocketHandshakeException(m, response);
+        }
+    }
+
+    private static String createNonce() {
+        byte[] bytes = new byte[16];
+        srandom.nextBytes(bytes);
+        return Base64.getEncoder().encodeToString(bytes);
+    }
+
+    static final class Result {
+
+        final String subprotocol;
+        final RawChannel channel;
+
+        private Result(String subprotocol, RawChannel channel) {
+            this.subprotocol = subprotocol;
+            this.channel = channel;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.httpclient/share/classes/java/net/http/WSOutgoingMessage.java	Thu May 12 18:47:08 2016 +0000
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2016, 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  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  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  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 java.net.http;
+
+import java.nio.ByteBuffer;
+import java.util.stream.Stream;
+
+abstract class WSOutgoingMessage {
+
+    interface Visitor {
+        void visit(Text message);
+        void visit(StreamedText message);
+        void visit(Binary message);
+        void visit(Ping message);
+        void visit(Pong message);
+        void visit(Close message);
+    }
+
+    abstract void accept(Visitor visitor);
+
+    private WSOutgoingMessage() { }
+
+    static final class Text extends WSOutgoingMessage {
+
+        public final boolean isLast;
+        public final CharSequence characters;
+
+        Text(boolean isLast, CharSequence characters) {
+            this.isLast = isLast;
+            this.characters = characters;
+        }
+
+        @Override
+        void accept(Visitor visitor) {
+            visitor.visit(this);
+        }
+
+        @Override
+        public String toString() {
+            return WSUtils.toStringSimple(this) + "[isLast=" + isLast
+                    + ", characters=" + WSUtils.toString(characters) + "]";
+        }
+    }
+
+    static final class StreamedText extends WSOutgoingMessage {
+
+        public final Stream<? extends CharSequence> characters;
+
+        StreamedText(Stream<? extends CharSequence> characters) {
+            this.characters = characters;
+        }
+
+        @Override
+        void accept(Visitor visitor) {
+            visitor.visit(this);
+        }
+
+        @Override
+        public String toString() {
+            return WSUtils.toStringSimple(this) + "[characters=" + characters + "]";
+        }
+    }
+
+    static final class Binary extends WSOutgoingMessage {
+
+        public final boolean isLast;
+        public final ByteBuffer bytes;
+
+        Binary(boolean isLast, ByteBuffer bytes) {
+            this.isLast = isLast;
+            this.bytes = bytes;
+        }
+
+        @Override
+        void accept(Visitor visitor) {
+            visitor.visit(this);
+        }
+
+        @Override
+        public String toString() {
+            return WSUtils.toStringSimple(this) + "[isLast=" + isLast
+                    + ", bytes=" + WSUtils.toString(bytes) + "]";
+        }
+    }
+
+    static final class Ping extends WSOutgoingMessage {
+
+        public final ByteBuffer bytes;
+
+        Ping(ByteBuffer bytes) {
+            this.bytes = bytes;
+        }
+
+        @Override
+        void accept(Visitor visitor) {
+            visitor.visit(this);
+        }
+
+        @Override
+        public String toString() {
+            return WSUtils.toStringSimple(this) + "[" + WSUtils.toString(bytes) + "]";
+        }
+    }
+
+    static final class Pong extends WSOutgoingMessage {
+
+        public final ByteBuffer bytes;
+
+        Pong(ByteBuffer bytes) {
+            this.bytes = bytes;
+        }
+
+        @Override
+        void accept(Visitor visitor) {
+            visitor.visit(this);
+        }
+
+        @Override
+        public String toString() {
+            return WSUtils.toStringSimple(this) + "[" + WSUtils.toString(bytes) + "]";
+        }
+    }
+
+    static final class Close extends WSOutgoingMessage {
+
+        public final ByteBuffer bytes;
+
+        Close(ByteBuffer bytes) {
+            this.bytes = bytes;
+        }
+
+        @Override
+        void accept(Visitor visitor) {
+            visitor.visit(this);
+        }
+
+        @Override
+        public String toString() {
+            return WSUtils.toStringSimple(this) + "[" + WSUtils.toString(bytes) + "]";
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.httpclient/share/classes/java/net/http/WSProtocolException.java	Thu May 12 18:47:08 2016 +0000
@@ -0,0 +1,68 @@
+package java.net.http;
+
+import java.net.http.WebSocket.CloseCode;
+
+import static java.net.http.WebSocket.CloseCode.PROTOCOL_ERROR;
+import static java.util.Objects.requireNonNull;
+
+//
+// Special kind of exception closed from the outside world.
+//
+// Used as a "marker exception" for protocol issues in the incoming data, so the
+// implementation could close the connection and specify an appropriate status
+// code.
+//
+// A separate 'section' argument makes it more uncomfortable to be lazy and to
+// leave a relevant spec reference empty :-) As a bonus all messages have the
+// same style.
+//
+final class WSProtocolException extends RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+    private final CloseCode closeCode;
+    private final String section;
+
+    WSProtocolException(String section, String detail) {
+        this(section, detail, PROTOCOL_ERROR);
+    }
+
+    WSProtocolException(String section, String detail, Throwable cause) {
+        this(section, detail, PROTOCOL_ERROR, cause);
+    }
+
+    private WSProtocolException(String section, String detail, CloseCode code) {
+        super(formatMessage(section, detail));
+        this.closeCode = requireNonNull(code);
+        this.section = section;
+    }
+
+    WSProtocolException(String section, String detail, CloseCode code,
+                        Throwable cause) {
+        super(formatMessage(section, detail), cause);
+        this.closeCode = requireNonNull(code);
+        this.section = section;
+    }
+
+    private static String formatMessage(String section, String detail) {
+        if (requireNonNull(section).isEmpty()) {
+            throw new IllegalArgumentException();
+        }
+        if (requireNonNull(detail).isEmpty()) {
+            throw new IllegalArgumentException();
+        }
+        return WSUtils.webSocketSpecViolation(section, detail);
+    }
+
+    CloseCode getCloseCode() {
+        return closeCode;
+    }
+
+    public String getSection() {
+        return section;
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + "[" + closeCode + "]";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.httpclient/share/classes/java/net/http/WSReceiver.java	Thu May 12 18:47:08 2016 +0000
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2016, 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 java.net.http;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.net.ProtocolException;
+import java.net.http.WebSocket.Listener;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.util.Optional;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+
+import static java.lang.System.Logger.Level.ERROR;
+import static java.net.http.WSUtils.EMPTY_BYTE_BUFFER;
+import static java.net.http.WSUtils.logger;
+
+/*
+ * Receives incoming data from the channel and converts it into a sequence of
+ * messages, which are then passed to the listener.
+ */
+final class WSReceiver {
+
+    private final Listener listener;
+    private final WebSocket webSocket;
+    private final Supplier<WSShared<ByteBuffer>> buffersSupplier =
+            new WSSharedPool<>(() -> ByteBuffer.allocateDirect(32768), 2);
+    private final RawChannel channel;
+    private final RawChannel.NonBlockingEvent channelEvent;
+    private final WSSignalHandler handler;
+    private final AtomicLong demand = new AtomicLong();
+    private final AtomicBoolean readable = new AtomicBoolean();
+    private boolean started;
+    private volatile boolean closed;
+    private final WSFrame.Reader reader = new WSFrame.Reader();
+    private final WSFrameConsumer frameConsumer;
+    private WSShared<ByteBuffer> buf = WSShared.wrap(EMPTY_BYTE_BUFFER);
+    private WSShared<ByteBuffer> data; // TODO: initialize with leftovers from the RawChannel
+
+    WSReceiver(Listener listener, WebSocket webSocket, Executor executor,
+               RawChannel channel) {
+        this.listener = listener;
+        this.webSocket = webSocket;
+        this.channel = channel;
+        handler = new WSSignalHandler(executor, this::react);
+        channelEvent = createChannelEvent();
+        this.frameConsumer = new WSFrameConsumer(new MessageConsumer());
+    }
+
+    private void react() {
+        synchronized (this) {
+            while (demand.get() > 0 && !closed) {
+                try {
+                    if (data == null) {
+                        if (!getData()) {
+                            break;
+                        }
+                    }
+                    reader.readFrame(data, frameConsumer);
+                    if (!data.hasRemaining()) {
+                        data.dispose();
+                        data = null;
+                    }
+                    // In case of exception we don't need to clean any state,
+                    // since it's the terminal condition anyway. Nothing will be
+                    // retried.
+                } catch (WSProtocolException e) {
+                    // Translate into ProtocolException
+                    closeExceptionally(new ProtocolException().initCause(e));
+                } catch (Exception e) {
+                    closeExceptionally(e);
+                }
+            }
+        }
+    }
+
+    long request(long n) {
+        long newDemand = demand.accumulateAndGet(n, (p, i) -> p + i < 0 ? Long.MAX_VALUE : p + i);
+        handler.signal();
+        assert newDemand >= 0 : newDemand;
+        return newDemand;
+    }
+
+    private boolean getData() throws IOException {
+        if (!readable.get()) {
+            return false;
+        }
+        if (!buf.hasRemaining()) {
+            buf.dispose();
+            buf = buffersSupplier.get();
+            assert buf.hasRemaining() : buf;
+        }
+        int oldPosition = buf.position();
+        int oldLimit = buf.limit();
+        int numRead = channel.read(buf.buffer());
+        if (numRead > 0) {
+            data = buf.share(oldPosition, oldPosition + numRead);
+            buf.select(buf.limit(), oldLimit); // Move window to the free region
+            return true;
+        } else if (numRead == 0) {
+            readable.set(false);
+            channel.registerEvent(channelEvent);
+            return false;
+        } else {
+            assert numRead < 0 : numRead;
+            throw new WSProtocolException
+                    ("7.2.1.", "Stream ended before a Close frame has been received");
+        }
+    }
+
+    void start() {
+        synchronized (this) {
+            if (started) {
+                throw new IllegalStateException("Already started");
+            }
+            started = true;
+            try {
+                channel.registerEvent(channelEvent);
+            } catch (IOException e) {
+                throw new UncheckedIOException(e);
+            }
+            try {
+                listener.onOpen(webSocket);
+            } catch (Exception e) {
+                closeExceptionally(new RuntimeException("onOpen threw an exception", e));
+            }
+        }
+    }
+
+    private void close() { // TODO: move to WS.java
+        closed = true;
+    }
+
+    private void closeExceptionally(Throwable error) {  // TODO: move to WS.java
+        close();
+        try {
+            listener.onError(webSocket, error);
+        } catch (Exception e) {
+            logger.log(ERROR, "onError threw an exception", e);
+        }
+    }
+
+    private final class MessageConsumer implements WSMessageConsumer {
+
+        @Override
+        public void onText(WebSocket.MessagePart part, WSDisposableText data) {
+            decrementDemand();
+            CompletionStage<?> cs;
+            try {
+                cs = listener.onText(webSocket, data, part);
+            } catch (Exception e) {
+                closeExceptionally(new RuntimeException("onText threw an exception", e));
+                return;
+            }
+            follow(cs, data, "onText");
+        }
+
+        @Override
+        public void onBinary(WebSocket.MessagePart part, WSShared<ByteBuffer> data) {
+            decrementDemand();
+            CompletionStage<?> cs;
+            try {
+                cs = listener.onBinary(webSocket, data.buffer(), part);
+            } catch (Exception e) {
+                closeExceptionally(new RuntimeException("onBinary threw an exception", e));
+                return;
+            }
+            follow(cs, data, "onBinary");
+        }
+
+        @Override
+        public void onPing(WSShared<ByteBuffer> data) {
+            decrementDemand();
+            CompletionStage<?> cs;
+            try {
+                cs = listener.onPing(webSocket, data.buffer());
+            } catch (Exception e) {
+                closeExceptionally(new RuntimeException("onPing threw an exception", e));
+                return;
+            }
+            follow(cs, data, "onPing");
+        }
+
+        @Override
+        public void onPong(WSShared<ByteBuffer> data) {
+            decrementDemand();
+            CompletionStage<?> cs;
+            try {
+                cs = listener.onPong(webSocket, data.buffer());
+            } catch (Exception e) {
+                closeExceptionally(new RuntimeException("onPong threw an exception", e));
+                return;
+            }
+            follow(cs, data, "onPong");
+        }
+
+        @Override
+        public void onClose(WebSocket.CloseCode code, CharSequence reason) {
+            decrementDemand();
+            try {
+                close();
+                listener.onClose(webSocket, Optional.ofNullable(code), reason.toString());
+            } catch (Exception e) {
+                logger.log(ERROR, "onClose threw an exception", e);
+            }
+        }
+    }
+
+    private void follow(CompletionStage<?> cs, WSDisposable d, String source) {
+        if (cs == null) {
+            d.dispose();
+        } else {
+            cs.whenComplete((whatever, error) -> {
+                if (error != null) {
+                    String m = "CompletionStage returned by " + source + " completed exceptionally";
+                    closeExceptionally(new RuntimeException(m, error));
+                }
+                d.dispose();
+            });
+        }
+    }
+
+    private void decrementDemand() {
+        long newDemand = demand.decrementAndGet();
+        assert newDemand >= 0 : newDemand;
+    }
+
+    private RawChannel.NonBlockingEvent createChannelEvent() {
+        return new RawChannel.NonBlockingEvent() {
+
+            @Override
+            public int interestOps() {
+                return SelectionKey.OP_READ;
+            }
+
+            @Override
+            public void handle() {
+                boolean wasNotReadable = readable.compareAndSet(false, true);
+                assert wasNotReadable;
+                handler.signal();
+            }
+
+            @Override
+            public String toString() {
+                return "Read readiness event [" + channel + "]";
+            }
+        };
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.httpclient/share/classes/java/net/http/WSShared.java	Thu May 12 18:47:08 2016 +0000
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2016, 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  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  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  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 java.net.http;
+
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+//
+//  +-----------+---------------+------------ ~ ------+
+//  |  shared#1 |    shared#2   | non-shared          |
+//  +-----------+---------------+------------ ~ ------+
+//  |                                                 |
+//  |<------------------  shared0  ---------- ~ ----->|
+//
+//
+// Objects of the type are not thread-safe. It's the responsibility of the
+// client to access shared buffers safely between threads.
+//
+// It would be perfect if we could extend java.nio.Buffer, but it's not an
+// option since Buffer and all its descendants have package-private
+// constructors.
+//
+abstract class WSShared<T extends Buffer> implements WSDisposable {
+
+    protected final AtomicBoolean disposed = new AtomicBoolean();
+    protected final T buffer;
+
+    protected WSShared(T buffer) {
+        this.buffer = Objects.requireNonNull(buffer);
+    }
+
+    static <T extends Buffer> WSShared<T> wrap(T buffer) {
+        return new WSShared<>(buffer) {
+            @Override
+            WSShared<T> share(int pos, int limit) {
+                throw new UnsupportedOperationException();
+            }