8141132: JEP 254: Compact Strings

Adopt a more space-efficient internal representation for strings.

Co-authored-by: Brent Christian <brent.christian@oracle.com>
Co-authored-by: Vivek Deshpande <vivek.r.deshpande@intel.com>
Co-authored-by: Charlie Hunt <charlie.hunt@oracle.com>
Co-authored-by: Vladimir Kozlov <vladimir.kozlov@oracle.com>
Co-authored-by: Roger Riggs <roger.riggs@oracle.com>
Co-authored-by: Xueming Shen <xueming.shen@oracle.com>
Co-authored-by: Aleksey Shipilev <aleksey.shipilev@oracle.com>
Co-authored-by: Sandhya Viswanathan <sandhya.viswanathan@intel.com>
Reviewed-by: alanb, bdelsart, coleenp, iklam, jiangli, jrose, kevinw, naoto, pliden, roland, smarks, twisti
diff --git a/jdk/make/data/charsetmapping/DoubleByte-X.java.template b/jdk/make/data/charsetmapping/DoubleByte-X.java.template
index 4ef582c..d97e2c4 100644
--- a/jdk/make/data/charsetmapping/DoubleByte-X.java.template
+++ b/jdk/make/data/charsetmapping/DoubleByte-X.java.template
@@ -50,12 +50,12 @@
 
     public CharsetDecoder newDecoder() {
         initb2c();
-        return new  DoubleByte.Decoder$DECTYPE$(this, b2c, b2cSB, $B2MIN$, $B2MAX$);
+        return new  DoubleByte.Decoder$DECTYPE$(this, b2c, b2cSB, $B2MIN$, $B2MAX$, $ASCIICOMPATIBLE$);
     }
 
     public CharsetEncoder newEncoder() {
         initc2b();
-        return new DoubleByte.Encoder$ENCTYPE$(this, $ENC_REPLACEMENT$ c2b, c2bIndex); 
+        return new DoubleByte.Encoder$ENCTYPE$(this, $ENC_REPLACEMENT$ c2b, c2bIndex, $ASCIICOMPATIBLE$);
     }
 
     $B2C$
diff --git a/jdk/make/data/charsetmapping/SingleByte-X.java.template b/jdk/make/data/charsetmapping/SingleByte-X.java.template
index 82af052..2acb1ef 100644
--- a/jdk/make/data/charsetmapping/SingleByte-X.java.template
+++ b/jdk/make/data/charsetmapping/SingleByte-X.java.template
@@ -48,11 +48,11 @@
     }
 
     public CharsetDecoder newDecoder() {
-        return new SingleByte.Decoder(this, b2c);
+        return new SingleByte.Decoder(this, b2c, $ASCIICOMPATIBLE$);
     }
 
     public CharsetEncoder newEncoder() {
-        return new SingleByte.Encoder(this, c2b, c2bIndex);
+        return new SingleByte.Encoder(this, c2b, c2bIndex, $ASCIICOMPATIBLE$);
     }
 
     private final static String b2cTable = $B2CTABLE$
diff --git a/jdk/make/mapfiles/libjava/mapfile-vers b/jdk/make/mapfiles/libjava/mapfile-vers
index c2e022c..7ae48fe 100644
--- a/jdk/make/mapfiles/libjava/mapfile-vers
+++ b/jdk/make/mapfiles/libjava/mapfile-vers
@@ -211,6 +211,7 @@
 		Java_java_lang_SecurityManager_getClassContext;
 		Java_java_lang_Shutdown_halt0;
 		Java_java_lang_String_intern;
+		Java_java_lang_StringUTF16_isBigEndian;
 		Java_java_lang_System_identityHashCode;
 		Java_java_lang_System_initProperties;
 		Java_java_lang_System_mapLibraryName;
diff --git a/jdk/make/mapfiles/libjava/reorder-sparc b/jdk/make/mapfiles/libjava/reorder-sparc
index 5ae7cca..3994c91 100644
--- a/jdk/make/mapfiles/libjava/reorder-sparc
+++ b/jdk/make/mapfiles/libjava/reorder-sparc
@@ -57,6 +57,7 @@
 text: .text%JNU_ClassString;
 text: .text%JNU_CopyObjectArray;
 text: .text%Java_java_lang_String_intern;
+text: .text%Java_java_lang_StringUTF16_isBigEndian;
 text: .text%Java_java_lang_ClassLoader_findLoadedClass0;
 text: .text%Java_java_lang_ClassLoader_findBootstrapClass;
 text: .text%Java_java_lang_Throwable_fillInStackTrace;
diff --git a/jdk/make/mapfiles/libjava/reorder-sparcv9 b/jdk/make/mapfiles/libjava/reorder-sparcv9
index f109866..63a667f 100644
--- a/jdk/make/mapfiles/libjava/reorder-sparcv9
+++ b/jdk/make/mapfiles/libjava/reorder-sparcv9
@@ -29,6 +29,7 @@
 text: .text%Java_sun_reflect_Reflection_getCallerClass__I;
 text: .text%Java_java_lang_Class_forName0;
 text: .text%Java_java_lang_String_intern;
+text: .text%Java_java_lang_StringUTF16_isBigEndian;
 text: .text%Java_java_lang_Float_floatToRawIntBits;
 text: .text%Java_java_lang_Double_doubleToRawLongBits;
 text: .text%Java_java_lang_ClassLoader_findLoadedClass0;
diff --git a/jdk/make/mapfiles/libjava/reorder-x86 b/jdk/make/mapfiles/libjava/reorder-x86
index 03609c1..c6c3fce 100644
--- a/jdk/make/mapfiles/libjava/reorder-x86
+++ b/jdk/make/mapfiles/libjava/reorder-x86
@@ -31,6 +31,7 @@
 text: .text%Java_sun_reflect_Reflection_getCallerClass__I;
 text: .text%Java_java_lang_Class_forName0;
 text: .text%Java_java_lang_String_intern;
+text: .text%Java_java_lang_StringUTF16_isBigEndian;
 text: .text%Java_sun_reflect_NativeConstructorAccessorImpl_newInstance0;
 text: .text%Java_java_lang_Throwable_fillInStackTrace;
 text: .text%Java_java_lang_System_setOut0;
diff --git a/jdk/make/src/classes/build/tools/charsetmapping/DBCS.java b/jdk/make/src/classes/build/tools/charsetmapping/DBCS.java
index bd4bd71..9ebadde 100644
--- a/jdk/make/src/classes/build/tools/charsetmapping/DBCS.java
+++ b/jdk/make/src/classes/build/tools/charsetmapping/DBCS.java
@@ -197,6 +197,7 @@
                        .replace("$B1MAX$"   , "0x" + Integer.toString(b1Max, 16))
                        .replace("$B2MIN$"   , "0x" + Integer.toString(b2Min, 16))
                        .replace("$B2MAX$"   , "0x" + Integer.toString(b2Max, 16))
+                       .replace("$ASCIICOMPATIBLE$", isASCII ? "true" : "false")
                        .replace("$B2C$", b2c)
                        .replace("$C2BLENGTH$", "0x" + Integer.toString(c2bOff, 16))
                        .replace("$NONROUNDTRIP_B2C$", b2cNR)
diff --git a/jdk/make/src/classes/build/tools/charsetmapping/SBCS.java b/jdk/make/src/classes/build/tools/charsetmapping/SBCS.java
index fa294f1..02c8d63 100644
--- a/jdk/make/src/classes/build/tools/charsetmapping/SBCS.java
+++ b/jdk/make/src/classes/build/tools/charsetmapping/SBCS.java
@@ -175,6 +175,9 @@
                 else
                     line = "        return (cs instanceof " + clzName + ");";
             }
+            if (line.indexOf("$ASCIICOMPATIBLE$") != -1) {
+                line = line.replace("$ASCIICOMPATIBLE$", isASCII ? "true" : "false");
+            }
             if (line.indexOf("$B2CTABLE$") != -1) {
                 line = line.replace("$B2CTABLE$", b2c);
             }
diff --git a/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java b/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java
index a921600..65f3c49 100644
--- a/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java
+++ b/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java
@@ -31,6 +31,12 @@
 import java.util.stream.IntStream;
 import java.util.stream.StreamSupport;
 
+import static java.lang.String.COMPACT_STRINGS;
+import static java.lang.String.UTF16;
+import static java.lang.String.LATIN1;
+import static java.lang.String.checkIndex;
+import static java.lang.String.checkOffset;
+
 /**
  * A mutable sequence of characters.
  * <p>
@@ -51,7 +57,12 @@
     /**
      * The value is used for character storage.
      */
-    char[] value;
+    byte[] value;
+
+    /**
+     * The id of the encoding used to encode the bytes in {@code value}.
+     */
+    byte coder;
 
     /**
      * The count is the number of characters used.
@@ -68,7 +79,13 @@
      * Creates an AbstractStringBuilder of the specified capacity.
      */
     AbstractStringBuilder(int capacity) {
-        value = new char[capacity];
+        if (COMPACT_STRINGS) {
+            value = new byte[capacity];
+            coder = LATIN1;
+        } else {
+            value = StringUTF16.newBytesFor(capacity);
+            coder = UTF16;
+        }
     }
 
     /**
@@ -90,7 +107,7 @@
      * @return  the current capacity
      */
     public int capacity() {
-        return value.length;
+        return value.length >> coder;
     }
 
     /**
@@ -110,8 +127,9 @@
      * @param   minimumCapacity   the minimum desired capacity.
      */
     public void ensureCapacity(int minimumCapacity) {
-        if (minimumCapacity > 0)
+        if (minimumCapacity > 0) {
             ensureCapacityInternal(minimumCapacity);
+        }
     }
 
     /**
@@ -120,24 +138,48 @@
      */
     private void ensureCapacityInternal(int minimumCapacity) {
         // overflow-conscious code
-        if (minimumCapacity - value.length > 0)
+        int capacity = value.length >> coder;
+        if (minimumCapacity - capacity > 0) {
             expandCapacity(minimumCapacity);
+        }
     }
 
     /**
      * This implements the expansion semantics of ensureCapacity with no
      * size check or synchronization.
      */
-    void expandCapacity(int minimumCapacity) {
-        int newCapacity = value.length * 2 + 2;
-        if (newCapacity - minimumCapacity < 0)
+    private void expandCapacity(int minimumCapacity) {
+        int newCapacity = (value.length >> coder) * 2 + 2;
+        if (newCapacity - minimumCapacity < 0) {
             newCapacity = minimumCapacity;
+        }
         if (newCapacity < 0) {
-            if (minimumCapacity < 0) // overflow
+            if (minimumCapacity < 0) {// overflow
                 throw new OutOfMemoryError();
+            }
             newCapacity = Integer.MAX_VALUE;
         }
-        value = Arrays.copyOf(value, newCapacity);
+        if (coder != LATIN1 && newCapacity > StringUTF16.MAX_LENGTH) {
+            if (minimumCapacity >= StringUTF16.MAX_LENGTH) {
+                throw new OutOfMemoryError();
+            }
+            newCapacity = StringUTF16.MAX_LENGTH;
+        }
+        this.value = Arrays.copyOf(value, newCapacity << coder);
+    }
+
+    /**
+     * If the coder is "isLatin1", this inflates the internal 8-bit storage
+     * to 16-bit <hi=0, low> pair storage.
+     */
+    private void inflate() {
+        if (!isLatin1()) {
+            return;
+        }
+        byte[] buf = StringUTF16.newBytesFor(value.length);
+        StringLatin1.inflateSB(value, buf, 0, count);
+        this.value = buf;
+        this.coder = UTF16;
     }
 
     /**
@@ -148,8 +190,9 @@
      * returned by a subsequent call to the {@link #capacity()} method.
      */
     public void trimToSize() {
-        if (count < value.length) {
-            value = Arrays.copyOf(value, count);
+        int length = count << coder;
+        if (length < value.length) {
+            value = Arrays.copyOf(value, length);
         }
     }
 
@@ -179,14 +222,17 @@
      *               {@code newLength} argument is negative.
      */
     public void setLength(int newLength) {
-        if (newLength < 0)
+        if (newLength < 0) {
             throw new StringIndexOutOfBoundsException(newLength);
-        ensureCapacityInternal(newLength);
-
-        if (count < newLength) {
-            Arrays.fill(value, count, newLength, '\0');
         }
-
+        ensureCapacityInternal(newLength);
+        if (count < newLength) {
+            if (isLatin1()) {
+                StringLatin1.fillNull(value, count, newLength);
+            } else {
+                StringUTF16.fillNull(value, count, newLength);
+            }
+        }
         count = newLength;
     }
 
@@ -209,9 +255,11 @@
      */
     @Override
     public char charAt(int index) {
-        if ((index < 0) || (index >= count))
-            throw new StringIndexOutOfBoundsException(index);
-        return value[index];
+        checkIndex(index, count);
+        if (isLatin1()) {
+            return (char)(value[index] & 0xff);
+        }
+        return StringUTF16.charAt(value, index);
     }
 
     /**
@@ -236,10 +284,11 @@
      *             sequence.
      */
     public int codePointAt(int index) {
-        if ((index < 0) || (index >= count)) {
-            throw new StringIndexOutOfBoundsException(index);
+        checkIndex(index, count);
+        if (isLatin1()) {
+            return value[index] & 0xff;
         }
-        return Character.codePointAtImpl(value, index, count);
+        return StringUTF16.codePointAtSB(value, index, count);
     }
 
     /**
@@ -265,10 +314,13 @@
      */
     public int codePointBefore(int index) {
         int i = index - 1;
-        if ((i < 0) || (i >= count)) {
+        if (i < 0 || i >= count) {
             throw new StringIndexOutOfBoundsException(index);
         }
-        return Character.codePointBeforeImpl(value, index, 0);
+        if (isLatin1()) {
+            return value[i] & 0xff;
+        }
+        return StringUTF16.codePointBeforeSB(value, index);
     }
 
     /**
@@ -295,7 +347,10 @@
         if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) {
             throw new IndexOutOfBoundsException();
         }
-        return Character.codePointCountImpl(value, beginIndex, endIndex - beginIndex);
+        if (isLatin1()) {
+            return endIndex - beginIndex;
+        }
+        return StringUTF16.codePointCountSB(value, beginIndex, endIndex);
     }
 
     /**
@@ -321,8 +376,8 @@
         if (index < 0 || index > count) {
             throw new IndexOutOfBoundsException();
         }
-        return Character.offsetByCodePointsImpl(value, 0, count,
-                                                index, codePointOffset);
+        return Character.offsetByCodePoints(this,
+                                            index, codePointOffset);
     }
 
     /**
@@ -355,13 +410,14 @@
      */
     public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
     {
-        if (srcBegin < 0)
-            throw new StringIndexOutOfBoundsException(srcBegin);
-        if ((srcEnd < 0) || (srcEnd > count))
-            throw new StringIndexOutOfBoundsException(srcEnd);
-        if (srcBegin > srcEnd)
-            throw new StringIndexOutOfBoundsException("srcBegin > srcEnd");
-        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
+        checkRangeSIOOBE(srcBegin, srcEnd, count);  // compatible to old version
+        int n = srcEnd - srcBegin;
+        checkRange(dstBegin, dstBegin + n, dst.length);
+        if (isLatin1()) {
+            StringLatin1.getCharsSB(value, srcBegin, srcEnd, dst, dstBegin);
+        } else {
+            StringUTF16.getCharsSB(value, srcBegin, srcEnd, dst, dstBegin);
+        }
     }
 
     /**
@@ -379,9 +435,15 @@
      *             negative or greater than or equal to {@code length()}.
      */
     public void setCharAt(int index, char ch) {
-        if ((index < 0) || (index >= count))
-            throw new StringIndexOutOfBoundsException(index);
-        value[index] = ch;
+        checkIndex(index, count);
+        if (isLatin1() && StringLatin1.canEncode(ch)) {
+            value[index] = (byte)ch;
+        } else {
+            if (isLatin1()) {
+                inflate();
+            }
+            StringUTF16.putCharSB(value, index, ch);
+        }
     }
 
     /**
@@ -418,35 +480,34 @@
      * @return  a reference to this object.
      */
     public AbstractStringBuilder append(String str) {
-        if (str == null)
+        if (str == null) {
             return appendNull();
+        }
         int len = str.length();
         ensureCapacityInternal(count + len);
-        str.getChars(0, len, value, count);
+        putStringAt(count, str);
         count += len;
         return this;
     }
 
     // Documentation in subclasses because of synchro difference
     public AbstractStringBuilder append(StringBuffer sb) {
-        if (sb == null)
-            return appendNull();
-        int len = sb.length();
-        ensureCapacityInternal(count + len);
-        sb.getChars(0, len, value, count);
-        count += len;
-        return this;
+        return this.append((AbstractStringBuilder)sb);
     }
 
     /**
      * @since 1.8
      */
     AbstractStringBuilder append(AbstractStringBuilder asb) {
-        if (asb == null)
+        if (asb == null) {
             return appendNull();
+        }
         int len = asb.length();
         ensureCapacityInternal(count + len);
-        asb.getChars(0, len, value, count);
+        if (getCoder() != asb.getCoder()) {
+            inflate();
+        }
+        asb.getBytes(value, count, coder);
         count += len;
         return this;
     }
@@ -454,25 +515,35 @@
     // Documentation in subclasses because of synchro difference
     @Override
     public AbstractStringBuilder append(CharSequence s) {
-        if (s == null)
+        if (s == null) {
             return appendNull();
-        if (s instanceof String)
+        }
+        if (s instanceof String) {
             return this.append((String)s);
-        if (s instanceof AbstractStringBuilder)
+        }
+        if (s instanceof AbstractStringBuilder) {
             return this.append((AbstractStringBuilder)s);
-
+        }
         return this.append(s, 0, s.length());
     }
 
     private AbstractStringBuilder appendNull() {
-        int c = count;
-        ensureCapacityInternal(c + 4);
-        final char[] value = this.value;
-        value[c++] = 'n';
-        value[c++] = 'u';
-        value[c++] = 'l';
-        value[c++] = 'l';
-        count = c;
+        ensureCapacityInternal(count + 4);
+        int count = this.count;
+        byte[] val = this.value;
+        if (isLatin1()) {
+            val[count++] = 'n';
+            val[count++] = 'u';
+            val[count++] = 'l';
+            val[count++] = 'l';
+        } else {
+            checkOffset(count + 4, val.length >> 1);
+            StringUTF16.putChar(val, count++, 'n');
+            StringUTF16.putChar(val, count++, 'u');
+            StringUTF16.putChar(val, count++, 'l');
+            StringUTF16.putChar(val, count++, 'l');
+        }
+        this.count = count;
         return this;
     }
 
@@ -507,21 +578,13 @@
      */
     @Override
     public AbstractStringBuilder append(CharSequence s, int start, int end) {
-        if (s == null)
+        if (s == null) {
             s = "null";
-        if ((start < 0) || (start > end) || (end > s.length()))
-            throw new IndexOutOfBoundsException(
-                "start " + start + ", end " + end + ", s.length() "
-                + s.length());
+        }
+        checkRange(start, end, s.length());
         int len = end - start;
         ensureCapacityInternal(count + len);
-        if (s instanceof String) {
-            ((String)s).getChars(start, end, value, count);
-        } else {
-            for (int i = start, j = count; i < end; i++, j++)
-                value[j] = s.charAt(i);
-        }
-        count += len;
+        appendChars(s, start, end);
         return this;
     }
 
@@ -544,8 +607,7 @@
     public AbstractStringBuilder append(char[] str) {
         int len = str.length;
         ensureCapacityInternal(count + len);
-        System.arraycopy(str, 0, value, count, len);
-        count += len;
+        appendChars(str, 0, len);
         return this;
     }
 
@@ -572,10 +634,10 @@
      *         or {@code offset+len > str.length}
      */
     public AbstractStringBuilder append(char str[], int offset, int len) {
-        if (len > 0)                // let arraycopy report AIOOBE for len < 0
-            ensureCapacityInternal(count + len);
-        System.arraycopy(str, offset, value, count, len);
-        count += len;
+        int end = offset + len;
+        checkRange(offset, end, str.length);
+        ensureCapacityInternal(count + len);
+        appendChars(str, offset, end);
         return this;
     }
 
@@ -592,20 +654,39 @@
      * @return  a reference to this object.
      */
     public AbstractStringBuilder append(boolean b) {
-        if (b) {
-            ensureCapacityInternal(count + 4);
-            value[count++] = 't';
-            value[count++] = 'r';
-            value[count++] = 'u';
-            value[count++] = 'e';
+        ensureCapacityInternal(count + (b ? 4 : 5));
+        int count = this.count;
+        byte[] val = this.value;
+        if (isLatin1()) {
+            if (b) {
+                val[count++] = 't';
+                val[count++] = 'r';
+                val[count++] = 'u';
+                val[count++] = 'e';
+            } else {
+                val[count++] = 'f';
+                val[count++] = 'a';
+                val[count++] = 'l';
+                val[count++] = 's';
+                val[count++] = 'e';
+            }
         } else {
-            ensureCapacityInternal(count + 5);
-            value[count++] = 'f';
-            value[count++] = 'a';
-            value[count++] = 'l';
-            value[count++] = 's';
-            value[count++] = 'e';
+            if (b) {
+                checkOffset(count + 4, val.length >> 1);
+                StringUTF16.putChar(val, count++, 't');
+                StringUTF16.putChar(val, count++, 'r');
+                StringUTF16.putChar(val, count++, 'u');
+                StringUTF16.putChar(val, count++, 'e');
+            } else {
+                checkOffset(count + 5, val.length >> 1);
+                StringUTF16.putChar(val, count++, 'f');
+                StringUTF16.putChar(val, count++, 'a');
+                StringUTF16.putChar(val, count++, 'l');
+                StringUTF16.putChar(val, count++, 's');
+                StringUTF16.putChar(val, count++, 'e');
+            }
         }
+        this.count = count;
         return this;
     }
 
@@ -627,7 +708,14 @@
     @Override
     public AbstractStringBuilder append(char c) {
         ensureCapacityInternal(count + 1);
-        value[count++] = c;
+        if (isLatin1() && StringLatin1.canEncode(c)) {
+            value[count++] = (byte)c;
+        } else {
+            if (isLatin1()) {
+                inflate();
+            }
+            StringUTF16.putCharSB(value, count++, c);
+        }
         return this;
     }
 
@@ -652,7 +740,13 @@
                                      : Integer.stringSize(i);
         int spaceNeeded = count + appendedLength;
         ensureCapacityInternal(spaceNeeded);
-        Integer.getChars(i, spaceNeeded, value);
+        if (isLatin1()) {
+            Integer.getChars(i, spaceNeeded, value);
+        } else {
+            byte[] val = this.value;
+            checkOffset(spaceNeeded, val.length >> 1);
+            Integer.getCharsUTF16(i, spaceNeeded, val);
+        }
         count = spaceNeeded;
         return this;
     }
@@ -678,7 +772,13 @@
                                      : Long.stringSize(l);
         int spaceNeeded = count + appendedLength;
         ensureCapacityInternal(spaceNeeded);
-        Long.getChars(l, spaceNeeded, value);
+        if (isLatin1()) {
+            Long.getChars(l, spaceNeeded, value);
+        } else {
+            byte[] val = this.value;
+            checkOffset(spaceNeeded, val.length >> 1);
+            Long.getCharsUTF16(l, spaceNeeded, val);
+        }
         count = spaceNeeded;
         return this;
     }
@@ -732,15 +832,13 @@
      *             greater than {@code end}.
      */
     public AbstractStringBuilder delete(int start, int end) {
-        if (start < 0)
-            throw new StringIndexOutOfBoundsException(start);
-        if (end > count)
+        if (end > count) {
             end = count;
-        if (start > end)
-            throw new StringIndexOutOfBoundsException();
+        }
+        checkRangeSIOOBE(start, end, count);
         int len = end - start;
         if (len > 0) {
-            System.arraycopy(value, start+len, value, start, count-end);
+            shift(end, -len);
             count -= len;
         }
         return this;
@@ -766,20 +864,10 @@
      * {@code codePoint} isn't a valid Unicode code point
      */
     public AbstractStringBuilder appendCodePoint(int codePoint) {
-        final int count = this.count;
-
         if (Character.isBmpCodePoint(codePoint)) {
-            ensureCapacityInternal(count + 1);
-            value[count] = (char) codePoint;
-            this.count = count + 1;
-        } else if (Character.isValidCodePoint(codePoint)) {
-            ensureCapacityInternal(count + 2);
-            Character.toSurrogates(codePoint, value, count);
-            this.count = count + 2;
-        } else {
-            throw new IllegalArgumentException();
+            return append((char)codePoint);
         }
-        return this;
+        return append(Character.toChars(codePoint));
     }
 
     /**
@@ -800,9 +888,8 @@
      *              {@code length()}.
      */
     public AbstractStringBuilder deleteCharAt(int index) {
-        if ((index < 0) || (index >= count))
-            throw new StringIndexOutOfBoundsException(index);
-        System.arraycopy(value, index+1, value, index, count-index-1);
+        checkIndex(index, count);
+        shift(index + 1, -1);
         count--;
         return this;
     }
@@ -827,22 +914,16 @@
      *             greater than {@code end}.
      */
     public AbstractStringBuilder replace(int start, int end, String str) {
-        if (start < 0)
-            throw new StringIndexOutOfBoundsException(start);
-        if (start > count)
-            throw new StringIndexOutOfBoundsException("start > length()");
-        if (start > end)
-            throw new StringIndexOutOfBoundsException("start > end");
-
-        if (end > count)
+        if (end > count) {
             end = count;
+        }
+        checkRangeSIOOBE(start, end, count);
         int len = str.length();
         int newCount = count + len - (end - start);
         ensureCapacityInternal(newCount);
-
-        System.arraycopy(value, end, value, start + len, count - end);
-        str.getChars(value, start);
+        shift(end, newCount - count);
         count = newCount;
+        putStringAt(start, str);
         return this;
     }
 
@@ -907,13 +988,16 @@
      *             greater than {@code end}.
      */
     public String substring(int start, int end) {
-        if (start < 0)
-            throw new StringIndexOutOfBoundsException(start);
-        if (end > count)
-            throw new StringIndexOutOfBoundsException(end);
-        if (start > end)
-            throw new StringIndexOutOfBoundsException(end - start);
-        return new String(value, start, end - start);
+        checkRangeSIOOBE(start, end, count);
+        if (isLatin1()) {
+            return StringLatin1.newString(value, start, end - start);
+        }
+        return StringUTF16.newStringSB(value, start, end - start);
+    }
+
+    private void shift(int offset, int n) {
+        System.arraycopy(value, offset << coder,
+                         value, (offset + n) << coder, (count - offset) << coder);
     }
 
     /**
@@ -940,16 +1024,12 @@
     public AbstractStringBuilder insert(int index, char[] str, int offset,
                                         int len)
     {
-        if ((index < 0) || (index > length()))
-            throw new StringIndexOutOfBoundsException(index);
-        if ((offset < 0) || (len < 0) || (offset > str.length - len))
-            throw new StringIndexOutOfBoundsException(
-                "offset " + offset + ", len " + len + ", str.length "
-                + str.length);
+        checkOffset(index, count);
+        checkRangeSIOOBE(offset, offset + len, str.length);
         ensureCapacityInternal(count + len);
-        System.arraycopy(value, index, value, index + len, count - index);
-        System.arraycopy(str, offset, value, index, len);
+        shift(index, len);
         count += len;
+        putCharsAt(index, str, offset, offset + len);
         return this;
     }
 
@@ -1008,15 +1088,15 @@
      * @throws     StringIndexOutOfBoundsException  if the offset is invalid.
      */
     public AbstractStringBuilder insert(int offset, String str) {
-        if ((offset < 0) || (offset > length()))
-            throw new StringIndexOutOfBoundsException(offset);
-        if (str == null)
+        checkOffset(offset, count);
+        if (str == null) {
             str = "null";
+        }
         int len = str.length();
         ensureCapacityInternal(count + len);
-        System.arraycopy(value, offset, value, offset + len, count - offset);
-        str.getChars(value, offset);
+        shift(offset, len);
         count += len;
+        putStringAt(offset, str);
         return this;
     }
 
@@ -1045,13 +1125,12 @@
      * @throws     StringIndexOutOfBoundsException  if the offset is invalid.
      */
     public AbstractStringBuilder insert(int offset, char[] str) {
-        if ((offset < 0) || (offset > length()))
-            throw new StringIndexOutOfBoundsException(offset);
+        checkOffset(offset, count);
         int len = str.length;
         ensureCapacityInternal(count + len);
-        System.arraycopy(value, offset, value, offset + len, count - offset);
-        System.arraycopy(str, 0, value, offset, len);
+        shift(offset, len);
         count += len;
+        putCharsAt(offset, str, 0, len);
         return this;
     }
 
@@ -1077,10 +1156,12 @@
      * @throws     IndexOutOfBoundsException  if the offset is invalid.
      */
     public AbstractStringBuilder insert(int dstOffset, CharSequence s) {
-        if (s == null)
+        if (s == null) {
             s = "null";
-        if (s instanceof String)
+        }
+        if (s instanceof String) {
             return this.insert(dstOffset, (String)s);
+        }
         return this.insert(dstOffset, s, 0, s.length());
     }
 
@@ -1128,23 +1209,19 @@
      *              {@code start} is greater than {@code end} or
      *              {@code end} is greater than {@code s.length()}
      */
-     public AbstractStringBuilder insert(int dstOffset, CharSequence s,
-                                         int start, int end) {
-        if (s == null)
+    public AbstractStringBuilder insert(int dstOffset, CharSequence s,
+                                        int start, int end)
+    {
+        if (s == null) {
             s = "null";
-        if ((dstOffset < 0) || (dstOffset > this.length()))
-            throw new IndexOutOfBoundsException("dstOffset "+dstOffset);
-        if ((start < 0) || (end < 0) || (start > end) || (end > s.length()))
-            throw new IndexOutOfBoundsException(
-                "start " + start + ", end " + end + ", s.length() "
-                + s.length());
+        }
+        checkOffset(dstOffset, count);
+        checkRange(start, end, s.length());
         int len = end - start;
         ensureCapacityInternal(count + len);
-        System.arraycopy(value, dstOffset, value, dstOffset + len,
-                         count - dstOffset);
-        for (int i=start; i<end; i++)
-            value[dstOffset++] = s.charAt(i);
+        shift(dstOffset, len);
         count += len;
+        putCharsAt(dstOffset, s, start, end);
         return this;
     }
 
@@ -1191,10 +1268,18 @@
      * @throws     IndexOutOfBoundsException  if the offset is invalid.
      */
     public AbstractStringBuilder insert(int offset, char c) {
+        checkOffset(offset, count);
         ensureCapacityInternal(count + 1);
-        System.arraycopy(value, offset, value, offset + 1, count - offset);
-        value[offset] = c;
+        shift(offset, 1);
         count += 1;
+        if (isLatin1() && StringLatin1.canEncode(c)) {
+            value[offset] = (byte)c;
+        } else {
+            if (isLatin1()) {
+                inflate();
+            }
+            StringUTF16.putCharSB(value, offset, c);
+        }
         return this;
     }
 
@@ -1326,7 +1411,7 @@
      *          or {@code -1} if there is no such occurrence.
      */
     public int indexOf(String str, int fromIndex) {
-        return String.indexOf(value, 0, count, str, fromIndex);
+        return String.indexOf(value, coder, count, str, fromIndex);
     }
 
     /**
@@ -1366,7 +1451,7 @@
      *          or {@code -1} if there is no such occurrence.
      */
     public int lastIndexOf(String str, int fromIndex) {
-        return String.lastIndexOf(value, 0, count, str, fromIndex);
+        return String.lastIndexOf(value, coder, count, str, fromIndex);
     }
 
     /**
@@ -1392,34 +1477,47 @@
      * @return  a reference to this object.
      */
     public AbstractStringBuilder reverse() {
-        boolean hasSurrogates = false;
+        byte[] val = this.value;
+        int count = this.count;
+        int coder = this.coder;
         int n = count - 1;
-        for (int j = (n-1) >> 1; j >= 0; j--) {
-            int k = n - j;
-            char cj = value[j];
-            char ck = value[k];
-            value[j] = ck;
-            value[k] = cj;
-            if (Character.isSurrogate(cj) ||
-                Character.isSurrogate(ck)) {
-                hasSurrogates = true;
+        if (COMPACT_STRINGS && coder == LATIN1) {
+            for (int j = (n-1) >> 1; j >= 0; j--) {
+                int k = n - j;
+                byte cj = val[j];
+                val[j] = val[k];
+                val[k] = cj;
             }
-        }
-        if (hasSurrogates) {
-            reverseAllValidSurrogatePairs();
+        } else {
+            checkOffset(count, val.length >> 1);
+            boolean hasSurrogates = false;
+            for (int j = (n-1) >> 1; j >= 0; j--) {
+                int k = n - j;
+                char cj = StringUTF16.getChar(val, j);
+                char ck = StringUTF16.getChar(val, k);
+                StringUTF16.putChar(val, j, ck);
+                StringUTF16.putChar(val, k, cj);
+                if (Character.isSurrogate(cj) ||
+                    Character.isSurrogate(ck)) {
+                    hasSurrogates = true;
+                }
+            }
+            if (hasSurrogates) {
+                reverseAllValidSurrogatePairs(val, count);
+            }
         }
         return this;
     }
 
     /** Outlined helper method for reverse() */
-    private void reverseAllValidSurrogatePairs() {
+    private void reverseAllValidSurrogatePairs(byte[] val, int count) {
         for (int i = 0; i < count - 1; i++) {
-            char c2 = value[i];
+            char c2 = StringUTF16.getChar(val, i);
             if (Character.isLowSurrogate(c2)) {
-                char c1 = value[i + 1];
+                char c1 = StringUTF16.getChar(val, i + 1);
                 if (Character.isHighSurrogate(c1)) {
-                    value[i++] = c1;
-                    value[i] = c2;
+                    StringUTF16.putChar(val, i++, c1);
+                    StringUTF16.putChar(val, i, c2);
                 }
             }
         }
@@ -1444,10 +1542,13 @@
      */
     @Override
     public IntStream chars() {
+        byte[] val = this.value; int count = this.count; byte coder = this.coder;
+        checkOffset(count, val.length >> coder);
         // Reuse String-based spliterator. This requires a supplier to
         // capture the value and count when the terminal operation is executed
         return StreamSupport.intStream(
-                () -> new String.IntCharArraySpliterator(value, 0, count, 0),
+                () -> coder == LATIN1 ? new StringLatin1.CharsSpliterator(val, 0, count, 0)
+                                      : new StringUTF16.CharsSpliterator(val, 0, count, 0),
                 Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED,
                 false);
     }
@@ -1458,10 +1559,13 @@
      */
     @Override
     public IntStream codePoints() {
+        byte[] val = this.value; int count = this.count; byte coder = this.coder;
+        checkOffset(count, val.length >> coder);
         // Reuse String-based spliterator. This requires a supplier to
         // capture the value and count when the terminal operation is executed
         return StreamSupport.intStream(
-                () -> new String.CodePointsSpliterator(value, 0, count, 0),
+                () -> coder == LATIN1 ? new StringLatin1.CharsSpliterator(val, 0, count, 0)
+                                      : new StringUTF16.CodePointsSpliterator(val, 0, count, 0),
                 Spliterator.ORDERED,
                 false);
     }
@@ -1469,8 +1573,147 @@
     /**
      * Needed by {@code String} for the contentEquals method.
      */
-    final char[] getValue() {
+    final byte[] getValue() {
         return value;
     }
 
+    /*
+     * Invoker guarantees it is in UTF16 (inflate itself for asb), if two
+     * coders are different and the dstBegin has enough space
+     *
+     * @param dstBegin  the char index, not offset of byte[]
+     * @param coder     the coder of dst[]
+     */
+    protected void getBytes(byte dst[], int dstBegin, byte coder) {
+        if (this.coder == coder) {
+            System.arraycopy(value, 0, dst, dstBegin << coder, count << coder);
+        } else {        // this.coder == LATIN && coder == UTF16
+            StringLatin1.inflateSB(value, dst, dstBegin, count);
+        }
+    }
+
+    /* for readObject() */
+    protected void initBytes(char[] value, int off, int len) {
+        if (String.COMPACT_STRINGS) {
+            this.value = StringUTF16.compress(value, off, len);
+            if (this.value != null) {
+                this.coder = LATIN1;
+                return;
+            }
+        }
+        this.coder = UTF16;
+        this.value = StringUTF16.toBytes(value, off, len);
+    }
+
+    final byte getCoder() {
+        return COMPACT_STRINGS ? coder : UTF16;
+    }
+
+    final boolean isLatin1() {
+        return COMPACT_STRINGS && coder == LATIN1;
+    }
+
+    private final void putCharsAt(int index, char[] s, int off, int end) {
+        if (isLatin1()) {
+            byte[] val = this.value;
+            for (int i = off, j = index; i < end; i++) {
+                char c = s[i];
+                if (StringLatin1.canEncode(c)) {
+                    val[j++] = (byte)c;
+                } else {
+                    inflate();
+                    StringUTF16.putCharsSB(this.value, j, s, i, end);
+                    return;
+                }
+            }
+        } else {
+            StringUTF16.putCharsSB(this.value, index, s, off, end);
+        }
+    }
+
+    private final void putCharsAt(int index, CharSequence s, int off, int end) {
+        if (isLatin1()) {
+            byte[] val = this.value;
+            for (int i = off, j = index; i < end; i++) {
+                char c = s.charAt(i);
+                if (StringLatin1.canEncode(c)) {
+                    val[j++] = (byte)c;
+                } else {
+                    inflate();
+                    StringUTF16.putCharsSB(this.value, j, s, i, end);
+                    return;
+                }
+            }
+        } else {
+            StringUTF16.putCharsSB(this.value, index, s, off, end);
+        }
+    }
+
+    private final void putStringAt(int index, String str) {
+        if (getCoder() != str.coder()) {
+            inflate();
+        }
+        byte[] val = this.value;
+        byte coder = this.coder;
+        checkOffset(index + str.length(), val.length >> coder);
+        str.getBytes(val, index, coder);
+    }
+
+    private final void appendChars(char[] s, int off, int end) {
+        if (isLatin1()) {
+            byte[] val = this.value;
+            for (int i = off, j = count; i < end; i++) {
+                char c = s[i];
+                if (StringLatin1.canEncode(c)) {
+                    val[j++] = (byte)c;
+                } else {
+                    count = j;
+                    inflate();
+                    StringUTF16.putCharsSB(this.value, j, s, i, end);
+                    count += end - i;
+                    return;
+                }
+            }
+        } else {
+            StringUTF16.putCharsSB(this.value, count, s, off, end);
+        }
+        count += end - off;
+    }
+
+    private final void appendChars(CharSequence s, int off, int end) {
+        if (isLatin1()) {
+            byte[] val = this.value;
+            for (int i = off, j = count; i < end; i++) {
+                char c = s.charAt(i);
+                if (StringLatin1.canEncode(c)) {
+                    val[j++] = (byte)c;
+                } else {
+                    count = j;
+                    inflate();
+                    StringUTF16.putCharsSB(this.value, j, s, i, end);
+                    count += end - i;
+                    return;
+                }
+            }
+        } else {
+            StringUTF16.putCharsSB(this.value, count, s, off, end);
+        }
+        count += end - off;
+    }
+
+    /* IndexOutOfBoundsException, if out of bounds */
+    private static void checkRange(int start, int end, int len) {
+        if (start < 0 || start > end || end > len) {
+            throw new IndexOutOfBoundsException(
+                "start " + start + ", end " + end + ", length " + len);
+        }
+    }
+
+    /* StringIndexOutOfBoundsException, if out of bounds */
+    private static void checkRangeSIOOBE(int start, int end, int len) {
+        if (start < 0 || start > end || end > len) {
+            throw new StringIndexOutOfBoundsException(
+                "start " + start + ", end " + end + ", length " + len);
+        }
+    }
 }
diff --git a/jdk/src/java.base/share/classes/java/lang/Integer.java b/jdk/src/java.base/share/classes/java/lang/Integer.java
index ed401db..e956c35 100644
--- a/jdk/src/java.base/share/classes/java/lang/Integer.java
+++ b/jdk/src/java.base/share/classes/java/lang/Integer.java
@@ -29,6 +29,10 @@
 import java.util.Objects;
 import jdk.internal.HotSpotIntrinsicCandidate;
 
+import static java.lang.String.COMPACT_STRINGS;
+import static java.lang.String.LATIN1;
+import static java.lang.String.UTF16;
+
 /**
  * The {@code Integer} class wraps a value of the primitive type
  * {@code int} in an object. An object of type {@code Integer}
@@ -138,25 +142,47 @@
             return toString(i);
         }
 
-        char buf[] = new char[33];
+        if (COMPACT_STRINGS) {
+            byte[] buf = new byte[33];
+            boolean negative = (i < 0);
+            int charPos = 32;
+
+            if (!negative) {
+                i = -i;
+            }
+
+            while (i <= -radix) {
+                buf[charPos--] = (byte)digits[-(i % radix)];
+                i = i / radix;
+            }
+            buf[charPos] = (byte)digits[-i];
+
+            if (negative) {
+                buf[--charPos] = '-';
+            }
+
+            return StringLatin1.newString(buf, charPos, (33 - charPos));
+        }
+        return toStringUTF16(i, radix);
+    }
+
+    private static String toStringUTF16(int i, int radix) {
+        byte[] buf = new byte[33 * 2];
         boolean negative = (i < 0);
         int charPos = 32;
-
         if (!negative) {
             i = -i;
         }
-
         while (i <= -radix) {
-            buf[charPos--] = digits[-(i % radix)];
+            StringUTF16.putChar(buf, charPos--, digits[-(i % radix)]);
             i = i / radix;
         }
-        buf[charPos] = digits[-i];
+        StringUTF16.putChar(buf, charPos, digits[-i]);
 
         if (negative) {
-            buf[--charPos] = '-';
+            StringUTF16.putChar(buf, --charPos, '-');
         }
-
-        return new String(buf, charPos, (33 - charPos));
+        return StringUTF16.newString(buf, charPos, (33 - charPos));
     }
 
     /**
@@ -312,12 +338,16 @@
         // assert shift > 0 && shift <=5 : "Illegal shift value";
         int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val);
         int chars = Math.max(((mag + (shift - 1)) / shift), 1);
-        char[] buf = new char[chars];
 
-        formatUnsignedInt(val, shift, buf, 0, chars);
-
-        // Use special constructor which takes over "buf".
-        return new String(buf, true);
+        if (COMPACT_STRINGS) {
+            byte[] buf = new byte[chars];
+            formatUnsignedInt(val, shift, buf, 0, chars);
+            return new String(buf, LATIN1);
+        } else {
+            byte[] buf = new byte[chars * 2];
+            formatUnsignedIntUTF16(val, shift, buf, 0, chars);
+            return new String(buf, UTF16);
+        }
     }
 
     /**
@@ -331,7 +361,7 @@
      * @param offset the offset in the destination buffer to start at
      * @param len the number of characters to write
      */
-     static void formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
+    static void formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
         // assert shift > 0 && shift <=5 : "Illegal shift value";
         // assert offset >= 0 && offset < buf.length : "illegal offset";
         // assert len > 0 && (offset + len) <= buf.length : "illegal length";
@@ -344,6 +374,28 @@
         } while (charPos > offset);
     }
 
+    /** byte[]/LATIN1 version    */
+    static void formatUnsignedInt(int val, int shift, byte[] buf, int offset, int len) {
+        int charPos = offset + len;
+        int radix = 1 << shift;
+        int mask = radix - 1;
+        do {
+            buf[--charPos] = (byte)Integer.digits[val & mask];
+            val >>>= shift;
+        } while (charPos > offset);
+    }
+
+    /** byte[]/UTF16 version    */
+    static void formatUnsignedIntUTF16(int val, int shift, byte[] buf, int offset, int len) {
+        int charPos = offset + len;
+        int radix = 1 << shift;
+        int mask = radix - 1;
+        do {
+            StringUTF16.putChar(buf, --charPos, Integer.digits[val & mask]);
+            val >>>= shift;
+        } while (charPos > offset);
+    }
+
     static final char [] DigitTens = {
         '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
         '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
@@ -401,9 +453,15 @@
         if (i == Integer.MIN_VALUE)
             return "-2147483648";
         int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
-        char[] buf = new char[size];
-        getChars(i, size, buf);
-        return new String(buf, true);
+        if (COMPACT_STRINGS) {
+            byte[] buf = new byte[size];
+            getChars(i, size, buf);
+            return new String(buf, LATIN1);
+        } else {
+            byte[] buf = new byte[size * 2];
+            getCharsUTF16(i, size, buf);
+            return new String(buf, UTF16);
+        }
     }
 
     /**
@@ -433,7 +491,7 @@
      *
      * Will fail if i == Integer.MIN_VALUE
      */
-    static void getChars(int i, int index, char[] buf) {
+    static void getChars(int i, int index, byte[] buf) {
         int q, r;
         int charPos = index;
         char sign = 0;
@@ -449,8 +507,8 @@
         // really: r = i - (q * 100);
             r = i - ((q << 6) + (q << 5) + (q << 2));
             i = q;
-            buf [--charPos] = DigitOnes[r];
-            buf [--charPos] = DigitTens[r];
+            buf [--charPos] = (byte)DigitOnes[r];
+            buf [--charPos] = (byte)DigitTens[r];
         }
 
         // Fall thru to fast mode for smaller numbers
@@ -458,12 +516,46 @@
         for (;;) {
             q = (i * 52429) >>> (16+3);
             r = i - ((q << 3) + (q << 1));  // r = i-(q*10) ...
-            buf [--charPos] = digits [r];
+            buf [--charPos] = (byte)digits [r];
             i = q;
             if (i == 0) break;
         }
         if (sign != 0) {
-            buf [--charPos] = sign;
+            buf [--charPos] = (byte)sign;
+        }
+    }
+
+    static void getCharsUTF16(int i, int index, byte[] buf) {
+        int q, r;
+        int charPos = index;
+        char sign = 0;
+
+        if (i < 0) {
+            sign = '-';
+            i = -i;
+        }
+
+        // Generate two digits per iteration
+        while (i >= 65536) {
+            q = i / 100;
+        // really: r = i - (q * 100);
+            r = i - ((q << 6) + (q << 5) + (q << 2));
+            i = q;
+            StringUTF16.putChar(buf, --charPos, DigitOnes[r]);
+            StringUTF16.putChar(buf, --charPos, DigitTens[r]);
+        }
+
+        // Fall thru to fast mode for smaller numbers
+        // assert(i <= 65536, i);
+        for (;;) {
+            q = (i * 52429) >>> (16+3);
+            r = i - ((q << 3) + (q << 1));  // r = i-(q*10) ...
+            StringUTF16.putChar(buf, --charPos, Integer.digits[r]);
+            i = q;
+            if (i == 0) break;
+        }
+        if (sign != 0) {
+            StringUTF16.putChar(buf, --charPos, sign);
         }
     }
 
diff --git a/jdk/src/java.base/share/classes/java/lang/Long.java b/jdk/src/java.base/share/classes/java/lang/Long.java
index 4f248d7..b68e2c2 100644
--- a/jdk/src/java.base/share/classes/java/lang/Long.java
+++ b/jdk/src/java.base/share/classes/java/lang/Long.java
@@ -30,6 +30,9 @@
 import java.util.Objects;
 import jdk.internal.HotSpotIntrinsicCandidate;
 
+import static java.lang.String.COMPACT_STRINGS;
+import static java.lang.String.LATIN1;
+import static java.lang.String.UTF16;
 
 /**
  * The {@code Long} class wraps a value of the primitive type {@code
@@ -124,25 +127,46 @@
             radix = 10;
         if (radix == 10)
             return toString(i);
-        char[] buf = new char[65];
+
+        if (COMPACT_STRINGS) {
+            byte[] buf = new byte[65];
+            int charPos = 64;
+            boolean negative = (i < 0);
+
+            if (!negative) {
+                i = -i;
+            }
+
+            while (i <= -radix) {
+                buf[charPos--] = (byte)Integer.digits[(int)(-(i % radix))];
+                i = i / radix;
+            }
+            buf[charPos] = (byte)Integer.digits[(int)(-i)];
+
+            if (negative) {
+                buf[--charPos] = '-';
+            }
+            return StringLatin1.newString(buf, charPos, (65 - charPos));
+        }
+        return toStringUTF16(i, radix);
+    }
+
+    private static String toStringUTF16(long i, int radix) {
+        byte[] buf = new byte[65 * 2];
         int charPos = 64;
         boolean negative = (i < 0);
-
         if (!negative) {
             i = -i;
         }
-
         while (i <= -radix) {
-            buf[charPos--] = Integer.digits[(int)(-(i % radix))];
+            StringUTF16.putChar(buf, charPos--, Integer.digits[(int)(-(i % radix))]);
             i = i / radix;
         }
-        buf[charPos] = Integer.digits[(int)(-i)];
-
+        StringUTF16.putChar(buf, charPos, Integer.digits[(int)(-i)]);
         if (negative) {
-            buf[--charPos] = '-';
+            StringUTF16.putChar(buf, --charPos, '-');
         }
-
-        return new String(buf, charPos, (65 - charPos));
+        return StringUTF16.newString(buf, charPos, (65 - charPos));
     }
 
     /**
@@ -355,10 +379,16 @@
         // assert shift > 0 && shift <=5 : "Illegal shift value";
         int mag = Long.SIZE - Long.numberOfLeadingZeros(val);
         int chars = Math.max(((mag + (shift - 1)) / shift), 1);
-        char[] buf = new char[chars];
 
-        formatUnsignedLong(val, shift, buf, 0, chars);
-        return new String(buf, true);
+        if (COMPACT_STRINGS) {
+            byte[] buf = new byte[chars];
+            formatUnsignedLong0(val, shift, buf, 0, chars);
+            return new String(buf, LATIN1);
+        } else {
+            byte[] buf = new byte[chars * 2];
+            formatUnsignedLong0UTF16(val, shift, buf, 0, chars);
+            return new String(buf, UTF16);
+        }
     }
 
     /**
@@ -385,6 +415,28 @@
         } while (charPos > offset);
     }
 
+    /** byte[]/LATIN1 version    */
+    static void formatUnsignedLong0(long val, int shift, byte[] buf, int offset, int len) {
+        int charPos = offset + len;
+        int radix = 1 << shift;
+        int mask = radix - 1;
+        do {
+            buf[--charPos] = (byte)Integer.digits[((int) val) & mask];
+            val >>>= shift;
+        } while (charPos > offset);
+    }
+
+    /** byte[]/UTF16 version    */
+    static void formatUnsignedLong0UTF16(long val, int shift, byte[] buf, int offset, int len) {
+        int charPos = offset + len;
+        int radix = 1 << shift;
+        int mask = radix - 1;
+        do {
+            StringUTF16.putChar(buf, --charPos, Integer.digits[((int) val) & mask]);
+            val >>>= shift;
+        } while (charPos > offset);
+    }
+
     /**
      * Returns a {@code String} object representing the specified
      * {@code long}.  The argument is converted to signed decimal
@@ -399,9 +451,15 @@
         if (i == Long.MIN_VALUE)
             return "-9223372036854775808";
         int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
-        char[] buf = new char[size];
-        getChars(i, size, buf);
-        return new String(buf, true);
+        if (COMPACT_STRINGS) {
+            byte[] buf = new byte[size];
+            getChars(i, size, buf);
+            return new String(buf, LATIN1);
+        } else {
+            byte[] buf = new byte[size * 2];
+            getCharsUTF16(i, size, buf);
+            return new String(buf, UTF16);
+        }
     }
 
     /**
@@ -431,7 +489,7 @@
      *
      * Will fail if i == Long.MIN_VALUE
      */
-    static void getChars(long i, int index, char[] buf) {
+    static void getChars(long i, int index, byte[] buf) {
         long q;
         int r;
         int charPos = index;
@@ -448,8 +506,8 @@
             // really: r = i - (q * 100);
             r = (int)(i - ((q << 6) + (q << 5) + (q << 2)));
             i = q;
-            buf[--charPos] = Integer.DigitOnes[r];
-            buf[--charPos] = Integer.DigitTens[r];
+            buf[--charPos] = (byte)Integer.DigitOnes[r];
+            buf[--charPos] = (byte)Integer.DigitTens[r];
         }
 
         // Get 2 digits/iteration using ints
@@ -460,8 +518,8 @@
             // really: r = i2 - (q * 100);
             r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2));
             i2 = q2;
-            buf[--charPos] = Integer.DigitOnes[r];
-            buf[--charPos] = Integer.DigitTens[r];
+            buf[--charPos] = (byte)Integer.DigitOnes[r];
+            buf[--charPos] = (byte)Integer.DigitTens[r];
         }
 
         // Fall thru to fast mode for smaller numbers
@@ -469,12 +527,59 @@
         for (;;) {
             q2 = (i2 * 52429) >>> (16+3);
             r = i2 - ((q2 << 3) + (q2 << 1));  // r = i2-(q2*10) ...
-            buf[--charPos] = Integer.digits[r];
+            buf[--charPos] = (byte)Integer.digits[r];
             i2 = q2;
             if (i2 == 0) break;
         }
         if (sign != 0) {
-            buf[--charPos] = sign;
+            buf[--charPos] = (byte)sign;
+        }
+    }
+
+    static void getCharsUTF16(long i, int index, byte[] buf) {
+        long q;
+        int r;
+        int charPos = index;
+        char sign = 0;
+
+        if (i < 0) {
+            sign = '-';
+            i = -i;
+        }
+
+        // Get 2 digits/iteration using longs until quotient fits into an int
+        while (i > Integer.MAX_VALUE) {
+            q = i / 100;
+            // really: r = i - (q * 100);
+            r = (int)(i - ((q << 6) + (q << 5) + (q << 2)));
+            i = q;
+            StringUTF16.putChar(buf, --charPos, Integer.DigitOnes[r]);
+            StringUTF16.putChar(buf, --charPos, Integer.DigitTens[r]);
+        }
+
+        // Get 2 digits/iteration using ints
+        int q2;
+        int i2 = (int)i;
+        while (i2 >= 65536) {
+            q2 = i2 / 100;
+            // really: r = i2 - (q * 100);
+            r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2));
+            i2 = q2;
+            StringUTF16.putChar(buf, --charPos, Integer.DigitOnes[r]);
+            StringUTF16.putChar(buf, --charPos, Integer.DigitTens[r]);
+        }
+
+        // Fall thru to fast mode for smaller numbers
+        // assert(i2 <= 65536, i2);
+        for (;;) {
+            q2 = (i2 * 52429) >>> (16+3);
+            r = i2 - ((q2 << 3) + (q2 << 1));  // r = i2-(q2*10) ...
+            StringUTF16.putChar(buf, --charPos, Integer.digits[r]);
+            i2 = q2;
+            if (i2 == 0) break;
+        }
+        if (sign != 0) {
+            StringUTF16.putChar(buf, --charPos, sign);
         }
     }
 
diff --git a/jdk/src/java.base/share/classes/java/lang/String.java b/jdk/src/java.base/share/classes/java/lang/String.java
index ce4262e..4d4ab35 100644
--- a/jdk/src/java.base/share/classes/java/lang/String.java
+++ b/jdk/src/java.base/share/classes/java/lang/String.java
@@ -36,7 +36,6 @@
 import java.util.Objects;
 import java.util.Spliterator;
 import java.util.StringJoiner;
-import java.util.function.IntConsumer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
@@ -119,8 +118,19 @@
 
 public final class String
     implements java.io.Serializable, Comparable<String>, CharSequence {
+
     /** The value is used for character storage. */
-    private final char value[];
+    private final byte[] value;
+
+    /**
+     * The identifier of the encoding used to encode the bytes in
+     * {@code value}. The supported values in this implementation are
+     *
+     * LATIN1
+     * UTF16
+     *
+     */
+    private final byte coder;
 
     /** Cache the hash code for the string */
     private int hash; // Default to 0
@@ -129,6 +139,49 @@
     private static final long serialVersionUID = -6849794470754667710L;
 
     /**
+     * If String compaction is disabled, the bytes in {@code value} are
+     * always encoded in UTF16.
+     *
+     * For methods with several possible implementation paths, when String
+     * compaction is disabled, only one code path is taken.
+     *
+     * The instance field value is generally opaque to optimizing JIT
+     * compilers. Therefore, in performance-sensitive place, an explicit
+     * check of the static boolean {@code COMPACT_STRINGS} is done first
+     * before checking the {@code coder} field since the static boolean
+     * {@code COMPACT_STRINGS} would be constant folded away by an
+     * optimizing JIT compiler. The idioms for these cases are as follows.
+     *
+     * For code such as:
+     *
+     *    if (coder == LATIN1) { ... }
+     *
+     * can be written more optimally as
+     *
+     *    if (coder() == LATIN1) { ... }
+     *
+     * or:
+     *
+     *    if (COMPACT_STRINGS && coder == LATIN1) { ... }
+     *
+     * An optimizing JIT compiler can fold the above conditional as:
+     *
+     *    COMPACT_STRINGS == true  => if (coder == LATIN1) { ... }
+     *    COMPACT_STRINGS == false => if (false)           { ... }
+     *
+     * @implNote
+     * The actual value for this field is injected by JVM. The static
+     * initialization block is used to set the value here to communicate
+     * that this static final field is not statically foldable, and to
+     * avoid any possible circular dependency during vm initialization.
+     */
+    static final boolean COMPACT_STRINGS;
+
+    static {
+        COMPACT_STRINGS = true;
+    }
+
+    /**
      * Class String is special cased within the Serialization Stream Protocol.
      *
      * A String instance is written into an ObjectOutputStream according to
@@ -145,6 +198,7 @@
      */
     public String() {
         this.value = "".value;
+        this.coder = "".coder;
     }
 
     /**
@@ -160,6 +214,7 @@
     @HotSpotIntrinsicCandidate
     public String(String original) {
         this.value = original.value;
+        this.coder = original.coder;
         this.hash = original.hash;
     }
 
@@ -173,7 +228,7 @@
      *         The initial value of the string
      */
     public String(char value[]) {
-        this.value = Arrays.copyOf(value, value.length);
+        this(value, 0, value.length, null);
     }
 
     /**
@@ -198,23 +253,12 @@
      *          {@code offset} is greater than {@code value.length - count}
      */
     public String(char value[], int offset, int count) {
-        if (offset < 0) {
-            throw new StringIndexOutOfBoundsException(offset);
-        }
-        if (count <= 0) {
-            if (count < 0) {
-                throw new StringIndexOutOfBoundsException(count);
-            }
-            if (offset <= value.length) {
-                this.value = "".value;
-                return;
-            }
-        }
-        // Note: offset or count might be near -1>>>1.
-        if (offset > value.length - count) {
-            throw new StringIndexOutOfBoundsException(offset + count);
-        }
-        this.value = Arrays.copyOfRange(value, offset, offset + count);
+        this(value, offset, count, rangeCheck(value, offset, count));
+    }
+
+    private static Void rangeCheck(char[] value, int offset, int count) {
+        checkBoundsOffCount(offset, count, value.length);
+        return null;
     }
 
     /**
@@ -246,48 +290,22 @@
      * @since  1.5
      */
     public String(int[] codePoints, int offset, int count) {
-        if (offset < 0) {
-            throw new StringIndexOutOfBoundsException(offset);
+        checkBoundsOffCount(offset, count, codePoints.length);
+        if (count == 0) {
+            this.value = "".value;
+            this.coder = "".coder;
+            return;
         }
-        if (count <= 0) {
-            if (count < 0) {
-                throw new StringIndexOutOfBoundsException(count);
-            }
-            if (offset <= codePoints.length) {
-                this.value = "".value;
+        if (COMPACT_STRINGS) {
+            byte[] val = StringLatin1.toBytes(codePoints, offset, count);
+            if (val != null) {
+                this.coder = LATIN1;
+                this.value = val;
                 return;
             }
         }
-        // Note: offset or count might be near -1>>>1.
-        if (offset > codePoints.length - count) {
-            throw new StringIndexOutOfBoundsException(offset + count);
-        }
-
-        final int end = offset + count;
-
-        // Pass 1: Compute precise size of char[]
-        int n = count;
-        for (int i = offset; i < end; i++) {
-            int c = codePoints[i];
-            if (Character.isBmpCodePoint(c))
-                continue;
-            else if (Character.isValidCodePoint(c))
-                n++;
-            else throw new IllegalArgumentException(Integer.toString(c));
-        }
-
-        // Pass 2: Allocate and fill in char[]
-        final char[] v = new char[n];
-
-        for (int i = offset, j = 0; i < end; i++, j++) {
-            int c = codePoints[i];
-            if (Character.isBmpCodePoint(c))
-                v[j] = (char)c;
-            else
-                Character.toSurrogates(c, v, j++);
-        }
-
-        this.value = v;
+        this.coder = UTF16;
+        this.value = StringUTF16.toBytes(codePoints, offset, count);
     }
 
     /**
@@ -332,20 +350,24 @@
      */
     @Deprecated
     public String(byte ascii[], int hibyte, int offset, int count) {
-        checkBounds(ascii, offset, count);
-        char[] value = new char[count];
-
-        if (hibyte == 0) {
-            for (int i = count; i-- > 0;) {
-                value[i] = (char)(ascii[i + offset] & 0xff);
-            }
+        checkBoundsOffCount(offset, count, ascii.length);
+        if (count == 0) {
+            this.value = "".value;
+            this.coder = "".coder;
+            return;
+        }
+        if (COMPACT_STRINGS && (byte)hibyte == 0) {
+            this.value = Arrays.copyOfRange(ascii, offset, offset + count);
+            this.coder = LATIN1;
         } else {
             hibyte <<= 8;
-            for (int i = count; i-- > 0;) {
-                value[i] = (char)(hibyte | (ascii[i + offset] & 0xff));
+            byte[] val = StringUTF16.newBytesFor(count);
+            for (int i = 0; i < count; i++) {
+                StringUTF16.putChar(val, i, hibyte | (ascii[offset++] & 0xff));
             }
+            this.value = val;
+            this.coder = UTF16;
         }
-        this.value = value;
     }
 
     /**
@@ -383,19 +405,6 @@
         this(ascii, hibyte, 0, ascii.length);
     }
 
-    /* Common private utility method used to bounds check the byte array
-     * and requested offset & length values used by the String(byte[],..)
-     * constructors.
-     */
-    private static void checkBounds(byte[] bytes, int offset, int length) {
-        if (length < 0)
-            throw new StringIndexOutOfBoundsException(length);
-        if (offset < 0)
-            throw new StringIndexOutOfBoundsException(offset);
-        if (offset > bytes.length - length)
-            throw new StringIndexOutOfBoundsException(offset + length);
-    }
-
     /**
      * Constructs a new {@code String} by decoding the specified subarray of
      * bytes using the specified charset.  The length of the new {@code String}
@@ -433,8 +442,11 @@
             throws UnsupportedEncodingException {
         if (charsetName == null)
             throw new NullPointerException("charsetName");
-        checkBounds(bytes, offset, length);
-        this.value = StringCoding.decode(charsetName, bytes, offset, length);
+        checkBoundsOffCount(offset, length, bytes.length);
+        StringCoding.Result ret =
+            StringCoding.decode(charsetName, bytes, offset, length);
+        this.value = ret.value;
+        this.coder = ret.coder;
     }
 
     /**
@@ -470,8 +482,11 @@
     public String(byte bytes[], int offset, int length, Charset charset) {
         if (charset == null)
             throw new NullPointerException("charset");
-        checkBounds(bytes, offset, length);
-        this.value = StringCoding.decode(charset, bytes, offset, length);
+        checkBoundsOffCount(offset, length, bytes.length);
+        StringCoding.Result ret =
+            StringCoding.decode(charset, bytes, offset, length);
+        this.value = ret.value;
+        this.coder = ret.coder;
     }
 
     /**
@@ -553,8 +568,10 @@
      * @since  1.1
      */
     public String(byte bytes[], int offset, int length) {
-        checkBounds(bytes, offset, length);
-        this.value = StringCoding.decode(bytes, offset, length);
+        checkBoundsOffCount(offset, length, bytes.length);
+        StringCoding.Result ret = StringCoding.decode(bytes, offset, length);
+        this.value = ret.value;
+        this.coder = ret.coder;
     }
 
     /**
@@ -587,9 +604,7 @@
      *         A {@code StringBuffer}
      */
     public String(StringBuffer buffer) {
-        synchronized(buffer) {
-            this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
-        }
+        this(buffer.toString());
     }
 
     /**
@@ -608,18 +623,20 @@
      * @since  1.5
      */
     public String(StringBuilder builder) {
-        this.value = Arrays.copyOf(builder.getValue(), builder.length());
+        this(builder, null);
     }
 
-    /*
+   /*
     * Package private constructor which shares value array for speed.
     * this constructor is always expected to be called with share==true.
     * a separate constructor is needed because we already have a public
     * String(char[]) constructor that makes a copy of the given char[].
     */
-    String(char[] value, boolean share) {
+    // TBD: this is kept for package internal use (Thread/System),
+    // should be removed if they all have a byte[] version
+    String(char[] val, boolean share) {
         // assert share : "unshared not supported";
-        this.value = value;
+        this(val, 0, val.length, null);
     }
 
     /**
@@ -631,7 +648,7 @@
      *          object.
      */
     public int length() {
-        return value.length;
+        return value.length >> coder();
     }
 
     /**
@@ -665,10 +682,11 @@
      *             string.
      */
     public char charAt(int index) {
-        if ((index < 0) || (index >= value.length)) {
-            throw new StringIndexOutOfBoundsException(index);
+        if (isLatin1()) {
+            return StringLatin1.charAt(value, index);
+        } else {
+            return StringUTF16.charAt(value, index);
         }
-        return value[index];
     }
 
     /**
@@ -694,10 +712,13 @@
      * @since      1.5
      */
     public int codePointAt(int index) {
-        if ((index < 0) || (index >= value.length)) {
-            throw new StringIndexOutOfBoundsException(index);
+        if (isLatin1()) {
+            checkIndex(index, value.length);
+            return value[index] & 0xff;
         }
-        return Character.codePointAtImpl(value, index, value.length);
+        int length = value.length >> 1;
+        checkIndex(index, length);
+        return StringUTF16.codePointAt(value, index, length);
     }
 
     /**
@@ -724,10 +745,13 @@
      */
     public int codePointBefore(int index) {
         int i = index - 1;
-        if ((i < 0) || (i >= value.length)) {
+        if (i < 0 || i >= length()) {
             throw new StringIndexOutOfBoundsException(index);
         }
-        return Character.codePointBeforeImpl(value, index, 0);
+        if (isLatin1()) {
+            return (value[i] & 0xff);
+        }
+        return StringUTF16.codePointBefore(value, index);
     }
 
     /**
@@ -752,10 +776,14 @@
      * @since  1.5
      */
     public int codePointCount(int beginIndex, int endIndex) {
-        if (beginIndex < 0 || endIndex > value.length || beginIndex > endIndex) {
+        if (beginIndex < 0 || beginIndex > endIndex ||
+            endIndex > length()) {
             throw new IndexOutOfBoundsException();
         }
-        return Character.codePointCountImpl(value, beginIndex, endIndex - beginIndex);
+        if (isLatin1()) {
+            return endIndex - beginIndex;
+        }
+        return StringUTF16.codePointCount(value, beginIndex, endIndex);
     }
 
     /**
@@ -779,19 +807,10 @@
      * @since 1.5
      */
     public int offsetByCodePoints(int index, int codePointOffset) {
-        if (index < 0 || index > value.length) {
+        if (index < 0 || index > length()) {
             throw new IndexOutOfBoundsException();
         }
-        return Character.offsetByCodePointsImpl(value, 0, value.length,
-                index, codePointOffset);
-    }
-
-    /**
-     * Copy characters from this string into dst starting at dstBegin.
-     * This method doesn't perform any range checking.
-     */
-    void getChars(char dst[], int dstBegin) {
-        System.arraycopy(value, 0, dst, dstBegin, value.length);
+        return Character.offsetByCodePoints(this, index, codePointOffset);
     }
 
     /**
@@ -825,16 +844,13 @@
      *                {@code dst.length}</ul>
      */
     public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
-        if (srcBegin < 0) {
-            throw new StringIndexOutOfBoundsException(srcBegin);
+        checkBoundsBeginEnd(srcBegin, srcEnd, length());
+        checkBoundsOffCount(dstBegin, srcEnd - srcBegin, dst.length);
+        if (isLatin1()) {
+            StringLatin1.getChars(value, srcBegin, srcEnd, dst, dstBegin);
+        } else {
+            StringUTF16.getChars(value, srcBegin, srcEnd, dst, dstBegin);
         }
-        if (srcEnd > value.length) {
-            throw new StringIndexOutOfBoundsException(srcEnd);
-        }
-        if (srcBegin > srcEnd) {
-            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
-        }
-        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
     }
 
     /**
@@ -882,24 +898,13 @@
      */
     @Deprecated
     public void getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin) {
-        if (srcBegin < 0) {
-            throw new StringIndexOutOfBoundsException(srcBegin);
-        }
-        if (srcEnd > value.length) {
-            throw new StringIndexOutOfBoundsException(srcEnd);
-        }
-        if (srcBegin > srcEnd) {
-            throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
-        }
+        checkBoundsBeginEnd(srcBegin, srcEnd, length());
         Objects.requireNonNull(dst);
-
-        int j = dstBegin;
-        int n = srcEnd;
-        int i = srcBegin;
-        char[] val = value;   /* avoid getfield opcode */
-
-        while (i < n) {
-            dst[j++] = (byte)val[i++];
+        checkBoundsOffCount(dstBegin, srcEnd - srcBegin, dst.length);
+        if (isLatin1()) {
+            StringLatin1.getBytes(value, srcBegin, srcEnd, dst, dstBegin);
+        } else {
+            StringUTF16.getBytes(value, srcBegin, srcEnd, dst, dstBegin);
         }
     }
 
@@ -926,7 +931,7 @@
     public byte[] getBytes(String charsetName)
             throws UnsupportedEncodingException {
         if (charsetName == null) throw new NullPointerException();
-        return StringCoding.encode(charsetName, value, 0, value.length);
+        return StringCoding.encode(charsetName, coder(), value);
     }
 
     /**
@@ -949,8 +954,8 @@
      */
     public byte[] getBytes(Charset charset) {
         if (charset == null) throw new NullPointerException();
-        return StringCoding.encode(charset, value, 0, value.length);
-    }
+        return StringCoding.encode(charset, coder(), value);
+     }
 
     /**
      * Encodes this {@code String} into a sequence of bytes using the
@@ -966,7 +971,7 @@
      * @since      1.1
      */
     public byte[] getBytes() {
-        return StringCoding.encode(value, 0, value.length);
+        return StringCoding.encode(coder(), value);
     }
 
     /**
@@ -987,23 +992,15 @@
      * @see  #compareTo(String)
      * @see  #equalsIgnoreCase(String)
      */
-    @HotSpotIntrinsicCandidate
     public boolean equals(Object anObject) {
         if (this == anObject) {
             return true;
         }
         if (anObject instanceof String) {
-            char[] v1 = value;
-            char[] v2 = ((String)anObject).value;
-            int n = v1.length;
-            if (n == v2.length) {
-                int i = 0;
-                while (n-- != 0) {
-                    if (v1[i] != v2[i])
-                        return false;
-                    i++;
-                }
-                return true;
+            String aString = (String)anObject;
+            if (coder() == aString.coder()) {
+                return isLatin1() ? StringLatin1.equals(value, aString.value)
+                                  : StringUTF16.equals(value, aString.value);
             }
         }
         return false;
@@ -1032,16 +1029,28 @@
     }
 
     private boolean nonSyncContentEquals(AbstractStringBuilder sb) {
-        char[] v1 = value;
-        char[] v2 = sb.getValue();
-        int n = v1.length;
-        if (n != sb.length()) {
+        int len = length();
+        if (len != sb.length()) {
             return false;
         }
-        for (int i = 0; i < n; i++) {
-            if (v1[i] != v2[i]) {
+        byte v1[] = value;
+        byte v2[] = sb.getValue();
+        if (coder() == sb.getCoder()) {
+            int n = v1.length;
+            for (int i = 0; i < n; i++) {
+                if (v1[i] != v2[i]) {
+                    return false;
+                }
+            }
+        } else {
+            if (!isLatin1()) {  // utf16 str and latin1 abs can never be "equal"
                 return false;
             }
+            for (int i = 0; i < len; i++) {
+                if ((char)(v1[i] & 0xff) != StringUTF16.getChar(v2, i)) {
+                    return false;
+                }
+            }
         }
         return true;
     }
@@ -1081,14 +1090,22 @@
             return equals(cs);
         }
         // Argument is a generic CharSequence
-        char[] v1 = value;
-        int n = v1.length;
-        if (n != cs.length()) {
+        int n = cs.length();
+        if (n != length()) {
             return false;
         }
-        for (int i = 0; i < n; i++) {
-            if (v1[i] != cs.charAt(i)) {
-                return false;
+        byte[] val = this.value;
+        if (isLatin1()) {
+            for (int i = 0; i < n; i++) {
+                if ((val[i] & 0xff) != cs.charAt(i)) {
+                    return false;
+                }
+            }
+        } else {
+            for (int i = 0; i < n; i++) {
+                if (StringUTF16.getChar(val, i) != cs.charAt(i)) {
+                    return false;
+                }
             }
         }
         return true;
@@ -1125,8 +1142,8 @@
     public boolean equalsIgnoreCase(String anotherString) {
         return (this == anotherString) ? true
                 : (anotherString != null)
-                && (anotherString.value.length == value.length)
-                && regionMatches(true, 0, anotherString, 0, value.length);
+                && (anotherString.length() == length())
+                && regionMatches(true, 0, anotherString, 0, length());
     }
 
     /**
@@ -1173,23 +1190,16 @@
      *          value greater than {@code 0} if this string is
      *          lexicographically greater than the string argument.
      */
-    @HotSpotIntrinsicCandidate
     public int compareTo(String anotherString) {
-        char[] v1 = value;
-        char[] v2 = anotherString.value;
-        int len1 = v1.length;
-        int len2 = v2.length;
-        int lim = Math.min(len1, len2);
-
-        for (int k = 0; k < lim; k++) {
-            char c1 = v1[k];
-            char c2 = v2[k];
-            if (c1 != c2) {
-                return c1 - c2;
-            }
+        byte v1[] = value;
+        byte v2[] = anotherString.value;
+        if (coder() == anotherString.coder()) {
+            return isLatin1() ? StringLatin1.compareTo(v1, v2)
+                              : StringUTF16.compareTo(v1, v2);
         }
-        return len1 - len2;
-    }
+        return isLatin1() ? StringLatin1.compareToUTF16(v1, v2)
+                          : StringUTF16.compareToLatin1(v1, v2);
+     }
 
     /**
      * A Comparator that orders {@code String} objects as by
@@ -1210,12 +1220,18 @@
         private static final long serialVersionUID = 8575799808933029326L;
 
         public int compare(String s1, String s2) {
+            byte v1[] = s1.value;
+            byte v2[] = s2.value;
             int n1 = s1.length();
             int n2 = s2.length();
+            boolean s1IsLatin1 = s1.isLatin1();
+            boolean s2IsLatin1 = s2.isLatin1();
             int min = Math.min(n1, n2);
             for (int i = 0; i < min; i++) {
-                char c1 = s1.charAt(i);
-                char c2 = s2.charAt(i);
+                char c1 = s1IsLatin1 ? StringLatin1.getChar(v1, i)
+                                     : StringUTF16.getChar(v1, i);
+                char c2 = s2IsLatin1 ? StringLatin1.getChar(v2, i)
+                                     : StringUTF16.getChar(v2, i);
                 if (c1 != c2) {
                     c1 = Character.toUpperCase(c1);
                     c2 = Character.toUpperCase(c2);
@@ -1294,21 +1310,41 @@
      *          exactly matches the specified subregion of the string argument;
      *          {@code false} otherwise.
      */
-    public boolean regionMatches(int toffset, String other, int ooffset,
-            int len) {
-        char[] ta = value;
-        int to = toffset;
-        char[] pa = other.value;
-        int po = ooffset;
+    public boolean regionMatches(int toffset, String other, int ooffset, int len) {
+        byte tv[] = value;
+        byte ov[] = other.value;
         // Note: toffset, ooffset, or len might be near -1>>>1.
-        if ((ooffset < 0) || (toffset < 0)
-                || (toffset > (long)ta.length - len)
-                || (ooffset > (long)pa.length - len)) {
+        if ((ooffset < 0) || (toffset < 0) ||
+             (toffset > (long)length() - len) ||
+             (ooffset > (long)other.length() - len)) {
             return false;
         }
-        while (len-- > 0) {
-            if (ta[to++] != pa[po++]) {
-                return false;
+        if (coder() == other.coder()) {
+            if (!isLatin1() && (len > 0)) {
+                toffset = toffset << 1;
+                ooffset = ooffset << 1;
+                len = len << 1;
+            }
+            while (len-- > 0) {
+                if (tv[toffset++] != ov[ooffset++]) {
+                    return false;
+                }
+            }
+        } else {
+            if (coder() == LATIN1) {
+                while (len-- > 0) {
+                    if (StringLatin1.getChar(tv, toffset++) !=
+                        StringUTF16.getChar(ov, ooffset++)) {
+                        return false;
+                    }
+                }
+            } else {
+                while (len-- > 0) {
+                    if (StringUTF16.getChar(tv, toffset++) !=
+                        StringLatin1.getChar(ov, ooffset++)) {
+                        return false;
+                    }
+                }
             }
         }
         return true;
@@ -1366,43 +1402,25 @@
      */
     public boolean regionMatches(boolean ignoreCase, int toffset,
             String other, int ooffset, int len) {
-        char[] ta = value;
-        int to = toffset;
-        char[] pa = other.value;
-        int po = ooffset;
+        if (!ignoreCase) {
+            return regionMatches(toffset, other, ooffset, len);
+        }
         // Note: toffset, ooffset, or len might be near -1>>>1.
         if ((ooffset < 0) || (toffset < 0)
-                || (toffset > (long)ta.length - len)
-                || (ooffset > (long)pa.length - len)) {
+                || (toffset > (long)length() - len)
+                || (ooffset > (long)other.length() - len)) {
             return false;
         }
-        while (len-- > 0) {
-            char c1 = ta[to++];
-            char c2 = pa[po++];
-            if (c1 == c2) {
-                continue;
-            }
-            if (ignoreCase) {
-                // If characters don't match but case may be ignored,
-                // try converting both characters to uppercase.
-                // If the results match, then the comparison scan should
-                // continue.
-                char u1 = Character.toUpperCase(c1);
-                char u2 = Character.toUpperCase(c2);
-                if (u1 == u2) {
-                    continue;
-                }
-                // Unfortunately, conversion to uppercase does not work properly
-                // for the Georgian alphabet, which has strange rules about case
-                // conversion.  So we need to make one last check before
-                // exiting.
-                if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
-                    continue;
-                }
-            }
-            return false;
+        byte tv[] = value;
+        byte ov[] = other.value;
+        if (coder() == other.coder()) {
+            return isLatin1()
+              ? StringLatin1.regionMatchesCI(tv, toffset, ov, ooffset, len)
+              : StringUTF16.regionMatchesCI(tv, toffset, ov, ooffset, len);
         }
-        return true;
+        return isLatin1()
+              ? StringLatin1.regionMatchesCI_UTF16(tv, toffset, ov, ooffset, len)
+              : StringUTF16.regionMatchesCI_Latin1(tv, toffset, ov, ooffset, len);
     }
 
     /**
@@ -1423,19 +1441,31 @@
      *          </pre>
      */
     public boolean startsWith(String prefix, int toffset) {
-        char[] ta = value;
-        int to = toffset;
-        char[] pa = prefix.value;
-        int po = 0;
-        int pc = pa.length;
         // Note: toffset might be near -1>>>1.
-        if ((toffset < 0) || (toffset > ta.length - pc)) {
+        if (toffset < 0 || toffset > length() - prefix.length()) {
             return false;
         }
-        while (--pc >= 0) {
-            if (ta[to++] != pa[po++]) {
+        byte ta[] = value;
+        byte pa[] = prefix.value;
+        int po = 0;
+        int pc = pa.length;
+        if (coder() == prefix.coder()) {
+            int to = isLatin1() ? toffset : toffset << 1;
+            while (po < pc) {
+                if (ta[to++] != pa[po++]) {
+                    return false;
+                }
+            }
+        } else {
+            if (isLatin1()) {  // && pcoder == UTF16
                 return false;
             }
+            // coder == UTF16 && pcoder == LATIN1)
+            while (po < pc) {
+                if (StringUTF16.getChar(ta, toffset++) != (pa[po++] & 0xff)) {
+                    return false;
+               }
+            }
         }
         return true;
     }
@@ -1469,7 +1499,7 @@
      *          as determined by the {@link #equals(Object)} method.
      */
     public boolean endsWith(String suffix) {
-        return startsWith(suffix, value.length - suffix.value.length);
+        return startsWith(suffix, length() - suffix.length());
     }
 
     /**
@@ -1486,16 +1516,11 @@
      * @return  a hash code value for this object.
      */
     public int hashCode() {
-        int h = hash;
-        if (h == 0) {
-            for (char v : value) {
-                h = 31 * h + v;
-            }
-            if (h != 0) {
-                hash = h;
-            }
+        if (hash == 0 && value.length > 0) {
+            hash = isLatin1() ? StringLatin1.hashCode(value)
+                              : StringUTF16.hashCode(value);
         }
-        return h;
+        return hash;
     }
 
     /**
@@ -1566,45 +1591,8 @@
      *          if the character does not occur.
      */
     public int indexOf(int ch, int fromIndex) {
-        final int max = value.length;
-        if (fromIndex < 0) {
-            fromIndex = 0;
-        } else if (fromIndex >= max) {
-            // Note: fromIndex might be near -1>>>1.
-            return -1;
-        }
-
-        if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
-            // handle most cases here (ch is a BMP code point or a
-            // negative value (invalid code point))
-            final char[] value = this.value;
-            for (int i = fromIndex; i < max; i++) {
-                if (value[i] == ch) {
-                    return i;
-                }
-            }
-            return -1;
-        } else {
-            return indexOfSupplementary(ch, fromIndex);
-        }
-    }
-
-    /**
-     * Handles (rare) calls of indexOf with a supplementary character.
-     */
-    private int indexOfSupplementary(int ch, int fromIndex) {
-        if (Character.isValidCodePoint(ch)) {
-            final char[] value = this.value;
-            final char hi = Character.highSurrogate(ch);
-            final char lo = Character.lowSurrogate(ch);
-            final int max = value.length - 1;
-            for (int i = fromIndex; i < max; i++) {
-                if (value[i] == hi && value[i + 1] == lo) {
-                    return i;
-                }
-            }
-        }
-        return -1;
+        return isLatin1() ? StringLatin1.indexOf(value, ch, fromIndex)
+                          : StringUTF16.indexOf(value, ch, fromIndex);
     }
 
     /**
@@ -1631,7 +1619,7 @@
      *          {@code -1} if the character does not occur.
      */
     public int lastIndexOf(int ch) {
-        return lastIndexOf(ch, value.length - 1);
+        return lastIndexOf(ch, length() - 1);
     }
 
     /**
@@ -1669,38 +1657,8 @@
      *          if the character does not occur before that point.
      */
     public int lastIndexOf(int ch, int fromIndex) {
-        if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
-            // handle most cases here (ch is a BMP code point or a
-            // negative value (invalid code point))
-            final char[] value = this.value;
-            int i = Math.min(fromIndex, value.length - 1);
-            for (; i >= 0; i--) {
-                if (value[i] == ch) {
-                    return i;
-                }
-            }
-            return -1;
-        } else {
-            return lastIndexOfSupplementary(ch, fromIndex);
-        }
-    }
-
-    /**
-     * Handles (rare) calls of lastIndexOf with a supplementary character.
-     */
-    private int lastIndexOfSupplementary(int ch, int fromIndex) {
-        if (Character.isValidCodePoint(ch)) {
-            final char[] value = this.value;
-            char hi = Character.highSurrogate(ch);
-            char lo = Character.lowSurrogate(ch);
-            int i = Math.min(fromIndex, value.length - 2);
-            for (; i >= 0; i--) {
-                if (value[i] == hi && value[i + 1] == lo) {
-                    return i;
-                }
-            }
-        }
-        return -1;
+        return isLatin1() ? StringLatin1.lastIndexOf(value, ch, fromIndex)
+                          : StringUTF16.lastIndexOf(value, ch, fromIndex);
     }
 
     /**
@@ -1717,9 +1675,15 @@
      * @return  the index of the first occurrence of the specified substring,
      *          or {@code -1} if there is no such occurrence.
      */
-    @HotSpotIntrinsicCandidate
     public int indexOf(String str) {
-        return indexOf(str, 0);
+        if (coder() == str.coder()) {
+            return isLatin1() ? StringLatin1.indexOf(value, str.value)
+                              : StringUTF16.indexOf(value, str.value);
+        }
+        if (coder() == LATIN1) {  // str.coder == UTF16
+            return -1;
+        }
+        return StringUTF16.indexOfLatin1(value, str.value);
     }
 
     /**
@@ -1740,8 +1704,7 @@
      *          or {@code -1} if there is no such occurrence.
      */
     public int indexOf(String str, int fromIndex) {
-        return indexOf(value, 0, value.length,
-                str.value, 0, str.value.length, fromIndex);
+        return indexOf(value, coder(), length(), str, fromIndex);
     }
 
     /**
@@ -1749,68 +1712,38 @@
      * source is the character array being searched, and the target
      * is the string being searched for.
      *
-     * @param   source       the characters being searched.
-     * @param   sourceOffset offset of the source string.
-     * @param   sourceCount  count of the source string.
-     * @param   target       the characters being searched for.
-     * @param   fromIndex    the index to begin searching from.
+     * @param   src       the characters being searched.
+     * @param   srcCoder  the coder of the source string.
+     * @param   srcCount  length of the source string.
+     * @param   tgtStr    the characters being searched for.
+     * @param   fromIndex the index to begin searching from.
      */
-    static int indexOf(char[] source, int sourceOffset, int sourceCount,
-            String target, int fromIndex) {
-        return indexOf(source, sourceOffset, sourceCount,
-                       target.value, 0, target.value.length,
-                       fromIndex);
-    }
+    static int indexOf(byte[] src, byte srcCoder, int srcCount,
+                       String tgtStr, int fromIndex) {
 
-    /**
-     * Code shared by String and StringBuffer to do searches. The
-     * source is the character array being searched, and the target
-     * is the string being searched for.
-     *
-     * @param   source       the characters being searched.
-     * @param   sourceOffset offset of the source string.
-     * @param   sourceCount  count of the source string.
-     * @param   target       the characters being searched for.
-     * @param   targetOffset offset of the target string.
-     * @param   targetCount  count of the target string.
-     * @param   fromIndex    the index to begin searching from.
-     */
-    static int indexOf(char[] source, int sourceOffset, int sourceCount,
-            char[] target, int targetOffset, int targetCount,
-            int fromIndex) {
-        if (fromIndex >= sourceCount) {
-            return (targetCount == 0 ? sourceCount : -1);
+        byte[] tgt    = tgtStr.value;
+        byte tgtCoder = tgtStr.coder();
+        int tgtCount  = tgtStr.length();
+
+        if (fromIndex >= srcCount) {
+            return (tgtCount == 0 ? srcCount : -1);
         }
         if (fromIndex < 0) {
             fromIndex = 0;
         }
-        if (targetCount == 0) {
+        if (tgtCount == 0) {
             return fromIndex;
         }
-
-        char first = target[targetOffset];
-        int max = sourceOffset + (sourceCount - targetCount);
-
-        for (int i = sourceOffset + fromIndex; i <= max; i++) {
-            /* Look for first character. */
-            if (source[i] != first) {
-                while (++i <= max && source[i] != first);
-            }
-
-            /* Found first character, now look at the rest of v2 */
-            if (i <= max) {
-                int j = i + 1;
-                int end = j + targetCount - 1;
-                for (int k = targetOffset + 1; j < end && source[j]
-                        == target[k]; j++, k++);
-
-                if (j == end) {
-                    /* Found whole string. */
-                    return i - sourceOffset;
-                }
-            }
+        if (srcCoder == tgtCoder) {
+            return srcCoder == LATIN1
+                ? StringLatin1.indexOf(src, srcCount, tgt, tgtCount, fromIndex)
+                : StringUTF16.indexOf(src, srcCount, tgt, tgtCount, fromIndex);
         }
-        return -1;
+        if (srcCoder == LATIN1) {    //  && tgtCoder == UTF16
+            return -1;
+        }
+        // srcCoder == UTF16 && tgtCoder == LATIN1) {
+        return StringUTF16.indexOfLatin1(src, srcCount, tgt, tgtCount, fromIndex);
     }
 
     /**
@@ -1829,7 +1762,7 @@
      *          or {@code -1} if there is no such occurrence.
      */
     public int lastIndexOf(String str) {
-        return lastIndexOf(str, value.length);
+        return lastIndexOf(str, length());
     }
 
     /**
@@ -1850,8 +1783,7 @@
      *          or {@code -1} if there is no such occurrence.
      */
     public int lastIndexOf(String str, int fromIndex) {
-        return lastIndexOf(value, 0, value.length,
-                str.value, 0, str.value.length, fromIndex);
+        return lastIndexOf(value, coder(), length(), str, fromIndex);
     }
 
     /**
@@ -1859,40 +1791,22 @@
      * source is the character array being searched, and the target
      * is the string being searched for.
      *
-     * @param   source       the characters being searched.
-     * @param   sourceOffset offset of the source string.
-     * @param   sourceCount  count of the source string.
-     * @param   target       the characters being searched for.
-     * @param   fromIndex    the index to begin searching from.
+     * @param   src         the characters being searched.
+     * @param   srcCoder  coder handles the mapping between bytes/chars
+     * @param   srcCount    count of the source string.
+     * @param   tgt         the characters being searched for.
+     * @param   fromIndex   the index to begin searching from.
      */
-    static int lastIndexOf(char[] source, int sourceOffset, int sourceCount,
-            String target, int fromIndex) {
-        return lastIndexOf(source, sourceOffset, sourceCount,
-                       target.value, 0, target.value.length,
-                       fromIndex);
-    }
-
-    /**
-     * Code shared by String and StringBuffer to do searches. The
-     * source is the character array being searched, and the target
-     * is the string being searched for.
-     *
-     * @param   source       the characters being searched.
-     * @param   sourceOffset offset of the source string.
-     * @param   sourceCount  count of the source string.
-     * @param   target       the characters being searched for.
-     * @param   targetOffset offset of the target string.
-     * @param   targetCount  count of the target string.
-     * @param   fromIndex    the index to begin searching from.
-     */
-    static int lastIndexOf(char[] source, int sourceOffset, int sourceCount,
-            char[] target, int targetOffset, int targetCount,
-            int fromIndex) {
+    static int lastIndexOf(byte[] src, byte srcCoder, int srcCount,
+                           String tgtStr, int fromIndex) {
+        byte[] tgt = tgtStr.value;
+        byte tgtCoder = tgtStr.coder();
+        int tgtCount = tgtStr.length();
         /*
          * Check arguments; return immediately where possible. For
          * consistency, don't check for null str.
          */
-        int rightIndex = sourceCount - targetCount;
+        int rightIndex = srcCount - tgtCount;
         if (fromIndex < 0) {
             return -1;
         }
@@ -1900,34 +1814,41 @@
             fromIndex = rightIndex;
         }
         /* Empty string always matches. */
-        if (targetCount == 0) {
+        if (tgtCount == 0) {
             return fromIndex;
         }
-
-        int strLastIndex = targetOffset + targetCount - 1;
-        char strLastChar = target[strLastIndex];
-        int min = sourceOffset + targetCount - 1;
+        if (srcCoder == tgtCoder) {
+            return srcCoder == LATIN1
+                ? StringLatin1.lastIndexOf(src, srcCount, tgt, tgtCount, fromIndex)
+                : StringUTF16.lastIndexOf(src, srcCount, tgt, tgtCount, fromIndex);
+        }
+        if (srcCoder == LATIN1) {    // && tgtCoder == UTF16
+            return -1;
+        }
+                                     // srcCoder == UTF16 && tgtCoder == LATIN1
+        int min = tgtCount - 1;
         int i = min + fromIndex;
+        int strLastIndex = tgtCount - 1;
 
+        char strLastChar = (char)(tgt[strLastIndex] & 0xff);
     startSearchForLastChar:
         while (true) {
-            while (i >= min && source[i] != strLastChar) {
+            while (i >= min && StringUTF16.getChar(src, i) != strLastChar) {
                 i--;
             }
             if (i < min) {
                 return -1;
             }
             int j = i - 1;
-            int start = j - (targetCount - 1);
+            int start = j - strLastIndex;
             int k = strLastIndex - 1;
-
             while (j > start) {
-                if (source[j--] != target[k--]) {
+                if (StringUTF16.getChar(src, j--) != (tgt[k--] & 0xff)) {
                     i--;
                     continue startSearchForLastChar;
                 }
             }
-            return start - sourceOffset + 1;
+            return start + 1;
         }
     }
 
@@ -1949,17 +1870,18 @@
      *             length of this {@code String} object.
      */
     public String substring(int beginIndex) {
-        if (beginIndex <= 0) {
-            if (beginIndex < 0) {
-                throw new StringIndexOutOfBoundsException(beginIndex);
-            }
-            return this;
+        if (beginIndex < 0) {
+            throw new StringIndexOutOfBoundsException(beginIndex);
         }
-        int subLen = value.length - beginIndex;
+        int subLen = length() - beginIndex;
         if (subLen < 0) {
             throw new StringIndexOutOfBoundsException(subLen);
         }
-        return new String(value, beginIndex, subLen);
+        if (beginIndex == 0) {
+            return this;
+        }
+        return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen)
+                          : StringUTF16.newString(value, beginIndex, subLen);
     }
 
     /**
@@ -1985,22 +1907,14 @@
      *             {@code endIndex}.
      */
     public String substring(int beginIndex, int endIndex) {
-        if (beginIndex <= 0) {
-            if (beginIndex < 0) {
-                throw new StringIndexOutOfBoundsException(beginIndex);
-            }
-            if (endIndex == value.length) {
-                return this;
-            }
-        }
-        if (endIndex > value.length) {
-            throw new StringIndexOutOfBoundsException(endIndex);
-        }
+        int length = length();
+        checkBoundsBeginEnd(beginIndex, endIndex, length);
         int subLen = endIndex - beginIndex;
-        if (subLen < 0) {
-            throw new StringIndexOutOfBoundsException(subLen);
+        if (beginIndex == 0 && endIndex == length) {
+            return this;
         }
-        return new String(value, beginIndex, subLen);
+        return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen)
+                          : StringUTF16.newString(value, beginIndex, subLen);
     }
 
     /**
@@ -2057,14 +1971,23 @@
      *          characters followed by the string argument's characters.
      */
     public String concat(String str) {
-        int otherLen = str.length();
-        if (otherLen == 0) {
+        int olen = str.length();
+        if (olen == 0) {
             return this;
         }
-        int len = value.length;
-        char[] buf = Arrays.copyOf(value, len + otherLen);
-        str.getChars(buf, len);
-        return new String(buf, true);
+        if (coder() == str.coder()) {
+            byte[] val = this.value;
+            byte[] oval = str.value;
+            int len = val.length + oval.length;
+            byte[] buf = Arrays.copyOf(val, len);
+            System.arraycopy(oval, 0, buf, val.length, oval.length);
+            return new String(buf, coder);
+        }
+        int len = length();
+        byte[] buf = StringUTF16.newBytesFor(len + olen);
+        getBytes(buf, 0, UTF16);
+        str.getBytes(buf, len, UTF16);
+        return new String(buf, UTF16);
     }
 
     /**
@@ -2098,26 +2021,10 @@
      */
     public String replace(char oldChar, char newChar) {
         if (oldChar != newChar) {
-            char[] val = value; /* avoid getfield opcode */
-            int len = val.length;
-            int i = -1;
-
-            while (++i < len) {
-                if (val[i] == oldChar) {
-                    break;
-                }
-            }
-            if (i < len) {
-                char[] buf = new char[len];
-                for (int j = 0; j < i; j++) {
-                    buf[j] = val[j];
-                }
-                while (i < len) {
-                    char c = val[i];
-                    buf[i] = (c == oldChar) ? newChar : c;
-                    i++;
-                }
-                return new String(buf, true);
+            String ret = isLatin1() ? StringLatin1.replace(value, oldChar, newChar)
+                                    : StringUTF16.replace(value, oldChar, newChar);
+            if (ret != null) {
+                return ret;
             }
         }
         return this;
@@ -2269,29 +2176,27 @@
      * @since 1.5
      */
     public String replace(CharSequence target, CharSequence replacement) {
-        String starget = target.toString();
-        String srepl = replacement.toString();
-        int j = indexOf(starget);
+        String tgtStr = target.toString();
+        String replStr = replacement.toString();
+        int j = indexOf(tgtStr);
         if (j < 0) {
             return this;
         }
-        int targLen = starget.length();
-        int targLen1 = Math.max(targLen, 1);
-        final char[] value = this.value;
-        final char[] replValue = srepl.value;
-        int newLenHint = value.length - targLen + replValue.length;
+        int tgtLen = tgtStr.length();
+        int tgtLen1 = Math.max(tgtLen, 1);
+        int thisLen = length();
+
+        int newLenHint = thisLen - tgtLen + replStr.length();
         if (newLenHint < 0) {
             throw new OutOfMemoryError();
         }
         StringBuilder sb = new StringBuilder(newLenHint);
         int i = 0;
         do {
-            sb.append(value, i, j - i)
-                    .append(replValue);
-            i = j + targLen;
-        } while (j < value.length && (j = indexOf(starget, j + targLen1)) > 0);
-
-        return sb.append(value, i, value.length - i).toString();
+            sb.append(this, i, j).append(replStr);
+            i = j + tgtLen;
+        } while (j < thisLen && (j = indexOf(tgtStr, j + tgtLen1)) > 0);
+        return sb.append(this, i, thisLen).toString();
     }
 
     /**
@@ -2388,7 +2293,7 @@
             the second is not the ascii digit or ascii letter.
          */
         char ch = 0;
-        if (((regex.value.length == 1 &&
+        if (((regex.length() == 1 &&
              ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
              (regex.length() == 2 &&
               regex.charAt(0) == '\\' &&
@@ -2408,8 +2313,9 @@
                     off = next + 1;
                 } else {    // last one
                     //assert (list.size() == limit - 1);
-                    list.add(substring(off, value.length));
-                    off = value.length;
+                    int last = length();
+                    list.add(substring(off, last));
+                    off = last;
                     break;
                 }
             }
@@ -2419,7 +2325,7 @@
 
             // Add remaining segment
             if (!limited || list.size() < limit)
-                list.add(substring(off, value.length));
+                list.add(substring(off, length()));
 
             // Construct result
             int resultSize = list.size();
@@ -2613,95 +2519,8 @@
      * @since   1.1
      */
     public String toLowerCase(Locale locale) {
-        if (locale == null) {
-            throw new NullPointerException();
-        }
-        int first;
-        boolean hasSurr = false;
-        final int len = value.length;
-
-        // Now check if there are any characters that need to be changed, or are surrogate
-        for (first = 0 ; first < len; first++) {
-            int cp = (int)value[first];
-            if (Character.isSurrogate((char)cp)) {
-                hasSurr = true;
-                break;
-            }
-            if (cp != Character.toLowerCase(cp)) {  // no need to check Character.ERROR
-                break;
-            }
-        }
-        if (first == len)
-            return this;
-        char[] result = new char[len];
-        System.arraycopy(value, 0, result, 0, first);  // Just copy the first few
-                                                       // lowerCase characters.
-        String lang = locale.getLanguage();
-        if (lang == "tr" || lang == "az" || lang == "lt") {
-            return toLowerCaseEx(result, first, locale, true);
-        }
-        if (hasSurr) {
-            return toLowerCaseEx(result, first, locale, false);
-        }
-        for (int i = first; i < len; i++) {
-            int cp = (int)value[i];
-            if (cp == '\u03A3' ||                       // GREEK CAPITAL LETTER SIGMA
-                Character.isSurrogate((char)cp)) {
-                return toLowerCaseEx(result, i, locale, false);
-            }
-            if (cp == '\u0130') {                       // LATIN CAPITAL LETTER I WITH DOT ABOVE
-                return toLowerCaseEx(result, i, locale, true);
-            }
-            cp = Character.toLowerCase(cp);
-            if (!Character.isBmpCodePoint(cp)) {
-                return toLowerCaseEx(result, i, locale, false);
-            }
-            result[i] = (char)cp;
-        }
-        return new String(result, true);
-    }
-
-    private String toLowerCaseEx(char[] result, int first, Locale locale, boolean localeDependent) {
-        int resultOffset = first;
-        int srcCount;
-        for (int i = first; i < value.length; i += srcCount) {
-            int srcChar = (int)value[i];
-            int lowerChar;
-            char[] lowerCharArray;
-            srcCount = 1;
-            if (Character.isSurrogate((char)srcChar)) {
-                srcChar = codePointAt(i);
-                srcCount = Character.charCount(srcChar);
-            }
-            if (localeDependent || srcChar == '\u03A3') { // GREEK CAPITAL LETTER SIGMA
-                lowerChar = ConditionalSpecialCasing.toLowerCaseEx(this, i, locale);
-            } else {
-                lowerChar = Character.toLowerCase(srcChar);
-            }
-            if (Character.isBmpCodePoint(lowerChar)) {    // Character.ERROR is not a bmp
-                result[resultOffset++] = (char)lowerChar;
-            } else {
-                if (lowerChar == Character.ERROR) {
-                    lowerCharArray = ConditionalSpecialCasing.toLowerCaseCharArray(this, i, locale);
-                } else if (srcCount == 2) {
-                    resultOffset += Character.toChars(lowerChar, result, resultOffset);
-                    continue;
-                } else {
-                    lowerCharArray = Character.toChars(lowerChar);
-                }
-                /* Grow result if needed */
-                int mapLen = lowerCharArray.length;
-                if (mapLen > srcCount) {
-                    char[] result2 = new char[result.length + mapLen - srcCount];
-                    System.arraycopy(result, 0, result2, 0, resultOffset);
-                    result = result2;
-                }
-                for (int x = 0; x < mapLen; ++x) {
-                    result[resultOffset++] = lowerCharArray[x];
-                }
-            }
-        }
-        return new String(result, 0, resultOffset);
+        return isLatin1() ? StringLatin1.toLowerCase(this, value, locale)
+                          : StringUTF16.toLowerCase(this, value, locale);
     }
 
     /**
@@ -2776,98 +2595,8 @@
      * @since   1.1
      */
     public String toUpperCase(Locale locale) {
-        if (locale == null) {
-            throw new NullPointerException();
-        }
-        int first;
-        boolean hasSurr = false;
-        final int len = value.length;
-
-        // Now check if there are any characters that need to be changed, or are surrogate
-        for (first = 0 ; first < len; first++ ) {
-            int cp = (int)value[first];
-            if (Character.isSurrogate((char)cp)) {
-                hasSurr = true;
-                break;
-            }
-            if (cp != Character.toUpperCaseEx(cp)) {   // no need to check Character.ERROR
-                break;
-            }
-        }
-        if (first == len) {
-            return this;
-        }
-        char[] result = new char[len];
-        System.arraycopy(value, 0, result, 0, first);  // Just copy the first few
-                                                       // upperCase characters.
-        String lang = locale.getLanguage();
-        if (lang == "tr" || lang == "az" || lang == "lt") {
-            return toUpperCaseEx(result, first, locale, true);
-        }
-        if (hasSurr) {
-            return toUpperCaseEx(result, first, locale, false);
-        }
-        for (int i = first; i < len; i++) {
-            int cp = (int)value[i];
-            if (Character.isSurrogate((char)cp)) {
-                return toUpperCaseEx(result, i, locale, false);
-            }
-            cp = Character.toUpperCaseEx(cp);
-            if (!Character.isBmpCodePoint(cp)) {    // Character.ERROR is not bmp
-                return toUpperCaseEx(result, i, locale, false);
-            }
-            result[i] = (char)cp;
-        }
-        return new String(result, true);
-    }
-
-    private String toUpperCaseEx(char[] result, int first, Locale locale,
-                                 boolean localeDependent) {
-        int resultOffset = first;
-        int srcCount;
-        for (int i = first; i < value.length; i += srcCount) {
-            int srcChar = (int)value[i];
-            int upperChar;
-            char[] upperCharArray;
-            srcCount = 1;
-            if (Character.isSurrogate((char)srcChar)) {
-                srcChar = codePointAt(i);
-                srcCount = Character.charCount(srcChar);
-            }
-            if (localeDependent) {
-                upperChar = ConditionalSpecialCasing.toUpperCaseEx(this, i, locale);
-            } else {
-                upperChar = Character.toUpperCaseEx(srcChar);
-            }
-            if (Character.isBmpCodePoint(upperChar)) {
-                result[resultOffset++] = (char)upperChar;
-            } else {
-                if (upperChar == Character.ERROR) {
-                    if (localeDependent) {
-                        upperCharArray =
-                            ConditionalSpecialCasing.toUpperCaseCharArray(this, i, locale);
-                    } else {
-                        upperCharArray = Character.toUpperCaseCharArray(srcChar);
-                    }
-                } else if (srcCount == 2) {
-                    resultOffset += Character.toChars(upperChar, result, resultOffset);
-                    continue;
-                } else {
-                    upperCharArray = Character.toChars(upperChar);
-                }
-                /* Grow result if needed */
-                int mapLen = upperCharArray.length;
-                if (mapLen > srcCount) {
-                    char[] result2 = new char[result.length + mapLen - srcCount];
-                    System.arraycopy(result, 0, result2, 0, resultOffset);
-                    result = result2;
-                 }
-                 for (int x = 0; x < mapLen; ++x) {
-                    result[resultOffset++] = upperCharArray[x];
-                 }
-            }
-        }
-        return new String(result, 0, resultOffset);
+        return isLatin1() ? StringLatin1.toUpperCase(this, value, locale)
+                          : StringUTF16.toUpperCase(this, value, locale);
     }
 
     /**
@@ -2925,17 +2654,9 @@
      *          trailing white space.
      */
     public String trim() {
-        char[] val = value;    /* avoid getfield opcode */
-        int end = val.length;
-        int beg = 0;
-
-        while ((beg < end) && (val[beg] <= ' ')) {
-            beg++;
-        }
-        while ((beg < end) && (val[end - 1] <= ' ')) {
-            end--;
-        }
-        return substring(beg, end);
+        String ret = isLatin1() ? StringLatin1.trim(value)
+                                : StringUTF16.trim(value);
+        return ret == null ? this : ret;
     }
 
     /**
@@ -2947,63 +2668,6 @@
         return this;
     }
 
-    static class IntCharArraySpliterator implements Spliterator.OfInt {
-        private final char[] array;
-        private int index;        // current index, modified on advance/split
-        private final int fence;  // one past last index
-        private final int cs;
-
-        IntCharArraySpliterator(char[] array, int acs) {
-            this(array, 0, array.length, acs);
-        }
-
-        IntCharArraySpliterator(char[] array, int origin, int fence, int acs) {
-            this.array = array;
-            this.index = origin;
-            this.fence = fence;
-            this.cs = acs | Spliterator.ORDERED | Spliterator.SIZED
-                      | Spliterator.SUBSIZED;
-        }
-
-        @Override
-        public OfInt trySplit() {
-            int lo = index, mid = (lo + fence) >>> 1;
-            return (lo >= mid)
-                   ? null
-                   : new IntCharArraySpliterator(array, lo, index = mid, cs);
-        }
-
-        @Override
-        public void forEachRemaining(IntConsumer action) {
-            char[] a; int i, hi; // hoist accesses and checks from loop
-            if (action == null)
-                throw new NullPointerException();
-            if ((a = array).length >= (hi = fence) &&
-                (i = index) >= 0 && i < (index = hi)) {
-                do { action.accept(a[i]); } while (++i < hi);
-            }
-        }
-
-        @Override
-        public boolean tryAdvance(IntConsumer action) {
-            if (action == null)
-                throw new NullPointerException();
-            if (index >= 0 && index < fence) {
-                action.accept(array[index++]);
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public long estimateSize() { return (long)(fence - index); }
-
-        @Override
-        public int characteristics() {
-            return cs;
-        }
-    }
-
     /**
      * Returns a stream of {@code int} zero-extending the {@code char} values
      * from this sequence.  Any char which maps to a <a
@@ -3016,93 +2680,11 @@
     @Override
     public IntStream chars() {
         return StreamSupport.intStream(
-                new IntCharArraySpliterator(value, Spliterator.IMMUTABLE), false);
+            isLatin1() ? new StringLatin1.CharsSpliterator(value, Spliterator.IMMUTABLE)
+                       : new StringUTF16.CharsSpliterator(value, Spliterator.IMMUTABLE),
+            false);
     }
 
-    static class CodePointsSpliterator implements Spliterator.OfInt {
-        private final char[] array;
-        private int index;        // current index, modified on advance/split
-        private final int fence;  // one past last index
-        private final int cs;
-
-        CodePointsSpliterator(char[] array, int acs) {
-            this(array, 0, array.length, acs);
-        }
-
-        CodePointsSpliterator(char[] array, int origin, int fence, int acs) {
-            this.array = array;
-            this.index = origin;
-            this.fence = fence;
-            this.cs = acs | Spliterator.ORDERED;
-        }
-
-        @Override
-        public OfInt trySplit() {
-            int lo = index, mid = (lo + fence) >>> 1;
-            if (lo >= mid)
-                return null;
-
-            int midOneLess;
-            // If the mid-point intersects a surrogate pair
-            if (Character.isLowSurrogate(array[mid]) &&
-                Character.isHighSurrogate(array[midOneLess = (mid -1)])) {
-                // If there is only one pair it cannot be split
-                if (lo >= midOneLess)
-                    return null;
-                // Shift the mid-point to align with the surrogate pair
-                return new CodePointsSpliterator(array, lo, index = midOneLess, cs);
-            }
-            return new CodePointsSpliterator(array, lo, index = mid, cs);
-        }
-
-        @Override
-        public void forEachRemaining(IntConsumer action) {
-            char[] a; int i, hi; // hoist accesses and checks from loop
-            if (action == null)
-                throw new NullPointerException();
-            if ((a = array).length >= (hi = fence) &&
-                (i = index) >= 0 && i < (index = hi)) {
-                do {
-                    i = advance(a, i, hi, action);
-                } while (i < hi);
-            }
-        }
-
-        @Override
-        public boolean tryAdvance(IntConsumer action) {
-            if (action == null)
-                throw new NullPointerException();
-            if (index >= 0 && index < fence) {
-                index = advance(array, index, fence, action);
-                return true;
-            }
-            return false;
-        }
-
-        // Advance one code point from the index, i, and return the next
-        // index to advance from
-        private static int advance(char[] a, int i, int hi, IntConsumer action) {
-            char c1 = a[i++];
-            int cp = c1;
-            if (Character.isHighSurrogate(c1) && i < hi) {
-                char c2 = a[i];
-                if (Character.isLowSurrogate(c2)) {
-                    i++;
-                    cp = Character.toCodePoint(c1, c2);
-                }
-            }
-            action.accept(cp);
-            return i;
-        }
-
-        @Override
-        public long estimateSize() { return (long)(fence - index); }
-
-        @Override
-        public int characteristics() {
-            return cs;
-        }
-    }
 
     /**
      * Returns a stream of code point values from this sequence.  Any surrogate
@@ -3118,7 +2700,9 @@
     @Override
     public IntStream codePoints() {
         return StreamSupport.intStream(
-                new CodePointsSpliterator(value, Spliterator.IMMUTABLE), false);
+            isLatin1() ? new StringLatin1.CharsSpliterator(value, Spliterator.IMMUTABLE)
+                       : new StringUTF16.CodePointsSpliterator(value, Spliterator.IMMUTABLE),
+            false);
     }
 
     /**
@@ -3129,10 +2713,8 @@
      *          the character sequence represented by this string.
      */
     public char[] toCharArray() {
-        // Cannot use Arrays.copyOf because of class initialization order issues
-        char[] result = new char[value.length];
-        System.arraycopy(value, 0, result, 0, value.length);
-        return result;
+        return isLatin1() ? StringLatin1.toChars(value)
+                          : StringUTF16.toChars(value);
     }
 
     /**
@@ -3315,7 +2897,10 @@
      *          as its single character the argument {@code c}.
      */
     public static String valueOf(char c) {
-        return new String(new char[]{c}, true);
+        if (COMPACT_STRINGS && StringLatin1.canEncode(c)) {
+            return new String(StringLatin1.toBytes(c), LATIN1);
+        }
+        return new String(StringUTF16.toBytes(c), UTF16);
     }
 
     /**
@@ -3398,4 +2983,145 @@
      *          guaranteed to be from a pool of unique strings.
      */
     public native String intern();
+
+    ////////////////////////////////////////////////////////////////
+
+    /**
+     * Copy character bytes from this string into dst starting at dstBegin.
+     * This method doesn't perform any range checking.
+     *
+     * Invoker guarantees: dst is in UTF16 (inflate itself for asb), if two
+     * coders are different, and dst is big enough (range check)
+     *
+     * @param dstBegin  the char index, not offset of byte[]
+     * @param coder     the coder of dst[]
+     */
+    void getBytes(byte dst[], int dstBegin, byte coder) {
+        if (coder() == coder) {
+            System.arraycopy(value, 0, dst, dstBegin << coder, value.length);
+        } else {    // this.coder == LATIN && coder == UTF16
+            StringLatin1.inflate(value, 0, dst, dstBegin, value.length);
+        }
+    }
+
+    /*
+     * Package private constructor. Trailing Void argument is there for
+     * disambiguating it against other (public) constructors.
+     *
+     * Stores the char[] value into a byte[] that each byte represents
+     * the8 low-order bits of the corresponding character, if the char[]
+     * contains only latin1 character. Or a byte[] that stores all
+     * characters in their byte sequences defined by the {@code StringUTF16}.
+     */
+    String(char[] value, int off, int len, Void sig) {
+        if (len == 0) {
+            this.value = "".value;
+            this.coder = "".coder;
+            return;
+        }
+        if (COMPACT_STRINGS) {
+            byte[] val = StringUTF16.compress(value, off, len);
+            if (val != null) {
+                this.value = val;
+                this.coder = LATIN1;
+                return;
+            }
+        }
+        this.coder = UTF16;
+        this.value = StringUTF16.toBytes(value, off, len);
+    }
+
+    /*
+     * Package private constructor. Trailing Void argument is there for
+     * disambiguating it against other (public) constructors.
+     */
+    String(AbstractStringBuilder asb, Void sig) {
+        byte[] val = asb.getValue();
+        int length = asb.length();
+        if (asb.isLatin1()) {
+            this.coder = LATIN1;
+            this.value = Arrays.copyOfRange(val, 0, length);
+        } else {
+            if (COMPACT_STRINGS) {
+                byte[] buf = StringUTF16.compress(val, 0, length);
+                if (buf != null) {
+                    this.coder = LATIN1;
+                    this.value = buf;
+                    return;
+                }
+            }
+            this.coder = UTF16;
+            this.value = Arrays.copyOfRange(val, 0, length << 1);
+        }
+    }
+
+   /*
+    * Package private constructor which shares value array for speed.
+    */
+    String(byte[] value, byte coder) {
+        this.value = value;
+        this.coder = coder;
+    }
+
+    byte coder() {
+        return COMPACT_STRINGS ? coder : UTF16;
+    }
+
+    private boolean isLatin1() {
+        return COMPACT_STRINGS && coder == LATIN1;
+    }
+
+    static final byte LATIN1 = 0;
+    static final byte UTF16  = 1;
+
+    /*
+     * StringIndexOutOfBoundsException  if {@code index} is
+     * negative or greater than or equal to {@code length}.
+     */
+    static void checkIndex(int index, int length) {
+        if (index < 0 || index >= length) {
+            throw new StringIndexOutOfBoundsException("index " + index);
+        }
+    }
+
+    /*
+     * StringIndexOutOfBoundsException  if {@code offset}
+     * is negative or greater than {@code length}.
+     */
+    static void checkOffset(int offset, int length) {
+        if (offset < 0 || offset > length) {
+            throw new StringIndexOutOfBoundsException("offset " + offset +
+                                                      ",length " + length);
+        }
+    }
+
+    /*
+     * Check {@code offset}, {@code count} against {@code 0} and {@code length}
+     * bounds.
+     *
+     * @throws  StringIndexOutOfBoundsException
+     *          If {@code offset} is negative, {@code count} is negative,
+     *          or {@code offset} is greater than {@code length - count}
+     */
+    private static void checkBoundsOffCount(int offset, int count, int length) {
+        if (offset < 0 || count < 0 || offset > length - count) {
+            throw new StringIndexOutOfBoundsException(
+                "offset " + offset + ", count " + count + ", length " + length);
+        }
+    }
+
+    /*
+     * Check {@code begin}, {@code end} against {@code 0} and {@code length}
+     * bounds.
+     *
+     * @throws  StringIndexOutOfBoundsException
+     *          If {@code begin} is negative, {@code begin} is greater than
+     *          {@code end}, or {@code end} is greater than {@code length}.
+     */
+    private static void checkBoundsBeginEnd(int begin, int end, int length) {
+        if (begin < 0 || begin > end || end > length) {
+            throw new StringIndexOutOfBoundsException(
+                "begin " + begin + ", end " + end + ", length " + length);
+        }
+    }
 }
diff --git a/jdk/src/java.base/share/classes/java/lang/StringBuffer.java b/jdk/src/java.base/share/classes/java/lang/StringBuffer.java
index a206454..65bbe14 100644
--- a/jdk/src/java.base/share/classes/java/lang/StringBuffer.java
+++ b/jdk/src/java.base/share/classes/java/lang/StringBuffer.java
@@ -104,7 +104,7 @@
      * A cache of the last value returned by toString. Cleared
      * whenever the StringBuffer is modified.
      */
-    private transient char[] toStringCache;
+    private transient String toStringCache;
 
     /** use serialVersionUID from JDK 1.0.2 for interoperability */
     static final long serialVersionUID = 3388685877147921107L;
@@ -169,15 +169,13 @@
 
     @Override
     public synchronized int capacity() {
-        return value.length;
+        return super.capacity();
     }
 
 
     @Override
     public synchronized void ensureCapacity(int minimumCapacity) {
-        if (minimumCapacity > value.length) {
-            expandCapacity(minimumCapacity);
-        }
+        super.ensureCapacity(minimumCapacity);
     }
 
     /**
@@ -204,9 +202,7 @@
      */
     @Override
     public synchronized char charAt(int index) {
-        if ((index < 0) || (index >= count))
-            throw new StringIndexOutOfBoundsException(index);
-        return value[index];
+        return super.charAt(index);
     }
 
     /**
@@ -261,10 +257,8 @@
      */
     @Override
     public synchronized void setCharAt(int index, char ch) {
-        if ((index < 0) || (index >= count))
-            throw new StringIndexOutOfBoundsException(index);
         toStringCache = null;
-        value[index] = ch;
+        super.setCharAt(index, ch);
     }
 
     @Override
@@ -680,9 +674,11 @@
     @HotSpotIntrinsicCandidate
     public synchronized String toString() {
         if (toStringCache == null) {
-            toStringCache = Arrays.copyOfRange(value, 0, count);
+            return toStringCache =
+                    isLatin1() ? StringLatin1.newString(value, 0, count)
+                               : StringUTF16.newString(value, 0, count);
         }
-        return new String(toStringCache, true);
+        return new String(toStringCache);
     }
 
     /**
@@ -710,7 +706,13 @@
     private synchronized void writeObject(java.io.ObjectOutputStream s)
         throws java.io.IOException {
         java.io.ObjectOutputStream.PutField fields = s.putFields();
-        fields.put("value", value);
+        char[] val = new char[capacity()];
+        if (isLatin1()) {
+            StringLatin1.getChars(value, 0, count, val, 0);
+        } else {
+            StringUTF16.getChars(value, 0, count, val, 0);
+        }
+        fields.put("value", val);
         fields.put("count", count);
         fields.put("shared", false);
         s.writeFields();
@@ -723,7 +725,12 @@
     private void readObject(java.io.ObjectInputStream s)
         throws java.io.IOException, ClassNotFoundException {
         java.io.ObjectInputStream.GetField fields = s.readFields();
-        value = (char[])fields.get("value", null);
+        char[] val = (char[])fields.get("value", null);
+        initBytes(val, 0, val.length);
         count = fields.get("count", 0);
     }
+
+    protected synchronized void getBytes(byte dst[], int dstBegin, byte coder) {
+        super.getBytes(dst, dstBegin, coder);
+    }
 }
diff --git a/jdk/src/java.base/share/classes/java/lang/StringBuilder.java b/jdk/src/java.base/share/classes/java/lang/StringBuilder.java
index 9d4ccf3..7d1e46a 100644
--- a/jdk/src/java.base/share/classes/java/lang/StringBuilder.java
+++ b/jdk/src/java.base/share/classes/java/lang/StringBuilder.java
@@ -412,7 +412,8 @@
     @HotSpotIntrinsicCandidate
     public String toString() {
         // Create a copy, don't share the array
-        return new String(value, 0, count);
+        return isLatin1() ? StringLatin1.newString(value, 0, count)
+                          : StringUTF16.newStringSB(value, 0, count);
     }
 
     /**
@@ -430,7 +431,13 @@
         throws java.io.IOException {
         s.defaultWriteObject();
         s.writeInt(count);
-        s.writeObject(value);
+        char[] val = new char[capacity()];
+        if (isLatin1()) {
+            StringLatin1.getChars(value, 0, count, val, 0);
+        } else {
+            StringUTF16.getChars(value, 0, count, val, 0);
+        }
+        s.writeObject(val);
     }
 
     /**
@@ -441,7 +448,8 @@
         throws java.io.IOException, ClassNotFoundException {
         s.defaultReadObject();
         count = s.readInt();
-        value = (char[]) s.readObject();
+        char[] val = (char[]) s.readObject();
+        initBytes(val, 0, val.length);
     }
 
 }
diff --git a/jdk/src/java.base/share/classes/java/lang/StringCoding.java b/jdk/src/java.base/share/classes/java/lang/StringCoding.java
index d770156..b1e25d5 100644
--- a/jdk/src/java.base/share/classes/java/lang/StringCoding.java
+++ b/jdk/src/java.base/share/classes/java/lang/StringCoding.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -38,11 +38,19 @@
 import java.nio.charset.IllegalCharsetNameException;
 import java.nio.charset.UnsupportedCharsetException;
 import java.util.Arrays;
+import jdk.internal.HotSpotIntrinsicCandidate;
 import sun.misc.MessageUtils;
 import sun.nio.cs.HistoricallyNamedCharset;
 import sun.nio.cs.ArrayDecoder;
 import sun.nio.cs.ArrayEncoder;
 
+import static java.lang.String.LATIN1;
+import static java.lang.String.UTF16;
+import static java.lang.String.COMPACT_STRINGS;
+import static java.nio.charset.StandardCharsets.ISO_8859_1;
+import static java.nio.charset.StandardCharsets.US_ASCII;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
 /**
  * Utility class for string encoding and decoding.
  */
@@ -72,23 +80,13 @@
 
     // Trim the given byte array to the given length
     //
-    private static byte[] safeTrim(byte[] ba, int len, Charset cs, boolean isTrusted) {
+    private static byte[] safeTrim(byte[] ba, int len, boolean isTrusted) {
         if (len == ba.length && (isTrusted || System.getSecurityManager() == null))
             return ba;
         else
             return Arrays.copyOf(ba, len);
     }
 
-    // Trim the given char array to the given length
-    //
-    private static char[] safeTrim(char[] ca, int len,
-                                   Charset cs, boolean isTrusted) {
-        if (len == ca.length && (isTrusted || System.getSecurityManager() == null))
-            return ca;
-        else
-            return Arrays.copyOf(ca, len);
-    }
-
     private static int scale(int len, float expansionFactor) {
         // We need to perform double, not float, arithmetic; otherwise
         // we lose low order bits when len is larger than 2**24.
@@ -117,21 +115,64 @@
         }
     }
 
+    static class Result {
+        byte[] value;
+        byte coder;
+
+        Result with() {
+            coder = COMPACT_STRINGS ? LATIN1 : UTF16;
+            value = new byte[0];
+            return this;
+        }
+
+        Result with(char[] val, int off, int len) {
+            if (String.COMPACT_STRINGS) {
+                byte[] bs = StringUTF16.compress(val, off, len);
+                if (bs != null) {
+                    value = bs;
+                    coder = LATIN1;
+                    return this;
+                }
+            }
+            coder = UTF16;
+            value = StringUTF16.toBytes(val, off, len);
+            return this;
+        }
+
+        Result with(byte[] val, byte coder) {
+            this.coder = coder;
+            value = val;
+            return this;
+        }
+    }
+
+    @HotSpotIntrinsicCandidate
+    private static boolean hasNegatives(byte[] ba, int off, int len) {
+        for (int i = off; i < off + len; i++) {
+            if (ba[i] < 0) {
+                return true;
+            }
+        }
+        return false;
+    }
 
     // -- Decoding --
-    private static class StringDecoder {
+    static class StringDecoder {
         private final String requestedCharsetName;
         private final Charset cs;
+        private final boolean isASCIICompatible;
         private final CharsetDecoder cd;
-        private final boolean isTrusted;
+        protected final Result result;
 
-        private StringDecoder(Charset cs, String rcn) {
+        StringDecoder(Charset cs, String rcn) {
             this.requestedCharsetName = rcn;
             this.cs = cs;
             this.cd = cs.newDecoder()
                 .onMalformedInput(CodingErrorAction.REPLACE)
                 .onUnmappableCharacter(CodingErrorAction.REPLACE);
-            this.isTrusted = (cs.getClass().getClassLoader0() == null);
+            this.result = new Result();
+            this.isASCIICompatible = (cd instanceof ArrayDecoder) &&
+                    ((ArrayDecoder)cd).isASCIICompatible();
         }
 
         String charsetName() {
@@ -144,36 +185,58 @@
             return requestedCharsetName;
         }
 
-        char[] decode(byte[] ba, int off, int len) {
+        Result decode(byte[] ba, int off, int len) {
+            if (len == 0) {
+                return result.with();
+            }
+            // fastpath for ascii compatible
+            if (isASCIICompatible && !hasNegatives(ba, off, len)) {
+                if (COMPACT_STRINGS) {
+                    return result.with(Arrays.copyOfRange(ba, off, off + len),
+                                      LATIN1);
+                } else {
+                    return result.with(StringLatin1.inflate(ba, off, len), UTF16);
+                }
+            }
             int en = scale(len, cd.maxCharsPerByte());
             char[] ca = new char[en];
-            if (len == 0)
-                return ca;
             if (cd instanceof ArrayDecoder) {
                 int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca);
-                return safeTrim(ca, clen, cs, isTrusted);
+                return result.with(ca, 0, clen);
+            }
+            cd.reset();
+            ByteBuffer bb = ByteBuffer.wrap(ba, off, len);
+            CharBuffer cb = CharBuffer.wrap(ca);
+            try {
+                CoderResult cr = cd.decode(bb, cb, true);
+                if (!cr.isUnderflow())
+                    cr.throwException();
+                cr = cd.flush(cb);
+                if (!cr.isUnderflow())
+                    cr.throwException();
+            } catch (CharacterCodingException x) {
+                // Substitution is always enabled,
+                // so this shouldn't happen
+                throw new Error(x);
+            }
+            return result.with(ca, 0, cb.position());
+        }
+    }
+
+    private static class StringDecoder8859_1 extends StringDecoder {
+        StringDecoder8859_1(Charset cs, String rcn) {
+            super(cs, rcn);
+        }
+        Result decode(byte[] ba, int off, int len) {
+            if (COMPACT_STRINGS) {
+                return result.with(Arrays.copyOfRange(ba, off, off + len), LATIN1);
             } else {
-                cd.reset();
-                ByteBuffer bb = ByteBuffer.wrap(ba, off, len);
-                CharBuffer cb = CharBuffer.wrap(ca);
-                try {
-                    CoderResult cr = cd.decode(bb, cb, true);
-                    if (!cr.isUnderflow())
-                        cr.throwException();
-                    cr = cd.flush(cb);
-                    if (!cr.isUnderflow())
-                        cr.throwException();
-                } catch (CharacterCodingException x) {
-                    // Substitution is always enabled,
-                    // so this shouldn't happen
-                    throw new Error(x);
-                }
-                return safeTrim(ca, cb.position(), cs, isTrusted);
+                return result.with(StringLatin1.inflate(ba, off, len), UTF16);
             }
         }
     }
 
-    static char[] decode(String charsetName, byte[] ba, int off, int len)
+    static Result decode(String charsetName, byte[] ba, int off, int len)
         throws UnsupportedEncodingException
     {
         StringDecoder sd = deref(decoder);
@@ -183,8 +246,15 @@
             sd = null;
             try {
                 Charset cs = lookupCharset(csn);
-                if (cs != null)
-                    sd = new StringDecoder(cs, csn);
+                if (cs != null) {
+                    if (cs == UTF_8) {
+                        sd = new StringDecoderUTF8(cs, csn);
+                    } else if (cs == ISO_8859_1) {
+                        sd = new StringDecoder8859_1(cs, csn);
+                    } else {
+                        sd = new StringDecoder(cs, csn);
+                    }
+                }
             } catch (IllegalCharsetNameException x) {}
             if (sd == null)
                 throw new UnsupportedEncodingException(csn);
@@ -193,7 +263,7 @@
         return sd.decode(ba, off, len);
     }
 
-    static char[] decode(Charset cs, byte[] ba, int off, int len) {
+    static Result decode(Charset cs, byte[] ba, int off, int len) {
         // (1)We never cache the "external" cs, the only benefit of creating
         // an additional StringDe/Encoder object to wrap it is to share the
         // de/encode() method. These SD/E objects are short-lived, the young-gen
@@ -210,44 +280,57 @@
         // check (... && (isTrusted || SM == null || getClassLoader0())) in trim
         // but it then can be argued that the SM is null when the operation
         // is started...
+        if (cs == UTF_8) {
+            return StringDecoderUTF8.decode(ba, off, len, new Result());
+        }
         CharsetDecoder cd = cs.newDecoder();
+        // ascii fastpath
+        if (cs == ISO_8859_1 || ((cd instanceof ArrayDecoder) &&
+                                 ((ArrayDecoder)cd).isASCIICompatible() &&
+                                 !hasNegatives(ba, off, len))) {
+             if (COMPACT_STRINGS) {
+                 return new Result().with(Arrays.copyOfRange(ba, off, off + len),
+                                          LATIN1);
+             } else {
+                 return new Result().with(StringLatin1.inflate(ba, off, len), UTF16);
+             }
+        }
         int en = scale(len, cd.maxCharsPerByte());
-        char[] ca = new char[en];
-        if (len == 0)
-            return ca;
-        boolean isTrusted = false;
-        if (System.getSecurityManager() != null) {
-            if (!(isTrusted = (cs.getClass().getClassLoader0() == null))) {
-                ba =  Arrays.copyOfRange(ba, off, off + len);
-                off = 0;
-            }
+        if (len == 0) {
+            return new Result().with();
+        }
+        if (System.getSecurityManager() != null &&
+            cs.getClass().getClassLoader0() != null) {
+            ba =  Arrays.copyOfRange(ba, off, off + len);
+            off = 0;
         }
         cd.onMalformedInput(CodingErrorAction.REPLACE)
           .onUnmappableCharacter(CodingErrorAction.REPLACE)
           .reset();
+
+        char[] ca = new char[en];
         if (cd instanceof ArrayDecoder) {
             int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca);
-            return safeTrim(ca, clen, cs, isTrusted);
-        } else {
-            ByteBuffer bb = ByteBuffer.wrap(ba, off, len);
-            CharBuffer cb = CharBuffer.wrap(ca);
-            try {
-                CoderResult cr = cd.decode(bb, cb, true);
-                if (!cr.isUnderflow())
-                    cr.throwException();
-                cr = cd.flush(cb);
-                if (!cr.isUnderflow())
-                    cr.throwException();
-            } catch (CharacterCodingException x) {
-                // Substitution is always enabled,
-                // so this shouldn't happen
-                throw new Error(x);
-            }
-            return safeTrim(ca, cb.position(), cs, isTrusted);
+            return new Result().with(ca, 0, clen);
         }
+        ByteBuffer bb = ByteBuffer.wrap(ba, off, len);
+        CharBuffer cb = CharBuffer.wrap(ca);
+        try {
+            CoderResult cr = cd.decode(bb, cb, true);
+            if (!cr.isUnderflow())
+                cr.throwException();
+            cr = cd.flush(cb);
+            if (!cr.isUnderflow())
+                cr.throwException();
+        } catch (CharacterCodingException x) {
+            // Substitution is always enabled,
+            // so this shouldn't happen
+            throw new Error(x);
+        }
+        return new Result().with(ca, 0, cb.position());
     }
 
-    static char[] decode(byte[] ba, int off, int len) {
+    static Result decode(byte[] ba, int off, int len) {
         String csn = Charset.defaultCharset().name();
         try {
             // use charset name decode() variant which provides caching.
@@ -273,6 +356,7 @@
     private static class StringEncoder {
         private Charset cs;
         private CharsetEncoder ce;
+        private final boolean isASCIICompatible;
         private final String requestedCharsetName;
         private final boolean isTrusted;
 
@@ -283,6 +367,8 @@
                 .onMalformedInput(CodingErrorAction.REPLACE)
                 .onUnmappableCharacter(CodingErrorAction.REPLACE);
             this.isTrusted = (cs.getClass().getClassLoader0() == null);
+            this.isASCIICompatible = (ce instanceof ArrayEncoder) &&
+                    ((ArrayEncoder)ce).isASCIICompatible();
         }
 
         String charsetName() {
@@ -295,36 +381,186 @@
             return requestedCharsetName;
         }
 
-        byte[] encode(char[] ca, int off, int len) {
+        byte[] encode(byte coder, byte[] val) {
+            // fastpath for ascii compatible
+            if (coder == LATIN1 && isASCIICompatible &&
+                !hasNegatives(val, 0, val.length)) {
+                return Arrays.copyOf(val, val.length);
+            }
+            int len = val.length >> coder;  // assume LATIN1=0/UTF16=1;
             int en = scale(len, ce.maxBytesPerChar());
             byte[] ba = new byte[en];
-            if (len == 0)
+            if (len == 0) {
                 return ba;
-            if (ce instanceof ArrayEncoder) {
-                int blen = ((ArrayEncoder)ce).encode(ca, off, len, ba);
-                return safeTrim(ba, blen, cs, isTrusted);
-            } else {
-                ce.reset();
-                ByteBuffer bb = ByteBuffer.wrap(ba);
-                CharBuffer cb = CharBuffer.wrap(ca, off, len);
-                try {
-                    CoderResult cr = ce.encode(cb, bb, true);
-                    if (!cr.isUnderflow())
-                        cr.throwException();
-                    cr = ce.flush(bb);
-                    if (!cr.isUnderflow())
-                        cr.throwException();
-                } catch (CharacterCodingException x) {
-                    // Substitution is always enabled,
-                    // so this shouldn't happen
-                    throw new Error(x);
-                }
-                return safeTrim(ba, bb.position(), cs, isTrusted);
             }
+            if (ce instanceof ArrayEncoder) {
+                if (!isTrusted) {
+                    val = Arrays.copyOf(val, val.length);
+                }
+                int blen = (coder == LATIN1 ) ? ((ArrayEncoder)ce).encodeFromLatin1(val, 0, len, ba)
+                                              : ((ArrayEncoder)ce).encodeFromUTF16(val, 0, len, ba);
+                if (blen != -1) {
+                    return safeTrim(ba, blen, isTrusted);
+                }
+            }
+            char[] ca = (coder == LATIN1 ) ? StringLatin1.toChars(val)
+                                           : StringUTF16.toChars(val);
+            ce.reset();
+            ByteBuffer bb = ByteBuffer.wrap(ba);
+            CharBuffer cb = CharBuffer.wrap(ca, 0, len);
+            try {
+                CoderResult cr = ce.encode(cb, bb, true);
+                if (!cr.isUnderflow())
+                    cr.throwException();
+                cr = ce.flush(bb);
+                if (!cr.isUnderflow())
+                    cr.throwException();
+            } catch (CharacterCodingException x) {
+                // Substitution is always enabled,
+                // so this shouldn't happen
+                throw new Error(x);
+            }
+            return safeTrim(ba, bb.position(), isTrusted);
         }
     }
 
-    static byte[] encode(String charsetName, char[] ca, int off, int len)
+    @HotSpotIntrinsicCandidate
+    private static int implEncodeISOArray(byte[] sa, int sp,
+                                          byte[] da, int dp, int len) {
+        int i = 0;
+        for (; i < len; i++) {
+            char c = StringUTF16.getChar(sa, sp++);
+            if (c > '\u00FF')
+                break;
+            da[dp++] = (byte)c;
+        }
+        return i;
+    }
+
+    static byte[] encode8859_1(byte coder, byte[] val) {
+        if (coder == LATIN1) {
+            return Arrays.copyOf(val, val.length);
+        }
+        int len = val.length >> 1;
+        byte[] dst = new byte[len];
+        int dp = 0;
+        int sp = 0;
+        int sl = len;
+        while (sp < sl) {
+            int ret = implEncodeISOArray(val, sp, dst, dp, len);
+            sp = sp + ret;
+            dp = dp + ret;
+            if (ret != len) {
+                char c = StringUTF16.getChar(val, sp++);
+                if (Character.isHighSurrogate(c) && sp < sl &&
+                    Character.isLowSurrogate(StringUTF16.getChar(val, sp))) {
+                    sp++;
+                }
+                dst[dp++] = '?';
+                len = sl - sp;
+            }
+        }
+        if (dp == dst.length) {
+            return dst;
+        }
+        return Arrays.copyOf(dst, dp);
+    }
+
+    static byte[] encodeASCII(byte coder, byte[] val) {
+        if (coder == LATIN1) {
+            byte[] dst = new byte[val.length];
+            for (int i = 0; i < val.length; i++) {
+                if (val[i] < 0) {
+                    dst[i] = '?';
+                } else {
+                    dst[i] = val[i];
+                }
+            }
+            return dst;
+        }
+        int len = val.length >> 1;
+        byte[] dst = new byte[len];
+        int dp = 0;
+        for (int i = 0; i < len; i++) {
+            char c = StringUTF16.getChar(val, i);
+            if (c < 0x80) {
+                dst[dp++] = (byte)c;
+                continue;
+            }
+            if (Character.isHighSurrogate(c) && i + 1 < len &&
+                Character.isLowSurrogate(StringUTF16.getChar(val, i + 1))) {
+                i++;
+            }
+            dst[dp++] = '?';
+        }
+        if (len == dp) {
+            return dst;
+        }
+        return Arrays.copyOf(dst, dp);
+    }
+
+   static byte[] encodeUTF8(byte coder, byte[] val) {
+        int dp = 0;
+        byte[] dst;
+        if (coder == LATIN1) {
+            dst = new byte[val.length << 1];
+            for (int sp = 0; sp < val.length; sp++) {
+                byte c = val[sp];
+                if (c < 0) {
+                    dst[dp++] = (byte)(0xc0 | ((c & 0xff) >> 6));
+                    dst[dp++] = (byte)(0x80 | (c & 0x3f));
+                } else {
+                    dst[dp++] = c;
+                }
+            }
+        } else {
+            int sp = 0;
+            int sl = val.length >> 1;
+            dst = new byte[sl * 3];
+            char c;
+            while (sp < sl && (c = StringUTF16.getChar(val, sp)) < '\u0080') {
+                // ascii fast loop;
+                dst[dp++] = (byte)c;
+                sp++;
+            }
+            while (sp < sl) {
+                c = StringUTF16.getChar(val, sp++);
+                if (c < 0x80) {
+                    dst[dp++] = (byte)c;
+                } else if (c < 0x800) {
+                    dst[dp++] = (byte)(0xc0 | (c >> 6));
+                    dst[dp++] = (byte)(0x80 | (c & 0x3f));
+                } else if (Character.isSurrogate(c)) {
+                    int uc = -1;
+                    char c2;
+                    if (Character.isHighSurrogate(c) && sp < sl &&
+                        Character.isLowSurrogate(c2 = StringUTF16.getChar(val, sp))) {
+                        uc = Character.toCodePoint(c, c2);
+                    }
+                    if (uc < 0) {
+                        dst[dp++] = '?';
+                    } else {
+                        dst[dp++] = (byte)(0xf0 | ((uc >> 18)));
+                        dst[dp++] = (byte)(0x80 | ((uc >> 12) & 0x3f));
+                        dst[dp++] = (byte)(0x80 | ((uc >>  6) & 0x3f));
+                        dst[dp++] = (byte)(0x80 | (uc & 0x3f));
+                        sp++;  // 2 chars
+                    }
+                } else {
+                    // 3 bytes, 16 bits
+                    dst[dp++] = (byte)(0xe0 | ((c >> 12)));
+                    dst[dp++] = (byte)(0x80 | ((c >>  6) & 0x3f));
+                    dst[dp++] = (byte)(0x80 | (c & 0x3f));
+                }
+            }
+        }
+        if (dp == dst.length) {
+            return dst;
+        }
+        return Arrays.copyOf(dst, dp);
+    }
+
+    static byte[] encode(String charsetName, byte coder, byte[] val)
         throws UnsupportedEncodingException
     {
         StringEncoder se = deref(encoder);
@@ -334,62 +570,88 @@
             se = null;
             try {
                 Charset cs = lookupCharset(csn);
-                if (cs != null)
+                if (cs != null) {
+                    if (cs == UTF_8) {
+                        return encodeUTF8(coder, val);
+                    } else if (cs == ISO_8859_1) {
+                        return encode8859_1(coder, val);
+                    } else if (cs == US_ASCII) {
+                        return encodeASCII(coder, val);
+                    }
                     se = new StringEncoder(cs, csn);
+                }
             } catch (IllegalCharsetNameException x) {}
-            if (se == null)
+            if (se == null) {
                 throw new UnsupportedEncodingException (csn);
+            }
             set(encoder, se);
         }
-        return se.encode(ca, off, len);
+        return se.encode(coder, val);
     }
 
-    static byte[] encode(Charset cs, char[] ca, int off, int len) {
+    static byte[] encode(Charset cs, byte coder, byte[] val) {
+        if (cs == UTF_8) {
+            return encodeUTF8(coder, val);
+        } else if (cs == ISO_8859_1) {
+            return encode8859_1(coder, val);
+        } else if (cs == US_ASCII) {
+            return encodeASCII(coder, val);
+        }
         CharsetEncoder ce = cs.newEncoder();
+        // fastpath for ascii compatible
+        if (coder == LATIN1 && (((ce instanceof ArrayEncoder) &&
+                                 ((ArrayEncoder)ce).isASCIICompatible() &&
+                                 !hasNegatives(val, 0, val.length)))) {
+            return Arrays.copyOf(val, val.length);
+        }
+        int len = val.length >> coder;  // assume LATIN1=0/UTF16=1;
         int en = scale(len, ce.maxBytesPerChar());
         byte[] ba = new byte[en];
-        if (len == 0)
+        if (len == 0) {
             return ba;
-        boolean isTrusted = false;
-        if (System.getSecurityManager() != null) {
-            if (!(isTrusted = (cs.getClass().getClassLoader0() == null))) {
-                ca =  Arrays.copyOfRange(ca, off, off + len);
-                off = 0;
-            }
         }
+        boolean isTrusted = System.getSecurityManager() == null ||
+                            cs.getClass().getClassLoader0() == null;
         ce.onMalformedInput(CodingErrorAction.REPLACE)
           .onUnmappableCharacter(CodingErrorAction.REPLACE)
           .reset();
         if (ce instanceof ArrayEncoder) {
-            int blen = ((ArrayEncoder)ce).encode(ca, off, len, ba);
-            return safeTrim(ba, blen, cs, isTrusted);
-        } else {
-            ByteBuffer bb = ByteBuffer.wrap(ba);
-            CharBuffer cb = CharBuffer.wrap(ca, off, len);
-            try {
-                CoderResult cr = ce.encode(cb, bb, true);
-                if (!cr.isUnderflow())
-                    cr.throwException();
-                cr = ce.flush(bb);
-                if (!cr.isUnderflow())
-                    cr.throwException();
-            } catch (CharacterCodingException x) {
-                throw new Error(x);
+            if (!isTrusted) {
+                val = Arrays.copyOf(val, val.length);
             }
-            return safeTrim(ba, bb.position(), cs, isTrusted);
+            int blen = (coder == LATIN1 ) ? ((ArrayEncoder)ce).encodeFromLatin1(val, 0, len, ba)
+                                          : ((ArrayEncoder)ce).encodeFromUTF16(val, 0, len, ba);
+            if (blen != -1) {
+                return safeTrim(ba, blen, isTrusted);
+            }
         }
+        char[] ca = (coder == LATIN1 ) ? StringLatin1.toChars(val)
+                                       : StringUTF16.toChars(val);
+        ByteBuffer bb = ByteBuffer.wrap(ba);
+        CharBuffer cb = CharBuffer.wrap(ca, 0, len);
+        try {
+            CoderResult cr = ce.encode(cb, bb, true);
+            if (!cr.isUnderflow())
+                cr.throwException();
+            cr = ce.flush(bb);
+            if (!cr.isUnderflow())
+                cr.throwException();
+        } catch (CharacterCodingException x) {
+            throw new Error(x);
+        }
+        return safeTrim(ba, bb.position(), isTrusted);
     }
 
-    static byte[] encode(char[] ca, int off, int len) {
+    static byte[] encode(byte coder, byte[] val) {
         String csn = Charset.defaultCharset().name();
         try {
             // use charset name encode() variant which provides caching.
-            return encode(csn, ca, off, len);
+            return encode(csn, coder, val);
         } catch (UnsupportedEncodingException x) {
             warnUnsupportedCharset(csn);
         }
         try {
-            return encode("ISO-8859-1", ca, off, len);
+            return encode("ISO-8859-1", coder, val);
         } catch (UnsupportedEncodingException x) {
             // If this code is hit during VM initialization, MessageUtils is
             // the only way we will be able to get any kind of error message.
diff --git a/jdk/src/java.base/share/classes/java/lang/StringDecoderUTF8.java b/jdk/src/java.base/share/classes/java/lang/StringDecoderUTF8.java
new file mode 100644
index 0000000..1b504df
--- /dev/null
+++ b/jdk/src/java.base/share/classes/java/lang/StringDecoderUTF8.java
@@ -0,0 +1,235 @@
+/*
+ * 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.
+ */
+
+package java.lang;
+
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+import static java.lang.String.LATIN1;
+import static java.lang.String.UTF16;
+import static java.lang.String.COMPACT_STRINGS;
+import static java.lang.Character.isSurrogate;
+import static java.lang.Character.highSurrogate;
+import static java.lang.Character.lowSurrogate;
+import static java.lang.Character.isSupplementaryCodePoint;
+import static java.lang.StringUTF16.putChar;
+
+class StringDecoderUTF8 extends StringCoding.StringDecoder {
+
+    StringDecoderUTF8(Charset cs, String rcn) {
+        super(cs, rcn);
+    }
+
+    private static boolean isNotContinuation(int b) {
+        return (b & 0xc0) != 0x80;
+    }
+
+    private static boolean isMalformed3(int b1, int b2, int b3) {
+        return (b1 == (byte)0xe0 && (b2 & 0xe0) == 0x80) ||
+               (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80;
+    }
+
+    private static boolean isMalformed3_2(int b1, int b2) {
+        return (b1 == (byte)0xe0 && (b2 & 0xe0) == 0x80) ||
+               (b2 & 0xc0) != 0x80;
+    }
+
+    private static boolean isMalformed4(int b2, int b3, int b4) {
+        return (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80 ||
+               (b4 & 0xc0) != 0x80;
+    }
+
+    private static boolean isMalformed4_2(int b1, int b2) {
+        return (b1 == 0xf0 && (b2  < 0x90 || b2 > 0xbf)) ||
+               (b1 == 0xf4 && (b2 & 0xf0) != 0x80) ||
+               (b2 & 0xc0) != 0x80;
+    }
+
+    private static boolean isMalformed4_3(int b3) {
+        return (b3 & 0xc0) != 0x80;
+    }
+
+    // for nb == 3/4
+    private static int malformedN(byte[] src, int sp, int nb) {
+        if (nb == 3) {
+            int b1 = src[sp++];
+            int b2 = src[sp++];    // no need to lookup b3
+            return ((b1 == (byte)0xe0 && (b2 & 0xe0) == 0x80) ||
+                    isNotContinuation(b2)) ? 1 : 2;
+        } else if (nb == 4) { // we don't care the speed here
+            int b1 = src[sp++] & 0xff;
+            int b2 = src[sp++] & 0xff;
+            if (b1 > 0xf4 ||
+                (b1 == 0xf0 && (b2 < 0x90 || b2 > 0xbf)) ||
+                (b1 == 0xf4 && (b2 & 0xf0) != 0x80) ||
+                isNotContinuation(b2))
+                return 1;
+            if (isNotContinuation(src[sp++]))
+                return 2;
+            return 3;
+        }
+        assert false;
+        return -1;
+    }
+
+    private static char repl = '\ufffd';
+
+    StringCoding.Result decode(byte[] src, int sp, int len) {
+        return decode(src, sp, len, result);
+    }
+
+    static StringCoding.Result decode(byte[] src, int sp, int len,
+                                      StringCoding.Result ret) {
+        int sl = sp + len;
+        byte[] dst = new byte[len];
+        int dp = 0;
+        if (COMPACT_STRINGS) {   // Latin1 only loop
+            while (sp < sl) {
+                int b1 = src[sp];
+                if (b1 >= 0) {
+                    dst[dp++] = (byte)b1;
+                    sp++;
+                    continue;
+                }
+                if ((b1 == (byte)0xc2 || b1 == (byte)0xc3) &&
+                    sp + 1 < sl) {
+                    int b2 = src[sp + 1];
+                    if (!isNotContinuation(b2)) {
+                        dst[dp++] = (byte)(((b1 << 6) ^ b2)^
+                                           (((byte) 0xC0 << 6) ^
+                                           ((byte) 0x80 << 0)));
+                        sp += 2;
+                        continue;
+                    }
+                }
+                // anything not a latin1, including the repl
+                // we have to go with the utf16
+                break;
+            }
+            if (sp == sl) {
+                if (dp != dst.length) {
+                    dst = Arrays.copyOf(dst, dp);
+                }
+                return ret.with(dst, LATIN1);
+            }
+        }
+        if (dp == 0) {
+            dst = new byte[len << 1];
+        } else {
+            byte[] buf = new byte[len << 1];
+            StringLatin1.inflate(dst, 0, buf, 0, dp);
+            dst = buf;
+        }
+        while (sp < sl) {
+            int b1 = src[sp++];
+            if (b1 >= 0) {
+                putChar(dst, dp++, (char) b1);
+            } else if ((b1 >> 5) == -2 && (b1 & 0x1e) != 0) {
+                if (sp < sl) {
+                    int b2 = src[sp++];
+                    if (isNotContinuation(b2)) {
+                        putChar(dst, dp++, repl);
+                        sp--;
+                    } else {
+                        putChar(dst, dp++, (char)(((b1 << 6) ^ b2)^
+                                                  (((byte) 0xC0 << 6) ^
+                                                  ((byte) 0x80 << 0))));
+                    }
+                    continue;
+                }
+                putChar(dst, dp++, repl);
+                break;
+            } else if ((b1 >> 4) == -2) {
+                if (sp + 1 < sl) {
+                    int b2 = src[sp++];
+                    int b3 = src[sp++];
+                    if (isMalformed3(b1, b2, b3)) {
+                        putChar(dst, dp++, repl);
+                        sp -= 3;
+                        sp += malformedN(src, sp, 3);
+                    } else {
+                        char c = (char)((b1 << 12) ^
+                                        (b2 <<  6) ^
+                                        (b3 ^
+                                         (((byte) 0xE0 << 12) ^
+                                         ((byte) 0x80 <<  6) ^
+                                         ((byte) 0x80 <<  0))));
+                        putChar(dst, dp++, isSurrogate(c) ?  repl : c);
+                    }
+                    continue;
+                }
+                if (sp  < sl && isMalformed3_2(b1, src[sp])) {
+                    putChar(dst, dp++, repl);
+                    continue;
+                }
+                putChar(dst, dp++, repl);
+                break;
+            } else if ((b1 >> 3) == -2) {
+                if (sp + 2 < sl) {
+                    int b2 = src[sp++];
+                    int b3 = src[sp++];
+                    int b4 = src[sp++];
+                    int uc = ((b1 << 18) ^
+                              (b2 << 12) ^
+                              (b3 <<  6) ^
+                              (b4 ^
+                               (((byte) 0xF0 << 18) ^
+                               ((byte) 0x80 << 12) ^
+                               ((byte) 0x80 <<  6) ^
+                               ((byte) 0x80 <<  0))));
+                    if (isMalformed4(b2, b3, b4) ||
+                        !isSupplementaryCodePoint(uc)) { // shortest form check
+                        putChar(dst, dp++, repl);
+                        sp -= 4;
+                        sp += malformedN(src, sp, 4);
+                    } else {
+                        putChar(dst, dp++, highSurrogate(uc));
+                        putChar(dst, dp++, lowSurrogate(uc));
+                    }
+                    continue;
+                }
+                b1 &= 0xff;
+                if (b1 > 0xf4 ||
+                    sp  < sl && isMalformed4_2(b1, src[sp] & 0xff)) {
+                    putChar(dst, dp++, repl);
+                    continue;
+                }
+                sp++;
+                putChar(dst, dp++, repl);
+                if (sp  < sl && isMalformed4_3(src[sp])) {
+                    continue;
+                }
+                break;
+            } else {
+                putChar(dst, dp++, repl);
+            }
+        }
+        if (dp != len) {
+            dst = Arrays.copyOf(dst, dp << 1);
+        }
+        return ret.with(dst, UTF16);
+    }
+}
diff --git a/jdk/src/java.base/share/classes/java/lang/StringLatin1.java b/jdk/src/java.base/share/classes/java/lang/StringLatin1.java
new file mode 100644
index 0000000..eb8ddc6
--- /dev/null
+++ b/jdk/src/java.base/share/classes/java/lang/StringLatin1.java
@@ -0,0 +1,600 @@
+/*
+ * 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.
+ */
+
+package java.lang;
+
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.Spliterator;
+import java.util.function.IntConsumer;
+import java.util.stream.IntStream;
+import jdk.internal.HotSpotIntrinsicCandidate;
+
+import static java.lang.String.LATIN1;
+import static java.lang.String.UTF16;
+import static java.lang.String.checkOffset;
+
+final class StringLatin1 {
+
+    public static char charAt(byte[] value, int index) {
+        if (index < 0 || index >= value.length) {
+            throw new StringIndexOutOfBoundsException(index);
+        }
+        return (char)(value[index] & 0xff);
+    }
+
+    public static boolean canEncode(int cp) {
+        return cp >>> 8 == 0;
+    }
+
+    public static int length(byte[] value) {
+        return value.length;
+    }
+
+    public static int codePointAt(byte[] value, int index, int end) {
+        return value[index] & 0xff;
+    }
+
+    public static int codePointBefore(byte[] value, int index) {
+        return value[index - 1] & 0xff;
+    }
+
+    public static int codePointCount(byte[] value, int beginIndex, int endIndex) {
+        return endIndex - beginIndex;
+    }
+
+    public static char[] toChars(byte[] value) {
+        char[] dst = new char[value.length];
+        inflate(value, 0, dst, 0, value.length);
+        return dst;
+    }
+
+    public static byte[] inflate(byte[] value, int off, int len) {
+        byte[] ret = StringUTF16.newBytesFor(len);
+        inflate(value, off, ret, 0, len);
+        return ret;
+    }
+
+    public static void getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin) {
+        inflate(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
+    }
+
+    public static void getBytes(byte[] value, int srcBegin, int srcEnd, byte dst[], int dstBegin) {
+        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static boolean equals(byte[] value, byte[] other) {
+        if (value.length == other.length) {
+            for (int i = 0; i < value.length; i++) {
+                if (value[i] != other[i]) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static int compareTo(byte[] value, byte[] other) {
+        int len1 = value.length;
+        int len2 = other.length;
+        int lim = Math.min(len1, len2);
+        for (int k = 0; k < lim; k++) {
+            if (value[k] != other[k]) {
+                return getChar(value, k) - getChar(other, k);
+            }
+        }
+        return len1 - len2;
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static int compareToUTF16(byte[] value, byte[] other) {
+        int len1 = length(value);
+        int len2 = StringUTF16.length(other);
+        int lim = Math.min(len1, len2);
+        for (int k = 0; k < lim; k++) {
+            char c1 = getChar(value, k);
+            char c2 = StringUTF16.getChar(other, k);
+            if (c1 != c2) {
+                return c1 - c2;
+            }
+        }
+        return len1 - len2;
+    }
+
+    public static int hashCode(byte[] value) {
+        int h = 0;
+        for (byte v : value) {
+            h = 31 * h + (v & 0xff);
+        }
+        return h;
+    }
+
+    public static int indexOf(byte[] value, int ch, int fromIndex) {
+        if (!canEncode(ch)) {
+            return -1;
+        }
+        int max = value.length;
+        if (fromIndex < 0) {
+            fromIndex = 0;
+        } else if (fromIndex >= max) {
+            // Note: fromIndex might be near -1>>>1.
+            return -1;
+        }
+        byte c = (byte)ch;
+        for (int i = fromIndex; i < max; i++) {
+            if (value[i] == c) {
+               return i;
+            }
+        }
+        return -1;
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static int indexOf(byte[] value, byte[] str) {
+        if (str.length == 0) {
+            return 0;
+        }
+        if (value.length == 0) {
+            return -1;
+        }
+        return indexOf(value, value.length, str, str.length, 0);
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) {
+        byte first = str[0];
+        int max = (valueCount - strCount);
+        for (int i = fromIndex; i <= max; i++) {
+            // Look for first character.
+            if (value[i] != first) {
+                while (++i <= max && value[i] != first);
+            }
+            // Found first character, now look at the rest of value
+            if (i <= max) {
+                int j = i + 1;
+                int end = j + strCount - 1;
+                for (int k = 1; j < end && value[j] == str[k]; j++, k++);
+                if (j == end) {
+                    // Found whole string.
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    public static int lastIndexOf(byte[] src, int srcCount,
+                                  byte[] tgt, int tgtCount, int fromIndex) {
+        int min = tgtCount - 1;
+        int i = min + fromIndex;
+        int strLastIndex = tgtCount - 1;
+        char strLastChar = (char)(tgt[strLastIndex] & 0xff);
+
+  startSearchForLastChar:
+        while (true) {
+            while (i >= min && (src[i] & 0xff) != strLastChar) {
+                i--;
+            }
+            if (i < min) {
+                return -1;
+            }
+            int j = i - 1;
+            int start = j - strLastIndex;
+            int k = strLastIndex - 1;
+            while (j > start) {
+                if ((src[j--] & 0xff) != (tgt[k--] & 0xff)) {
+                    i--;
+                    continue startSearchForLastChar;
+                }
+            }
+            return start + 1;
+        }
+    }
+
+    public static int lastIndexOf(final byte[] value, int ch, int fromIndex) {
+        if (!canEncode(ch)) {
+            return -1;
+        }
+        int off  = Math.min(fromIndex, value.length - 1);
+        for (; off >= 0; off--) {
+            if (value[off] == (byte)ch) {
+                return off;
+            }
+        }
+        return -1;
+    }
+
+    public static String replace(byte[] value, char oldChar, char newChar) {
+        if (canEncode(oldChar)) {
+            int len = value.length;
+            int i = -1;
+            while (++i < len) {
+                if (value[i] == (byte)oldChar) {
+                    break;
+                }
+            }
+            if (i < len) {
+                if (canEncode(newChar)) {
+                    byte buf[] = new byte[len];
+                    for (int j = 0; j < i; j++) {    // TBD arraycopy?
+                        buf[j] = value[j];
+                    }
+                    while (i < len) {
+                        byte c = value[i];
+                        buf[i] = (c == (byte)oldChar) ? (byte)newChar : c;
+                        i++;
+                    }
+                    return new String(buf, LATIN1);
+                } else {
+                    byte[] buf = StringUTF16.newBytesFor(len);
+                    // inflate from latin1 to UTF16
+                    inflate(value, 0, buf, 0, i);
+                    while (i < len) {
+                        char c = (char)(value[i] & 0xff);
+                        StringUTF16.putChar(buf, i, (c == oldChar) ? newChar : c);
+                        i++;
+                    }
+                    return new String(buf, UTF16);
+                }
+            }
+        }
+        return null; // for string to return this;
+    }
+
+    // case insensitive
+    public static boolean regionMatchesCI(byte[] value, int toffset,
+                                          byte[] other, int ooffset, int len) {
+        int last = toffset + len;
+        while (toffset < last) {
+            char c1 = (char)(value[toffset++] & 0xff);
+            char c2 = (char)(other[ooffset++] & 0xff);
+            if (c1 == c2) {
+                continue;
+            }
+            char u1 = Character.toUpperCase(c1);
+            char u2 = Character.toUpperCase(c2);
+            if (u1 == u2) {
+                continue;
+            }
+            if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
+                continue;
+            }
+            return false;
+        }
+        return true;
+    }
+
+    public static boolean regionMatchesCI_UTF16(byte[] value, int toffset,
+                                                byte[] other, int ooffset, int len) {
+        int last = toffset + len;
+        while (toffset < last) {
+            char c1 = (char)(value[toffset++] & 0xff);
+            char c2 = StringUTF16.getChar(other, ooffset++);
+            if (c1 == c2) {
+                continue;
+            }
+            char u1 = Character.toUpperCase(c1);
+            char u2 = Character.toUpperCase(c2);
+            if (u1 == u2) {
+                continue;
+            }
+            if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
+                continue;
+            }
+            return false;
+        }
+        return true;
+    }
+
+    public static String toLowerCase(String str, byte[] value, Locale locale) {
+        if (locale == null) {
+            throw new NullPointerException();
+        }
+        int first;
+        final int len = value.length;
+        // Now check if there are any characters that need to be changed, or are surrogate
+        for (first = 0 ; first < len; first++) {
+            int cp = value[first] & 0xff;
+            if (cp != Character.toLowerCase(cp)) {  // no need to check Character.ERROR
+                break;
+            }
+        }
+        if (first == len)
+            return str;
+        String lang = locale.getLanguage();
+        if (lang == "tr" || lang == "az" || lang == "lt") {
+            return toLowerCaseEx(str, value, first, locale, true);
+        }
+        byte[] result = new byte[len];
+        System.arraycopy(value, 0, result, 0, first);  // Just copy the first few
+                                                       // lowerCase characters.
+        for (int i = first; i < len; i++) {
+            int cp = value[i] & 0xff;
+            cp = Character.toLowerCase(cp);
+            if (!canEncode(cp)) {                      // not a latin1 character
+                return toLowerCaseEx(str, value, first, locale, false);
+            }
+            result[i] = (byte)cp;
+        }
+        return new String(result, LATIN1);
+    }
+
+    private static String toLowerCaseEx(String str, byte[] value,
+                                        int first, Locale locale, boolean localeDependent)
+    {
+        byte[] result = StringUTF16.newBytesFor(value.length);
+        int resultOffset = 0;
+        for (int i = 0; i < first; i++) {
+            StringUTF16.putChar(result, resultOffset++, value[i] & 0xff);
+        }
+        for (int i = first; i < value.length; i++) {
+            int srcChar = value[i] & 0xff;
+            int lowerChar;
+            char[] lowerCharArray;
+            if (localeDependent) {
+                lowerChar = ConditionalSpecialCasing.toLowerCaseEx(str, i, locale);
+            } else {
+                lowerChar = Character.toLowerCase(srcChar);
+            }
+            if (Character.isBmpCodePoint(lowerChar)) {    // Character.ERROR is not a bmp
+                StringUTF16.putChar(result, resultOffset++, lowerChar);
+            } else {
+                if (lowerChar == Character.ERROR) {
+                    lowerCharArray = ConditionalSpecialCasing.toLowerCaseCharArray(str, i, locale);
+                } else {
+                    lowerCharArray = Character.toChars(lowerChar);
+                }
+                /* Grow result if needed */
+                int mapLen = lowerCharArray.length;
+                if (mapLen > 1) {
+                    byte[] result2 = StringUTF16.newBytesFor((result.length >> 1) + mapLen - 1);
+                    System.arraycopy(result, 0, result2, 0, resultOffset << 1);
+                    result = result2;
+                }
+                for (int x = 0; x < mapLen; ++x) {
+                    StringUTF16.putChar(result, resultOffset++, lowerCharArray[x]);
+                }
+            }
+        }
+        return StringUTF16.newString(result, 0, resultOffset);
+    }
+
+    public static String toUpperCase(String str, byte[] value, Locale locale) {
+        if (locale == null) {
+            throw new NullPointerException();
+        }
+        int first;
+        final int len = value.length;
+
+        // Now check if there are any characters that need to be changed, or are surrogate
+        for (first = 0 ; first < len; first++ ) {
+            int cp = value[first] & 0xff;
+            if (cp != Character.toUpperCaseEx(cp)) {   // no need to check Character.ERROR
+                break;
+            }
+        }
+        if (first == len) {
+            return str;
+        }
+        String lang = locale.getLanguage();
+        if (lang == "tr" || lang == "az" || lang == "lt") {
+            return toUpperCaseEx(str, value, first, locale, true);
+        }
+        byte[] result = new byte[len];
+        System.arraycopy(value, 0, result, 0, first);  // Just copy the first few
+                                                       // upperCase characters.
+        for (int i = first; i < len; i++) {
+            int cp = value[i] & 0xff;
+            cp = Character.toUpperCaseEx(cp);
+            if (!canEncode(cp)) {                      // not a latin1 character
+                return toUpperCaseEx(str, value, first, locale, false);
+            }
+            result[i] = (byte)cp;
+        }
+        return new String(result, LATIN1);
+    }
+
+    private static String toUpperCaseEx(String str, byte[] value,
+                                        int first, Locale locale, boolean localeDependent)
+    {
+        byte[] result = StringUTF16.newBytesFor(value.length);
+        int resultOffset = 0;
+        for (int i = 0; i < first; i++) {
+            StringUTF16.putChar(result, resultOffset++, value[i] & 0xff);
+        }
+        for (int i = first; i < value.length; i++) {
+            int srcChar = value[i] & 0xff;
+            int upperChar;
+            char[] upperCharArray;
+            if (localeDependent) {
+                upperChar = ConditionalSpecialCasing.toUpperCaseEx(str, i, locale);
+            } else {
+                upperChar = Character.toUpperCaseEx(srcChar);
+            }
+            if (Character.isBmpCodePoint(upperChar)) {
+                StringUTF16.putChar(result, resultOffset++, upperChar);
+            } else {
+                if (upperChar == Character.ERROR) {
+                    if (localeDependent) {
+                        upperCharArray =
+                            ConditionalSpecialCasing.toUpperCaseCharArray(str, i, locale);
+                    } else {
+                        upperCharArray = Character.toUpperCaseCharArray(srcChar);
+                    }
+                } else {
+                    upperCharArray = Character.toChars(upperChar);
+                }
+                /* Grow result if needed */
+                int mapLen = upperCharArray.length;
+                if (mapLen > 1) {
+                    byte[] result2 = StringUTF16.newBytesFor((result.length >> 1) + mapLen - 1);
+                    System.arraycopy(result, 0, result2, 0, resultOffset << 1);
+                    result = result2;
+                }
+                for (int x = 0; x < mapLen; ++x) {
+                    StringUTF16.putChar(result, resultOffset++, upperCharArray[x]);
+                }
+            }
+        }
+        return StringUTF16.newString(result, 0, resultOffset);
+    }
+
+    public static String trim(byte[] value) {
+        int len = value.length;
+        int st = 0;
+        while ((st < len) && ((value[st] & 0xff) <= ' ')) {
+            st++;
+        }
+        while ((st < len) && ((value[len - 1] & 0xff) <= ' ')) {
+            len--;
+        }
+        return ((st > 0) || (len < value.length)) ?
+            newString(value, st, len - st) : null;
+    }
+
+    public static void putChar(byte[] val, int index, int c) {
+        //assert (canEncode(c));
+        val[index] = (byte)(c);
+    }
+
+    public static char getChar(byte[] val, int index) {
+        return (char)(val[index] & 0xff);
+    }
+
+    public static byte[] toBytes(int[] val, int off, int len) {
+        byte[] ret = new byte[len];
+        for (int i = 0; i < len; i++) {
+            int cp = val[off++];
+            if (!canEncode(cp)) {
+                return null;
+            }
+            ret[i] = (byte)cp;
+        }
+        return ret;
+    }
+
+    public static byte[] toBytes(char c) {
+        return new byte[] { (byte)c };
+    }
+
+    public static String newString(byte[] val, int index, int len) {
+        return new String(Arrays.copyOfRange(val, index, index + len),
+                          LATIN1);
+    }
+
+    public static void fillNull(byte[] val, int index, int end) {
+        Arrays.fill(val, index, end, (byte)0);
+    }
+
+    // inflatedCopy byte[] -> char[]
+    @HotSpotIntrinsicCandidate
+    private static void inflate(byte[] src, int srcOff, char[] dst, int dstOff, int len) {
+        for (int i = 0; i < len; i++) {
+            dst[dstOff++] = (char)(src[srcOff++] & 0xff);
+        }
+    }
+
+    // inflatedCopy byte[] -> byte[]
+    @HotSpotIntrinsicCandidate
+    public static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
+        for (int i = 0; i < len; i++) {
+            StringUTF16.putChar(dst, dstOff++, src[srcOff++] & 0xff);
+        }
+    }
+
+    static class CharsSpliterator implements Spliterator.OfInt {
+        private final byte[] array;
+        private int index;        // current index, modified on advance/split
+        private final int fence;  // one past last index
+        private final int cs;
+
+        CharsSpliterator(byte[] array, int acs) {
+            this(array, 0, array.length, acs);
+        }
+
+        CharsSpliterator(byte[] array, int origin, int fence, int acs) {
+            this.array = array;
+            this.index = origin;
+            this.fence = fence;
+            this.cs = acs | Spliterator.ORDERED | Spliterator.SIZED
+                      | Spliterator.SUBSIZED;
+        }
+
+        @Override
+        public OfInt trySplit() {
+            int lo = index, mid = (lo + fence) >>> 1;
+            return (lo >= mid)
+                   ? null
+                   : new CharsSpliterator(array, lo, index = mid, cs);
+        }
+
+        @Override
+        public void forEachRemaining(IntConsumer action) {
+            byte[] a; int i, hi; // hoist accesses and checks from loop
+            if (action == null)
+                throw new NullPointerException();
+            if ((a = array).length >= (hi = fence) &&
+                (i = index) >= 0 && i < (index = hi)) {
+                do { action.accept(a[i] & 0xff); } while (++i < hi);
+            }
+        }
+
+        @Override
+        public boolean tryAdvance(IntConsumer action) {
+            if (action == null)
+                throw new NullPointerException();
+            if (index >= 0 && index < fence) {
+                action.accept(array[index++] & 0xff);
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public long estimateSize() { return (long)(fence - index); }
+
+        @Override
+        public int characteristics() {
+            return cs;
+        }
+    }
+
+    ////////////////////////////////////////////////////////////////
+
+    public static void getCharsSB(byte[] val, int srcBegin, int srcEnd, char dst[], int dstBegin) {
+        checkOffset(srcEnd, val.length);
+        getChars(val, srcBegin, srcEnd, dst, dstBegin);
+    }
+
+    public static void inflateSB(byte[] val, byte[] dst, int dstOff, int count) {
+        checkOffset(count, val.length);
+        checkOffset(dstOff + count, dst.length >> 1);  // dst is utf16
+        inflate(val, 0, dst, dstOff, count);
+    }
+}
diff --git a/jdk/src/java.base/share/classes/java/lang/StringUTF16.java b/jdk/src/java.base/share/classes/java/lang/StringUTF16.java
new file mode 100644
index 0000000..56550d7
--- /dev/null
+++ b/jdk/src/java.base/share/classes/java/lang/StringUTF16.java
@@ -0,0 +1,971 @@
+/*
+ * 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.
+ */
+
+package java.lang;
+
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.Spliterator;
+import java.util.function.IntConsumer;
+import jdk.internal.HotSpotIntrinsicCandidate;
+
+import static java.lang.String.UTF16;
+import static java.lang.String.LATIN1;
+import static java.lang.String.checkIndex;
+import static java.lang.String.checkOffset;
+
+final class StringUTF16 {
+
+    public static byte[] newBytesFor(int len) {
+        if (len < 0) {
+            throw new NegativeArraySizeException();
+        }
+        if (len > MAX_LENGTH) {
+            throw new OutOfMemoryError("UTF16 String size is " + len +
+                                       ", should be less than " + MAX_LENGTH);
+        }
+        return new byte[len << 1];
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static void putChar(byte[] val, int index, int c) {
+        index <<= 1;
+        val[index++] = (byte)(c >> HI_BYTE_SHIFT);
+        val[index]   = (byte)(c >> LO_BYTE_SHIFT);
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static char getChar(byte[] val, int index) {
+        index <<= 1;
+        return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) |
+                      ((val[index]   & 0xff) << LO_BYTE_SHIFT));
+    }
+
+    public static char charAt(byte[] value, int index) {
+        if (index < 0 || index >= value.length >> 1) {
+            throw new StringIndexOutOfBoundsException(index);
+        }
+        return getChar(value, index);
+    }
+
+    public static int length(byte[] value) {
+        return value.length >> 1;
+    }
+
+    public static int codePointAt(byte[] value, int index, int end) {
+        char c1 = getChar(value, index);
+        if (Character.isHighSurrogate(c1) && ++index < end) {
+            char c2 = getChar(value, index);
+            if (Character.isLowSurrogate(c2)) {
+               return Character.toCodePoint(c1, c2);
+            }
+        }
+        return c1;
+    }
+
+    public static int codePointBefore(byte[] value, int index) {
+        char c2 = getChar(value, --index);
+        if (Character.isLowSurrogate(c2) && index > 0) {
+            char c1 = getChar(value, --index);
+            if (Character.isHighSurrogate(c1)) {
+               return Character.toCodePoint(c1, c2);
+            }
+        }
+        return c2;
+    }
+
+    public static int codePointCount(byte[] value, int beginIndex, int endIndex) {
+        int count = endIndex - beginIndex;
+        for (int i = beginIndex; i < endIndex; ) {
+            if (Character.isHighSurrogate(getChar(value, i++)) &&
+                i < endIndex &&
+                Character.isLowSurrogate(getChar(value, i))) {
+                count--;
+                i++;
+            }
+        }
+        return count;
+    }
+
+    public static char[] toChars(byte[] value) {
+        char[] dst = new char[value.length >> 1];
+        getChars(value, 0, dst.length, dst, 0);
+        return dst;
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static byte[] toBytes(char[] value, int off, int len) {
+        byte[] val = newBytesFor(len);
+        for (int i = 0; i < len; i++) {
+            putChar(val, i, value[off++]);
+        }
+        return val;
+    }
+
+    public static byte[] compress(char[] val, int off, int len) {
+        byte[] ret = new byte[len];
+        if (compress(val, off, ret, 0, len) == len) {
+            return ret;
+        }
+        return null;
+    }
+
+    public static byte[] compress(byte[] val, int off, int len) {
+        byte[] ret = new byte[len];
+        if (compress(val, off, ret, 0, len) == len) {
+            return ret;
+        }
+        return null;
+    }
+
+    // compressedCopy char[] -> byte[]
+    @HotSpotIntrinsicCandidate
+    private static int compress(char[] src, int srcOff, byte[] dst, int dstOff, int len) {
+        for (int i = 0; i < len; i++) {
+            int c = src[srcOff++];
+            if (c >>> 8 != 0) {
+                return 0;
+            }
+            dst[dstOff++] = (byte)c;
+        }
+        return len;
+    }
+
+    // compressedCopy byte[] -> byte[]
+    @HotSpotIntrinsicCandidate
+    public static int compress(byte[] src, int srcOff, byte[] dst, int dstOff, int len) {
+        for (int i = 0; i < len; i++) {
+            int c = getChar(src, srcOff++);
+            if (c >>> 8 != 0) {
+                return 0;
+            }
+            dst[dstOff++] = (byte)c;
+        }
+        return len;
+    }
+
+    public static byte[] toBytes(int[] val, int index, int len) {
+        final int end = index + len;
+        // Pass 1: Compute precise size of char[]
+        int n = len;
+        for (int i = index; i < end; i++) {
+            int cp = val[i];
+            if (Character.isBmpCodePoint(cp))
+                continue;
+            else if (Character.isValidCodePoint(cp))
+                n++;
+            else throw new IllegalArgumentException(Integer.toString(cp));
+        }
+        // Pass 2: Allocate and fill in <high, low> pair
+        byte[] buf = newBytesFor(n);
+        for (int i = index, j = 0; i < end; i++, j++) {
+            int cp = val[i];
+            if (Character.isBmpCodePoint(cp)) {
+                putChar(buf, j, cp);
+            } else {
+                putChar(buf, j++, Character.highSurrogate(cp));
+                putChar(buf, j, Character.lowSurrogate(cp));
+            }
+        }
+        return buf;
+    }
+
+    public static byte[] toBytes(char c) {
+        byte[] result = new byte[2];
+        putChar(result, 0, c);
+        return result;
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static void getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin) {
+        for (int i = srcBegin; i < srcEnd; i++) {
+            dst[dstBegin++] = getChar(value, i);
+        }
+    }
+
+    /* @see java.lang.String.getBytes(int, int, byte[], int) */
+    public static void getBytes(byte[] value, int srcBegin, int srcEnd, byte dst[], int dstBegin) {
+        srcBegin <<= 1;
+        srcEnd <<= 1;
+        for (int i = srcBegin + (1 >> LO_BYTE_SHIFT); i < srcEnd; i += 2) {
+            dst[dstBegin++] = value[i];
+        }
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static boolean equals(byte[] value, byte[] other) {
+        if (value.length == other.length) {
+            int len = value.length >> 1;
+            for (int i = 0; i < len; i++) {
+                if (getChar(value, i) != getChar(other, i)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static int compareTo(byte[] value, byte[] other) {
+        int len1 = length(value);
+        int len2 = length(other);
+        int lim = Math.min(len1, len2);
+        for (int k = 0; k < lim; k++) {
+            char c1 = getChar(value, k);
+            char c2 = getChar(other, k);
+            if (c1 != c2) {
+                return c1 - c2;
+            }
+        }
+        return len1 - len2;
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static int compareToLatin1(byte[] value, byte[] other) {
+        int len1 = length(value);
+        int len2 = StringLatin1.length(other);
+        int lim = Math.min(len1, len2);
+        for (int k = 0; k < lim; k++) {
+            char c1 = getChar(value, k);
+            char c2 = StringLatin1.getChar(other, k);
+            if (c1 != c2) {
+                return c1 - c2;
+            }
+        }
+        return len1 - len2;
+    }
+
+    public static int hashCode(byte[] value) {
+        int h = 0;
+        int length = value.length >> 1;
+        for (int i = 0; i < length; i++) {
+            h = 31 * h + getChar(value, i);
+        }
+        return h;
+    }
+
+    public static int indexOf(byte[] value, int ch, int fromIndex) {
+        int max = value.length >> 1;
+        if (fromIndex < 0) {
+            fromIndex = 0;
+        } else if (fromIndex >= max) {
+            // Note: fromIndex might be near -1>>>1.
+            return -1;
+        }
+        if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
+            // handle most cases here (ch is a BMP code point or a
+            // negative value (invalid code point))
+            return indexOfChar(value, ch, fromIndex, max);
+        } else {
+            return indexOfSupplementary(value, ch, fromIndex, max);
+        }
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static int indexOf(byte[] value, byte[] str) {
+        if (str.length == 0) {
+            return 0;
+        }
+        if (value.length == 0) {
+            return -1;
+        }
+        return indexOf(value, length(value), str, length(str), 0);
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) {
+        char first = getChar(str, 0);
+        int max = (valueCount - strCount);
+        for (int i = fromIndex; i <= max; i++) {
+            // Look for first character.
+            if (getChar(value, i) != first) {
+                while (++i <= max && getChar(value, i) != first);
+            }
+            // Found first character, now look at the rest of value
+            if (i <= max) {
+                int j = i + 1;
+                int end = j + strCount - 1;
+                for (int k = 1; j < end && getChar(value, j) == getChar(str, k); j++, k++);
+                if (j == end) {
+                    // Found whole string.
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Handles indexOf Latin1 substring in UTF16 string.
+     */
+    @HotSpotIntrinsicCandidate
+    public static int indexOfLatin1(byte[] value, byte[] str) {
+        if (str.length == 0) {
+            return 0;
+        }
+        if (value.length == 0) {
+            return -1;
+        }
+        return indexOfLatin1(value, length(value), str, str.length, 0);
+    }
+
+    @HotSpotIntrinsicCandidate
+    public static int indexOfLatin1(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) {
+        char first = (char)(tgt[0] & 0xff);
+        int max = (srcCount - tgtCount);
+        for (int i = fromIndex; i <= max; i++) {
+            // Look for first character.
+            if (getChar(src, i) != first) {
+                while (++i <= max && getChar(src, i) != first);
+            }
+            // Found first character, now look at the rest of v2
+            if (i <= max) {
+                int j = i + 1;
+                int end = j + tgtCount - 1;
+                for (int k = 1;
+                     j < end && getChar(src, j) == (tgt[k] & 0xff);
+                     j++, k++);
+                if (j == end) {
+                    // Found whole string.
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    @HotSpotIntrinsicCandidate
+    private static int indexOfChar(byte[] value, int ch, int fromIndex, int max) {
+        for (int i = fromIndex; i < max; i++) {
+            if (getChar(value, i) == ch) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Handles (rare) calls of indexOf with a supplementary character.
+     */
+    private static int indexOfSupplementary(byte[] value, int ch, int fromIndex, int max) {
+        if (Character.isValidCodePoint(ch)) {
+            final char hi = Character.highSurrogate(ch);
+            final char lo = Character.lowSurrogate(ch);
+            for (int i = fromIndex; i < max - 1; i++) {
+                if (getChar(value, i) == hi && getChar(value, i + 1 ) == lo) {
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    public static int lastIndexOf(byte[] src, int srcCount,
+                                  byte[] tgt, int tgtCount, int fromIndex) {
+        int min = tgtCount - 1;
+        int i = min + fromIndex;
+        int strLastIndex = tgtCount - 1;
+        char strLastChar = getChar(tgt, strLastIndex);
+
+    startSearchForLastChar:
+        while (true) {
+            while (i >= min && getChar(src, i) != strLastChar) {
+                i--;
+            }
+            if (i < min) {
+                return -1;
+            }
+            int j = i - 1;
+            int start = j - strLastIndex;
+            int k = strLastIndex - 1;
+            while (j > start) {
+                if (getChar(src, j--) != getChar(tgt, k--)) {
+                    i--;
+                    continue startSearchForLastChar;
+                }
+            }
+            return start + 1;
+        }
+    }
+
+    public static int lastIndexOf(byte[] value, int ch, int fromIndex) {
+        if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
+            // handle most cases here (ch is a BMP code point or a
+            // negative value (invalid code point))
+            int i = Math.min(fromIndex, (value.length >> 1) - 1);
+            for (; i >= 0; i--) {
+                if (getChar(value, i) == ch) {
+                    return i;
+                }
+            }
+            return -1;
+        } else {
+            return lastIndexOfSupplementary(value, ch, fromIndex);
+        }
+    }
+
+    /**
+     * Handles (rare) calls of lastIndexOf with a supplementary character.
+     */
+    private static int lastIndexOfSupplementary(final byte[] value, int ch, int fromIndex) {
+        if (Character.isValidCodePoint(ch)) {
+            char hi = Character.highSurrogate(ch);
+            char lo = Character.lowSurrogate(ch);
+            int i = Math.min(fromIndex, (value.length >> 1) - 2);
+            for (; i >= 0; i--) {
+                if (getChar(value, i) == hi && getChar(value, i + 1) == lo) {
+                    return i;
+                }
+            }
+        }
+        return -1;
+    }
+
+    public static String replace(byte[] value, char oldChar, char newChar) {
+        int len = value.length >> 1;
+        int i = -1;
+        while (++i < len) {
+            if (getChar(value, i) == oldChar) {
+                break;
+            }
+        }
+        if (i < len) {
+            byte buf[] = new byte[value.length];
+            for (int j = 0; j < i; j++) {
+                putChar(buf, j, getChar(value, j)); // TBD:arraycopy?
+            }
+            while (i < len) {
+                char c = getChar(value, i);
+                putChar(buf, i, c == oldChar ? newChar : c);
+                i++;
+           }
+           // Check if we should try to compress to latin1
+           if (String.COMPACT_STRINGS &&
+               !StringLatin1.canEncode(oldChar) &&
+               StringLatin1.canEncode(newChar)) {
+               byte[] val = compress(buf, 0, len);
+               if (val != null) {
+                   return new String(val, LATIN1);
+               }
+           }
+           return new String(buf, UTF16);
+        }
+        return null;
+    }
+
+    public static boolean regionMatchesCI(byte[] value, int toffset,
+                                          byte[] other, int ooffset, int len) {
+        int last = toffset + len;
+        while (toffset < last) {
+            char c1 = getChar(value, toffset++);
+            char c2 = getChar(other, ooffset++);
+            if (c1 == c2) {
+                continue;
+            }
+            // try converting both characters to uppercase.
+            // If the results match, then the comparison scan should
+            // continue.
+            char u1 = Character.toUpperCase(c1);
+            char u2 = Character.toUpperCase(c2);
+            if (u1 == u2) {
+                continue;
+            }
+            // Unfortunately, conversion to uppercase does not work properly
+            // for the Georgian alphabet, which has strange rules about case
+            // conversion.  So we need to make one last check before
+            // exiting.
+            if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
+                continue;
+            }
+            return false;
+        }
+        return true;
+    }
+
+    public static boolean regionMatchesCI_Latin1(byte[] value, int toffset,
+                                                 byte[] other, int ooffset,
+                                                 int len) {
+        int last = toffset + len;
+        while (toffset < last) {
+            char c1 = getChar(value, toffset++);
+            char c2 = (char)(other[ooffset++] & 0xff);
+            if (c1 == c2) {
+                continue;
+            }
+            char u1 = Character.toUpperCase(c1);
+            char u2 = Character.toUpperCase(c2);
+            if (u1 == u2) {
+                continue;
+            }
+            if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) {
+                continue;
+            }
+            return false;
+        }
+        return true;
+    }
+
+    public static String toLowerCase(String str, byte[] value, Locale locale) {
+        if (locale == null) {
+            throw new NullPointerException();
+        }
+        int first;
+        boolean hasSurr = false;
+        final int len = value.length >> 1;
+
+        // Now check if there are any characters that need to be changed, or are surrogate
+        for (first = 0 ; first < len; first++) {
+            int cp = (int)getChar(value, first);
+            if (Character.isSurrogate((char)cp)) {
+                hasSurr = true;
+                break;
+            }
+            if (cp != Character.toLowerCase(cp)) {  // no need to check Character.ERROR
+                break;
+            }
+        }
+        if (first == len)
+            return str;
+        byte[] result = new byte[value.length];
+        System.arraycopy(value, 0, result, 0, first << 1);  // Just copy the first few
+                                                            // lowerCase characters.
+        String lang = locale.getLanguage();
+        if (lang == "tr" || lang == "az" || lang == "lt") {
+            return toLowerCaseEx(str, value, result, first, locale, true);
+        }
+        if (hasSurr) {
+            return toLowerCaseEx(str, value, result, first, locale, false);
+        }
+        int bits = 0;
+        for (int i = first; i < len; i++) {
+            int cp = (int)getChar(value, i);
+            if (cp == '\u03A3' ||                       // GREEK CAPITAL LETTER SIGMA
+                Character.isSurrogate((char)cp)) {
+                return toLowerCaseEx(str, value, result, i, locale, false);
+            }
+            if (cp == '\u0130') {                       // LATIN CAPITAL LETTER I WITH DOT ABOVE
+                return toLowerCaseEx(str, value, result, i, locale, true);
+            }
+            cp = Character.toLowerCase(cp);
+            if (!Character.isBmpCodePoint(cp)) {
+                return toLowerCaseEx(str, value, result, i, locale, false);
+            }
+            bits |= cp;
+            putChar(result, i, cp);
+        }
+        if (bits >>> 8 != 0) {
+            return new String(result, UTF16);
+        } else {
+            return newString(result, 0, len);
+        }
+    }
+
+    private static String toLowerCaseEx(String str, byte[] value,
+                                        byte[] result, int first, Locale locale,
+                                        boolean localeDependent) {
+        int resultOffset = first;
+        int length = value.length >> 1;
+        int srcCount;
+        for (int i = first; i < length; i += srcCount) {
+            int srcChar = getChar(value, i);
+            int lowerChar;
+            char[] lowerCharArray;
+            srcCount = 1;
+            if (Character.isSurrogate((char)srcChar)) {
+                srcChar = codePointAt(value, i, length);
+                srcCount = Character.charCount(srcChar);
+            }
+            if (localeDependent ||
+                srcChar == '\u03A3' ||  // GREEK CAPITAL LETTER SIGMA
+                srcChar == '\u0130') {  // LATIN CAPITAL LETTER I WITH DOT ABOVE
+                lowerChar = ConditionalSpecialCasing.toLowerCaseEx(str, i, locale);
+            } else {
+                lowerChar = Character.toLowerCase(srcChar);
+            }
+            if (Character.isBmpCodePoint(lowerChar)) {    // Character.ERROR is not a bmp
+                putChar(result, resultOffset++, lowerChar);
+            } else {
+                if (lowerChar == Character.ERROR) {
+                    lowerCharArray = ConditionalSpecialCasing.toLowerCaseCharArray(str, i, locale);
+                } else {
+                    lowerCharArray = Character.toChars(lowerChar);
+                }
+                /* Grow result if needed */
+                int mapLen = lowerCharArray.length;
+                if (mapLen > srcCount) {
+                    byte[] result2 = newBytesFor((result.length >> 1) + mapLen - srcCount);
+                    System.arraycopy(result, 0, result2, 0, resultOffset << 1);
+                    result = result2;
+                }
+                for (int x = 0; x < mapLen; ++x) {
+                    putChar(result, resultOffset++, lowerCharArray[x]);
+                }
+            }
+        }
+        return newString(result, 0, resultOffset);
+    }
+
+    public static String toUpperCase(String str, byte[] value, Locale locale) {
+        if (locale == null) {
+            throw new NullPointerException();
+        }
+        int first;
+        boolean hasSurr = false;
+        final int len = value.length >> 1;
+
+        // Now check if there are any characters that need to be changed, or are surrogate
+        for (first = 0 ; first < len; first++) {
+            int cp = (int)getChar(value, first);
+            if (Character.isSurrogate((char)cp)) {
+                hasSurr = true;
+                break;
+            }
+            if (cp != Character.toUpperCaseEx(cp)) {   // no need to check Character.ERROR
+                break;
+            }
+        }
+        if (first == len) {
+            return str;
+        }
+        byte[] result = new byte[value.length];
+        System.arraycopy(value, 0, result, 0, first << 1); // Just copy the first few
+                                                           // upperCase characters.
+        String lang = locale.getLanguage();
+        if (lang == "tr" || lang == "az" || lang == "lt") {
+            return toUpperCaseEx(str, value, result, first, locale, true);
+        }
+        if (hasSurr) {
+            return toUpperCaseEx(str, value, result, first, locale, false);
+        }
+        int bits = 0;
+        for (int i = first; i < len; i++) {
+            int cp = (int)getChar(value, i);
+            if (Character.isSurrogate((char)cp)) {
+                return toUpperCaseEx(str, value, result, i, locale, false);
+            }
+            cp = Character.toUpperCaseEx(cp);
+            if (!Character.isBmpCodePoint(cp)) {    // Character.ERROR is not bmp
+                return toUpperCaseEx(str, value, result, i, locale, false);
+            }
+            bits |= cp;
+            putChar(result, i, cp);
+        }
+        if (bits >>> 8 != 0) {
+            return new String(result, UTF16);
+        } else {
+            return newString(result, 0, len);
+        }
+    }
+
+    private static String toUpperCaseEx(String str, byte[] value,
+                                        byte[] result, int first,
+                                        Locale locale, boolean localeDependent)
+    {
+        int resultOffset = first;
+        int length = value.length >> 1;
+        int srcCount;
+        for (int i = first; i < length; i += srcCount) {
+            int srcChar = getChar(value, i);
+            int upperChar;
+            char[] upperCharArray;
+            srcCount = 1;
+            if (Character.isSurrogate((char)srcChar)) {
+                srcChar = codePointAt(value, i, length);
+                srcCount = Character.charCount(srcChar);
+            }
+            if (localeDependent) {
+                upperChar = ConditionalSpecialCasing.toUpperCaseEx(str, i, locale);
+            } else {
+                upperChar = Character.toUpperCaseEx(srcChar);
+            }
+            if (Character.isBmpCodePoint(upperChar)) {
+                putChar(result, resultOffset++, upperChar);
+            } else {
+                if (upperChar == Character.ERROR) {
+                    if (localeDependent) {
+                        upperCharArray =
+                            ConditionalSpecialCasing.toUpperCaseCharArray(str, i, locale);
+                    } else {
+                        upperCharArray = Character.toUpperCaseCharArray(srcChar);
+                    }
+                } else {
+                    upperCharArray = Character.toChars(upperChar);
+                }
+                /* Grow result if needed */
+                int mapLen = upperCharArray.length;
+                if (mapLen > srcCount) {
+                    byte[] result2 = newBytesFor((result.length >> 1) + mapLen - srcCount);
+                    System.arraycopy(result, 0, result2, 0, resultOffset << 1);
+                    result = result2;
+                 }
+                 for (int x = 0; x < mapLen; ++x) {
+                    putChar(result, resultOffset++, upperCharArray[x]);
+                 }
+            }
+        }
+        return newString(result, 0, resultOffset);
+    }
+
+    public static String trim(byte[] value) {
+        int length = value.length >> 1;
+        int len = length;
+        int st = 0;
+        while (st < len && getChar(value, st) <= ' ') {
+            st++;
+        }
+        while (st < len && getChar(value, len - 1) <= ' ') {
+            len--;
+        }
+        return ((st > 0) || (len < length )) ?
+            new String(Arrays.copyOfRange(value, st << 1, len << 1), UTF16) :
+            null;
+    }
+
+    public static void putChars(byte[] val, int index, char[] str, int off, int end) {
+        while (off < end) {
+            putChar(val, index++, str[off++]);
+        }
+    }
+
+    public static String newString(byte[] val, int index, int len) {
+        if (String.COMPACT_STRINGS) {
+            byte[] buf = compress(val, index, len);
+            if (buf != null) {
+                return new String(buf, LATIN1);
+            }
+        }
+        int last = index + len;
+        return new String(Arrays.copyOfRange(val, index << 1, last << 1), UTF16);
+    }
+
+    public static void fillNull(byte[] val, int index, int end) {
+        Arrays.fill(val, index << 1, end << 1, (byte)0);
+    }
+
+    static class CharsSpliterator implements Spliterator.OfInt {
+        private final byte[] array;
+        private int index;        // current index, modified on advance/split
+        private final int fence;  // one past last index
+        private final int cs;
+
+        CharsSpliterator(byte[] array, int acs) {
+            this(array, 0, array.length >> 1, acs);
+        }
+
+        CharsSpliterator(byte[] array, int origin, int fence, int acs) {
+            this.array = array;
+            this.index = origin;
+            this.fence = fence;
+            this.cs = acs | Spliterator.ORDERED | Spliterator.SIZED
+                      | Spliterator.SUBSIZED;
+        }
+
+        @Override
+        public OfInt trySplit() {
+            int lo = index, mid = (lo + fence) >>> 1;
+            return (lo >= mid)
+                   ? null
+                   : new CharsSpliterator(array, lo, index = mid, cs);
+        }
+
+        @Override
+        public void forEachRemaining(IntConsumer action) {
+            byte[] a; int i, hi; // hoist accesses and checks from loop
+            if (action == null)
+                throw new NullPointerException();
+            if (((a = array).length >> 1) >= (hi = fence) &&
+                (i = index) >= 0 && i < (index = hi)) {
+                do { action.accept(getChar(a, i)); } while (++i < hi);
+            }
+        }
+
+        @Override
+        public boolean tryAdvance(IntConsumer action) {
+            if (action == null)
+                throw new NullPointerException();
+            if (index >= 0 && index < fence) {
+                action.accept(getChar(array, index++));
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        public long estimateSize() { return (long)(fence - index); }
+
+        @Override
+        public int characteristics() {
+            return cs;
+        }
+    }
+
+    static class CodePointsSpliterator implements Spliterator.OfInt {
+        private final byte[] array;
+        private int index;        // current index, modified on advance/split
+        private final int fence;  // one past last index
+        private final int cs;
+
+        CodePointsSpliterator(byte[] array, int acs) {
+            this(array, 0, array.length >> 1, acs);
+        }
+
+        CodePointsSpliterator(byte[] array, int origin, int fence, int acs) {
+            this.array = array;
+            this.index = origin;
+            this.fence = fence;
+            this.cs = acs | Spliterator.ORDERED;
+        }
+
+        @Override
+        public OfInt trySplit() {
+            int lo = index, mid = (lo + fence) >>> 1;
+            if (lo >= mid)
+                return null;
+
+            int midOneLess;
+            // If the mid-point intersects a surrogate pair
+            if (Character.isLowSurrogate(getChar(array, mid)) &&
+                Character.isHighSurrogate(getChar(array, midOneLess = (mid -1)))) {
+                // If there is only one pair it cannot be split
+                if (lo >= midOneLess)
+                    return null;
+                // Shift the mid-point to align with the surrogate pair
+                return new CodePointsSpliterator(array, lo, index = midOneLess, cs);
+            }
+            return new CodePointsSpliterator(array, lo, index = mid, cs);
+        }
+
+        @Override
+        public void forEachRemaining(IntConsumer action) {
+            byte[] a; int i, hi; // hoist accesses and checks from loop
+            if (action == null)
+                throw new NullPointerException();
+            if (((a = array).length >> 1) >= (hi = fence) &&
+                (i = index) >= 0 && i < (index = hi)) {
+                do {
+                    i = advance(a, i, hi, action);
+                } while (i < hi);
+            }
+        }
+
+        @Override
+        public boolean tryAdvance(IntConsumer action) {
+            if (action == null)
+                throw new NullPointerException();
+            if (index >= 0 && index < fence) {
+                index = advance(array, index, fence, action);
+                return true;
+            }
+            return false;
+        }
+
+        // Advance one code point from the index, i, and return the next
+        // index to advance from
+        private static int advance(byte[] a, int i, int hi, IntConsumer action) {
+            char c1 = getChar(a, i++);
+            int cp = c1;
+            if (Character.isHighSurrogate(c1) && i < hi) {
+                char c2 = getChar(a, i);
+                if (Character.isLowSurrogate(c2)) {
+                    i++;
+                    cp = Character.toCodePoint(c1, c2);
+                }
+            }
+            action.accept(cp);
+            return i;
+        }
+
+        @Override
+        public long estimateSize() { return (long)(fence - index); }
+
+        @Override
+        public int characteristics() {
+            return cs;
+        }
+    }
+
+    ////////////////////////////////////////////////////////////////
+
+    public static void getCharsSB(byte[] val, int srcBegin, int srcEnd, char dst[], int dstBegin) {
+        checkOffset(srcEnd, val.length >> 1);
+        getChars(val, srcBegin, srcEnd, dst, dstBegin);
+    }
+
+    public static void putCharSB(byte[] val, int index, int c) {
+        checkIndex(index, val.length >> 1);
+        putChar(val, index, c);
+    }
+
+    public static void putCharsSB(byte[] val, int index, char[] ca, int off, int end) {
+        checkOffset(index + end - off, val.length >> 1);
+        putChars(val, index, ca, off, end);
+    }
+
+    public static void putCharsSB(byte[] val, int index, CharSequence s, int off, int end) {
+        checkOffset(index + end - off, val.length >> 1);
+        for (int i = off; i < end; i++) {
+            putChar(val, index++, s.charAt(i));
+        }
+    }
+
+    public static int codePointAtSB(byte[] val, int index, int end) {
+        checkOffset(end, val.length >> 1);
+        return codePointAt(val, index, end);
+    }
+
+    public static int codePointBeforeSB(byte[] val, int index) {
+        checkOffset(index, val.length >> 1);
+        return codePointBefore(val, index);
+    }
+
+    public static int codePointCountSB(byte[] val, int beginIndex, int endIndex) {
+        checkOffset(endIndex, val.length >> 1);
+        return codePointCount(val, beginIndex, endIndex);
+    }
+
+    public static String newStringSB(byte[] val, int index, int len) {
+        checkOffset(index + len, val.length >> 1);
+        return newString(val, index, len);
+    }
+
+    ////////////////////////////////////////////////////////////////
+
+    private static native boolean isBigEndian();
+
+    static final int HI_BYTE_SHIFT;
+    static final int LO_BYTE_SHIFT;
+    static {
+        if (isBigEndian()) {
+            HI_BYTE_SHIFT = 8;
+            LO_BYTE_SHIFT = 0;
+        } else {
+            HI_BYTE_SHIFT = 0;
+            LO_BYTE_SHIFT = 8;
+        }
+    }
+
+    static final int MAX_LENGTH = Integer.MAX_VALUE >> 1;
+}
diff --git a/jdk/src/java.base/share/classes/java/util/Arrays.java b/jdk/src/java.base/share/classes/java/util/Arrays.java
index 3596298..ac09b59 100644
--- a/jdk/src/java.base/share/classes/java/util/Arrays.java
+++ b/jdk/src/java.base/share/classes/java/util/Arrays.java
@@ -2685,6 +2685,7 @@
      * @param a2 the other array to be tested for equality
      * @return {@code true} if the two arrays are equal
      */
+    @HotSpotIntrinsicCandidate
     public static boolean equals(byte[] a, byte[] a2) {
         if (a==a2)
             return true;
diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/ArrayDecoder.java b/jdk/src/java.base/share/classes/sun/nio/cs/ArrayDecoder.java
index 6b0abe9..0ba7330 100644
--- a/jdk/src/java.base/share/classes/sun/nio/cs/ArrayDecoder.java
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/ArrayDecoder.java
@@ -32,4 +32,8 @@
 
 public interface ArrayDecoder {
     int decode(byte[] src, int off, int len, char[] dst);
+
+    default boolean isASCIICompatible() {
+        return false;
+    }
 }
diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/ArrayEncoder.java b/jdk/src/java.base/share/classes/sun/nio/cs/ArrayEncoder.java
index 2ef46e7..b4ced42 100644
--- a/jdk/src/java.base/share/classes/sun/nio/cs/ArrayEncoder.java
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/ArrayEncoder.java
@@ -26,10 +26,24 @@
 package sun.nio.cs;
 
 /*
- * FastPath char[]->byte[] encoder, REPLACE on malformed input or
+ * FastPath char[]/byte[] -> byte[] encoder, REPLACE on malformed input or
  * unmappable input.
  */
 
 public interface ArrayEncoder {
+
+    //  is only used by j.u.zip.ZipCoder for utf8
     int encode(char[] src, int off, int len, byte[] dst);
+
+    default int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) {
+        return -1;
+    }
+
+    default int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) {
+        return -1;
+    }
+
+    default boolean isASCIICompatible() {
+        return false;
+    }
 }
diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/DoubleByte.java b/jdk/src/java.base/share/classes/sun/nio/cs/DoubleByte.java
index 6121ab2..9f8d97f 100644
--- a/jdk/src/java.base/share/classes/sun/nio/cs/DoubleByte.java
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/DoubleByte.java
@@ -115,6 +115,7 @@
         final char[] b2cSB;
         final int b2Min;
         final int b2Max;
+        final boolean isASCIICompatible;
 
         // for SimpleEUC override
         protected CoderResult crMalformedOrUnderFlow(int b) {
@@ -132,16 +133,23 @@
 
         public Decoder(Charset cs, float avgcpb, float maxcpb,
                        char[][] b2c, char[] b2cSB,
-                       int b2Min, int b2Max) {
+                       int b2Min, int b2Max,
+                       boolean isASCIICompatible) {
             super(cs, avgcpb, maxcpb);
             this.b2c = b2c;
             this.b2cSB = b2cSB;
             this.b2Min = b2Min;
             this.b2Max = b2Max;
+            this.isASCIICompatible = isASCIICompatible;
+        }
+
+        public Decoder(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max,
+                       boolean isASCIICompatible) {
+            this(cs, 0.5f, 1.0f, b2c, b2cSB, b2Min, b2Max, isASCIICompatible);
         }
 
         public Decoder(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
-            this(cs, 0.5f, 1.0f, b2c, b2cSB, b2Min, b2Max);
+            this(cs, 0.5f, 1.0f, b2c, b2cSB, b2Min, b2Max, false);
         }
 
         protected CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) {
@@ -215,6 +223,7 @@
                 return decodeBufferLoop(src, dst);
         }
 
+        @Override
         public int decode(byte[] src, int sp, int len, char[] dst) {
             int dp = 0;
             int sl = sp + len;
@@ -230,12 +239,12 @@
                             if (b2c[b1] == B2C_UNMAPPABLE ||  // isNotLeadingByte
                                 b2c[b2] != B2C_UNMAPPABLE ||  // isLeadingByte
                                 decodeSingle(b2) != UNMAPPABLE_DECODING) {
-                                sp--;
+                               sp--;
                             }
                         }
                     }
                     if (c == UNMAPPABLE_DECODING) {
-                        c = repl;
+                         c = repl;
                     }
                 }
                 dst[dp++] = c;
@@ -243,6 +252,11 @@
             return dp;
         }
 
+        @Override
+        public boolean isASCIICompatible() {
+            return isASCIICompatible;
+        }
+
         public void implReset() {
             super.implReset();
         }
@@ -274,8 +288,14 @@
         private int  currentState;
 
         public Decoder_EBCDIC(Charset cs,
-                       char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
-            super(cs, b2c, b2cSB, b2Min, b2Max);
+                              char[][] b2c, char[] b2cSB, int b2Min, int b2Max,
+                              boolean isASCIICompatible) {
+            super(cs, b2c, b2cSB, b2Min, b2Max, isASCIICompatible);
+        }
+
+        public Decoder_EBCDIC(Charset cs,
+                              char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
+            super(cs, b2c, b2cSB, b2Min, b2Max, false);
         }
 
         public void implReset() {
@@ -403,6 +423,7 @@
             }
         }
 
+        @Override
         public int decode(byte[] src, int sp, int len, char[] dst) {
             int dp = 0;
             int sl = sp + len;
@@ -451,8 +472,13 @@
             b2cSB_UNMAPPABLE = new char[0x100];
             Arrays.fill(b2cSB_UNMAPPABLE, UNMAPPABLE_DECODING);
         }
+        public Decoder_DBCSONLY(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max,
+                                boolean isASCIICompatible) {
+            super(cs, 0.5f, 1.0f, b2c, b2cSB_UNMAPPABLE, b2Min, b2Max, isASCIICompatible);
+        }
+
         public Decoder_DBCSONLY(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
-            super(cs, 0.5f, 1.0f, b2c, b2cSB_UNMAPPABLE, b2Min, b2Max);
+            super(cs, 0.5f, 1.0f, b2c, b2cSB_UNMAPPABLE, b2Min, b2Max, false);
         }
     }
 
@@ -464,8 +490,9 @@
         private final int SS3 =  0x8F;
 
         public Decoder_EUC_SIM(Charset cs,
-                        char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
-            super(cs, b2c, b2cSB, b2Min, b2Max);
+                               char[][] b2c, char[] b2cSB, int b2Min, int b2Max,
+                               boolean isASCIICompatible) {
+            super(cs, b2c, b2cSB, b2Min, b2Max, isASCIICompatible);
         }
 
         // No support provided for G2/G3 for SimpleEUC
@@ -481,6 +508,7 @@
             return CoderResult.unmappableForLength(2);
         }
 
+        @Override
         public int decode(byte[] src, int sp, int len, char[] dst) {
             int dp = 0;
             int sl = sp + len;
@@ -515,17 +543,25 @@
         private final char[] c2b;
         private final char[] c2bIndex;
         protected Surrogate.Parser sgp;
+        final boolean isASCIICompatible;
 
         public Encoder(Charset cs, char[] c2b, char[] c2bIndex) {
+            this(cs, c2b, c2bIndex, false);
+        }
+
+        public Encoder(Charset cs, char[] c2b, char[] c2bIndex, boolean isASCIICompatible) {
             super(cs, 2.0f, 2.0f);
             this.c2b = c2b;
             this.c2bIndex = c2bIndex;
+            this.isASCIICompatible = isASCIICompatible;
         }
 
-        public Encoder(Charset cs, float avg, float max, byte[] repl, char[] c2b, char[] c2bIndex) {
+        public Encoder(Charset cs, float avg, float max, byte[] repl, char[] c2b, char[] c2bIndex,
+                       boolean isASCIICompatible) {
             super(cs, avg, max, repl);
             this.c2b = c2b;
             this.c2bIndex = c2bIndex;
+            this.isASCIICompatible = isASCIICompatible;
         }
 
         public boolean canEncode(char c) {
@@ -624,6 +660,7 @@
             repl = newReplacement;
         }
 
+        @Override
         public int encode(char[] src, int sp, int len, byte[] dst) {
             int dp = 0;
             int sl = sp + len;
@@ -647,11 +684,69 @@
                 } else {                          // SingleByte
                     dst[dp++] = (byte)bb;
                 }
+            }
+            return dp;
+        }
+
+        @Override
+        public int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) {
+            int dp = 0;
+            int sl = sp + len;
+            while (sp < sl) {
+                char c = (char)(src[sp++] & 0xff);
+                int bb = encodeChar(c);
+                if (bb == UNMAPPABLE_ENCODING) {
+                    // no surrogate pair in latin1 string
+                    dst[dp++] = repl[0];
+                    if (repl.length > 1) {
+                        dst[dp++] = repl[1];
+                    }
+                    continue;
+                } //else
+                if (bb > MAX_SINGLEBYTE) { // DoubleByte
+                    dst[dp++] = (byte)(bb >> 8);
+                    dst[dp++] = (byte)bb;
+                } else {                   // SingleByte
+                    dst[dp++] = (byte)bb;
+                }
 
             }
             return dp;
         }
 
+        @Override
+        public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) {
+            int dp = 0;
+            int sl = sp + len;
+            while (sp < sl) {
+                char c = StringUTF16.getChar(src, sp++);
+                int bb = encodeChar(c);
+                if (bb == UNMAPPABLE_ENCODING) {
+                    if (Character.isHighSurrogate(c) && sp < sl &&
+                        Character.isLowSurrogate(StringUTF16.getChar(src, sp))) {
+                        sp++;
+                    }
+                    dst[dp++] = repl[0];
+                    if (repl.length > 1) {
+                        dst[dp++] = repl[1];
+                    }
+                    continue;
+                } //else
+                if (bb > MAX_SINGLEBYTE) { // DoubleByte
+                    dst[dp++] = (byte)(bb >> 8);
+                    dst[dp++] = (byte)bb;
+                } else {                   // SingleByte
+                    dst[dp++] = (byte)bb;
+                }
+            }
+            return dp;
+        }
+
+        @Override
+        public boolean isASCIICompatible() {
+            return isASCIICompatible;
+        }
+
         public int encodeChar(char ch) {
             return c2b[c2bIndex[ch >> 8] + (ch & 0xff)];
         }
@@ -741,9 +836,11 @@
     }
 
     public static class Encoder_DBCSONLY extends Encoder {
+
         public Encoder_DBCSONLY(Charset cs, byte[] repl,
-                         char[] c2b, char[] c2bIndex) {
-            super(cs, 2.0f, 2.0f, repl, c2b, c2bIndex);
+                                char[] c2b, char[] c2bIndex,
+                                boolean isASCIICompatible) {
+            super(cs, 2.0f, 2.0f, repl, c2b, c2bIndex, isASCIICompatible);
         }
 
         public int encodeChar(char ch) {
@@ -754,8 +851,6 @@
         }
     }
 
-
-
     public static class Encoder_EBCDIC extends Encoder {
         static final int SBCS = 0;
         static final int DBCS = 1;
@@ -764,8 +859,9 @@
 
         protected int  currentState = SBCS;
 
-        public Encoder_EBCDIC(Charset cs, char[] c2b, char[] c2bIndex) {
-            super(cs, 4.0f, 5.0f, new byte[] {(byte)0x6f}, c2b, c2bIndex);
+        public Encoder_EBCDIC(Charset cs, char[] c2b, char[] c2bIndex,
+                              boolean isASCIICompatible) {
+            super(cs, 4.0f, 5.0f, new byte[] {(byte)0x6f}, c2b, c2bIndex, isASCIICompatible);
         }
 
         protected void implReset() {
@@ -878,6 +974,7 @@
             }
         }
 
+        @Override
         public int encode(char[] src, int sp, int len, byte[] dst) {
             int dp = 0;
             int sl = sp + len;
@@ -917,12 +1014,88 @@
             }
             return dp;
         }
+
+        @Override
+        public int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) {
+            int dp = 0;
+            int sl = sp + len;
+            while (sp < sl) {
+                char c = (char)(src[sp++] & 0xff);
+                int bb = encodeChar(c);
+                if (bb == UNMAPPABLE_ENCODING) {
+                    // no surrogate pair in latin1 string
+                    dst[dp++] = repl[0];
+                    if (repl.length > 1)
+                        dst[dp++] = repl[1];
+                    continue;
+                } //else
+                if (bb > MAX_SINGLEBYTE) {           // DoubleByte
+                    if (currentState == SBCS) {
+                        currentState = DBCS;
+                        dst[dp++] = SO;
+                    }
+                    dst[dp++] = (byte)(bb >> 8);
+                    dst[dp++] = (byte)bb;
+                } else {                             // SingleByte
+                    if (currentState == DBCS) {
+                         currentState = SBCS;
+                         dst[dp++] = SI;
+                    }
+                    dst[dp++] = (byte)bb;
+                }
+            }
+            if (currentState == DBCS) {
+                 currentState = SBCS;
+                 dst[dp++] = SI;
+            }
+            return dp;
+        }
+
+        @Override
+        public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) {
+            int dp = 0;
+            int sl = sp + len;
+            while (sp < sl) {
+                char c = StringUTF16.getChar(src, sp++);
+                int bb = encodeChar(c);
+                if (bb == UNMAPPABLE_ENCODING) {
+                    if (Character.isHighSurrogate(c) && sp < sl &&
+                        Character.isLowSurrogate(StringUTF16.getChar(src, sp))) {
+                        sp++;
+                    }
+                    dst[dp++] = repl[0];
+                    if (repl.length > 1)
+                        dst[dp++] = repl[1];
+                    continue;
+                } //else
+                if (bb > MAX_SINGLEBYTE) {           // DoubleByte
+                    if (currentState == SBCS) {
+                        currentState = DBCS;
+                        dst[dp++] = SO;
+                    }
+                    dst[dp++] = (byte)(bb >> 8);
+                    dst[dp++] = (byte)bb;
+                } else {                             // SingleByte
+                    if (currentState == DBCS) {
+                         currentState = SBCS;
+                         dst[dp++] = SI;
+                    }
+                    dst[dp++] = (byte)bb;
+                }
+            }
+            if (currentState == DBCS) {
+                 currentState = SBCS;
+                 dst[dp++] = SI;
+            }
+            return dp;
+        }
     }
 
     // EUC_SIMPLE
     public static class Encoder_EUC_SIM extends Encoder {
-        public Encoder_EUC_SIM(Charset cs, char[] c2b, char[] c2bIndex) {
-            super(cs, c2b, c2bIndex);
+        public Encoder_EUC_SIM(Charset cs, char[] c2b, char[] c2bIndex,
+                               boolean isASCIICompatible) {
+            super(cs, c2b, c2bIndex, isASCIICompatible);
         }
     }
 
diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/HKSCS.java b/jdk/src/java.base/share/classes/sun/nio/cs/HKSCS.java
index 773c7a0..ed920e7 100644
--- a/jdk/src/java.base/share/classes/sun/nio/cs/HKSCS.java
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/HKSCS.java
@@ -53,7 +53,7 @@
             // super(cs, 0.5f, 1.0f);
             // need to extends DoubleByte.Decoder so the
             // sun.io can use it. this implementation
-            super(cs, 0.5f, 1.0f, null, null, 0, 0);
+            super(cs, 0.5f, 1.0f, null, null, 0, 0, true);
             this.big5Dec = big5Dec;
             this.b2cBmp = b2cBmp;
             this.b2cSupp = b2cSupp;
@@ -239,7 +239,7 @@
                           char[][] c2bBmp,
                           char[][] c2bSupp)
         {
-            super(cs, null, null);
+            super(cs, null, null, true);
             this.big5Enc = big5Enc;
             this.c2bBmp = c2bBmp;
             this.c2bSupp = c2bSupp;
@@ -389,6 +389,33 @@
             return dp;
         }
 
+        public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) {
+            int dp = 0;
+            int sl = sp + len;
+            int dl = dst.length;
+            while (sp < sl) {
+                char c = StringUTF16.getChar(src, sp++);
+                int bb = encodeChar(c);
+                if (bb == UNMAPPABLE_ENCODING) {
+                    if (!Character.isHighSurrogate(c) || sp == sl ||
+                        !Character.isLowSurrogate(StringUTF16.getChar(src,sp)) ||
+                        (bb = encodeSupp(Character.toCodePoint(c, StringUTF16.getChar(src, sp++))))
+                        == UNMAPPABLE_ENCODING) {
+                        dst[dp++] = repl[0];
+                        if (repl.length > 1)
+                            dst[dp++] = repl[1];
+                        continue;
+                    }
+                }
+                if (bb > MAX_SINGLEBYTE) { // DoubleByte
+                    dst[dp++] = (byte)(bb >> 8);
+                    dst[dp++] = (byte)bb;
+                } else {                   // SingleByte
+                    dst[dp++] = (byte)bb;
+                }
+            }
+            return dp;
+        }
 
         static char[] C2B_UNMAPPABLE = new char[0x100];
         static {
diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java b/jdk/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java
index 3018133..b5d93a8f 100644
--- a/jdk/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java
@@ -132,6 +132,10 @@
                 dst[dp++] = (char)(src[sp++] & 0xff);
             return dp;
         }
+
+        public boolean isASCIICompatible() {
+            return true;
+        }
     }
 
     private static class Encoder extends CharsetEncoder
@@ -297,5 +301,9 @@
             }
             return dp;
         }
+
+        public boolean isASCIICompatible() {
+            return true;
+        }
     }
 }
diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/SingleByte.java b/jdk/src/java.base/share/classes/sun/nio/cs/SingleByte.java
index 6fd6faf..29a4246 100644
--- a/jdk/src/java.base/share/classes/sun/nio/cs/SingleByte.java
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/SingleByte.java
@@ -49,10 +49,18 @@
     public static final class Decoder extends CharsetDecoder
                                       implements ArrayDecoder {
         private final char[] b2c;
+        private final boolean isASCIICompatible;
 
         public Decoder(Charset cs, char[] b2c) {
             super(cs, 1.0f, 1.0f);
             this.b2c = b2c;
+            this.isASCIICompatible = false;
+        }
+
+        public Decoder(Charset cs, char[] b2c, boolean isASCIICompatible) {
+            super(cs, 1.0f, 1.0f);
+            this.b2c = b2c;
+            this.isASCIICompatible = isASCIICompatible;
         }
 
         private CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) {
@@ -116,6 +124,7 @@
             repl = newReplacement.charAt(0);
         }
 
+        @Override
         public int decode(byte[] src, int sp, int len, char[] dst) {
             if (len > dst.length)
                 len = dst.length;
@@ -129,6 +138,11 @@
             }
             return dp;
         }
+
+        @Override
+        public boolean isASCIICompatible() {
+            return isASCIICompatible;
+        }
     }
 
     public static final class Encoder extends CharsetEncoder
@@ -136,11 +150,13 @@
         private Surrogate.Parser sgp;
         private final char[] c2b;
         private final char[] c2bIndex;
+        private final boolean isASCIICompatible;
 
-        public Encoder(Charset cs, char[] c2b, char[] c2bIndex) {
+        public Encoder(Charset cs, char[] c2b, char[] c2bIndex, boolean isASCIICompatible) {
             super(cs, 1.0f, 1.0f);
             this.c2b = c2b;
             this.c2bIndex = c2bIndex;
+            this.isASCIICompatible = isASCIICompatible;
         }
 
         public boolean canEncode(char c) {
@@ -252,6 +268,51 @@
             }
             return dp;
         }
+
+        @Override
+        public int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) {
+            int dp = 0;
+            int sl = sp + Math.min(len, dst.length);
+            while (sp < sl) {
+                char c = (char)(src[sp++] & 0xff);
+                int b = encode(c);
+                if (b == UNMAPPABLE_ENCODING) {
+                    dst[dp++] = repl;
+                } else {
+                    dst[dp++] = (byte)b;
+                }
+            }
+            return dp;
+        }
+
+        @Override
+        public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) {
+            int dp = 0;
+            int sl = sp + Math.min(len, dst.length);
+            while (sp < sl) {
+                char c = StringUTF16.getChar(src, sp++);
+                int b = encode(c);
+                if (b != UNMAPPABLE_ENCODING) {
+                    dst[dp++] = (byte)b;
+                    continue;
+                }
+                if (Character.isHighSurrogate(c) && sp < sl &&
+                    Character.isLowSurrogate(StringUTF16.getChar(src, sp))) {
+                    if (len > dst.length) {
+                        sl++;
+                        len--;
+                    }
+                    sp++;
+                }
+                dst[dp++] = repl;
+            }
+            return dp;
+        }
+
+        @Override
+        public boolean isASCIICompatible() {
+            return isASCIICompatible;
+        }
     }
 
     // init the c2b and c2bIndex tables from b2c.
diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/StringUTF16.java b/jdk/src/java.base/share/classes/sun/nio/cs/StringUTF16.java
new file mode 100644
index 0000000..f1608ca
--- /dev/null
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/StringUTF16.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+package sun.nio.cs;
+
+import static sun.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET;
+import static sun.misc.Unsafe.ARRAY_BYTE_INDEX_SCALE;
+
+class StringUTF16 {
+
+    public static char getChar(byte[] val, int index) {
+        return unsafe.getChar(val,
+                              ARRAY_BYTE_BASE_OFFSET + ARRAY_BYTE_INDEX_SCALE * index * 2L);
+    }
+
+    private static final sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe();
+}
diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/US_ASCII.java b/jdk/src/java.base/share/classes/sun/nio/cs/US_ASCII.java
index 816a1ac..39b7df0 100644
--- a/jdk/src/java.base/share/classes/sun/nio/cs/US_ASCII.java
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/US_ASCII.java
@@ -146,6 +146,10 @@
             }
             return dp;
         }
+
+        public boolean isASCIICompatible() {
+            return true;
+        }
     }
 
     private static class Encoder extends CharsetEncoder
@@ -259,6 +263,10 @@
             }
             return dp;
         }
+
+        public boolean isASCIICompatible() {
+            return true;
+        }
     }
 
 }
diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/UTF_8.java b/jdk/src/java.base/share/classes/sun/nio/cs/UTF_8.java
index 3ee2341..06a4340 100644
--- a/jdk/src/java.base/share/classes/sun/nio/cs/UTF_8.java
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/UTF_8.java
@@ -549,6 +549,10 @@
             }
             return dp;
         }
+
+        public boolean isASCIICompatible() {
+            return true;
+        }
     }
 
     private static final class Encoder extends CharsetEncoder
@@ -742,5 +746,9 @@
             }
             return dp;
         }
+
+        public boolean isASCIICompatible() {
+            return true;
+        }
     }
 }
diff --git a/jdk/src/java.base/share/native/libjava/String.c b/jdk/src/java.base/share/native/libjava/String.c
index 7c8170e..cc7f740 100644
--- a/jdk/src/java.base/share/native/libjava/String.c
+++ b/jdk/src/java.base/share/native/libjava/String.c
@@ -31,3 +31,14 @@
 {
     return JVM_InternString(env, this);
 }
+
+JNIEXPORT jboolean JNICALL
+Java_java_lang_StringUTF16_isBigEndian(JNIEnv *env, jclass cls)
+{
+  unsigned int endianTest = 0xff000000;
+  if (((char*)(&endianTest))[0] != 0) {
+    return JNI_TRUE;
+  } else {
+    return JNI_FALSE;
+  }
+}
diff --git a/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/Big5_Solaris.java.template b/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/Big5_Solaris.java.template
index e1810bd9..2099fbb 100644
--- a/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/Big5_Solaris.java.template
+++ b/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/Big5_Solaris.java.template
@@ -51,12 +51,12 @@
 
     public CharsetDecoder newDecoder() {
         initb2c();
-        return new  DoubleByte.Decoder(this, b2c, b2cSB, 0x40, 0xfe);
+        return new  DoubleByte.Decoder(this, b2c, b2cSB, 0x40, 0xfe, true);
     }
 
     public CharsetEncoder newEncoder() {
         initc2b();
-        return new DoubleByte.Encoder(this, c2b, c2bIndex);
+        return new DoubleByte.Encoder(this, c2b, c2bIndex, true);
     }
 
     static char[][] b2c;
diff --git a/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/IBM834.java b/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/IBM834.java
index 513dc55..aeb4ded 100644
--- a/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/IBM834.java
+++ b/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/IBM834.java
@@ -62,7 +62,7 @@
     protected static class Encoder extends DoubleByte.Encoder_DBCSONLY {
         public Encoder(Charset cs) {
             super(cs, new byte[] {(byte)0xfe, (byte)0xfe},
-                  IBM933.c2b, IBM933.c2bIndex);
+                  IBM933.c2b, IBM933.c2bIndex, false);
         }
 
         public int encodeChar(char ch) {
diff --git a/jdk/test/java/lang/String/Chars.java b/jdk/test/java/lang/String/Chars.java
new file mode 100644
index 0000000..ab6771b
--- /dev/null
+++ b/jdk/test/java/lang/String/Chars.java
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+    @test
+    @bug 8054307
+    @summary test chars() and codePoints()
+*/
+
+import java.util.Arrays;
+import java.util.Random;
+
+public class Chars {
+
+    public static void main(String[] args) {
+        Random r = new Random();
+        for (int i = 0; i < 10; i++) {
+            int n = 100 + r.nextInt(100);
+            char[] cc = new char[n];
+            int[]  ccExp = new int[n];
+            int[]  cpExp = new int[n];
+            // latin1
+            for (int j = 0; j < n; j++) {
+                cc[j] = (char)(ccExp[j] = cpExp[j] = r.nextInt(0x80));
+            }
+            testChars(cc, ccExp);
+            testCPs(cc, cpExp);
+
+            // bmp without surrogates
+            for (int j = 0; j < n; j++) {
+                cc[j] = (char)(ccExp[j] = cpExp[j] = r.nextInt(0x8000));
+            }
+            testChars(cc, ccExp);
+            testCPs(cc, cpExp);
+
+            // bmp with surrogates
+            int k = 0;
+            for (int j = 0; j < n; j++) {
+                if (j % 9 ==  5 && j + 1 < n) {
+                    int cp = 0x10000 + r.nextInt(2000);
+                    cpExp[k++] = cp;
+                    Character.toChars(cp, cc, j);
+                    ccExp[j] = cc[j];
+                    ccExp[j + 1] = cc[j + 1];
+                    j++;
+                } else {
+                    cc[j] = (char)(ccExp[j] = cpExp[k++] = r.nextInt(0x8000));
+                }
+            }
+            cpExp = Arrays.copyOf(cpExp, k);
+            testChars(cc, ccExp);
+            testCPs(cc, cpExp);
+        }
+    }
+
+    static void testChars(char[] cc, int[] expected) {
+        String str = new String(cc);
+        if (!Arrays.equals(expected, str.chars().toArray())) {
+            throw new RuntimeException("chars/codePoints() failed!");
+        }
+    }
+
+    static void testCPs(char[] cc, int[] expected) {
+        String str = new String(cc);
+        if (!Arrays.equals(expected, str.codePoints().toArray())) {
+            throw new RuntimeException("chars/codePoints() failed!");
+        }
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/CharAt.java b/jdk/test/java/lang/String/CompactString/CharAt.java
new file mode 100644
index 0000000..f70c143
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/CharAt.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.stream.IntStream;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.charAt.
+ * @run testng/othervm -XX:+CompactStrings CharAt
+ * @run testng/othervm -XX:-CompactStrings CharAt
+ */
+
+public class CharAt extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+                new Object[] { STRING_L1, new char[] { 'A' } },
+                new Object[] { STRING_L2, new char[] { 'A', 'B' } },
+                new Object[] { STRING_L4, new char[] { 'A', 'B', 'C', 'D' } },
+                new Object[] { STRING_LLONG,
+                        new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' } },
+                new Object[] { STRING_U1, new char[] { '\uFF21' } },
+                new Object[] { STRING_U2, new char[] { '\uFF21', '\uFF22' } },
+                new Object[] { STRING_M12, new char[] { '\uFF21', 'A' } },
+                new Object[] { STRING_M11, new char[] { 'A', '\uFF21' } }, };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testCharAt(String str, char[] expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            IntStream
+                                    .range(0, str.length())
+                                    .forEach(
+                                            i -> assertEquals(
+                                                    str.charAt(i),
+                                                    expected[i],
+                                                    String.format(
+                                                            "testing String(%s).charAt(%d), source : %s, ",
+                                                            escapeNonASCIIs(data),
+                                                            i, source)));
+                        });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/CodePointAt.java b/jdk/test/java/lang/String/CompactString/CodePointAt.java
new file mode 100644
index 0000000..5333f0b
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/CodePointAt.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.stream.IntStream;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.codePointAt.
+ * @run testng/othervm -XX:+CompactStrings CodePointAt
+ * @run testng/othervm -XX:-CompactStrings CodePointAt
+ */
+
+public class CodePointAt extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+
+                new Object[] { STRING_L1, new int[] { 'A' } },
+                new Object[] { STRING_L2, new int[] { 'A', 'B' } },
+                new Object[] { STRING_L4, new int[] { 'A', 'B', 'C', 'D' } },
+                new Object[] { STRING_LLONG,
+                        new int[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' } },
+                new Object[] { STRING_U1, new int[] { '\uFF21' } },
+                new Object[] { STRING_U2, new int[] { '\uFF21', '\uFF22' } },
+                new Object[] { STRING_M12, new int[] { '\uFF21', 'A' } },
+                new Object[] { STRING_M11, new int[] { 'A', '\uFF21' } },
+                new Object[] {
+                        STRING_SUPPLEMENTARY,
+                        new int[] { Character.toCodePoint('\uD801', '\uDC00'),
+                                '\uDC00',
+                                Character.toCodePoint('\uD801', '\uDC01'),
+                                '\uDC01', '\uFF21', 'A' }, } };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testCodePointAt(String str, int[] expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            IntStream
+                                    .range(0, str.length())
+                                    .forEach(
+                                            i -> assertEquals(
+                                                    str.codePointAt(i),
+                                                    expected[i],
+                                                    String.format(
+                                                            "testing String(%s).codePointAt(%d), source : %s, ",
+                                                            escapeNonASCIIs(data),
+                                                            i, source)));
+                        });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/CodePointBefore.java b/jdk/test/java/lang/String/CompactString/CodePointBefore.java
new file mode 100644
index 0000000..81f16bc
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/CodePointBefore.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.stream.IntStream;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.codePointBefore.
+ * @run testng/othervm -XX:+CompactStrings CodePointBefore
+ * @run testng/othervm -XX:-CompactStrings CodePointBefore
+ */
+
+public class CodePointBefore extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+
+                new Object[] { STRING_L1, new int[] { 'A' } },
+                new Object[] { STRING_L2, new int[] { 'A', 'B' } },
+                new Object[] { STRING_L4, new int[] { 'A', 'B', 'C', 'D' } },
+                new Object[] { STRING_LLONG,
+                        new int[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' } },
+                new Object[] { STRING_U1, new int[] { '\uFF21' } },
+                new Object[] { STRING_U2, new int[] { '\uFF21', '\uFF22' } },
+                new Object[] { STRING_M12, new int[] { '\uFF21', 'A' } },
+                new Object[] { STRING_M11, new int[] { 'A', '\uFF21' } },
+                new Object[] {
+                        STRING_SUPPLEMENTARY,
+                        new int[] { '\uD801', Character.toCodePoint('\uD801', '\uDC00'),
+                                '\uD801', Character.toCodePoint('\uD801', '\uDC01'),
+                                '\uFF21', 'A' }, } };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testCodePointBefore(String str, int[] expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            IntStream
+                                    .range(0, str.length())
+                                    .forEach(
+                                            i -> assertEquals(
+                                                    str.codePointBefore(i + 1),
+                                                    expected[i],
+                                                    String.format(
+                                                            "testing String(%s).codePointBefore(%d), source : %s, ",
+                                                            escapeNonASCIIs(data),
+                                                            i + 1, source)));
+                        });
+    }
+
+}
diff --git a/jdk/test/java/lang/String/CompactString/CodePointCount.java b/jdk/test/java/lang/String/CompactString/CodePointCount.java
new file mode 100644
index 0000000..0ddc82a
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/CodePointCount.java
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.codePointCount.
+ * @run testng/othervm -XX:+CompactStrings CodePointCount
+ * @run testng/othervm -XX:-CompactStrings CodePointCount
+ */
+
+public class CodePointCount extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] { new Object[] { STRING_EMPTY, 0, 0, 0 },
+                new Object[] { STRING_L1, 0, 1, 1 },
+                new Object[] { STRING_L1, 1, 1, 0 },
+                new Object[] { STRING_L2, 0, 2, 2 },
+                new Object[] { STRING_L2, 0, 1, 1 },
+                new Object[] { STRING_L2, 1, 2, 1 },
+                new Object[] { STRING_L4, 0, 4, 4 },
+                new Object[] { STRING_L4, 0, 1, 1 },
+                new Object[] { STRING_L4, 2, 4, 2 },
+                new Object[] { STRING_LLONG, 0, 8, 8 },
+                new Object[] { STRING_LLONG, 0, 5, 5 },
+                new Object[] { STRING_LLONG, 4, 8, 4 },
+                new Object[] { STRING_LLONG, 0, 7, 7 },
+                new Object[] { STRING_U1, 0, 1, 1 },
+                new Object[] { STRING_U2, 0, 2, 2 },
+                new Object[] { STRING_U2, 0, 1, 1 },
+                new Object[] { STRING_U2, 1, 2, 1 },
+                new Object[] { STRING_M12, 0, 2, 2 },
+                new Object[] { STRING_M12, 0, 1, 1 },
+                new Object[] { STRING_M12, 1, 2, 1 },
+                new Object[] { STRING_M11, 0, 2, 2 },
+                new Object[] { STRING_M11, 0, 1, 1 },
+                new Object[] { STRING_M11, 1, 2, 1 },
+                new Object[] { STRING_SUPPLEMENTARY, 0, 1, 1 },
+                new Object[] { STRING_SUPPLEMENTARY, 0, 2, 1 },
+                new Object[] { STRING_SUPPLEMENTARY, 0, 3, 2 },
+                new Object[] { STRING_SUPPLEMENTARY, 0, 5, 3 },
+                new Object[] { STRING_SUPPLEMENTARY, 0, 6, 4 },
+                new Object[] { STRING_SUPPLEMENTARY, 1, 4, 2 },
+                new Object[] { STRING_SUPPLEMENTARY, 1, 6, 4 },
+                new Object[] { STRING_SUPPLEMENTARY, 2, 4, 1 },};
+    }
+
+    @Test(dataProvider = "provider")
+    public void testCodePointCount(String str, int beginIndex, int endIndex,
+            int expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertEquals(
+                                    data.codePointCount(beginIndex, endIndex),
+                                    expected,
+                                    String.format(
+                                            "testing String(%s).codePointCount(%d, %d), source : %s, ",
+                                            escapeNonASCIIs(data), beginIndex,
+                                            endIndex, source));
+                        });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/CompactString.java b/jdk/test/java/lang/String/CompactString/CompactString.java
new file mode 100644
index 0000000..c22eacc
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/CompactString.java
@@ -0,0 +1,307 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.testng.annotations.BeforeClass;
+
+/*
+ * Base class of tests for Compact String.
+ *
+ */
+public class CompactString {
+
+    final Map<String, Map<String, String>> map = new HashMap<>();
+
+    enum StringSources {
+        EMPTY(STRING_EMPTY, BYTE_ARRAY_EMTPY, CHAR_ARRAY_EMPTY,
+                POINT_ARRAY_EMTPY), LDUPLICATE(STRING_LDUPLICATE,
+                BYTE_ARRAY_LDUPLICATE, CHAR_ARRAY_LDUPLICATE,
+                POINT_ARRAY_LDUPLICATE), LLONG(STRING_LLONG, BYTE_ARRAY_LLONG,
+                CHAR_ARRAY_LLONG, POINT_ARRAY_LLONG), L1(STRING_L1,
+                BYTE_ARRAY_L1, CHAR_ARRAY_L1, POINT_ARRAY_L1), L2(STRING_L2,
+                BYTE_ARRAY_L2, CHAR_ARRAY_L2, POINT_ARRAY_L2), L4(STRING_L4,
+                BYTE_ARRAY_L4, CHAR_ARRAY_L4, POINT_ARRAY_L4), UDUPLICATE(
+                STRING_UDUPLICATE, BYTE_ARRAY_UDUPLICATE,
+                CHAR_ARRAY_UDUPLICATE, POINT_ARRAY_UDUPLICATE), U1(STRING_U1,
+                BYTE_ARRAY_U1, CHAR_ARRAY_U1, POINT_ARRAY_U1), U2(STRING_U2,
+                BYTE_ARRAY_U2, CHAR_ARRAY_U2, POINT_ARRAY_U2), MDUPLICATE1(
+                STRING_MDUPLICATE1, BYTE_ARRAY_MDUPLICATE1,
+                CHAR_ARRAY_MDUPLICATE1, POINT_ARRAY_MDUPLICATE1), MDUPLICATE2(
+                STRING_MDUPLICATE2, BYTE_ARRAY_MDUPLICATE2,
+                CHAR_ARRAY_MDUPLICATE2, POINT_ARRAY_MDUPLICATE2), MLONG1(
+                STRING_MLONG1, BYTE_ARRAY_MLONG1, CHAR_ARRAY_MLONG1,
+                POINT_ARRAY_MLONG1), MLONG2(STRING_MLONG2, BYTE_ARRAY_MLONG2,
+                CHAR_ARRAY_MLONG2, POINT_ARRAY_MLONG2), M11(STRING_M11,
+                BYTE_ARRAY_M11, CHAR_ARRAY_M11, POINT_ARRAY_M11), M12(
+                STRING_M12, BYTE_ARRAY_M12, CHAR_ARRAY_M12, POINT_ARRAY_M12), SUPPLEMENTARY(
+                STRING_SUPPLEMENTARY, BYTE_ARRAY_SUPPLEMENTARY,
+                CHAR_ARRAY_SUPPLEMENTARY, POINT_ARRAY_SUPPLEMENTARY), SUPPLEMENTARY_LOWERCASE(
+                STRING_SUPPLEMENTARY_LOWERCASE,
+                BYTE_ARRAY_SUPPLEMENTARY_LOWERCASE,
+                CHAR_ARRAY_SUPPLEMENTARY_LOWERCASE,
+                POINT_ARRAY_SUPPLEMENTARY_LOWERCASE);
+
+        private StringSources(String s, byte[] b, char[] c, int[] i) {
+            str = s;
+            ba = b;
+            ca = c;
+            ia = i;
+        }
+
+        String getString() {
+            return str;
+        }
+
+        byte[] getByteArray() {
+            return ba;
+        }
+
+        char[] getCharArray() {
+            return ca;
+        }
+
+        int[] getIntArray() {
+            return ia;
+        }
+
+        private final String str;
+        private final byte[] ba;
+        private final char[] ca;
+        private final int[] ia;
+    }
+
+    protected static final String DEFAULT_CHARSET_NAME = "UTF-8";
+    protected static final Charset DEFAULT_CHARSET = Charset
+            .forName(DEFAULT_CHARSET_NAME);
+
+    protected static final String STRING_EMPTY = "";
+    protected static final byte[] BYTE_ARRAY_EMTPY = new byte[0];
+    protected static final char[] CHAR_ARRAY_EMPTY = new char[0];
+    protected static final int[] POINT_ARRAY_EMTPY = new int[0];
+
+    protected static final String STRING_LDUPLICATE = "ABABABABAB";
+    protected static final byte[] BYTE_ARRAY_LDUPLICATE = new byte[] { 'A', 'B',
+            'A', 'B', 'A', 'B', 'A', 'B', 'A', 'B' };
+    protected static final char[] CHAR_ARRAY_LDUPLICATE = new char[] { 'A', 'B',
+            'A', 'B', 'A', 'B', 'A', 'B', 'A', 'B' };
+    protected static final int[] POINT_ARRAY_LDUPLICATE = new int[] { 'A', 'B',
+            'A', 'B', 'A', 'B', 'A', 'B', 'A', 'B' };
+
+    protected static final String STRING_LLONG = "ABCDEFGH";
+    protected static final byte[] BYTE_ARRAY_LLONG = new byte[] { 'A', 'B', 'C',
+            'D', 'E', 'F', 'G', 'H' };
+    protected static final char[] CHAR_ARRAY_LLONG = new char[] { 'A', 'B', 'C',
+            'D', 'E', 'F', 'G', 'H' };
+    protected static final int[] POINT_ARRAY_LLONG = new int[] { 'A', 'B', 'C',
+            'D', 'E', 'F', 'G', 'H' };
+
+    protected static final String STRING_L1 = "A";
+    protected static final byte[] BYTE_ARRAY_L1 = new byte[] { 'A' };
+    protected static final char[] CHAR_ARRAY_L1 = new char[] { 'A' };
+    protected static final int[] POINT_ARRAY_L1 = new int[] { 'A' };
+
+    protected static final String STRING_L2 = "AB";
+    protected static final byte[] BYTE_ARRAY_L2 = new byte[] { 'A', 'B' };
+    protected static final char[] CHAR_ARRAY_L2 = new char[] { 'A', 'B' };
+    protected static final int[] POINT_ARRAY_L2 = new int[] { 'A', 'B' };
+
+    protected static final String STRING_L4 = "ABCD";
+    protected static final byte[] BYTE_ARRAY_L4 = new byte[] { 'A', 'B', 'C', 'D' };
+    protected static final char[] CHAR_ARRAY_L4 = new char[] { 'A', 'B', 'C', 'D' };
+    protected static final int[] POINT_ARRAY_L4 = new int[] { 'A', 'B', 'C', 'D' };
+
+    /*
+     * Because right now ASCII is the default encoding parameter for source code
+     * in JDK build environment, so we escape them. same as below.
+     */
+    protected static final String STRING_UDUPLICATE = "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22";
+    protected static final byte[] BYTE_ARRAY_UDUPLICATE = getBytes(STRING_UDUPLICATE);
+    protected static final char[] CHAR_ARRAY_UDUPLICATE = new char[] { '\uFF21',
+            '\uFF22', '\uFF21', '\uFF22', '\uFF21', '\uFF22', '\uFF21',
+            '\uFF22', '\uFF21', '\uFF22' };
+    protected static final int[] POINT_ARRAY_UDUPLICATE = new int[] { '\uFF21',
+            '\uFF22', '\uFF21', '\uFF22', '\uFF21', '\uFF22', '\uFF21',
+            '\uFF22', '\uFF21', '\uFF22' };
+
+    protected static final String STRING_U1 = "\uFF21";
+    protected static final byte[] BYTE_ARRAY_U1 = getBytes(STRING_U1);
+    protected static final char[] CHAR_ARRAY_U1 = new char[] { '\uFF21' };
+    protected static final int[] POINT_ARRAY_U1 = new int[] { '\uFF21' };
+
+    protected static final String STRING_U2 = "\uFF21\uFF22";
+    protected static final byte[] BYTE_ARRAY_U2 = getBytes(STRING_U2);
+    protected static final char[] CHAR_ARRAY_U2 = new char[] { '\uFF21', '\uFF22' };
+    protected static final int[] POINT_ARRAY_U2 = new int[] { '\uFF21', '\uFF22' };
+
+    protected static final String STRING_MDUPLICATE1 = "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A";
+    protected static final byte[] BYTE_ARRAY_MDUPLICATE1 = getBytes(STRING_MDUPLICATE1);
+    protected static final char[] CHAR_ARRAY_MDUPLICATE1 = new char[] { '\uFF21',
+            'A', '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A' };
+    protected static final int[] POINT_ARRAY_MDUPLICATE1 = new int[] { '\uFF21',
+            'A', '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A' };
+
+    protected static final String STRING_MDUPLICATE2 = "A\uFF21A\uFF21A\uFF21A\uFF21A\uFF21";
+    protected static final byte[] BYTE_ARRAY_MDUPLICATE2 = getBytes(STRING_MDUPLICATE2);
+    protected static final char[] CHAR_ARRAY_MDUPLICATE2 = new char[] { 'A',
+            '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A',
+            '\uFF21' };
+    protected static final int[] POINT_ARRAY_MDUPLICATE2 = new int[] { 'A',
+            '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A',
+            '\uFF21' };
+
+    protected static final String STRING_MLONG1 = "A\uFF21B\uFF22C\uFF23D\uFF24E\uFF25F\uFF26G\uFF27H\uFF28";
+    protected static final byte[] BYTE_ARRAY_MLONG1 = getBytes(STRING_MLONG1);
+    protected static final char[] CHAR_ARRAY_MLONG1 = new char[] { 'A', '\uFF21',
+            'B', '\uFF22', 'C', '\uFF23', 'D', '\uFF24', 'E', '\uFF25', 'F',
+            '\uFF26', 'G', '\uFF27', 'H', '\uFF28' };
+    protected static final int[] POINT_ARRAY_MLONG1 = new int[] { 'A', '\uFF21',
+            'B', '\uFF22', 'C', '\uFF23', 'D', '\uFF24', 'E', '\uFF25', 'F',
+            '\uFF26', 'G', '\uFF27', 'H', '\uFF28' };
+
+    protected static final String STRING_MLONG2 = "\uFF21A\uFF22B\uFF23C\uFF24D\uFF25E\uFF26F\uFF27G\uFF28H";
+    protected static final byte[] BYTE_ARRAY_MLONG2 = getBytes(STRING_MLONG2);
+    protected static final char[] CHAR_ARRAY_MLONG2 = new char[] { '\uFF21', 'A',
+            '\uFF22', 'B', '\uFF23', 'C', '\uFF24', 'D', '\uFF25', 'E',
+            '\uFF26', 'F', '\uFF27', 'G', '\uFF28', 'H' };
+    protected static final int[] POINT_ARRAY_MLONG2 = new int[] { '\uFF21', 'A',
+            '\uFF22', 'B', '\uFF23', 'C', '\uFF24', 'D', '\uFF25', 'E',
+            '\uFF26', 'F', '\uFF27', 'G', '\uFF28', 'H' };
+
+    protected static final String STRING_M11 = "A\uFF21";
+    protected static final byte[] BYTE_ARRAY_M11 = getBytes(STRING_M11);
+    protected static final char[] CHAR_ARRAY_M11 = new char[] { 'A', '\uFF21' };
+    protected static final int[] POINT_ARRAY_M11 = new int[] { 'A', '\uFF21' };
+
+    protected static final String STRING_M12 = "\uFF21A";
+    protected static final byte[] BYTE_ARRAY_M12 = getBytes(STRING_M12);
+    protected static final char[] CHAR_ARRAY_M12 = new char[] { '\uFF21', 'A' };
+    protected static final int[] POINT_ARRAY_M12 = new int[] { '\uFF21', 'A' };
+
+    protected static final String STRING_SUPPLEMENTARY = "\uD801\uDC00\uD801\uDC01\uFF21A";
+    protected static final byte[] BYTE_ARRAY_SUPPLEMENTARY = getBytes(STRING_SUPPLEMENTARY);
+    protected static final char[] CHAR_ARRAY_SUPPLEMENTARY = new char[] {
+            '\uD801', '\uDC00', '\uD801', '\uDC01', '\uFF21', 'A' };
+    protected static final int[] POINT_ARRAY_SUPPLEMENTARY = new int[] {
+            '\uD801', '\uDC00', '\uD801', '\uDC01', '\uFF21', 'A' };
+
+    protected static final String STRING_SUPPLEMENTARY_LOWERCASE = "\uD801\uDC28\uD801\uDC29\uFF41a";
+    protected static final byte[] BYTE_ARRAY_SUPPLEMENTARY_LOWERCASE = getBytes(STRING_SUPPLEMENTARY_LOWERCASE);
+    protected static final char[] CHAR_ARRAY_SUPPLEMENTARY_LOWERCASE = new char[] {
+            '\uD801', '\uDC28', '\uD801', '\uDC29', '\uFF41', 'a' };
+    protected static final int[] POINT_ARRAY_SUPPLEMENTARY_LOWERCASE = new int[] {
+            '\uD801', '\uDC28', '\uD801', '\uDC29', '\uFF41', 'a' };
+
+    protected static final String SRC_BYTE_ARRAY_WITH_CHARSETNAME = "source from byte array with charset name";
+    protected static final String SRC_BYTE_ARRAY_WITH_CHARSET = "source from byte array with charset";
+    protected static final String SRC_CHAR_ARRAY = "source from char array";
+    protected static final String SRC_POINT_ARRAY = "source from code point array";
+    protected static final String SRC_STRING = "source from String";
+    protected static final String SRC_STRINGBUFFER = "source from StringBuffer";
+    protected static final String SRC_STRINGBUILDER = "source from StringBuilder";
+    protected static final String SRC_COPYVALUEOF = "source from copyValueOf from char array";
+    protected static final String SRC_VALUEOF = "source from valueOf from char array";
+
+    static {
+        System.out
+                .println(String
+                        .format("====== The platform's default charset is \"%s\", we're using \"%s\" for testing.",
+                                Charset.defaultCharset().name(),
+                                DEFAULT_CHARSET_NAME));
+    }
+
+    private static byte[] getBytes(String str) {
+        byte[] res = null;
+        try {
+            res = str.getBytes(DEFAULT_CHARSET_NAME);
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+            throw new RuntimeException("caught UnsupportedEncodingException!!!", e);
+        }
+        return res;
+    }
+
+    private void setUpOneString(String content, byte[] ba, char[] ca, int[] cpa)
+            throws UnsupportedEncodingException {
+        final Map<String, String> m = new HashMap<>();
+        m.put(SRC_BYTE_ARRAY_WITH_CHARSETNAME, new String(ba,
+                DEFAULT_CHARSET_NAME));
+        m.put(SRC_BYTE_ARRAY_WITH_CHARSET, new String(ba, DEFAULT_CHARSET));
+        m.put(SRC_CHAR_ARRAY, new String(ca));
+        m.put(SRC_POINT_ARRAY, new String(cpa, 0, cpa.length));
+        m.put(SRC_STRING, new String(content));
+        m.put(SRC_STRINGBUFFER, new String(new StringBuffer(content)));
+        m.put(SRC_STRINGBUILDER, new String(new StringBuilder(content)));
+        m.put(SRC_COPYVALUEOF, String.copyValueOf(ca));
+        m.put(SRC_VALUEOF, String.valueOf(ca));
+        map.put(content, m);
+    }
+
+    /*
+     * Set up the test data, use 9 ways to construct one String.
+     *
+     * @throws UnsupportedEncodingException
+     *         If the named charset is not supported in setUpOneString(xxx).
+     */
+    @BeforeClass
+    public void setUp() throws UnsupportedEncodingException {
+        for (StringSources src : StringSources.values()) {
+            setUpOneString(src.getString(), src.getByteArray(),
+                    src.getCharArray(), src.getIntArray());
+        }
+    }
+
+    /*
+     * Because right now system default charset in JPRT environment is only
+     * guaranteed to support ASCII characters in log, so we escape them.
+     */
+    protected String escapeNonASCIIs(String str) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < str.length(); i++) {
+            char c = str.charAt(i);
+            if (c > 0x7F) {
+                sb.append("\\u").append(Integer.toHexString((int) c));
+            } else {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+
+    /*
+     * Because right now system default charset in JPRT environment is only
+     * guaranteed to support ASCII characters in log, so we escape them.
+     */
+    protected String escapeNonASCII(char c) {
+        StringBuilder sb = new StringBuilder();
+        if (c > 0x7F) {
+            sb.append("\\u").append(Integer.toHexString((int) c));
+        } else {
+            sb.append(c);
+        }
+        return sb.toString();
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/CompareTo.java b/jdk/test/java/lang/String/CompactString/CompareTo.java
new file mode 100644
index 0000000..96f07cd
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/CompareTo.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.compareTo.
+ * @run testng/othervm -XX:+CompactStrings CompareTo
+ * @run testng/othervm -XX:-CompactStrings CompareTo
+ */
+
+public class CompareTo extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+
+        new Object[] { STRING_EMPTY, "A", -1 },
+                new Object[] { STRING_EMPTY, "\uFF21", -1 },
+                new Object[] { STRING_L1, "AB", -1 },
+                new Object[] { STRING_L1, "A", 0 },
+                new Object[] { STRING_L1, "a", -32 },
+                new Object[] { STRING_L1, "\uFF21", -65248 },
+                new Object[] { STRING_L2, "AB", 0 },
+                new Object[] { STRING_L2, "Ab", -32 },
+                new Object[] { STRING_L2, "AA", 1 },
+                new Object[] { STRING_L2, "\uFF21", -65248 },
+                new Object[] { STRING_L2, "A\uFF21", -65247 },
+                new Object[] { STRING_L4, "ABC", 1 },
+                new Object[] { STRING_L4, "AB", 2 },
+                new Object[] { STRING_L4, "ABcD", -32 },
+                new Object[] { STRING_L4, "ABCD\uFF21\uFF21", -2 },
+                new Object[] { STRING_L4, "ABCD\uFF21", -1 },
+                new Object[] { STRING_LLONG, "ABCDEFG\uFF21", -65241 },
+                new Object[] { STRING_LLONG, "AB", 6 },
+                new Object[] { STRING_LLONG, "ABCD", 4 },
+                new Object[] { STRING_LLONG, "ABCDEFGH\uFF21\uFF21", -2 },
+                new Object[] { STRING_U1, "\uFF21", 0 },
+                new Object[] { STRING_U1, "\uFF22", -1 },
+                new Object[] { STRING_U1, "\uFF21\uFF22", -1 },
+                new Object[] { STRING_U1, "A", 65248 },
+                new Object[] { STRING_U2, "\uFF21\uFF22", 0 },
+                new Object[] { STRING_U2, "\uFF22", -1 },
+                new Object[] { STRING_U2, "\uFF21\uFF21", 1 },
+                new Object[] { STRING_U2, "A", 65248 },
+                new Object[] { STRING_M12, "\uFF21A", 0 },
+                new Object[] { STRING_M12, "A\uFF21", 65248 },
+                new Object[] { STRING_M12, "\uFF21\uFF21", -65248 },
+                new Object[] { STRING_M11, "A\uFF21", 0 },
+                new Object[] { STRING_M11, "\uFF21A", -65248 },
+                new Object[] { STRING_M11, "AA", 65248 }, };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testCompareTo(String str, String anotherString, int expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertEquals(
+                                    data.compareTo(anotherString),
+                                    expected,
+                                    String.format(
+                                            "testing String(%s).compareTo(%s), source : %s, ",
+                                            escapeNonASCIIs(data),
+                                            escapeNonASCIIs(anotherString),
+                                            source));
+                        });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/CompareToIgnoreCase.java b/jdk/test/java/lang/String/CompactString/CompareToIgnoreCase.java
new file mode 100644
index 0000000..3b9cef7
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/CompareToIgnoreCase.java
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.compareToIgnoreCase.
+ * @run testng/othervm -XX:+CompactStrings CompareToIgnoreCase
+ * @run testng/othervm -XX:-CompactStrings CompareToIgnoreCase
+ */
+
+public class CompareToIgnoreCase extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+
+                new Object[] { STRING_EMPTY, "A", -1 },
+                new Object[] { STRING_L1, "a", 0 },
+                new Object[] { STRING_L1, "A", 0 },
+                new Object[] { STRING_L1, "\uFF21", -65248 },
+                new Object[] { STRING_L1, "B", -1 },
+                new Object[] { STRING_L2, "AB", 0 },
+                new Object[] { STRING_L2, "aB", 0 },
+                new Object[] { STRING_L2, "\uFF21", -65248 },
+                new Object[] { STRING_L2, "A\uFF21", -65247 },
+                new Object[] { STRING_L4, "ABCD", 0 },
+                new Object[] { STRING_L4, "abcd", 0 },
+                new Object[] { STRING_L4, "ABc\uFF21", -65245 },
+                new Object[] { STRING_LLONG, "ABCDEFGH", 0 },
+                new Object[] { STRING_LLONG, "abcdefgh", 0 },
+                new Object[] { STRING_LLONG, "ABCDEFG\uFF21", -65241 },
+                new Object[] { STRING_LLONG, "abcdefg\uFF21", -65241 },
+                new Object[] { STRING_U1, "\uFF41", 0 },
+                new Object[] { STRING_U1,
+                        "\uFF41\uFF42\uFF43\uFF44\uFF45\uFF46\uFF47\uFF48", -7 },
+                new Object[] { STRING_U1, "A", 65248 },
+                new Object[] { STRING_U2, "\uFF41", 1 },
+                new Object[] { STRING_U2, "\uFF41\uFF42", 0 },
+                new Object[] { STRING_U2,
+                        "\uFF41\uFF42\uFF43\uFF44\uFF45\uFF46\uFF47\uFF48", -6 },
+                new Object[] { STRING_M12, "\uFF41a", 0 },
+                new Object[] { STRING_M12, "\uFF41\uFF42", -65249 },
+                new Object[] { STRING_M11, "a\uFF41", 0 },
+                new Object[] { STRING_M11, "a\uFF42", -1 }, };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testCompareToIgnoreCase(String str, String anotherString,
+            int expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertEquals(
+                                    data.compareToIgnoreCase(anotherString),
+                                    expected,
+                                    String.format(
+                                            "testing String(%s).compareToIgnoreCase(%s), source : %s, ",
+                                            escapeNonASCIIs(data),
+                                            escapeNonASCIIs(anotherString),
+                                            source));
+                        });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/Concat.java b/jdk/test/java/lang/String/CompactString/Concat.java
new file mode 100644
index 0000000..de12389
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/Concat.java
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.concat.
+ * @run testng/othervm -XX:+CompactStrings Concat
+ * @run testng/othervm -XX:-CompactStrings Concat
+ */
+
+public class Concat extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+                new Object[] { STRING_EMPTY, "ABC", "ABC" },
+                new Object[] { STRING_EMPTY,
+                        "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"),
+                        "ABC\uFF21\uFF22\uFF23DEF" },
+                new Object[] {
+                        STRING_EMPTY,
+                        "\uFF21\uFF22\uFF23".concat("ABC").concat(
+                                "\uFF24\uFF25\uFF26"),
+                        "\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" },
+                new Object[] { STRING_L1,
+                        "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"),
+                        "AABC\uFF21\uFF22\uFF23DEF" },
+                new Object[] {
+                        STRING_L1,
+                        "\uFF21\uFF22\uFF23".concat("ABC").concat(
+                                "\uFF24\uFF25\uFF26"),
+                        "A\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" },
+                new Object[] { STRING_L2,
+                        "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"),
+                        "ABABC\uFF21\uFF22\uFF23DEF" },
+                new Object[] {
+                        STRING_L2,
+                        "\uFF21\uFF22\uFF23".concat("ABC").concat(
+                                "\uFF24\uFF25\uFF26"),
+                        "AB\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" },
+                new Object[] { STRING_L4,
+                        "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"),
+                        "ABCDABC\uFF21\uFF22\uFF23DEF" },
+                new Object[] {
+                        STRING_L4,
+                        "\uFF21\uFF22\uFF23".concat("ABC").concat(
+                                "\uFF24\uFF25\uFF26"),
+                        "ABCD\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" },
+                new Object[] { STRING_LLONG,
+                        "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"),
+                        "ABCDEFGHABC\uFF21\uFF22\uFF23DEF" },
+                new Object[] {
+                        STRING_LLONG,
+                        "\uFF21\uFF22\uFF23".concat("ABC").concat(
+                                "\uFF24\uFF25\uFF26"),
+                        "ABCDEFGH\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" },
+                new Object[] { STRING_U1,
+                        "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"),
+                        "\uFF21ABC\uFF21\uFF22\uFF23DEF" },
+                new Object[] {
+                        STRING_U1,
+                        "\uFF21\uFF22\uFF23".concat("ABC").concat(
+                                "\uFF24\uFF25\uFF26"),
+                        "\uFF21\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" },
+                new Object[] { STRING_U2,
+                        "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"),
+                        "\uFF21\uFF22ABC\uFF21\uFF22\uFF23DEF" },
+                new Object[] {
+                        STRING_U2,
+                        "\uFF21\uFF22\uFF23".concat("ABC").concat(
+                                "\uFF24\uFF25\uFF26"),
+                        "\uFF21\uFF22\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" },
+                new Object[] { STRING_M12,
+                        "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"),
+                        "\uFF21AABC\uFF21\uFF22\uFF23DEF" },
+                new Object[] {
+                        STRING_M12,
+                        "\uFF21\uFF22\uFF23".concat("ABC").concat(
+                                "\uFF24\uFF25\uFF26"),
+                        "\uFF21A\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" },
+                new Object[] { STRING_M11,
+                        "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"),
+                        "A\uFF21ABC\uFF21\uFF22\uFF23DEF" },
+                new Object[] {
+                        STRING_M11,
+                        "\uFF21\uFF22\uFF23".concat("ABC").concat(
+                                "\uFF24\uFF25\uFF26"),
+                        "A\uFF21\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" }, };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testConcat(String str, String anotherString, String expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertEquals(
+                                    data.concat(anotherString),
+                                    expected,
+                                    String.format(
+                                            "testing String(%s).concat(%s), source : %s, ",
+                                            escapeNonASCIIs(data),
+                                            escapeNonASCIIs(anotherString),
+                                            source));
+                        });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/Contains.java b/jdk/test/java/lang/String/CompactString/Contains.java
new file mode 100644
index 0000000..ad182b8
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/Contains.java
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.contains.
+ * @run testng/othervm -XX:+CompactStrings Contains
+ * @run testng/othervm -XX:-CompactStrings Contains
+ */
+
+public class Contains extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+
+        new Object[] { STRING_EMPTY, "", true },
+                new Object[] { STRING_EMPTY, "A", false },
+                new Object[] { STRING_EMPTY, "\uFF21", false },
+                new Object[] { STRING_L1, "", true },
+                new Object[] { STRING_L1, "A", true },
+                new Object[] { STRING_L1, "\uFF21", false },
+                new Object[] { STRING_L2, "", true },
+                new Object[] { STRING_L2, "A", true },
+                new Object[] { STRING_L2, "AB", true },
+                new Object[] { STRING_L2, "B", true },
+                new Object[] { STRING_L2, "ABC", false },
+                new Object[] { STRING_L2, "ab", false },
+                new Object[] { STRING_L4, "ABCD", true },
+                new Object[] { STRING_L4, "BC", true },
+                new Object[] { STRING_LLONG, "ABCDEFGH", true },
+                new Object[] { STRING_LLONG, "BCDEFGH", true },
+                new Object[] { STRING_LLONG, "EF", true },
+                new Object[] { STRING_U1, "", true },
+                new Object[] { STRING_U1, "\uFF21", true },
+                new Object[] { STRING_U1, "a", false },
+                new Object[] { STRING_U1, "\uFF21B", false },
+                new Object[] { STRING_U2, "", true },
+                new Object[] { STRING_U2, "\uFF21\uFF22", true },
+                new Object[] { STRING_U2, "a", false },
+                new Object[] { STRING_U2, "\uFF21B", false },
+                new Object[] { STRING_M12, "\uFF21A", true },
+                new Object[] { STRING_M12, "\uFF21", true },
+                new Object[] { STRING_M12, "A", true },
+                new Object[] { STRING_M12, "A\uFF21", false },
+                new Object[] { STRING_M11, "A\uFF21", true },
+                new Object[] { STRING_M11, "Ab", false }, };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testContains(String str, String anotherString, boolean expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertEquals(
+                                    data.contains(anotherString),
+                                    expected,
+                                    String.format(
+                                            "testing String(%s).contains(%s), source : %s, ",
+                                            escapeNonASCIIs(data),
+                                            escapeNonASCIIs(anotherString),
+                                            source));
+                        });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/EndsWith.java b/jdk/test/java/lang/String/CompactString/EndsWith.java
new file mode 100644
index 0000000..07485b4
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/EndsWith.java
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.endsWith.
+ * @run testng/othervm -XX:+CompactStrings EndsWith
+ * @run testng/othervm -XX:-CompactStrings EndsWith
+ */
+
+public class EndsWith extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] { new Object[] { STRING_EMPTY, "", true },
+                new Object[] { STRING_EMPTY, "A", false },
+                new Object[] { STRING_L1, "A", true },
+                new Object[] { STRING_L1, "", true },
+                new Object[] { STRING_L1, " ", false },
+                new Object[] { STRING_L2, "AB", true },
+                new Object[] { STRING_L2, "B", true },
+                new Object[] { STRING_L2, "", true },
+                new Object[] { STRING_L2, "A", false },
+                new Object[] { STRING_L4, "ABCD", true },
+                new Object[] { STRING_L4, "CD", true },
+                new Object[] { STRING_L4, "D", true },
+                new Object[] { STRING_L4, "", true },
+                new Object[] { STRING_L4, "BC", false },
+                new Object[] { STRING_LLONG, "ABCDEFGH", true },
+                new Object[] { STRING_LLONG, "EFGH", true },
+                new Object[] { STRING_LLONG, "", true },
+                new Object[] { STRING_LLONG, "CDEF", false },
+                new Object[] { STRING_LLONG, "\uFF28", false },
+                new Object[] { STRING_U1, "\uFF21", true },
+                new Object[] { STRING_U1, "", true },
+                new Object[] { STRING_U1, "\uFF22", false },
+                new Object[] { STRING_U1, "B", false },
+                new Object[] { STRING_U2, "\uFF21\uFF22", true },
+                new Object[] { STRING_U2, "\uFF22", true },
+                new Object[] { STRING_U2, "", true },
+                new Object[] { STRING_U2, "\uFF21", false },
+                new Object[] { STRING_M12, "\uFF21A", true },
+                new Object[] { STRING_M12, "A", true },
+                new Object[] { STRING_M12, "", true },
+                new Object[] { STRING_M12, "AA", false },
+                new Object[] { STRING_M11, "A\uFF21", true },
+                new Object[] { STRING_M11, "\uFF21", true },
+                new Object[] { STRING_M11, "", true },
+                new Object[] { STRING_M11, "\uFF21\uFF21", false }, };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testEndsWith(String str, String suffix, boolean expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertEquals(
+                                    data.endsWith(suffix),
+                                    expected,
+                                    String.format(
+                                            "testing String(%s).endsWith(%s), source : %s, ",
+                                            escapeNonASCIIs(data),
+                                            escapeNonASCIIs(suffix), source));
+                        });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/Equals.java b/jdk/test/java/lang/String/CompactString/Equals.java
new file mode 100644
index 0000000..101a015
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/Equals.java
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.equals.
+ * @run testng/othervm -XX:+CompactStrings Equals
+ * @run testng/othervm -XX:-CompactStrings Equals
+ */
+
+public class Equals extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+                new Object[] { STRING_EMPTY, "", true },
+                new Object[] { STRING_EMPTY, "A", false },
+                new Object[] { STRING_EMPTY, new StringBuffer(""), false },
+                new Object[] { STRING_L1, "A", true },
+                new Object[] { STRING_L1, "", false },
+                new Object[] { STRING_L1, new StringBuffer("A"), false },
+                new Object[] { STRING_L2, "AB", true },
+                new Object[] { STRING_L2, "", false },
+                new Object[] { STRING_L2, new StringBuilder("AB"), false },
+                new Object[] { STRING_L4, "ABCD", true },
+                new Object[] { STRING_L4, "abc", false },
+                new Object[] { STRING_L4, "", false },
+                new Object[] { STRING_LLONG, "ABCDEFGH", true },
+                new Object[] { STRING_LLONG, "ABCDEFG", false },
+                new Object[] { STRING_LLONG, new StringBuilder("ABCDEFGH"),
+                        false },
+                new Object[] { STRING_U1, "\uFF21", true },
+                new Object[] { STRING_U1, "", false },
+                new Object[] { STRING_U2, "\uFF21\uFF22", true },
+                new Object[] { STRING_U2, "\uFF21", false },
+                new Object[] { STRING_U2, "", false },
+                new Object[] { STRING_U2, new StringBuilder("\uFF21\uFF22"),
+                        false },
+                new Object[] { STRING_M12, "\uFF21A", true },
+                new Object[] { STRING_M12, "A\uFF21", false },
+                new Object[] { STRING_M11, "A\uFF21", true },
+                new Object[] { STRING_M11, new StringBuilder("\uFF21A"), false }, };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testEquals(String str, Object obj, boolean expected) {
+        map.get(str).forEach(
+                (source, data) -> {
+                    assertEquals(data.equals(obj), expected, String.format(
+                            "testing String(%s).equals(%s), source : %s, ",
+                            escapeNonASCIIs(data),
+                            escapeNonASCIIs(obj.toString()), source));
+                });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/EqualsIgnoreCase.java b/jdk/test/java/lang/String/CompactString/EqualsIgnoreCase.java
new file mode 100644
index 0000000..0721c07
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/EqualsIgnoreCase.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.equalsIgnoreCase.
+ * @run testng/othervm -XX:+CompactStrings EqualsIgnoreCase
+ * @run testng/othervm -XX:-CompactStrings EqualsIgnoreCase
+ */
+
+public class EqualsIgnoreCase extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+
+        new Object[] { STRING_EMPTY, "", true },
+                new Object[] { STRING_L1, "a", true },
+                new Object[] { STRING_L2, "aB", true },
+                new Object[] { STRING_L4, "AbCd", true },
+                new Object[] { STRING_LLONG, "aBcDeFgH", true },
+                new Object[] { STRING_U1, "\uFF41", true },
+                new Object[] { STRING_U1, "\uFF21", true },
+                new Object[] { STRING_U2, "\uFF41\uFF42", true },
+                new Object[] { STRING_U2, "\uFF41\uFF22", true },
+                new Object[] { STRING_U2, "\uFF21\uFF42", true },
+                new Object[] { STRING_M12, "\uFF41a", true },
+                new Object[] { STRING_M12, "\uFF21A", true },
+                new Object[] { STRING_M11, "a\uFF41", true },
+                new Object[] { STRING_M11, "A\uFF21", true },
+
+        };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testEqualsIgnoreCase(String str, String anotherString,
+            boolean expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertEquals(
+                                    data.equalsIgnoreCase(anotherString),
+                                    expected,
+                                    String.format(
+                                            "testing String(%s).equalsIgnoreCase(%s), source : %s, ",
+                                            escapeNonASCIIs(data),
+                                            escapeNonASCIIs(anotherString),
+                                            source));
+                        });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/GetChars.java b/jdk/test/java/lang/String/CompactString/GetChars.java
new file mode 100644
index 0000000..3ea3dce
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/GetChars.java
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.Arrays;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertTrue;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.getChars.
+ * @run testng/othervm -XX:+CompactStrings GetChars
+ * @run testng/othervm -XX:-CompactStrings GetChars
+ */
+
+public class GetChars extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+
+                new Object[] { STRING_EMPTY, 0, STRING_EMPTY.length(),
+                        new char[STRING_EMPTY.length()], 0, CHAR_ARRAY_EMPTY },
+                new Object[] { STRING_L1, 0, STRING_L1.length(),
+                        new char[STRING_L1.length()], 0, CHAR_ARRAY_L1 },
+                new Object[] { STRING_L2, 0, STRING_L2.length(),
+                        new char[STRING_L2.length()], 0, CHAR_ARRAY_L2 },
+                new Object[] { STRING_L4, 0, STRING_L4.length(),
+                        new char[STRING_L4.length()], 0, CHAR_ARRAY_L4 },
+                new Object[] { STRING_LLONG, 0, STRING_LLONG.length(),
+                        new char[STRING_LLONG.length()], 0, CHAR_ARRAY_LLONG },
+                new Object[] { STRING_U1, 0, STRING_U1.length(),
+                        new char[STRING_U1.length()], 0, CHAR_ARRAY_U1 },
+                new Object[] { STRING_U2, 0, STRING_U2.length(),
+                        new char[STRING_U2.length()], 0, CHAR_ARRAY_U2 },
+                new Object[] { STRING_M12, 0, STRING_M12.length(),
+                        new char[STRING_M12.length()], 0, CHAR_ARRAY_M12 },
+                new Object[] { STRING_M11, 0, STRING_M11.length(),
+                        new char[STRING_M11.length()], 0, CHAR_ARRAY_M11 },
+                new Object[] { STRING_UDUPLICATE, 0,
+                        STRING_UDUPLICATE.length(),
+                        new char[STRING_UDUPLICATE.length()], 0,
+                        CHAR_ARRAY_UDUPLICATE },
+                new Object[] { STRING_MDUPLICATE1, 0,
+                        STRING_MDUPLICATE1.length(),
+                        new char[STRING_MDUPLICATE1.length()], 0,
+                        CHAR_ARRAY_MDUPLICATE1 }, };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testGetChars(String str, int srcBegin, int srcEnd, char[] dst,
+            int dstBegin, char[] expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            data.getChars(srcBegin, srcEnd, dst, dstBegin);
+                            assertTrue(
+                                    Arrays.equals(dst, expected),
+                                    String.format(
+                                            "testing String(%s).getChars(%d, %d, %s, %d), source : %s, ",
+                                            escapeNonASCIIs(data), srcBegin,
+                                            srcEnd, escapeNonASCIIs(Arrays
+                                                    .toString(dst)), dstBegin,
+                                            source));
+                        });
+    }
+
+}
diff --git a/jdk/test/java/lang/String/CompactString/IndexOf.java b/jdk/test/java/lang/String/CompactString/IndexOf.java
new file mode 100644
index 0000000..49fd9e5
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/IndexOf.java
@@ -0,0 +1,249 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.indexOf.
+ * @run testng/othervm -XX:+CompactStrings IndexOf
+ * @run testng/othervm -XX:-CompactStrings IndexOf
+ */
+
+public class IndexOf extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+
+                new Object[] { STRING_EMPTY, (int) 'A', -1 },
+                new Object[] { STRING_L1, (int) 'A', 0 },
+                new Object[] { STRING_L2, (int) 'A', 0 },
+                new Object[] { STRING_L2, (int) 'B', 1 },
+                new Object[] { STRING_L4, (int) 'A', 0 },
+                new Object[] { STRING_L4, (int) 'D', 3 },
+                new Object[] { STRING_L4, (int) 'E', -1 },
+                new Object[] { STRING_LLONG, (int) 'A', 0 },
+                new Object[] { STRING_LLONG, (int) 'H', 7 },
+                new Object[] { STRING_U1, (int) '\uFF21', 0 },
+                new Object[] { STRING_U1, (int) 'A', -1 },
+                new Object[] { STRING_U2, (int) '\uFF21', 0 },
+                new Object[] { STRING_U2, (int) '\uFF22', 1 },
+                new Object[] { STRING_M12, (int) '\uFF21', 0 },
+                new Object[] { STRING_M12, (int) 'A', 1 },
+                new Object[] { STRING_M11, (int) 'A', 0 },
+                new Object[] { STRING_M11, (int) '\uFF21', 1 },
+                new Object[] { STRING_UDUPLICATE, (int) '\uFF21', 0 },
+                new Object[] { STRING_UDUPLICATE, (int) '\uFF22', 1 },
+                new Object[] { STRING_SUPPLEMENTARY, 'A', 5 },
+                new Object[] { STRING_SUPPLEMENTARY, '\uFF21', 4 },
+                new Object[] { STRING_SUPPLEMENTARY,
+                        Character.toCodePoint('\uD801', '\uDC00'), 0 },
+                new Object[] { STRING_SUPPLEMENTARY,
+                        Character.toCodePoint('\uD801', '\uDC01'), 2 }, };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testIndexOf(String str, int ch, int expected) {
+        map.get(str).forEach(
+                (source, data) -> {
+                    assertEquals(data.indexOf(ch), expected, String.format(
+                            "testing String(%s).indexOf(%d), source : %s, ",
+                            escapeNonASCIIs(data), ch, source));
+                });
+    }
+
+    @DataProvider
+    public Object[][] provider2() {
+        return new Object[][] {
+
+        new Object[] { STRING_EMPTY, (int) 'A', 0, -1 },
+                new Object[] { STRING_L1, (int) 'A', 0, 0 },
+                new Object[] { STRING_L1, (int) 'A', 1, -1 },
+                new Object[] { STRING_L1, (int) 'B', 0, -1 },
+                new Object[] { STRING_L2, (int) 'A', 0, 0 },
+                new Object[] { STRING_L2, (int) 'A', 1, -1 },
+                new Object[] { STRING_L2, (int) 'B', 0, 1 },
+                new Object[] { STRING_L2, (int) 'B', 1, 1 },
+                new Object[] { STRING_L4, (int) 'A', 0, 0 },
+                new Object[] { STRING_L4, (int) 'D', 2, 3 },
+                new Object[] { STRING_L4, (int) 'B', 2, -1 },
+                new Object[] { STRING_LLONG, (int) 'A', 0, 0 },
+                new Object[] { STRING_LLONG, (int) 'H', 5, 7 },
+                new Object[] { STRING_U1, (int) '\uFF21', 0, 0 },
+                new Object[] { STRING_U1, (int) 'A', 0, -1 },
+                new Object[] { STRING_U2, (int) '\uFF21', 0, 0 },
+                new Object[] { STRING_U2, (int) '\uFF22', 0, 1 },
+                new Object[] { STRING_M12, (int) '\uFF21', 0, 0 },
+                new Object[] { STRING_M12, (int) 'A', 1, 1 },
+                new Object[] { STRING_M11, (int) 'A', 0, 0 },
+                new Object[] { STRING_M11, (int) '\uFF21', 1, 1 },
+                new Object[] { STRING_UDUPLICATE, (int) '\uFF21', 1, 2 },
+                new Object[] { STRING_UDUPLICATE, (int) '\uFF22', 1, 1 }, };
+    }
+
+    @Test(dataProvider = "provider2")
+    public void testIndexOf(String str, int ch, int fromIndex, int expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertEquals(
+                                    data.indexOf(ch, fromIndex),
+                                    expected,
+                                    String.format(
+                                            "testing String(%s).indexOf(%d, %d), source : %s, ",
+                                            escapeNonASCIIs(data), ch,
+                                            fromIndex, source));
+                        });
+    }
+
+    @DataProvider
+    public Object[][] provider3() {
+        return new Object[][] {
+
+                new Object[] { STRING_EMPTY, "A", -1 },
+                new Object[] { STRING_L1, "A", 0 },
+                new Object[] { STRING_L1, "AB", -1 },
+                new Object[] { STRING_L2, "A", 0 },
+                new Object[] { STRING_L2, "B", 1 },
+                new Object[] { STRING_L2, "AB", 0 },
+                new Object[] { STRING_L2, "AC", -1 },
+                new Object[] { STRING_L2, "ABC", -1 },
+                new Object[] { STRING_L4, "ABCD", 0 },
+                new Object[] { STRING_L4, "D", 3 },
+                new Object[] { STRING_LLONG, "ABCDEFGH", 0 },
+                new Object[] { STRING_LLONG, "EFGH", 4 },
+                new Object[] { STRING_LLONG, "EFGHI", -1 },
+                new Object[] { STRING_U1, "\uFF21", 0 },
+                new Object[] { STRING_U1, "\uFF21A", -1 },
+                new Object[] { STRING_U2, "\uFF21\uFF22", 0 },
+                new Object[] { STRING_U2, "\uFF22", 1 },
+                new Object[] { STRING_U2, "A\uFF22", -1 },
+                new Object[] { STRING_M12, "\uFF21A", 0 },
+                new Object[] { STRING_M12, "A", 1 },
+                new Object[] { STRING_M12, "\uFF21\uFF21", -1 },
+                new Object[] { STRING_M11, "A\uFF21", 0 },
+                new Object[] { STRING_M11, "\uFF21", 1 },
+                new Object[] { STRING_M11, "A", 0 },
+                new Object[] {
+                        STRING_UDUPLICATE,
+                        "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22",
+                        0 },
+                new Object[] { STRING_UDUPLICATE,
+                        "\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21", 1 },
+                new Object[] {
+                        STRING_UDUPLICATE,
+                        "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21",
+                        -1 }, };
+    }
+
+    @Test(dataProvider = "provider3")
+    public void testIndexOf(String str, String anotherString, int expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertEquals(
+                                    data.indexOf(anotherString),
+                                    expected,
+                                    String.format(
+                                            "testing String(%s).indexOf(%s), source : %s, ",
+                                            escapeNonASCIIs(data),
+                                            escapeNonASCIIs(anotherString),
+                                            source));
+                        });
+    }
+
+    @DataProvider
+    public Object[][] provider4() {
+        return new Object[][] {
+
+                new Object[] { STRING_EMPTY, "A", 0, -1 },
+                new Object[] { STRING_L1, "A", 0, 0 },
+                new Object[] { STRING_L1, "A", 1, -1 },
+                new Object[] { STRING_L1, "AB", 0, -1 },
+                new Object[] { STRING_L2, "A", 0, 0 },
+                new Object[] { STRING_L2, "B", 0, 1 },
+                new Object[] { STRING_L2, "AB", 0, 0 },
+                new Object[] { STRING_L2, "AB", 1, -1 },
+                new Object[] { STRING_L4, "ABCD", 0, 0 },
+                new Object[] { STRING_L4, "BC", 0, 1 },
+                new Object[] { STRING_L4, "A", 0, 0 },
+                new Object[] { STRING_L4, "CD", 0, 2 },
+                new Object[] { STRING_L4, "A", 2, -1 },
+                new Object[] { STRING_L4, "ABCDE", 0, -1 },
+                new Object[] { STRING_LLONG, "ABCDEFGH", 0, 0 },
+                new Object[] { STRING_LLONG, "DEFGH", 0, 3 },
+                new Object[] { STRING_LLONG, "A", 0, 0 },
+                new Object[] { STRING_LLONG, "GHI", 0, -1 },
+                new Object[] { STRING_U1, "\uFF21", 0, 0 },
+                new Object[] { STRING_U1, "\uFF21A", 0, -1 },
+                new Object[] { STRING_U2, "\uFF21\uFF22", 0, 0 },
+                new Object[] { STRING_U2, "\uFF22", 0, 1 },
+                new Object[] { STRING_U2, "\uFF21", 1, -1 },
+                new Object[] { STRING_M12, "\uFF21A", 0, 0 },
+                new Object[] { STRING_M12, "A", 1, 1 },
+                new Object[] { STRING_M12, "\uFF21A", 1, -1 },
+                new Object[] { STRING_M12, "\uFF21", 0, 0 },
+                new Object[] { STRING_M11, "A\uFF21", 0, 0 },
+                new Object[] { STRING_M11, "\uFF21", 1, 1 },
+                new Object[] { STRING_M11, "A\uFF21", 1, -1 },
+                new Object[] { STRING_M11, "A\uFF21A", 0, -1 },
+                new Object[] {
+                        STRING_UDUPLICATE,
+                        "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22",
+                        0, 0 },
+                new Object[] {
+                        STRING_UDUPLICATE,
+                        "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22",
+                        1, -1 },
+                new Object[] {
+                        STRING_UDUPLICATE,
+                        "\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22",
+                        1, 1 },
+                new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22\uFF21\uFF22",
+                        4, 4 },
+                new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22\uFF21\uFF22",
+                        7, -1 }, };
+    }
+
+    @Test(dataProvider = "provider4")
+    public void testIndexOf(String str, String anotherString, int fromIndex,
+            int expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertEquals(
+                                    data.indexOf(anotherString, fromIndex),
+                                    expected,
+                                    String.format(
+                                            "testing String(%s).indexOf(%s), source : %s, ",
+                                            escapeNonASCIIs(data),
+                                            escapeNonASCIIs(anotherString),
+                                            fromIndex, source));
+                        });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/Intern.java b/jdk/test/java/lang/String/CompactString/Intern.java
new file mode 100644
index 0000000..fc2b06d
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/Intern.java
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertTrue;
+
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.intern.
+ * @run testng/othervm -XX:+CompactStrings Intern
+ * @run testng/othervm -XX:-CompactStrings Intern
+ */
+
+public class Intern extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+
+                new Object[] { STRING_EMPTY, "" },
+                new Object[] { STRING_L1, "A" },
+                new Object[] { STRING_LLONG, "ABCDEFGH" },
+                new Object[] { STRING_U1, "\uFF21" },
+                new Object[] { STRING_U2, "\uFF21\uFF22" },
+                new Object[] { STRING_M12, "\uFF21A" },
+                new Object[] { STRING_M11, "A\uFF21" },
+                new Object[] { STRING_MDUPLICATE1,
+                        "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A" }, };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testIntern(String str, String expected) {
+        map.get(str).forEach(
+                (source, data) -> {
+                    assertTrue(data.intern() == expected, String.format(
+                            "testing String(%s).intern(), source : %s, ",
+                            escapeNonASCIIs(data), source));
+                });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/LastIndexOf.java b/jdk/test/java/lang/String/CompactString/LastIndexOf.java
new file mode 100644
index 0000000..eccc9cc
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/LastIndexOf.java
@@ -0,0 +1,225 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.lastIndexOf.
+ * @run testng/othervm -XX:+CompactStrings LastIndexOf
+ * @run testng/othervm -XX:-CompactStrings LastIndexOf
+ */
+
+public class LastIndexOf extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+
+                new Object[] { STRING_EMPTY, (int) 'A', -1 },
+                new Object[] { STRING_L1, (int) 'A', 0 },
+                new Object[] { STRING_L2, (int) 'A', 0 },
+                new Object[] { STRING_L2, (int) 'B', 1 },
+                new Object[] { STRING_L4, (int) 'A', 0 },
+                new Object[] { STRING_L4, (int) 'D', 3 },
+                new Object[] { STRING_LLONG, (int) 'A', 0 },
+                new Object[] { STRING_LLONG, (int) 'H', 7 },
+                new Object[] { STRING_U1, (int) '\uFF21', 0 },
+                new Object[] { STRING_U1, (int) 'B', -1 },
+                new Object[] { STRING_U2, (int) '\uFF21', 0 },
+                new Object[] { STRING_U2, (int) '\uFF22', 1 },
+                new Object[] { STRING_M12, (int) '\uFF21', 0 },
+                new Object[] { STRING_M12, (int) 'A', 1 },
+                new Object[] { STRING_M11, (int) 'A', 0 },
+                new Object[] { STRING_M11, (int) '\uFF21', 1 },
+                new Object[] { STRING_UDUPLICATE, (int) '\uFF22', 9 },
+                new Object[] { STRING_UDUPLICATE, (int) '\uFF21', 8 },
+                new Object[] { STRING_SUPPLEMENTARY,
+                        Character.toCodePoint('\uD801', '\uDC01'), 2 }, };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testLastIndexOf(String str, int ch, int expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertEquals(
+                                    data.lastIndexOf(ch),
+                                    expected,
+                                    String.format(
+                                            "testing String(%s).lastIndexOf(%d), source : %s, ",
+                                            escapeNonASCIIs(data), ch, source));
+                        });
+    }
+
+    @DataProvider
+    public Object[][] provider2() {
+        return new Object[][] {
+
+        new Object[] { STRING_EMPTY, (int) 'A', 0, -1 },
+                new Object[] { STRING_L1, (int) 'A', 0, 0 },
+                new Object[] { STRING_L1, (int) 'A', 1, 0 },
+                new Object[] { STRING_L2, (int) 'A', 0, 0 },
+                new Object[] { STRING_L2, (int) 'B', 1, 1 },
+                new Object[] { STRING_L2, (int) 'B', 2, 1 },
+                new Object[] { STRING_L4, (int) 'A', 0, 0 },
+                new Object[] { STRING_L4, (int) 'C', 2, 2 },
+                new Object[] { STRING_L4, (int) 'C', 1, -1 },
+                new Object[] { STRING_LLONG, (int) 'A', 0, 0 },
+                new Object[] { STRING_LLONG, (int) 'H', 7, 7 },
+                new Object[] { STRING_LLONG, (int) 'H', 6, -1 },
+                new Object[] { STRING_U1, (int) '\uFF21', 0, 0 },
+                new Object[] { STRING_U1, (int) '\uFF21', 7, 0 },
+                new Object[] { STRING_U2, (int) '\uFF21', 0, 0 },
+                new Object[] { STRING_U2, (int) '\uFF22', 0, -1 },
+                new Object[] { STRING_M12, (int) '\uFF21', 0, 0 },
+                new Object[] { STRING_M12, (int) 'A', 1, 1 },
+                new Object[] { STRING_M12, (int) 'A', 0, -1 },
+                new Object[] { STRING_M11, (int) 'A', 0, 0 },
+                new Object[] { STRING_M11, (int) '\uFF21', 1, 1 },
+                new Object[] { STRING_M11, (int) '\uFF21', 0, -1 },
+                new Object[] { STRING_UDUPLICATE, (int) '\uFF21', 5, 4 },
+                new Object[] { STRING_UDUPLICATE, (int) '\uFF21', 6, 6 },
+                new Object[] { STRING_UDUPLICATE, (int) '\uFF22', 5, 5 },
+                new Object[] { STRING_UDUPLICATE, (int) '\uFF22', 6, 5 }, };
+    }
+
+    @Test(dataProvider = "provider2")
+    public void testLastIndexOf(String str, int ch, int fromIndex, int expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertEquals(
+                                    data.lastIndexOf(ch, fromIndex),
+                                    expected,
+                                    String.format(
+                                            "testing String(%s).lastIndexOf(%d, %d), source : %s, ",
+                                            escapeNonASCIIs(data), ch,
+                                            fromIndex, source));
+                        });
+    }
+
+    @DataProvider
+    public Object[][] provider3() {
+        return new Object[][] {
+
+                new Object[] { STRING_EMPTY, "A", -1 },
+                new Object[] { STRING_L1, "A", 0 },
+                new Object[] { STRING_L1, "AB", -1 },
+
+                new Object[] { STRING_L2, "AB", 0 },
+                new Object[] { STRING_L2, "B", 1 },
+                new Object[] { STRING_L4, "ABCD", 0 },
+                new Object[] { STRING_L4, "B", 1 },
+                new Object[] { STRING_LLONG, "ABCD", 0 },
+                new Object[] { STRING_LLONG, "GH", 6 },
+                new Object[] { STRING_U1, "\uFF21", 0 },
+                new Object[] { STRING_U1, "\uFF22", -1 },
+                new Object[] { STRING_U2, "\uFF21\uFF22", 0 },
+                new Object[] { STRING_U2, "\uFF22", 1 },
+                new Object[] { STRING_M12, "\uFF21A", 0 },
+                new Object[] { STRING_M12, "A", 1 },
+                new Object[] { STRING_M11, "A\uFF21", 0 },
+                new Object[] { STRING_M11, "\uFF21", 1 },
+                new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22\uFF21\uFF22", 6 },
+                new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22", 8 }, };
+    }
+
+    @Test(dataProvider = "provider3")
+    public void testLastIndexOf(String str, String anotherString, int expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertEquals(
+                                    data.lastIndexOf(anotherString),
+                                    expected,
+                                    String.format(
+                                            "testing String(%s).lastIndexOf(%s), source : %s, ",
+                                            escapeNonASCIIs(data),
+                                            escapeNonASCIIs(anotherString),
+                                            source));
+                        });
+    }
+
+    @DataProvider
+    public Object[][] provider4() {
+        return new Object[][] {
+
+                new Object[] { STRING_EMPTY, "A", 0, -1 },
+                new Object[] { STRING_L2, "AB", 0, 0 },
+
+                new Object[] { STRING_L1, "AB", -1, -1 },
+
+                new Object[] { STRING_L2, "B", 1, 1 },
+                new Object[] { STRING_L2, "B", 0, -1 },
+                new Object[] { STRING_L4, "ABC", 3, 0 },
+                new Object[] { STRING_L4, "ABC", 0, 0 },
+                new Object[] { STRING_L4, "ABC", 1, 0 },
+                new Object[] { STRING_L4, "BC", 1, 1 },
+                new Object[] { STRING_L4, "BC", 0, -1 },
+                new Object[] { STRING_LLONG, "ABCDEFGH", 0, 0 },
+                new Object[] { STRING_LLONG, "EFGH", 7, 4 },
+                new Object[] { STRING_LLONG, "EFGH", 3, -1 },
+                new Object[] { STRING_U1, "\uFF21", 0, 0 },
+                new Object[] { STRING_U1, "\uFF21", 7, 0 },
+                new Object[] { STRING_U2, "\uFF21\uFF22", 0, 0 },
+                new Object[] { STRING_U2, "\uFF21\uFF22", 1, 0 },
+                new Object[] { STRING_M12, "\uFF21A", 0, 0 },
+                new Object[] { STRING_M12, "A", 1, 1 },
+                new Object[] { STRING_M12, "A", 0, -1 },
+                new Object[] { STRING_M11, "A\uFF21", 0, 0 },
+                new Object[] { STRING_M11, "A\uFF21", 1, 0 },
+                new Object[] { STRING_M11, "\uFF21", 0, -1 },
+                new Object[] {
+                        STRING_UDUPLICATE,
+                        "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22",
+                        9, 0 },
+                new Object[] {
+                        STRING_UDUPLICATE,
+                        "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22",
+                        0, 0 },
+                new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22", 6, 6 },
+                new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22\uFF21", 6, 6 }, };
+    }
+
+    @Test(dataProvider = "provider4")
+    public void testLastIndexOf(String str, String anotherString,
+            int fromIndex, int expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertEquals(
+                                    data.lastIndexOf(anotherString, fromIndex),
+                                    expected,
+                                    String.format(
+                                            "testing String(%s).lastIndexOf(%s, %d), source : %s, ",
+                                            escapeNonASCIIs(data),
+                                            escapeNonASCIIs(anotherString),
+                                            fromIndex, source));
+                        });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/Length.java b/jdk/test/java/lang/String/CompactString/Length.java
new file mode 100644
index 0000000..ab2b9d5
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/Length.java
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.length.
+ * @run testng/othervm -XX:+CompactStrings Length
+ * @run testng/othervm -XX:-CompactStrings Length
+ */
+
+public class Length extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+
+        new Object[] { STRING_EMPTY, 0 }, new Object[] { STRING_L1, 1 },
+                new Object[] { STRING_L2, 2 },
+                new Object[] { STRING_LLONG, 8 },
+                new Object[] { STRING_U1, 1 }, new Object[] { STRING_U2, 2 },
+                new Object[] { STRING_M12, 2 }, new Object[] { STRING_M11, 2 },
+                new Object[] { STRING_UDUPLICATE, 10 },
+                new Object[] { STRING_SUPPLEMENTARY, 6 }, };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testLength(String str, int expected) {
+        map.get(str).forEach(
+                (source, data) -> {
+                    assertEquals(data.length(), expected, String.format(
+                            "testing String(%s).length(), source : %s, ",
+                            escapeNonASCIIs(data), source));
+                });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/Numbers.java b/jdk/test/java/lang/String/CompactString/Numbers.java
new file mode 100644
index 0000000..ad9f17a
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/Numbers.java
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is testing
+ *          Integer/Long's methods related to String.
+ * @run testng/othervm -XX:+CompactStrings Numbers
+ * @run testng/othervm -XX:-CompactStrings Numbers
+ */
+
+public class Numbers {
+
+    /*
+     * Data provider for testIntegerLong
+     *
+     * @return input parameter for testIntegerLong
+     */
+    @DataProvider
+    public Object[][] numbers() {
+        return new Object[][] {
+                { Integer.toBinaryString(Integer.MAX_VALUE),
+                        "1111111111111111111111111111111" },
+                { Integer.toBinaryString(Integer.MIN_VALUE),
+                        "10000000000000000000000000000000" },
+                { Integer.toBinaryString(7), "111" },
+                { Integer.toBinaryString(0), "0" },
+                { Integer.toOctalString(Integer.MAX_VALUE), "17777777777" },
+                { Integer.toOctalString(Integer.MIN_VALUE), "20000000000" },
+                { Integer.toOctalString(9), "11" },
+                { Integer.toOctalString(0), "0" },
+                { Integer.toHexString(Integer.MAX_VALUE), "7fffffff" },
+                { Integer.toHexString(Integer.MIN_VALUE), "80000000" },
+                { Integer.toHexString(17), "11" },
+                { Integer.toHexString(0), "0" },
+                { Integer.toString(Integer.MAX_VALUE, 2),
+                        "1111111111111111111111111111111" },
+                { Integer.toString(Integer.MIN_VALUE, 2),
+                        "-10000000000000000000000000000000" },
+                { Integer.toString(7, 2), "111" },
+                { Integer.toString(0, 2), "0" },
+                { Integer.toString(Integer.MAX_VALUE, 8), "17777777777" },
+                { Integer.toString(Integer.MIN_VALUE, 8), "-20000000000" },
+                { Integer.toString(9, 8), "11" },
+                { Integer.toString(Integer.MAX_VALUE, 16), "7fffffff" },
+                { Integer.toString(Integer.MIN_VALUE, 16), "-80000000" },
+                { Integer.toString(17, 16), "11" },
+                { Long.toBinaryString(Long.MAX_VALUE),
+                        "111111111111111111111111111111111111111111111111111111111111111" },
+                { Long.toBinaryString(Long.MIN_VALUE),
+                        "1000000000000000000000000000000000000000000000000000000000000000" },
+                { Long.toOctalString(Long.MAX_VALUE), "777777777777777777777" },
+                { Long.toOctalString(Long.MIN_VALUE), "1000000000000000000000" },
+                { Long.toHexString(Long.MAX_VALUE), "7fffffffffffffff" },
+                { Long.toHexString(Long.MIN_VALUE), "8000000000000000" },
+                { Long.toString(Long.MAX_VALUE, 2),
+                        "111111111111111111111111111111111111111111111111111111111111111" },
+                { Long.toString(Long.MIN_VALUE, 2),
+                        "-1000000000000000000000000000000000000000000000000000000000000000" },
+                { Long.toString(Long.MAX_VALUE, 8), "777777777777777777777" },
+                { Long.toString(Long.MIN_VALUE, 8), "-1000000000000000000000" },
+                { Long.toString(Long.MAX_VALUE, 16), "7fffffffffffffff" },
+                { Long.toString(Long.MIN_VALUE, 16), "-8000000000000000" } };
+    }
+
+    /*
+     * test Integer/Long's methods related to String.
+     *
+     * @param res
+     *            real result
+     * @param expected
+     *            expected result
+     */
+    @Test(dataProvider = "numbers")
+    public void testIntegerLong(String res, String expected) {
+        assertEquals(res, expected);
+    }
+
+}
diff --git a/jdk/test/java/lang/String/CompactString/OffsetByCodePoints.java b/jdk/test/java/lang/String/CompactString/OffsetByCodePoints.java
new file mode 100644
index 0000000..44ed607
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/OffsetByCodePoints.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.offsetByCodePoints.
+ * @run testng/othervm -XX:+CompactStrings OffsetByCodePoints
+ * @run testng/othervm -XX:-CompactStrings OffsetByCodePoints
+ */
+
+public class OffsetByCodePoints extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+
+        new Object[] { STRING_SUPPLEMENTARY, 0, 1, 2 },
+                new Object[] { STRING_SUPPLEMENTARY, 0, 3, 5 },
+                new Object[] { STRING_SUPPLEMENTARY, 1, 1, 2 },
+                new Object[] { STRING_SUPPLEMENTARY, 1, 3, 5 },
+                new Object[] { STRING_SUPPLEMENTARY, 2, 1, 4 },
+                new Object[] { STRING_SUPPLEMENTARY, 2, 2, 5 },
+                new Object[] { STRING_SUPPLEMENTARY, 2, 3, 6 },
+                new Object[] { STRING_SUPPLEMENTARY, 3, 1, 4 },
+                new Object[] { STRING_SUPPLEMENTARY, 3, 2, 5 },
+                new Object[] { STRING_SUPPLEMENTARY, 3, 3, 6 }, };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testOffsetByCodePoints(String str, int index,
+            int codePointOffset, int expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertEquals(
+                                    data.offsetByCodePoints(index,
+                                            codePointOffset),
+                                    expected,
+                                    String.format(
+                                            "testing String(%s).offsetByCodePoints(%d, %d), source : %s, ",
+                                            escapeNonASCIIs(data), index,
+                                            codePointOffset, source));
+                        });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/RegionMatches.java b/jdk/test/java/lang/String/CompactString/RegionMatches.java
new file mode 100644
index 0000000..5301c0e
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/RegionMatches.java
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.regionMatches.
+ * @run testng/othervm -XX:+CompactStrings RegionMatches
+ * @run testng/othervm -XX:-CompactStrings RegionMatches
+ */
+
+public class RegionMatches extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+
+                new Object[] { STRING_EMPTY, true, 0, "", 0, 0, true },
+                new Object[] { STRING_EMPTY, true, 0, "", 0, 1, false },
+                new Object[] { STRING_EMPTY, true, 0, "A", 0, 0, true },
+                new Object[] { STRING_EMPTY, true, 0, "", 0, 0, true },
+                new Object[] { STRING_EMPTY, true, 0, "", 0, 1, false },
+                new Object[] { STRING_L1, false, 0, "a", 0, 1, false },
+                new Object[] { STRING_L1, false, 0, "BA", 1, 1, true },
+                new Object[] { STRING_L1, false, 0, "Ba", 1, 1, false },
+                new Object[] { STRING_L1, true, 0, "a", 0, 1, true },
+                new Object[] { STRING_L1, true, 0, "BA", 1, 1, true },
+                new Object[] { STRING_L1, true, 0, "Ba", 1, 1, true },
+                new Object[] { STRING_L2, true, 1, "b", 0, 1, true },
+                new Object[] { STRING_L2, true, 1, "B", 0, 1, true },
+                new Object[] { STRING_L2, true, 0, "xaBc", 1, 2, true },
+                new Object[] { STRING_L2, false, 0, "AB", 0, 2, true },
+                new Object[] { STRING_L2, false, 0, "Ab", 0, 2, false },
+                new Object[] { STRING_L2, false, 1, "BAB", 2, 1, true },
+                new Object[] { STRING_LLONG, true, 1, "bCdEF", 0, 5, true },
+                new Object[] { STRING_LLONG, false, 2, "CDEFG", 0, 5, true },
+                new Object[] { STRING_LLONG, true, 2, "CDEFg", 0, 5, true },
+                new Object[] { STRING_U1, true, 0, "\uFF41", 0, 1, true },
+                new Object[] { STRING_U1, false, 0, "\uFF41", 0, 1, false },
+                new Object[] { STRING_MDUPLICATE1, true, 0, "\uFF41a\uFF41", 0,
+                        3, true },
+                new Object[] { STRING_MDUPLICATE1, false, 0, "\uFF21a\uFF21",
+                        0, 3, false },
+                new Object[] { STRING_SUPPLEMENTARY, true, 1, "\uDC00\uD801",
+                        0, 2, true },
+                new Object[] { STRING_SUPPLEMENTARY, true, 4, "\uFF21", 0, 1,
+                        true },
+                new Object[] { STRING_SUPPLEMENTARY, true, 5, "A", 0, 1, true },
+                new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, false, 0,
+                        "\uD801\uDC28\uD801\uDC29", 0, 4, true },
+                new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, true, 1,
+                        "\uDC28\uD801", 0, 2, true },
+                new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, true, 1,
+                        "\uDC00\uD801", 0, 2, false },
+                new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, true, 4,
+                        "\uFF21", 0, 1, true },
+                new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, false, 4,
+                        "\uFF21", 0, 1, false },
+                new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, false, 4,
+                        "\uFF41", 0, 1, true }, };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testRegionMatches(String str, boolean ignoreCase, int toffset,
+            String other, int ooffset, int len, boolean expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertEquals(
+                                    data.regionMatches(ignoreCase, toffset,
+                                            other, ooffset, len),
+                                    expected,
+                                    String.format(
+                                            "testing String(%s).regionMatches(%b, %d, %s, %d, %d), source : %s, ",
+                                            escapeNonASCIIs(data), ignoreCase,
+                                            toffset, escapeNonASCIIs(other),
+                                            ooffset, len, source));
+                        });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/Replace.java b/jdk/test/java/lang/String/CompactString/Replace.java
new file mode 100644
index 0000000..95540f1
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/Replace.java
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.replace.
+ * @run testng/othervm -XX:+CompactStrings Replace
+ * @run testng/othervm -XX:-CompactStrings Replace
+ */
+
+public class Replace extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+
+                new Object[] { STRING_L1, 'A', 'B', "B" },
+                new Object[] { STRING_L1, 'A', 'A', "A" },
+                new Object[] { STRING_L1, 'A', '\uFF21', "\uFF21" },
+                new Object[] { STRING_L2, 'A', 'B', "BB" },
+                new Object[] { STRING_L2, 'B', 'A', "AA" },
+                new Object[] { STRING_L2, 'C', 'A', "AB" },
+                new Object[] { STRING_L2, 'B', '\uFF21', "A\uFF21" },
+                new Object[] { STRING_U1, '\uFF21', 'A', "A" },
+                new Object[] { STRING_U1, '\uFF22', 'A', "\uFF21" },
+                new Object[] { STRING_U2, '\uFF22', 'A', "\uFF21A" },
+                new Object[] { STRING_M12, 'A', '\uFF21', "\uFF21\uFF21" },
+                new Object[] { STRING_M11, '\uFF21', 'A', "AA" },
+                new Object[] { STRING_UDUPLICATE, '\uFF21', 'A',
+                        "A\uFF22A\uFF22A\uFF22A\uFF22A\uFF22" },
+                new Object[] { STRING_MDUPLICATE1, '\uFF21', 'A', "AAAAAAAAAA" },
+                new Object[] { STRING_MDUPLICATE1, 'A', '\uFF21',
+                        "\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21" }, };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testReplace(String str, char oldChar, char newChar,
+            String expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertEquals(
+                                    data.replace(oldChar, newChar),
+                                    expected,
+                                    String.format(
+                                            "testing String(%s).replace(%s, %s), source : %s, ",
+                                            escapeNonASCIIs(data),
+                                            escapeNonASCII(oldChar),
+                                            escapeNonASCII(newChar), source));
+                        });
+    }
+
+    @DataProvider
+    public Object[][] provider2() {
+        return new Object[][] {
+
+                new Object[] { STRING_EMPTY, "", "ABC", "ABC" },
+                new Object[] { STRING_EMPTY, "", "", "" },
+                new Object[] { STRING_L1, "A", "B", "B" },
+                new Object[] { STRING_L1, "A", "A", "A" },
+                new Object[] { STRING_L2, "B", "\uFF21", "A\uFF21" },
+                new Object[] { STRING_LLONG, "BCD", "\uFF21", "A\uFF21EFGH" },
+                new Object[] { STRING_U1, "\uFF21", "A", "A" },
+                new Object[] { STRING_U1, "\uFF21", "A\uFF21", "A\uFF21" },
+                new Object[] { STRING_U2, "\uFF21", "A", "A\uFF22" },
+                new Object[] { STRING_U2, "\uFF22", "A", "\uFF21A" },
+                new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22", "AB",
+                        "ABABABABAB" },
+                new Object[] { STRING_MDUPLICATE1, "\uFF21", "A", "AAAAAAAAAA" },
+                new Object[] { STRING_MDUPLICATE1, "A", "\uFF21",
+                        "\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21" }, };
+    }
+
+    @Test(dataProvider = "provider2")
+    public void testReplace(String str, CharSequence target,
+            CharSequence replacement, String expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertEquals(
+                                    data.replace(target, replacement),
+                                    expected,
+                                    String.format(
+                                            "testing String(%s).replace(%s, %s), source : %s, ",
+                                            escapeNonASCIIs(data),
+                                            escapeNonASCIIs(target.toString()),
+                                            escapeNonASCIIs(replacement
+                                                    .toString()), source));
+                        });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/SerializationTest.java b/jdk/test/java/lang/String/CompactString/SerializationTest.java
new file mode 100644
index 0000000..e4c94c5
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/SerializationTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static jdk.testlibrary.SerializationUtils.*;
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.SerializationUtils
+ * @summary Tests Compact String. This one is testing String serialization
+ *          among -XX:+CompactStrings/-XX:-CompactStrings/LegacyString
+ * @run testng/othervm -XX:+CompactStrings SerializationTest
+ * @run testng/othervm -XX:-CompactStrings SerializationTest
+ */
+
+public class SerializationTest {
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+                // every byte array is serialized from corresponding String object
+                // by previous JDK(build 1.8.0_45-b14).
+                new Object[] { "", new byte[] { -84, -19, 0, 5, 116, 0, 0 } },
+                new Object[] { "A", new byte[] { -84, -19, 0, 5, 116, 0, 1, 65 } },
+                new Object[] { "AB", new byte[] { -84, -19, 0, 5, 116, 0, 2, 65, 66 } },
+                new Object[] { "abcdefghijk",
+                        new byte[] {-84, -19, 0, 5, 116, 0, 11, 97, 98, 99, 100, 101,
+                        102, 103, 104, 105, 106, 107 } },
+                new Object[] { "\uff21", new byte[] { -84, -19, 0, 5, 116, 0, 3, -17, -68, -95 } },
+                new Object[] { "\uff21\uff22", new byte[] { -84, -19, 0, 5, 116, 0, 6, -17, -68,
+                        -95, -17, -68, -94 } },
+                new Object[] { "\uff21A\uff21A\uff21A\uff21A\uff21A",
+                        new byte[] { -84, -19, 0, 5, 116, 0, 20, -17, -68, -95, 65, -17, -68,
+                        -95, 65, -17, -68, -95, 65, -17, -68, -95, 65, -17, -68, -95, 65 } },
+                new Object[] { "A\uff21B\uff22C\uff23D\uff24E\uff25F\uff26G\uff27H\uff28",
+                        new byte[] { -84, -19, 0, 5, 116, 0, 32, 65, -17, -68, -95, 66, -17, -68,
+                        -94, 67, -17, -68, -93, 68, -17, -68, -92, 69, -17, -68, -91, 70, -17,
+                        -68, -90, 71, -17, -68, -89, 72, -17, -68, -88 } },
+                new Object[] { "\uff21A\uff22B\uff23C\uff24D\uff25E\uff26F\uff27G\uff28H",
+                        new byte[] { -84, -19, 0, 5, 116, 0, 32, -17, -68, -95, 65, -17, -68,
+                        -94, 66, -17, -68, -93, 67, -17, -68, -92, 68, -17, -68, -91, 69, -17,
+                        -68, -90, 70, -17, -68, -89, 71, -17, -68, -88, 72 } },
+                new Object[] { "\ud801\udc00\ud801\udc01\uff21A",
+                        new byte[] { -84, -19, 0, 5, 116, 0, 16, -19, -96, -127, -19, -80, -128,
+                        -19, -96, -127, -19, -80, -127, -17, -68, -95, 65 } },
+                new Object[] { "\uff21\uff22\uff21\uff22\uff21\uff22\uff21\uff22\uff21\uff22",
+                        new byte[] { -84, -19, 0, 5, 116, 0, 30, -17, -68, -95, -17, -68, -94, -17,
+                        -68, -95, -17, -68, -94, -17, -68, -95, -17, -68, -94, -17, -68, -95, -17,
+                        -68, -94, -17, -68, -95, -17, -68, -94 } } };
+    }
+
+    /*
+     * Verify serialization works between Compact String/Legacy String
+     */
+    @Test(dataProvider = "provider")
+    public void test(String strContent, byte[] baInJDK8) throws Exception {
+        // Serialize a String object into byte array.
+        byte[] ba = serialize(strContent);
+        assertEquals(ba, baInJDK8);
+        // Deserialize a String object from byte array which is generated by previous JDK(build 1.8.0_45-b14).
+        Object obj = deserialize(ba);
+        assertEquals(obj.getClass(), String.class);
+        assertEquals((String)obj, strContent);
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/Split.java b/jdk/test/java/lang/String/CompactString/Split.java
new file mode 100644
index 0000000..2707ae3
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/Split.java
@@ -0,0 +1,181 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.Arrays;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertTrue;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.split.
+ * @run testng/othervm -XX:+CompactStrings Split
+ * @run testng/othervm -XX:-CompactStrings Split
+ */
+
+public class Split extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+                new Object[] { STRING_L1, "", 0, new String[] { "A" } },
+                new Object[] { STRING_L1, "", 1, new String[] { "A" } },
+                new Object[] { STRING_L1, "", 2, new String[] { "A", "" } },
+                new Object[] { STRING_L1, "A", 0, new String[] {} },
+                new Object[] { STRING_L2, "A", 0, new String[] { "", "B" } },
+                new Object[] { STRING_L2, "B", 0, new String[] { "A" } },
+                new Object[] { STRING_LLONG, "D", 0,
+                        new String[] { "ABC", "EFGH" } },
+                new Object[] { STRING_LLONG, "[D]", 0,
+                        new String[] { "ABC", "EFGH" } },
+                new Object[] { STRING_LLONG, "CD", 0,
+                        new String[] { "AB", "EFGH" } },
+                new Object[] { STRING_LLONG, "DC", 0,
+                        new String[] { "ABCDEFGH" } },
+                new Object[] { STRING_LLONG, "[CF]", 0,
+                        new String[] { "AB", "DE", "GH" } },
+                new Object[] { STRING_LLONG, "[CF]", 1,
+                        new String[] { "ABCDEFGH" } },
+                new Object[] { STRING_LLONG, "[CF]", 2,
+                        new String[] { "AB", "DEFGH" } },
+                new Object[] { STRING_LLONG, "[FC]", 0,
+                        new String[] { "AB", "DE", "GH" } },
+                new Object[] { STRING_LLONG, "[FC]", 1,
+                        new String[] { "ABCDEFGH" } },
+                new Object[] { STRING_LLONG, "[FC]", 2,
+                        new String[] { "AB", "DEFGH" } },
+                new Object[] { STRING_U1, "", 0, new String[] { "\uFF21" } },
+                new Object[] { STRING_U1, "", 1, new String[] { "\uFF21" } },
+                new Object[] { STRING_U1, "", 2, new String[] { "\uFF21", "" } },
+                new Object[] { STRING_U1, "\uFF21", 0, new String[] {} },
+                new Object[] { STRING_M12, "\uFF21", 0,
+                        new String[] { "", "A" } },
+                new Object[] { STRING_M12, "A", 0, new String[] { "\uFF21" } },
+                new Object[] {
+                        STRING_UDUPLICATE,
+                        "\uFF21",
+                        0,
+                        new String[] { "", "\uFF22", "\uFF22", "\uFF22",
+                                "\uFF22", "\uFF22" } },
+                new Object[] {
+                        STRING_UDUPLICATE,
+                        "\uFF21",
+                        2,
+                        new String[] { "",
+                                "\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22" } },
+                new Object[] {
+                        STRING_UDUPLICATE,
+                        "\uFF21",
+                        4,
+                        new String[] { "", "\uFF22", "\uFF22",
+                                "\uFF22\uFF21\uFF22\uFF21\uFF22" } },
+                new Object[] {
+                        STRING_UDUPLICATE,
+                        "\uFF22",
+                        0,
+                        new String[] { "\uFF21", "\uFF21", "\uFF21", "\uFF21",
+                                "\uFF21" } },
+                new Object[] {
+                        STRING_UDUPLICATE,
+                        "\uFF22",
+                        3,
+                        new String[] { "\uFF21", "\uFF21",
+                                "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22" } },
+
+                new Object[] { STRING_MDUPLICATE1, "\uFF21", 0,
+                        new String[] { "", "A", "A", "A", "A", "A" } },
+                new Object[] { STRING_MDUPLICATE1, "\uFF21", 3,
+                        new String[] { "", "A", "A\uFF21A\uFF21A\uFF21A" } },
+                new Object[] {
+                        STRING_MDUPLICATE1,
+                        "A",
+                        0,
+                        new String[] { "\uFF21", "\uFF21", "\uFF21", "\uFF21",
+                                "\uFF21" } },
+                new Object[] {
+                        STRING_MDUPLICATE1,
+                        "A",
+                        4,
+                        new String[] { "\uFF21", "\uFF21", "\uFF21",
+                                "\uFF21A\uFF21A" } },
+                new Object[] { STRING_SUPPLEMENTARY, "\uD801\uDC01", 0,
+                        new String[] { "\uD801\uDC00", "\uFF21A" } },
+                new Object[] { STRING_SUPPLEMENTARY, "\uDC01", 0,
+                        new String[] { "\uD801\uDC00\uD801\uDC01\uFF21A" } },
+                new Object[] { STRING_SUPPLEMENTARY, "\uD801\uDC01", 0,
+                        new String[] { "\uD801\uDC00", "\uFF21A" } },
+                new Object[] { STRING_SUPPLEMENTARY, "[\uD801\uDC01\uFF21]", 0,
+                        new String[] { "\uD801\uDC00", "", "A" } },
+                new Object[] { STRING_SUPPLEMENTARY, "[\uD801\uDC01\uFF21]", 1,
+                        new String[] { "\uD801\uDC00\uD801\uDC01\uFF21A" } },
+                new Object[] { STRING_SUPPLEMENTARY, "[\uD801\uDC01\uFF21]", 2,
+                        new String[] { "\uD801\uDC00", "\uFF21A" } },
+                new Object[] { STRING_SUPPLEMENTARY, "[\uFF21\uD801\uDC01]", 0,
+                        new String[] { "\uD801\uDC00", "", "A" } },
+                new Object[] { STRING_SUPPLEMENTARY, "[\uFF21\uD801\uDC01]", 1,
+                        new String[] { "\uD801\uDC00\uD801\uDC01\uFF21A" } },
+                new Object[] { STRING_SUPPLEMENTARY, "[\uFF21\uD801\uDC01]", 2,
+                        new String[] { "\uD801\uDC00", "\uFF21A" } },
+                new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, "\uDC01", 0,
+                        new String[] { "\uD801\uDC28\uD801\uDC29\uFF41a" } },
+                new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, "\uD801\uDC29",
+                        0, new String[] { "\uD801\uDC28", "\uFF41a" } },
+                new Object[] { STRING_SUPPLEMENTARY_LOWERCASE,
+                        "[\uD801\uDC29\uFF41]", 0,
+                        new String[] { "\uD801\uDC28", "", "a" } },
+                new Object[] { STRING_SUPPLEMENTARY_LOWERCASE,
+                        "[\uD801\uDC29\uFF41]", 1,
+                        new String[] { "\uD801\uDC28\uD801\uDC29\uFF41a" } },
+                new Object[] { STRING_SUPPLEMENTARY_LOWERCASE,
+                        "[\uD801\uDC29\uFF41]", 2,
+                        new String[] { "\uD801\uDC28", "\uFF41a" } },
+                new Object[] { STRING_SUPPLEMENTARY_LOWERCASE,
+                        "[\uFF41\uD801\uDC29]", 0,
+                        new String[] { "\uD801\uDC28", "", "a" } },
+                new Object[] { STRING_SUPPLEMENTARY_LOWERCASE,
+                        "[\uFF41\uD801\uDC29]", 1,
+                        new String[] { "\uD801\uDC28\uD801\uDC29\uFF41a" } },
+                new Object[] { STRING_SUPPLEMENTARY_LOWERCASE,
+                        "[\uFF41\uD801\uDC29]", 2,
+                        new String[] { "\uD801\uDC28", "\uFF41a" } }, };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testSplit(String str, String regex, int limit, String[] expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertTrue(
+                                    Arrays.equals(data.split(regex, limit),
+                                            expected),
+                                    String.format(
+                                            "testing String(%s).split(%s, %d), source : %s, ",
+                                            escapeNonASCIIs(data),
+                                            escapeNonASCIIs(regex), limit,
+                                            source));
+                        });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/StartsWith.java b/jdk/test/java/lang/String/CompactString/StartsWith.java
new file mode 100644
index 0000000..97109bb
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/StartsWith.java
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.startsWith.
+ * @run testng/othervm -XX:+CompactStrings StartsWith
+ * @run testng/othervm -XX:-CompactStrings StartsWith
+ */
+
+public class StartsWith extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+                new Object[] {STRING_EMPTY, "", 0, true},
+                new Object[] {STRING_EMPTY, "A", 0, false},
+                new Object[] {STRING_EMPTY, "", 0, true},
+                new Object[] {STRING_EMPTY, "", -1, false},
+                new Object[] {STRING_L1, "A", 0, true},
+                new Object[] {STRING_L1, "A", -1, false},
+                new Object[] {STRING_L1, "A", 1, false},
+                new Object[] {STRING_L2, "B", 1, true},
+                new Object[] {STRING_L2, "B", 0, false},
+                new Object[] {STRING_L2, "A", 0, true},
+                new Object[] {STRING_L2, "AB", 1, false},
+                new Object[] {STRING_L4, "ABC", 0, true},
+                new Object[] {STRING_LLONG, "ABCDEFGH", 0, true},
+                new Object[] {STRING_LLONG, "ABCDE", 0, true},
+                new Object[] {STRING_LLONG, "CDE", 0, false},
+                new Object[] {STRING_LLONG, "FG", 5, true},
+                new Object[] {STRING_U1, "\uFF21", 0, true},
+                new Object[] {STRING_U1, "", 1, true},
+                new Object[] {STRING_U1, "\uFF21", 0, true},
+                new Object[] {STRING_U1, "A", 0, false},
+                new Object[] {STRING_U2, "\uFF21\uFF22", 0, true},
+                new Object[] {STRING_U2, "\uFF21", 0, true},
+                new Object[] {STRING_U2, "\uFF22", 0, false},
+                new Object[] {STRING_U2, "", 0, true},
+                new Object[] {STRING_M12, "\uFF21", 0, true},
+                new Object[] {STRING_M12, "\uFF21A", 0, true},
+                new Object[] {STRING_M12, "A", 0, false},
+                new Object[] {STRING_M12, "\uFF21A", 0, true},
+                new Object[] {STRING_M12, "A", 1, true},
+                new Object[] {STRING_M11, "A", 0, true},
+                new Object[] {STRING_M11, "A\uFF21", 0, true},
+                new Object[] {STRING_M11, "A\uFF21", 0, true},
+                new Object[] {STRING_M11, "\uFF21", 1, true},
+                new Object[] {STRING_UDUPLICATE,
+                        "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22",
+                        0, true},
+                new Object[] {STRING_UDUPLICATE,
+                        "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", 0, true},
+                new Object[] {STRING_UDUPLICATE,
+                        "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", 2, true},
+                new Object[] {STRING_UDUPLICATE,
+                        "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", 5, false},
+                new Object[] {STRING_MDUPLICATE1,
+                        "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A", 0, true},
+                new Object[] {STRING_MDUPLICATE1,
+                        "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21", 0, true},
+                new Object[] {STRING_MDUPLICATE1, "A\uFF21A\uFF21A\uFF21A", 1, true},
+                new Object[] {STRING_SUPPLEMENTARY, "\uDC01\uFF21", 3, true},
+        };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testStartsWith(String str, String prefix, int toffset,
+            boolean expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertEquals(
+                                    data.startsWith(prefix, toffset),
+                                    expected,
+                                    String.format(
+                                            "testing String(%s).startsWith(%s, %d), source : %s, ",
+                                            escapeNonASCIIs(data),
+                                            escapeNonASCIIs(prefix), toffset,
+                                            source));
+                        });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/SubString.java b/jdk/test/java/lang/String/CompactString/SubString.java
new file mode 100644
index 0000000..f34f161
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/SubString.java
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.subString.
+ * @run testng/othervm -XX:+CompactStrings SubString
+ * @run testng/othervm -XX:-CompactStrings SubString
+ */
+
+public class SubString extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+
+                new Object[] { STRING_EMPTY, 0, 0, "" },
+                new Object[] { STRING_L1, 0, 1, "A" },
+                new Object[] { STRING_L1, 1, 1, "" },
+                new Object[] { STRING_L2, 0, 2, "AB" },
+                new Object[] { STRING_L2, 1, 2, "B" },
+                new Object[] { STRING_LLONG, 0, 8, "ABCDEFGH" },
+                new Object[] { STRING_LLONG, 7, 8, "H" },
+                new Object[] { STRING_LLONG, 8, 8, "" },
+                new Object[] { STRING_LLONG, 3, 7, "DEFG" },
+                new Object[] { STRING_U1, 0, 1, "\uFF21" },
+                new Object[] { STRING_U1, 1, 1, "" },
+                new Object[] { STRING_U1, 0, 0, "" },
+                new Object[] { STRING_U2, 0, 2, "\uFF21\uFF22" },
+                new Object[] { STRING_U2, 1, 2, "\uFF22" },
+                new Object[] { STRING_U2, 2, 2, "" },
+                new Object[] { STRING_U2, 0, 2, "\uFF21\uFF22" },
+                new Object[] { STRING_U2, 1, 2, "\uFF22" },
+                new Object[] { STRING_M12, 1, 2, "A" },
+                new Object[] { STRING_M11, 0, 1, "A" },
+                new Object[] { STRING_M11, 1, 2, "\uFF21" },
+                new Object[] { STRING_UDUPLICATE, 1, 5,
+                        "\uFF22\uFF21\uFF22\uFF21" },
+                new Object[] { STRING_MDUPLICATE1, 9, 10, "A" },
+                new Object[] { STRING_MDUPLICATE1, 7, 8, "A" }, };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testSubstring(String str, int beginIndex, int endIndex,
+            String expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertEquals(
+                                    data.substring(beginIndex, endIndex),
+                                    expected,
+                                    String.format(
+                                            "testing String(%s).substring(%d, %d), source : %s, ",
+                                            escapeNonASCIIs(data), beginIndex,
+                                            endIndex, source));
+                        });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/ToCharArray.java b/jdk/test/java/lang/String/CompactString/ToCharArray.java
new file mode 100644
index 0000000..bf7dbde
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/ToCharArray.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.Arrays;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertTrue;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.toCharArray.
+ * @run testng/othervm -XX:+CompactStrings ToCharArray
+ * @run testng/othervm -XX:-CompactStrings ToCharArray
+ */
+
+public class ToCharArray extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+                new Object[] { STRING_EMPTY, new char[] {} },
+                new Object[] { STRING_L1, new char[] { 'A' } },
+                new Object[] { STRING_L2, new char[] { 'A', 'B' } },
+                new Object[] { STRING_LLONG,
+                        new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' } },
+                new Object[] { STRING_U1, new char[] { '\uFF21' } },
+                new Object[] { STRING_U2, new char[] { '\uFF21', '\uFF22' } },
+                new Object[] { STRING_M12, new char[] { '\uFF21', 'A' } },
+                new Object[] { STRING_M11, new char[] { 'A', '\uFF21' } }, };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testToCharArray(String str, char[] expected) {
+        map.get(str)
+                .forEach(
+                        (source, data) -> {
+                            assertTrue(
+                                    Arrays.equals(data.toCharArray(), expected),
+                                    String.format(
+                                            "testing String(%s).toCharArray(), source : %s, ",
+                                            escapeNonASCIIs(data), source));
+                        });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/ToLowerCase.java b/jdk/test/java/lang/String/CompactString/ToLowerCase.java
new file mode 100644
index 0000000..aa49aad
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/ToLowerCase.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.toLowerCase.
+ * @run testng/othervm -XX:+CompactStrings ToLowerCase
+ * @run testng/othervm -XX:-CompactStrings ToLowerCase
+ */
+
+public class ToLowerCase extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+                new Object[] { STRING_EMPTY, "" },
+                new Object[] { STRING_L1, "a" },
+                new Object[] { STRING_L2, "ab" },
+                new Object[] { STRING_U1, "\uFF41" },
+                new Object[] { STRING_MDUPLICATE1,
+                        "\uFF41a\uFF41a\uFF41a\uFF41a\uFF41a" },
+                new Object[] { STRING_SUPPLEMENTARY,
+                        "\uD801\uDC28\uD801\uDC29\uFF41a" },
+                new Object[] { STRING_SUPPLEMENTARY_LOWERCASE,
+                        "\uD801\uDC28\uD801\uDC29\uFF41a" },
+                new Object[] { STRING_SUPPLEMENTARY,
+                        STRING_SUPPLEMENTARY_LOWERCASE } };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testToLowerCase(String str, String expected) {
+        map.get(str).forEach(
+                (source, data) -> {
+                    assertEquals(data.toLowerCase(), expected, String.format(
+                            "testing String(%s).toLowerCase(), source : %s, ",
+                            escapeNonASCIIs(data), source));
+                });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/ToUpperCase.java b/jdk/test/java/lang/String/CompactString/ToUpperCase.java
new file mode 100644
index 0000000..d3eced3
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/ToUpperCase.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.toUpperCase.
+ * @run testng/othervm -XX:+CompactStrings ToUpperCase
+ * @run testng/othervm -XX:-CompactStrings ToUpperCase
+ */
+
+public class ToUpperCase extends CompactString {
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+
+                new Object[] { STRING_EMPTY, "" },
+                new Object[] { STRING_L1, "A" },
+                new Object[] { STRING_L2, "AB" },
+                new Object[] { STRING_U1, "\uFF21" },
+                new Object[] { STRING_MDUPLICATE1,
+                        "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A" },
+                new Object[] { STRING_SUPPLEMENTARY,
+                        "\uD801\uDC00\uD801\uDC01\uFF21A" },
+
+                new Object[] { STRING_SUPPLEMENTARY_LOWERCASE,
+                        "\uD801\uDC00\uD801\uDC01\uFF21A" },
+                new Object[] { STRING_SUPPLEMENTARY_LOWERCASE,
+                        STRING_SUPPLEMENTARY }, };
+    }
+
+    @Test(dataProvider = "provider")
+    public void testToUpperCase(String str, String expected) {
+        map.get(str).forEach(
+                (source, data) -> {
+                    assertEquals(data.toUpperCase(), expected, String.format(
+                            "testing String(%s).toUpperCase(), source : %s, ",
+                            escapeNonASCIIs(data), source));
+                });
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/Trim.java b/jdk/test/java/lang/String/CompactString/Trim.java
new file mode 100644
index 0000000..03d0f9f
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/Trim.java
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.trim.
+ * @run testng/othervm -XX:+CompactStrings Trim
+ * @run testng/othervm -XX:-CompactStrings Trim
+ */
+
+public class Trim {
+
+    /*
+     * Data provider for testTrim
+     *
+     * @return input parameter for testTrim
+     */
+    @DataProvider
+    public Object[][] trims() {
+        return new Object[][] {
+                { " \t \t".trim(), "" },
+                { "\t \t ".trim(), "" },
+                { "\t A B C\t ".trim(), "A B C" },
+                { " \t A B C \t".trim(), "A B C" },
+                { "\t \uFF21 \uFF22 \uFF23\t ".trim(), "\uFF21 \uFF22 \uFF23" },
+                { " \t \uFF21 \uFF22 \uFF23 \t".trim(), "\uFF21 \uFF22 \uFF23" },
+                { " \t \uFF41 \uFF42 \uFF43 \t".trim(), "\uFF41 \uFF42 \uFF43" },
+                { " \t A\uFF21 B\uFF22 C\uFF23 \t".trim(),
+                        "A\uFF21 B\uFF22 C\uFF23" } };
+    }
+
+    /*
+     * test trim().
+     *
+     * @param res
+     *            real result
+     * @param expected
+     *            expected result
+     */
+    @Test(dataProvider = "trims")
+    public void testTrim(String res, String expected) {
+        assertEquals(res, expected);
+    }
+
+}
diff --git a/jdk/test/java/lang/String/CompactString/VMOptionsTest.java b/jdk/test/java/lang/String/CompactString/VMOptionsTest.java
new file mode 100644
index 0000000..7fb4781
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/VMOptionsTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.*;
+import java.lang.reflect.Field;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.*;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is testing
+ *          if Compact String enable/disable VM Options is indeed working in String class,
+ *          it's verified by testing if the VM option affect coder and
+ *          COMPACT_STRINGS field in String class.
+ * @run testng/othervm -XX:+CompactStrings -DCompactStringEnabled=true VMOptionsTest
+ * @run testng/othervm -XX:-CompactStrings -DCompactStringEnabled=false VMOptionsTest
+ * @run testng/othervm -DCompactStringEnabled=true VMOptionsTest
+ */
+
+public class VMOptionsTest {
+    boolean compactStringEnabled;
+    // corresponding "COMPACT_STRINGS" field in String class.
+    Field COMPACT_STRINGS;
+    // corresponding "coder" field in String class.
+    Field coder;
+
+    // corresponding coder type in String class.
+    final byte LATIN1 = 0;
+    final byte UTF16  = 1;
+
+    @BeforeClass
+    public void setUp() throws Exception {
+        compactStringEnabled = Boolean.valueOf(System.getProperty("CompactStringEnabled", null));
+        COMPACT_STRINGS = String.class.getDeclaredField("COMPACT_STRINGS");
+        COMPACT_STRINGS.setAccessible(true);
+        coder = String.class.getDeclaredField("coder");
+        coder.setAccessible(true);
+    }
+
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+                new Object[] {"", LATIN1},
+                new Object[] {"abc", LATIN1},
+                new Object[] {"A\uff21", UTF16},
+                new Object[] {"\uff21\uff22", UTF16}
+        };
+    }
+
+    /*
+     * verify the coder field in String objects.
+     */
+    @Test(dataProvider = "provider")
+    public void testCoder(String str, byte expected) throws Exception {
+        byte c = (byte) coder.get(str);
+        expected = compactStringEnabled ? expected : UTF16;
+        assertEquals(c, expected);
+    }
+
+    /*
+     * verify the COMPACT_STRINGS flag in String objects.
+     */
+    @Test(dataProvider = "provider")
+    public void testCompactStringFlag(String str, byte ignore) throws Exception {
+        assertTrue(COMPACT_STRINGS.get(str).equals(compactStringEnabled));
+    }
+}
diff --git a/jdk/test/java/lang/String/CompactString/ValueOf.java b/jdk/test/java/lang/String/CompactString/ValueOf.java
new file mode 100644
index 0000000..cf47416
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/ValueOf.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is for String.valueOf.
+ *          valueOf(char[] data) is not tested here.
+ * @run testng/othervm -XX:+CompactStrings ValueOf
+ * @run testng/othervm -XX:-CompactStrings ValueOf
+ */
+
+public class ValueOf {
+
+    /*
+     * Data provider for testValueOf
+     *
+     * @return input parameter for testValueOf
+     */
+    @DataProvider
+    public Object[][] valueOfs() {
+        return new Object[][] { { String.valueOf(true), "true" },
+                { String.valueOf(false), "false" },
+                { String.valueOf(1.0f), "1.0" },
+                { String.valueOf(0.0f), "0.0" },
+                { String.valueOf(Float.MAX_VALUE), "3.4028235E38" },
+                { String.valueOf(Float.MIN_VALUE), "1.4E-45" },
+                { String.valueOf(1.0d), "1.0" },
+                { String.valueOf(0.0d), "0.0" },
+                { String.valueOf(Double.MAX_VALUE), "1.7976931348623157E308" },
+                { String.valueOf(Double.MIN_VALUE), "4.9E-324" },
+                { String.valueOf(1), "1" }, { String.valueOf(0), "0" },
+                { String.valueOf(Integer.MAX_VALUE), "2147483647" },
+                { String.valueOf(Integer.MIN_VALUE), "-2147483648" },
+                { String.valueOf(1L), "1" }, { String.valueOf(0L), "0" },
+                { String.valueOf(Long.MAX_VALUE), "9223372036854775807" },
+                { String.valueOf(Long.MIN_VALUE), "-9223372036854775808" } };
+    }
+
+    /*
+     * test String.valueOf(xxx).
+     *
+     * @param res
+     *            real result
+     * @param expected
+     *            expected result
+     */
+    @Test(dataProvider = "valueOfs")
+    public void testValueOf(String res, String expected) {
+        assertEquals(res, expected);
+    }
+
+}
diff --git a/jdk/test/java/lang/String/LiteralReplace.java b/jdk/test/java/lang/String/LiteralReplace.java
index f72e105..32fc779 100644
--- a/jdk/test/java/lang/String/LiteralReplace.java
+++ b/jdk/test/java/lang/String/LiteralReplace.java
@@ -22,7 +22,7 @@
  */
 
 /* @test
- * @bug 8058779
+ * @bug 8058779 8054307
  * @library /lib/testlibrary/
  * @build jdk.testlibrary.RandomFactory
  * @run testng LiteralReplace
@@ -104,6 +104,109 @@
             {"abcdefgh", "[a-h]", "X", "abcdefgh"},
             {"aa+", "a+", "", "a"},
             {"^abc$", "abc", "x", "^x$"},
+
+            // more with non-latin1 characters
+            {"\u4e00\u4e00\u4e00",
+             "\u4e00\u4e00",
+             "\u4e01",
+             "\u4e01\u4e00"},
+
+            {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08",
+             "\u4e03\u4e04\u4e05",
+             "\u4e10\u4e11\u4e12",
+             "\u4e00\u4e01\u4e02\u4e10\u4e11\u4e12\u4e06\u4e07\u4e08"},
+
+            {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08",
+             "ABC",
+             "\u4e10\u4e11\u4e12",
+             "\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08"},
+
+            {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e02\u4e03\u4e07\u4e08",
+             "\u4e02\u4e03",
+             "\u4e12\u4e13",
+             "\u4e00\u4e01\u4e12\u4e13\u4e04\u4e12\u4e13\u4e07\u4e08"},
+
+            {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e02\u4e03\u4e07\u4e08",
+             "\u4e02\u4e03",
+             "ab",
+             "\u4e00\u4e01ab\u4e04ab\u4e07\u4e08"},
+
+            {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07",
+             "",
+             "_",
+             "_\u4e00_\u4e01_\u4e02_\u4e03_\u4e04_\u4e05_\u4e06_\u4e07_"},
+            {"^\u4e00\u4e01\u4e02$",
+             "\u4e00\u4e01\u4e02",
+             "\u4e03",
+             "^\u4e03$"},
+
+            {"", "\u4e00", "\u4e01", ""},
+            {"", "", "\u4e00\u4e01\u4e02", "\u4e00\u4e01\u4e02"},
+
+            {"^\u4e00\u4e01\u4e02$",
+             "\u4e00\u4e01\u4e02",
+             "X",
+             "^X$"},
+
+            {"abcdefgh",
+             "def",
+             "\u4e01",
+             "abc\u4e01gh"},
+
+            {"abcdefgh",
+             "def",
+             "\u4e01\u4e02",
+             "abc\u4e01\u4e02gh"},
+
+            {"abcdefabcgh",
+             "abc",
+             "\u4e01\u4e02",
+             "\u4e01\u4e02def\u4e01\u4e02gh"},
+
+            {"abcdefabcghabc",
+             "abc",
+             "\u4e01\u4e02",
+             "\u4e01\u4e02def\u4e01\u4e02gh\u4e01\u4e02"},
+
+            {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05",
+             "\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05",
+             "abcd",
+             "abcd"},
+
+            {"\u4e00\u4e01",
+             "\u4e00\u4e01",
+             "abcdefg",
+             "abcdefg"},
+
+            {"\u4e00\u4e01xyz",
+             "\u4e00\u4e01",
+             "abcdefg",
+             "abcdefgxyz"},
+
+            {"\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00",
+             "\u4e00\u4e00",
+             "\u4e00\u4e00\u4e00",
+             "\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00"},
+
+            {"\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00",
+             "\u4e00\u4e00\u4e00",
+             "\u4e00\u4e00",
+             "\u4e00\u4e00\u4e00\u4e00"},
+
+            {"\u4e00.\u4e01.\u4e02.\u4e03.\u4e04.",
+             ".",
+             "-",
+             "\u4e00-\u4e01-\u4e02-\u4e03-\u4e04-"},
+
+            {"\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00",
+             "\u4e00",
+             "",
+             ""},
+
+            {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05",
+             "\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05",
+             "",
+             ""},
         };
     }
 
diff --git a/jdk/test/java/lang/String/ToLowerCase.java b/jdk/test/java/lang/String/ToLowerCase.java
index 784f810..9b5574d 100644
--- a/jdk/test/java/lang/String/ToLowerCase.java
+++ b/jdk/test/java/lang/String/ToLowerCase.java
@@ -23,7 +23,7 @@
 
 /*
     @test
-    @bug 4217441 4533872 4900935 8020037 8032012 8041791 8042589
+    @bug 4217441 4533872 4900935 8020037 8032012 8041791 8042589 8054307
     @summary toLowerCase should lower-case Greek Sigma correctly depending
              on the context (final/non-final).  Also it should handle
              Locale specific (lt, tr, and az) lowercasings and supplementary
@@ -134,14 +134,60 @@
         }
         test(src.toString(), Locale.US, exp.toString());
 
+        // test latin1
+        src = new StringBuilder(0x100);
+        exp = new StringBuilder(0x100);
+        for (int cp = 0; cp < 0x100; cp++) {
+            int lowerCase = Character.toLowerCase(cp);
+            if (lowerCase == -1) {    //Character.ERROR
+                continue;
+            }
+            src.appendCodePoint(cp);
+            exp.appendCodePoint(lowerCase);
+        }
+        test(src.toString(), Locale.US, exp.toString());
+
+        // test non-latin1 -> latin1
+        src = new StringBuilder(0x100).append("abc");
+        exp = new StringBuilder(0x100).append("abc");
+        for (int cp = 0x100; cp < 0x10000; cp++) {
+            int lowerCase  = Character.toLowerCase(cp);
+            if (lowerCase < 0x100 && cp != '\u0130') {
+                src.appendCodePoint(cp);
+                exp.appendCodePoint(lowerCase);
+            }
+        }
+        test(src.toString(), Locale.US, exp.toString());
     }
 
     static void test(String in, Locale locale, String expected) {
+        test0(in, locale,expected);
+        for (String[] ss :  new String[][] {
+                                new String[] {"abc",      "abc"},
+                                new String[] {"aBc",      "abc"},
+                                new String[] {"ABC",      "abc"},
+                                new String[] {"ab\u4e00", "ab\u4e00"},
+                                new String[] {"aB\u4e00", "ab\u4e00"},
+                                new String[] {"AB\u4e00", "ab\u4e00"},
+                                new String[] {"ab\uD800\uDC00", "ab\uD800\uDC00"},
+                                new String[] {"aB\uD800\uDC00", "ab\uD800\uDC00"},
+                                new String[] {"AB\uD800\uDC00", "ab\uD800\uDC00"},
+                                new String[] {"ab\uD801\uDC1C", "ab\uD801\uDC44"},
+                                new String[] {"aB\uD801\uDC1C", "ab\uD801\uDC44"},
+                                new String[] {"AB\uD801\uDC1C", "ab\uD801\uDC44"},
+
+                            }) {
+            test0(ss[0] + " " + in, locale, ss[1] + " " + expected);
+            test0(in + " " + ss[0], locale, expected + " " + ss[1]);
+        }
+    }
+
+    static void test0(String in, Locale locale, String expected) {
         String result = in.toLowerCase(locale);
         if (!result.equals(expected)) {
             System.err.println("input: " + in + ", locale: " + locale +
                     ", expected: " + expected + ", actual: " + result);
             throw new RuntimeException();
         }
-   }
+    }
 }
diff --git a/jdk/test/java/lang/String/ToUpperCase.java b/jdk/test/java/lang/String/ToUpperCase.java
index 020c2ae..b7cebc1 100644
--- a/jdk/test/java/lang/String/ToUpperCase.java
+++ b/jdk/test/java/lang/String/ToUpperCase.java
@@ -23,7 +23,7 @@
 
 /*
     @test
-    @bug 4219630 4304573 4533872 4900935 8042589
+    @bug 4219630 4304573 4533872 4900935 8042589 8054307
     @summary toUpperCase should upper-case German sharp s correctly even if
              it's the only character in the string. should also uppercase
              all of the 1:M char mappings correctly.  Also it should handle
@@ -97,14 +97,66 @@
         test("A\uD801\uDC44", Locale.ROOT, "A\uD801\uDC1c");
         test("a\uD801\uDC28\uD801\uDC29\uD801\uDC2A", Locale.US, "A\uD801\uDC00\uD801\uDC01\uD801\uDC02");
         test("A\uD801\uDC28a\uD801\uDC29b\uD801\uDC2Ac", Locale.US, "A\uD801\uDC00A\uD801\uDC01B\uD801\uDC02C");
+
+        // test latin1 only case
+        StringBuilder src = new StringBuilder(0x100);
+        StringBuilder exp = new StringBuilder(0x100);
+        for (int cp = 0; cp < 0x100; cp++) {
+            int upperCase = Character.toUpperCase(cp);
+            if (upperCase == -1) {    //Character.ERROR
+                continue;
+            }
+            src.appendCodePoint(cp);
+            if (cp == '\u00df') {
+                exp.append("SS");     // need Character.toUpperCaseEx()
+            } else {
+                exp.appendCodePoint(upperCase);
+            }
+        }
+        test(src.toString(), Locale.US, exp.toString());
+
+        // test non-latin1 -> latin1
+        src = new StringBuilder(0x100).append("ABC");
+        exp = new StringBuilder(0x100).append("ABC");
+        for (int cp = 0x100; cp < 0x10000; cp++) {
+            int upperCase  = Character.toUpperCase(cp);
+            if (upperCase < 0x100) {
+                src.appendCodePoint(cp);
+                exp.appendCodePoint(upperCase);
+            }
+        }
+        test(src.toString(), Locale.US, exp.toString());
+
     }
 
     static void test(String in, Locale locale, String expected) {
+        test0(in, locale,expected);
+        // trigger different code paths
+        for (String[] ss :  new String[][] {
+                                new String[] {"abc",      "ABC"},
+                                new String[] {"AbC",      "ABC"},
+                                new String[] {"ABC",      "ABC"},
+                                new String[] {"AB\u4e00", "AB\u4e00"},
+                                new String[] {"ab\u4e00", "AB\u4e00"},
+                                new String[] {"aB\u4e00", "AB\u4e00"},
+                                new String[] {"AB\uD800\uDC00", "AB\uD800\uDC00"},
+                                new String[] {"Ab\uD800\uDC00", "AB\uD800\uDC00"},
+                                new String[] {"ab\uD800\uDC00", "AB\uD800\uDC00"},
+                                new String[] {"AB\uD801\uDC44", "AB\uD801\uDC1C"},
+                                new String[] {"Ab\uD801\uDC44", "AB\uD801\uDC1C"},
+                                new String[] {"ab\uD801\uDC44", "AB\uD801\uDC1C"},
+                            }) {
+            test0(ss[0] + " " + in, locale, ss[1] + " " + expected);
+            test0(in + " " + ss[0], locale, expected + " " + ss[1]);
+        }
+    }
+
+    static void test0(String in, Locale locale, String expected) {
         String result = in.toUpperCase(locale);
         if (!result.equals(expected)) {
             System.err.println("input: " + in + ", locale: " + locale +
                     ", expected: " + expected + ", actual: " + result);
             throw new RuntimeException();
         }
-   }
+    }
 }
diff --git a/jdk/test/java/lang/StringBuffer/CompactStringBuffer.java b/jdk/test/java/lang/StringBuffer/CompactStringBuffer.java
new file mode 100644
index 0000000..bc0ec5f
--- /dev/null
+++ b/jdk/test/java/lang/StringBuffer/CompactStringBuffer.java
@@ -0,0 +1,489 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.Arrays;
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This test is testing StringBuffer
+ *          behavior related to Compact String.
+ * @run testng/othervm -XX:+CompactStrings CompactStringBuffer
+ * @run testng/othervm -XX:-CompactStrings CompactStringBuffer
+ */
+
+public class CompactStringBuffer {
+
+    /*
+     * Tests for "A"
+     */
+    @Test
+    public void testCompactStringBufferForLatinA() {
+        final String ORIGIN = "A";
+        /*
+         * Because right now ASCII is the default encoding parameter for source
+         * code in JDK build environment, so we escape them. same as below.
+         */
+        check(new StringBuffer(ORIGIN).append(new char[] { '\uFF21' }),
+                "A\uFF21");
+        check(new StringBuffer(ORIGIN).append(new StringBuffer("\uFF21")),
+                "A\uFF21");
+        check(new StringBuffer(ORIGIN).append("\uFF21"), "A\uFF21");
+        check(new StringBuffer(ORIGIN).append(new StringBuffer("\uFF21")),
+                "A\uFF21");
+        check(new StringBuffer(ORIGIN).delete(0, 1), "");
+        check(new StringBuffer(ORIGIN).delete(0, 0), "A");
+        check(new StringBuffer(ORIGIN).deleteCharAt(0), "");
+        assertEquals(new StringBuffer(ORIGIN).indexOf("A", 0), 0);
+        assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21", 0), -1);
+        assertEquals(new StringBuffer(ORIGIN).indexOf("", 0), 0);
+        assertEquals(new StringBuffer(ORIGIN).insert(1, "\uD801\uDC00")
+                .indexOf("A", 0), 0);
+        assertEquals(new StringBuffer(ORIGIN).insert(0, "\uD801\uDC00")
+                .indexOf("A", 0), 2);
+        check(new StringBuffer(ORIGIN).insert(0, new char[] {}), "A");
+        check(new StringBuffer(ORIGIN).insert(1, new char[] { '\uFF21' }),
+                "A\uFF21");
+        check(new StringBuffer(ORIGIN).insert(0, new char[] { '\uFF21' }),
+                "\uFF21A");
+        check(new StringBuffer(ORIGIN).insert(0, new StringBuffer("\uFF21")),
+                "\uFF21A");
+        check(new StringBuffer(ORIGIN).insert(1, new StringBuffer("\uFF21")),
+                "A\uFF21");
+        check(new StringBuffer(ORIGIN).insert(0, ""), "A");
+        check(new StringBuffer(ORIGIN).insert(0, "\uFF21"), "\uFF21A");
+        check(new StringBuffer(ORIGIN).insert(1, "\uFF21"), "A\uFF21");
+        assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), 0);
+        assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), -1);
+        assertEquals(new StringBuffer(ORIGIN).lastIndexOf(""), 1);
+        check(new StringBuffer(ORIGIN).replace(0, 0, "\uFF21"), "\uFF21A");
+        check(new StringBuffer(ORIGIN).replace(0, 1, "\uFF21"), "\uFF21");
+        checkSetCharAt(new StringBuffer(ORIGIN), 0, '\uFF21', "\uFF21");
+        checkSetLength(new StringBuffer(ORIGIN), 0, "");
+        checkSetLength(new StringBuffer(ORIGIN), 1, "A");
+        check(new StringBuffer(ORIGIN).substring(0), "A");
+        check(new StringBuffer(ORIGIN).substring(1), "");
+    }
+
+    /*
+     * Tests for "\uFF21"
+     */
+    @Test
+    public void testCompactStringBufferForNonLatinA() {
+        final String ORIGIN = "\uFF21";
+        check(new StringBuffer(ORIGIN).append(new char[] { 'A' }), "\uFF21A");
+        check(new StringBuffer(ORIGIN).append(new StringBuffer("A")), "\uFF21A");
+        check(new StringBuffer(ORIGIN).append("A"), "\uFF21A");
+        check(new StringBuffer(ORIGIN).append(new StringBuffer("A")), "\uFF21A");
+        check(new StringBuffer(ORIGIN).delete(0, 1), "");
+        check(new StringBuffer(ORIGIN).delete(0, 0), "\uFF21");
+        check(new StringBuffer(ORIGIN).deleteCharAt(0), "");
+        assertEquals(new StringBuffer(ORIGIN).indexOf("A", 0), -1);
+        assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21", 0), 0);
+        assertEquals(new StringBuffer(ORIGIN).indexOf("", 0), 0);
+        check(new StringBuffer(ORIGIN).insert(0, new char[] {}), "\uFF21");
+        check(new StringBuffer(ORIGIN).insert(1, new char[] { 'A' }), "\uFF21A");
+        check(new StringBuffer(ORIGIN).insert(0, new char[] { 'A' }), "A\uFF21");
+        check(new StringBuffer(ORIGIN).insert(0, new StringBuffer("A")),
+                "A\uFF21");
+        check(new StringBuffer(ORIGIN).insert(1, new StringBuffer("A")),
+                "\uFF21A");
+        check(new StringBuffer(ORIGIN).insert(0, ""), "\uFF21");
+        check(new StringBuffer(ORIGIN).insert(0, "A"), "A\uFF21");
+        check(new StringBuffer(ORIGIN).insert(1, "A"), "\uFF21A");
+        assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), -1);
+        assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), 0);
+        assertEquals(new StringBuffer(ORIGIN).lastIndexOf(""), 1);
+        check(new StringBuffer(ORIGIN).replace(0, 0, "A"), "A\uFF21");
+        check(new StringBuffer(ORIGIN).replace(0, 1, "A"), "A");
+        checkSetCharAt(new StringBuffer(ORIGIN), 0, 'A', "A");
+        checkSetLength(new StringBuffer(ORIGIN), 0, "");
+        checkSetLength(new StringBuffer(ORIGIN), 1, "\uFF21");
+        check(new StringBuffer(ORIGIN).substring(0), "\uFF21");
+        check(new StringBuffer(ORIGIN).substring(1), "");
+    }
+
+    /*
+     * Tests for "\uFF21A"
+     */
+    @Test
+    public void testCompactStringBufferForMixedA1() {
+        final String ORIGIN = "\uFF21A";
+        check(new StringBuffer(ORIGIN).delete(0, 1), "A");
+        check(new StringBuffer(ORIGIN).delete(1, 2), "\uFF21");
+        check(new StringBuffer(ORIGIN).deleteCharAt(1), "\uFF21");
+        check(new StringBuffer(ORIGIN).deleteCharAt(0), "A");
+        assertEquals(new StringBuffer(ORIGIN).indexOf("A", 0), 1);
+        assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21", 0), 0);
+        assertEquals(new StringBuffer(ORIGIN).indexOf("", 0), 0);
+        check(new StringBuffer(ORIGIN).insert(1, new char[] { 'A' }), "\uFF21AA");
+        check(new StringBuffer(ORIGIN).insert(0, new char[] { '\uFF21' }),
+                "\uFF21\uFF21A");
+        assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), 1);
+        assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), 0);
+        assertEquals(new StringBuffer(ORIGIN).lastIndexOf(""), 2);
+        check(new StringBuffer(ORIGIN).replace(0, 0, "A"), "A\uFF21A");
+        check(new StringBuffer(ORIGIN).replace(0, 1, "A"), "AA");
+        checkSetCharAt(new StringBuffer(ORIGIN), 0, 'A', "AA");
+        checkSetLength(new StringBuffer(ORIGIN), 0, "");
+        checkSetLength(new StringBuffer(ORIGIN), 1, "\uFF21");
+        check(new StringBuffer(ORIGIN).substring(0), "\uFF21A");
+        check(new StringBuffer(ORIGIN).substring(1), "A");
+    }
+
+    /*
+     * Tests for "A\uFF21"
+     */
+    @Test
+    public void testCompactStringBufferForMixedA2() {
+        final String ORIGIN = "A\uFF21";
+        check(new StringBuffer(ORIGIN).replace(1, 2, "A"), "AA");
+        checkSetLength(new StringBuffer(ORIGIN), 1, "A");
+        check(new StringBuffer(ORIGIN).substring(0), "A\uFF21");
+        check(new StringBuffer(ORIGIN).substring(1), "\uFF21");
+        check(new StringBuffer(ORIGIN).substring(0, 1), "A");
+    }
+
+    /*
+     * Tests for "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A"
+     */
+    @Test
+    public void testCompactStringBufferForDuplicatedMixedA1() {
+        final String ORIGIN = "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A";
+        checkSetLength(new StringBuffer(ORIGIN), 1, "\uFF21");
+        assertEquals(new StringBuffer(ORIGIN).indexOf("A", 5), 5);
+        assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21", 5), 6);
+        assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), 9);
+        assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), 8);
+        assertEquals(new StringBuffer(ORIGIN).lastIndexOf(""), 10);
+        check(new StringBuffer(ORIGIN).substring(9), "A");
+        check(new StringBuffer(ORIGIN).substring(8), "\uFF21A");
+    }
+
+    /*
+     * Tests for "A\uFF21A\uFF21A\uFF21A\uFF21A\uFF21"
+     */
+    @Test
+    public void testCompactStringBufferForDuplicatedMixedA2() {
+        final String ORIGIN = "A\uFF21A\uFF21A\uFF21A\uFF21A\uFF21";
+        checkSetLength(new StringBuffer(ORIGIN), 1, "A");
+        assertEquals(new StringBuffer(ORIGIN).indexOf("A", 5), 6);
+        assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21", 5), 5);
+        assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), 8);
+        assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), 9);
+        check(new StringBuffer(ORIGIN).substring(9), "\uFF21");
+        check(new StringBuffer(ORIGIN).substring(8), "A\uFF21");
+    }
+
+    /*
+     * Tests for "\uD801\uDC00\uD801\uDC01"
+     */
+    @Test
+    public void testCompactStringForSupplementaryCodePoint() {
+        final String ORIGIN = "\uD801\uDC00\uD801\uDC01";
+        check(new StringBuffer(ORIGIN).append("A"), "\uD801\uDC00\uD801\uDC01A");
+        check(new StringBuffer(ORIGIN).append("\uFF21"),
+                "\uD801\uDC00\uD801\uDC01\uFF21");
+        check(new StringBuffer(ORIGIN).appendCodePoint('A'),
+                "\uD801\uDC00\uD801\uDC01A");
+        check(new StringBuffer(ORIGIN).appendCodePoint('\uFF21'),
+                "\uD801\uDC00\uD801\uDC01\uFF21");
+        assertEquals(new StringBuffer(ORIGIN).charAt(0), '\uD801');
+        assertEquals(new StringBuffer(ORIGIN).codePointAt(0),
+                Character.codePointAt(ORIGIN, 0));
+        assertEquals(new StringBuffer(ORIGIN).codePointAt(1),
+                Character.codePointAt(ORIGIN, 1));
+        assertEquals(new StringBuffer(ORIGIN).codePointBefore(2),
+                Character.codePointAt(ORIGIN, 0));
+        assertEquals(new StringBuffer(ORIGIN).codePointCount(1, 3), 2);
+        check(new StringBuffer(ORIGIN).delete(0, 2), "\uD801\uDC01");
+        check(new StringBuffer(ORIGIN).delete(0, 3), "\uDC01");
+        check(new StringBuffer(ORIGIN).deleteCharAt(1), "\uD801\uD801\uDC01");
+        checkGetChars(new StringBuffer(ORIGIN), 0, 3, new char[] { '\uD801',
+                '\uDC00', '\uD801' });
+        assertEquals(new StringBuffer(ORIGIN).indexOf("\uD801\uDC01"), 2);
+        assertEquals(new StringBuffer(ORIGIN).indexOf("\uDC01"), 3);
+        assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21"), -1);
+        assertEquals(new StringBuffer(ORIGIN).indexOf("A"), -1);
+        check(new StringBuffer(ORIGIN).insert(0, "\uFF21"),
+                "\uFF21\uD801\uDC00\uD801\uDC01");
+        check(new StringBuffer(ORIGIN).insert(1, "\uFF21"),
+                "\uD801\uFF21\uDC00\uD801\uDC01");
+        check(new StringBuffer(ORIGIN).insert(1, "A"),
+                "\uD801A\uDC00\uD801\uDC01");
+        assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uDC00\uD801"), 1);
+        assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uD801"), 2);
+        assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), -1);
+        assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), -1);
+        assertEquals(new StringBuffer(ORIGIN).length(), 4);
+        assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(1, 1), 2);
+        assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(0, 1), 2);
+        check(new StringBuffer(ORIGIN).replace(0, 2, "A"), "A\uD801\uDC01");
+        check(new StringBuffer(ORIGIN).replace(0, 3, "A"), "A\uDC01");
+        check(new StringBuffer(ORIGIN).replace(0, 2, "\uFF21"),
+                "\uFF21\uD801\uDC01");
+        check(new StringBuffer(ORIGIN).replace(0, 3, "\uFF21"), "\uFF21\uDC01");
+        check(new StringBuffer(ORIGIN).reverse(), "\uD801\uDC01\uD801\uDC00");
+        checkSetCharAt(new StringBuffer(ORIGIN), 1, '\uDC01',
+                "\uD801\uDC01\uD801\uDC01");
+        checkSetCharAt(new StringBuffer(ORIGIN), 1, 'A', "\uD801A\uD801\uDC01");
+        checkSetLength(new StringBuffer(ORIGIN), 2, "\uD801\uDC00");
+        checkSetLength(new StringBuffer(ORIGIN), 3, "\uD801\uDC00\uD801");
+        check(new StringBuffer(ORIGIN).substring(1, 3), "\uDC00\uD801");
+    }
+
+    /*
+     * Tests for "A\uD801\uDC00\uFF21"
+     */
+    @Test
+    public void testCompactStringForSupplementaryCodePointMixed1() {
+        final String ORIGIN = "A\uD801\uDC00\uFF21";
+        assertEquals(new StringBuffer(ORIGIN).codePointBefore(3),
+                Character.codePointAt(ORIGIN, 1));
+        assertEquals(new StringBuffer(ORIGIN).codePointBefore(2), '\uD801');
+        assertEquals(new StringBuffer(ORIGIN).codePointBefore(1), 'A');
+        assertEquals(new StringBuffer(ORIGIN).codePointCount(0, 3), 2);
+        assertEquals(new StringBuffer(ORIGIN).codePointCount(0, 4), 3);
+        check(new StringBuffer(ORIGIN).delete(0, 1), "\uD801\uDC00\uFF21");
+        check(new StringBuffer(ORIGIN).delete(0, 1).delete(2, 3), "\uD801\uDC00");
+        check(new StringBuffer(ORIGIN).deleteCharAt(3).deleteCharAt(0),
+                "\uD801\uDC00");
+        assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21"), 3);
+        assertEquals(new StringBuffer(ORIGIN).indexOf("A"), 0);
+        assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), 3);
+        assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), 0);
+        assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(0, 1), 1);
+        assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(1, 1), 3);
+        check(new StringBuffer(ORIGIN).replace(1, 3, "A"), "AA\uFF21");
+        check(new StringBuffer(ORIGIN).replace(1, 4, "A"), "AA");
+        check(new StringBuffer(ORIGIN).replace(1, 4, ""), "A");
+        check(new StringBuffer(ORIGIN).reverse(), "\uFF21\uD801\uDC00A");
+        checkSetLength(new StringBuffer(ORIGIN), 1, "A");
+        check(new StringBuffer(ORIGIN).substring(0, 1), "A");
+    }
+
+    /*
+     * Tests for "\uD801\uDC00\uFF21A"
+     */
+    @Test
+    public void testCompactStringForSupplementaryCodePointMixed2() {
+        final String ORIGIN = "\uD801\uDC00\uFF21A";
+        assertEquals(new StringBuffer(ORIGIN).codePointBefore(3),
+                Character.codePointAt(ORIGIN, 2));
+        assertEquals(new StringBuffer(ORIGIN).codePointBefore(2),
+                Character.codePointAt(ORIGIN, 0));
+        assertEquals(new StringBuffer(ORIGIN).codePointBefore(1), '\uD801');
+        assertEquals(new StringBuffer(ORIGIN).codePointCount(0, 3), 2);
+        assertEquals(new StringBuffer(ORIGIN).codePointCount(0, 4), 3);
+        check(new StringBuffer(ORIGIN).delete(0, 2), "\uFF21A");
+        check(new StringBuffer(ORIGIN).delete(0, 3), "A");
+        check(new StringBuffer(ORIGIN).deleteCharAt(0).deleteCharAt(0)
+                .deleteCharAt(0), "A");
+        assertEquals(new StringBuffer(ORIGIN).indexOf("A"), 3);
+        assertEquals(new StringBuffer(ORIGIN).delete(0, 3).indexOf("A"), 0);
+        assertEquals(new StringBuffer(ORIGIN).replace(0, 3, "B").indexOf("A"),
+                1);
+        assertEquals(new StringBuffer(ORIGIN).substring(3, 4).indexOf("A"), 0);
+        assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(1, 1), 2);
+        assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(0, 1), 2);
+        assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(2, 1), 3);
+        check(new StringBuffer(ORIGIN).replace(0, 3, "B"), "BA");
+        check(new StringBuffer(ORIGIN).reverse(), "A\uFF21\uD801\uDC00");
+    }
+
+    /*
+     * Tests for "\uD801A\uDC00\uFF21"
+     */
+    @Test
+    public void testCompactStringForSupplementaryCodePointMixed3() {
+        final String ORIGIN = "\uD801A\uDC00\uFF21";
+        assertEquals(new StringBuffer(ORIGIN).codePointAt(1), 'A');
+        assertEquals(new StringBuffer(ORIGIN).codePointAt(3), '\uFF21');
+        assertEquals(new StringBuffer(ORIGIN).codePointBefore(1), '\uD801');
+        assertEquals(new StringBuffer(ORIGIN).codePointBefore(2), 'A');
+        assertEquals(new StringBuffer(ORIGIN).codePointBefore(3), '\uDC00');
+        assertEquals(new StringBuffer(ORIGIN).codePointCount(0, 3), 3);
+        assertEquals(new StringBuffer(ORIGIN).codePointCount(1, 3), 2);
+        assertEquals(new StringBuffer(ORIGIN).delete(0, 1).delete(1, 3)
+                .indexOf("A"), 0);
+        assertEquals(
+                new StringBuffer(ORIGIN).replace(0, 1, "B").replace(2, 4, "C")
+                        .indexOf("A"), 1);
+        assertEquals(new StringBuffer(ORIGIN).substring(1, 4).substring(0, 1)
+                .indexOf("A"), 0);
+        assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(0, 1), 1);
+        assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(1, 1), 2);
+        assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(2, 1), 3);
+        check(new StringBuffer(ORIGIN).reverse(), "\uFF21\uDC00A\uD801");
+    }
+
+    /*
+     * Tests for "A\uDC01\uFF21\uD801"
+     */
+    @Test
+    public void testCompactStringForSupplementaryCodePointMixed4() {
+        final String ORIGIN = "A\uDC01\uFF21\uD801";
+        assertEquals(new StringBuffer(ORIGIN).codePointAt(1), '\uDC01');
+        assertEquals(new StringBuffer(ORIGIN).codePointAt(3), '\uD801');
+        assertEquals(new StringBuffer(ORIGIN).codePointBefore(1), 'A');
+        assertEquals(new StringBuffer(ORIGIN).codePointBefore(2), '\uDC01');
+        assertEquals(new StringBuffer(ORIGIN).codePointBefore(3), '\uFF21');
+        assertEquals(new StringBuffer(ORIGIN).codePointCount(0, 3), 3);
+        assertEquals(new StringBuffer(ORIGIN).codePointCount(1, 3), 2);
+        assertEquals(new StringBuffer(ORIGIN).delete(1, 4).indexOf("A"), 0);
+        assertEquals(new StringBuffer(ORIGIN).replace(1, 4, "B").indexOf("A"),
+                0);
+        assertEquals(new StringBuffer(ORIGIN).substring(0, 1).indexOf("A"), 0);
+        assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(0, 1), 1);
+        assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(1, 1), 2);
+        assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(2, 1), 3);
+        check(new StringBuffer(ORIGIN).reverse(), "\uD801\uFF21\uDC01A");
+    }
+
+    @Test
+    public void testCompactStringMisc() {
+        String ascii = "abcdefgh";
+        String asciiMixed = "abc" + "\u4e00\u4e01\u4e02" + "fgh";
+        String bmp = "\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08";
+        String bmpMixed = "\u4e00\u4e01\u4e02" + "ABC" + "\u4e06\u4e07\u4e08";
+
+        check(new StringBuffer().append(ascii).delete(0, 20).toString(),
+              "");
+        check(new StringBuffer().append(ascii).delete(3, 20).toString(),
+              "abc");
+        check(new StringBuffer().append(ascii).delete(3, 6).toString(),
+              "abcgh");
+        check(new StringBuffer().append(ascii).deleteCharAt(0).toString(),
+              "bcdefgh");
+        check(new StringBuffer().append(ascii).deleteCharAt(3).toString(),
+              "abcefgh");
+        check(new StringBuffer().append(asciiMixed).delete(3, 6).toString(),
+              "abcfgh");
+        check(new StringBuffer().append(asciiMixed).deleteCharAt(3).toString(),
+              "abc\u4e01\u4e02fgh");
+        check(new StringBuffer().append(asciiMixed).deleteCharAt(3)
+                                                   .deleteCharAt(3)
+                                                   .deleteCharAt(3).toString(),
+              "abcfgh");
+        check(new StringBuffer().append(bmp).delete(0, 20).toString(),
+              "");
+        check(new StringBuffer().append(bmp).delete(3, 20).toString(),
+              "\u4e00\u4e01\u4e02");
+        check(new StringBuffer().append(bmp).delete(3, 6).toString(),
+              "\u4e00\u4e01\u4e02\u4e06\u4e07\u4e08");
+        check(new StringBuffer().append(bmp).deleteCharAt(0).toString(),
+              "\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08");
+        check(new StringBuffer().append(bmp).deleteCharAt(3).toString(),
+              "\u4e00\u4e01\u4e02\u4e04\u4e05\u4e06\u4e07\u4e08");
+        check(new StringBuffer().append(bmpMixed).delete(3, 6).toString(),
+              "\u4e00\u4e01\u4e02\u4e06\u4e07\u4e08");
+
+        ////////////////////////////////////////////////////////////////////
+        check(new StringBuffer().append(ascii).replace(3, 6, "AB").toString(),
+              "abcABgh");
+        check(new StringBuffer().append(asciiMixed).replace(3, 6, "AB").toString(),
+              "abcABfgh");
+        check(new StringBuffer().append(bmp).replace(3, 6, "AB").toString(),
+              "\u4e00\u4e01\u4e02AB\u4e06\u4e07\u4e08");
+
+        check(new StringBuffer().append(bmpMixed).replace(3, 6, "").toString(),
+              "\u4e00\u4e01\u4e02\u4e06\u4e07\u4e08");
+
+        check(new StringBuffer().append(ascii).replace(3, 6, "\u4e01\u4e02").toString(),
+              "abc\u4e01\u4e02gh");
+
+        ////////////////////////////////////////////////////////////////////
+        check(new StringBuffer().append(ascii).insert(3, "").toString(),
+              "abcdefgh");
+        check(new StringBuffer().append(ascii).insert(3, "AB").toString(),
+              "abcABdefgh");
+        check(new StringBuffer().append(ascii).insert(3, "\u4e01\u4e02").toString(),
+              "abc\u4e01\u4e02defgh");
+
+        check(new StringBuffer().append(asciiMixed).insert(0, 'A').toString(),
+              "Aabc\u4e00\u4e01\u4e02fgh");
+        check(new StringBuffer().append(asciiMixed).insert(3, "A").toString(),
+              "abcA\u4e00\u4e01\u4e02fgh");
+
+        check(new StringBuffer().append(ascii).insert(3, 1234567).toString(),
+              "abc1234567defgh");
+        check(new StringBuffer().append(bmp).insert(3, 1234567).toString(),
+              "\u4e00\u4e01\u4e021234567\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08");
+
+        ////////////////////////////////////////////////////////////////////
+        check(new StringBuffer().append(ascii).append(1.23456).toString(),
+              "abcdefgh1.23456");
+        check(new StringBuffer().append(bmp).append(1.23456).toString(),
+              "\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e081.23456");
+    }
+
+    private void checkGetChars(StringBuffer sb, int srcBegin, int srcEnd,
+            char expected[]) {
+        char[] dst = new char[srcEnd - srcBegin];
+        sb.getChars(srcBegin, srcEnd, dst, 0);
+        assertTrue(Arrays.equals(dst, expected));
+    }
+
+    private void checkSetCharAt(StringBuffer sb, int index, char ch,
+            String expected) {
+        sb.setCharAt(index, ch);
+        check(sb, expected);
+    }
+
+    private void checkSetLength(StringBuffer sb, int newLength, String expected) {
+        sb.setLength(newLength);
+        check(sb, expected);
+    }
+
+    private void check(StringBuffer sb, String expected) {
+        check(sb.toString(), expected);
+    }
+
+    private void check(String str, String expected) {
+        assertTrue(str.equals(expected), String.format(
+                "Get (%s) but expect (%s), ", escapeNonASCIIs(str),
+                escapeNonASCIIs(expected)));
+    }
+
+    /*
+     * Because right now system default charset in JPRT environment is only
+     * guaranteed to support ASCII characters in log, so we escape them.
+     */
+    private String escapeNonASCIIs(String str) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < str.length(); i++) {
+            char c = str.charAt(i);
+            if (c > 0x7F) {
+                sb.append("\\u").append(Integer.toHexString((int) c));
+            } else {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+}
diff --git a/jdk/test/java/lang/StringBuffer/CompactStringBufferSerialization.java b/jdk/test/java/lang/StringBuffer/CompactStringBufferSerialization.java
new file mode 100644
index 0000000..346c824
--- /dev/null
+++ b/jdk/test/java/lang/StringBuffer/CompactStringBufferSerialization.java
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.*;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static jdk.testlibrary.SerializationUtils.*;
+import static org.testng.Assert.*;
+
+/*
+ * @test
+ * @bug 8077559
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.SerializationUtils
+ * @summary Tests Compact String. This one is testing StringBuffer serialization
+ *          among -XX:+CompactStrings/-XX:-CompactStrings/LegacyStringBuffer
+ * @run testng/othervm -XX:+CompactStrings CompactStringBufferSerialization
+ * @run testng/othervm -XX:-CompactStrings CompactStringBufferSerialization
+ */
+
+public class CompactStringBufferSerialization {
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+                // every byte array is serialized from corresponding StringBuilder object
+                // by previous JDK(build 1.8.0_45-b14).
+                new Object[] {
+                        new StringBuffer(""),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102,
+                                102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101,
+                                100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 0, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80,
+                                -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                0, 0, 0, 0, 0, 0, 0, 120 } },
+                new Object[] {
+                        new StringBuffer("A"),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102,
+                                102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101,
+                                100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 1, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80,
+                                -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 17, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } },
+                new Object[] {
+                        new StringBuffer("AB"),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102,
+                                102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101,
+                                100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 2, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80,
+                                -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 18, 0, 65, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } },
+                new Object[] {
+                        new StringBuffer("abcdefghijk"),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102,
+                                102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101,
+                                100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 11, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80,
+                                -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 27, 0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0, 103, 0, 104, 0, 105, 0,
+                                106, 0, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } },
+                new Object[] {
+                        new StringBuffer("\uff21"),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102,
+                                102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101,
+                                100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 1, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80,
+                                -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 17, -1, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } },
+                new Object[] {
+                        new StringBuffer("\uff21\uff22"),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102,
+                                102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101,
+                                100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 2, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80,
+                                -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 18, -1, 33, -1, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } },
+                new Object[] {
+                        new StringBuffer("\uff21A\uff21A\uff21A\uff21A\uff21A"),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102,
+                                102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101,
+                                100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 10, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80,
+                                -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 26, -1, 33, 0, 65, -1, 33, 0, 65, -1, 33, 0, 65, -1, 33, 0, 65, -1, 33, 0, 65,
+                                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } },
+                new Object[] {
+                        new StringBuffer("A\uff21B\uff22C\uff23D\uff24E\uff25F\uff26G\uff27H\uff28"),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102,
+                                102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101,
+                                100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 16, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80,
+                                -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 32, 0, 65, -1, 33, 0, 66, -1, 34, 0, 67, -1, 35, 0, 68, -1, 36, 0, 69, -1, 37,
+                                0, 70, -1, 38, 0, 71, -1, 39, 0, 72, -1, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                0, 0, 0, 0, 0, 120 } },
+                new Object[] {
+                        new StringBuffer("\uff21A\uff22B\uff23C\uff24D\uff25E\uff26F\uff27G\uff28H"),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102,
+                                102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101,
+                                100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 16, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80,
+                                -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 32, -1, 33, 0, 65, -1, 34, 0, 66, -1, 35, 0, 67, -1, 36, 0, 68, -1, 37, 0, 69,
+                                -1, 38, 0, 70, -1, 39, 0, 71, -1, 40, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                0, 0, 0, 0, 0, 120 } },
+                new Object[] {
+                        new StringBuffer("\ud801\udc00\ud801\udc01\uff21A"),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102,
+                                102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101,
+                                100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 6, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80,
+                                -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 22, -40, 1, -36, 0, -40, 1, -36, 1, -1, 33, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } },
+                new Object[] {
+                        new StringBuffer("\uff21\uff22\uff21\uff22\uff21\uff22\uff21\uff22\uff21\uff22"),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102,
+                                102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101,
+                                100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 10, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80,
+                                -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 26, -1, 33, -1, 34, -1, 33, -1, 34, -1, 33, -1, 34, -1, 33, -1, 34, -1, 33, -1,
+                                34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } } };
+    }
+
+    /*
+     * Verify serialization works between Compact StringBuffer/Legacy StringBuffer
+     */
+    @Test(dataProvider = "provider")
+    public void test(StringBuffer sbContent, byte[] baInJDK8) throws Exception {
+        // Serialize a StringBuffer object into byte array.
+        byte[] ba = serialize(sbContent);
+        assertEquals(ba, baInJDK8);
+        // Deserialize a StringBuffer object from byte array which is generated by previous JDK(build 1.8.0_45-b14).
+        Object obj = deserialize(ba);
+        assertEquals(obj.getClass(), StringBuffer.class);
+        assertTrue(equals((StringBuffer)obj, sbContent));
+    }
+
+    boolean equals(StringBuffer sb, StringBuffer expected) {
+        if(sb.length() == expected.length()
+                && sb.capacity() == expected.capacity()
+                && sb.toString().equals(expected.toString())) {
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/jdk/test/java/lang/StringBuffer/Exceptions.java b/jdk/test/java/lang/StringBuffer/Exceptions.java
index 5f68b80..061b3d7 100644
--- a/jdk/test/java/lang/StringBuffer/Exceptions.java
+++ b/jdk/test/java/lang/StringBuffer/Exceptions.java
@@ -94,7 +94,7 @@
 
         System.out.println("StringBuffer.replace(int start, int end, String str)");
         tryCatch("  -1, 2, \" \"",
-                 new StringIndexOutOfBoundsException(-1),
+                 new StringIndexOutOfBoundsException("start -1, end 2, length 7"),
                  new Runnable() {
                 public void run() {
                     StringBuffer sb = new StringBuffer("hilbert");
@@ -102,14 +102,14 @@
                 }});
 
         tryCatch("  7, 8, \" \"",
-                 new StringIndexOutOfBoundsException("start > length()"),
+                 new StringIndexOutOfBoundsException("start 7, end 6, length 6"),
                  new Runnable() {
                 public void run() {
                     StringBuffer sb = new StringBuffer("banach");
                     sb.replace(7, 8, " ");
                 }});
         tryCatch("  2, 1, \" \"",
-                 new StringIndexOutOfBoundsException("start > end"),
+                 new StringIndexOutOfBoundsException("start 2, end 1, length 7"),
                  new Runnable() {
                 public void run() {
                     StringBuffer sb = new StringBuffer("riemann");
diff --git a/jdk/test/java/lang/StringBuilder/BuilderForwarding.java b/jdk/test/java/lang/StringBuilder/BuilderForwarding.java
index a78c323..82e4010 100644
--- a/jdk/test/java/lang/StringBuilder/BuilderForwarding.java
+++ b/jdk/test/java/lang/StringBuilder/BuilderForwarding.java
@@ -264,4 +264,3 @@
         }
     }
 }
-
diff --git a/jdk/test/java/lang/StringBuilder/CompactStringBuilder.java b/jdk/test/java/lang/StringBuilder/CompactStringBuilder.java
new file mode 100644
index 0000000..7c69326
--- /dev/null
+++ b/jdk/test/java/lang/StringBuilder/CompactStringBuilder.java
@@ -0,0 +1,414 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.Arrays;
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+/*
+ * @test
+ * @bug 8054307 8077559
+ * @summary Tests Compact String. This test is testing StringBuilder
+ *          behavior related to Compact String.
+ * @run testng/othervm -XX:+CompactStrings CompactStringBuilder
+ * @run testng/othervm -XX:-CompactStrings CompactStringBuilder
+ */
+
+public class CompactStringBuilder {
+
+    /*
+     * Tests for "A"
+     */
+    @Test
+    public void testCompactStringBuilderForLatinA() {
+        final String ORIGIN = "A";
+        /*
+         * Because right now ASCII is the default encoding parameter for source
+         * code in JDK build environment, so we escape them. same as below.
+         */
+        check(new StringBuilder(ORIGIN).append(new char[] { '\uFF21' }),
+                "A\uFF21");
+        check(new StringBuilder(ORIGIN).append(new StringBuffer("\uFF21")),
+                "A\uFF21");
+        check(new StringBuilder(ORIGIN).append("\uFF21"), "A\uFF21");
+        check(new StringBuilder(ORIGIN).append(new StringBuffer("\uFF21")),
+                "A\uFF21");
+        check(new StringBuilder(ORIGIN).delete(0, 1), "");
+        check(new StringBuilder(ORIGIN).delete(0, 0), "A");
+        check(new StringBuilder(ORIGIN).deleteCharAt(0), "");
+        assertEquals(new StringBuilder(ORIGIN).indexOf("A", 0), 0);
+        assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21", 0), -1);
+        assertEquals(new StringBuilder(ORIGIN).indexOf("", 0), 0);
+        assertEquals(new StringBuilder(ORIGIN).insert(1, "\uD801\uDC00")
+                .indexOf("A", 0), 0);
+        assertEquals(new StringBuilder(ORIGIN).insert(0, "\uD801\uDC00")
+                .indexOf("A", 0), 2);
+        check(new StringBuilder(ORIGIN).insert(0, new char[] {}), "A");
+        check(new StringBuilder(ORIGIN).insert(1, new char[] { '\uFF21' }),
+                "A\uFF21");
+        check(new StringBuilder(ORIGIN).insert(0, new char[] { '\uFF21' }),
+                "\uFF21A");
+        check(new StringBuilder(ORIGIN).insert(0, new StringBuffer("\uFF21")),
+                "\uFF21A");
+        check(new StringBuilder(ORIGIN).insert(1, new StringBuffer("\uFF21")),
+                "A\uFF21");
+        check(new StringBuilder(ORIGIN).insert(0, ""), "A");
+        check(new StringBuilder(ORIGIN).insert(0, "\uFF21"), "\uFF21A");
+        check(new StringBuilder(ORIGIN).insert(1, "\uFF21"), "A\uFF21");
+        assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), 0);
+        assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), -1);
+        assertEquals(new StringBuilder(ORIGIN).lastIndexOf(""), 1);
+        check(new StringBuilder(ORIGIN).replace(0, 0, "\uFF21"), "\uFF21A");
+        check(new StringBuilder(ORIGIN).replace(0, 1, "\uFF21"), "\uFF21");
+        checkSetCharAt(new StringBuilder(ORIGIN), 0, '\uFF21', "\uFF21");
+        checkSetLength(new StringBuilder(ORIGIN), 0, "");
+        checkSetLength(new StringBuilder(ORIGIN), 1, "A");
+        check(new StringBuilder(ORIGIN).substring(0), "A");
+        check(new StringBuilder(ORIGIN).substring(1), "");
+    }
+
+    /*
+     * Tests for "\uFF21"
+     */
+    @Test
+    public void testCompactStringBuilderForNonLatinA() {
+        final String ORIGIN = "\uFF21";
+        check(new StringBuilder(ORIGIN).append(new char[] { 'A' }), "\uFF21A");
+        check(new StringBuilder(ORIGIN).append(new StringBuffer("A")), "\uFF21A");
+        check(new StringBuilder(ORIGIN).append("A"), "\uFF21A");
+        check(new StringBuilder(ORIGIN).append(new StringBuffer("A")), "\uFF21A");
+        check(new StringBuilder(ORIGIN).delete(0, 1), "");
+        check(new StringBuilder(ORIGIN).delete(0, 0), "\uFF21");
+        check(new StringBuilder(ORIGIN).deleteCharAt(0), "");
+        assertEquals(new StringBuilder(ORIGIN).indexOf("A", 0), -1);
+        assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21", 0), 0);
+        assertEquals(new StringBuilder(ORIGIN).indexOf("", 0), 0);
+        check(new StringBuilder(ORIGIN).insert(0, new char[] {}), "\uFF21");
+        check(new StringBuilder(ORIGIN).insert(1, new char[] { 'A' }), "\uFF21A");
+        check(new StringBuilder(ORIGIN).insert(0, new char[] { 'A' }), "A\uFF21");
+        check(new StringBuilder(ORIGIN).insert(0, new StringBuffer("A")),
+                "A\uFF21");
+        check(new StringBuilder(ORIGIN).insert(1, new StringBuffer("A")),
+                "\uFF21A");
+        check(new StringBuilder(ORIGIN).insert(0, ""), "\uFF21");
+        check(new StringBuilder(ORIGIN).insert(0, "A"), "A\uFF21");
+        check(new StringBuilder(ORIGIN).insert(1, "A"), "\uFF21A");
+        assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), -1);
+        assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), 0);
+        assertEquals(new StringBuilder(ORIGIN).lastIndexOf(""), 1);
+        check(new StringBuilder(ORIGIN).replace(0, 0, "A"), "A\uFF21");
+        check(new StringBuilder(ORIGIN).replace(0, 1, "A"), "A");
+        checkSetCharAt(new StringBuilder(ORIGIN), 0, 'A', "A");
+        checkSetLength(new StringBuilder(ORIGIN), 0, "");
+        checkSetLength(new StringBuilder(ORIGIN), 1, "\uFF21");
+        check(new StringBuilder(ORIGIN).substring(0), "\uFF21");
+        check(new StringBuilder(ORIGIN).substring(1), "");
+    }
+
+    /*
+     * Tests for "\uFF21A"
+     */
+    @Test
+    public void testCompactStringBuilderForMixedA1() {
+        final String ORIGIN = "\uFF21A";
+        check(new StringBuilder(ORIGIN).delete(0, 1), "A");
+        check(new StringBuilder(ORIGIN).delete(1, 2), "\uFF21");
+        check(new StringBuilder(ORIGIN).deleteCharAt(1), "\uFF21");
+        check(new StringBuilder(ORIGIN).deleteCharAt(0), "A");
+        assertEquals(new StringBuilder(ORIGIN).indexOf("A", 0), 1);
+        assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21", 0), 0);
+        assertEquals(new StringBuilder(ORIGIN).indexOf("", 0), 0);
+        check(new StringBuilder(ORIGIN).insert(1, new char[] { 'A' }),
+                "\uFF21AA");
+        check(new StringBuilder(ORIGIN).insert(0, new char[] { '\uFF21' }),
+                "\uFF21\uFF21A");
+        assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), 1);
+        assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), 0);
+        assertEquals(new StringBuilder(ORIGIN).lastIndexOf(""), 2);
+        check(new StringBuilder(ORIGIN).replace(0, 0, "A"), "A\uFF21A");
+        check(new StringBuilder(ORIGIN).replace(0, 1, "A"), "AA");
+        checkSetCharAt(new StringBuilder(ORIGIN), 0, 'A', "AA");
+        checkSetLength(new StringBuilder(ORIGIN), 0, "");
+        checkSetLength(new StringBuilder(ORIGIN), 1, "\uFF21");
+        check(new StringBuilder(ORIGIN).substring(0), "\uFF21A");
+        check(new StringBuilder(ORIGIN).substring(1), "A");
+    }
+
+    /*
+     * Tests for "A\uFF21"
+     */
+    @Test
+    public void testCompactStringBuilderForMixedA2() {
+        final String ORIGIN = "A\uFF21";
+        check(new StringBuilder(ORIGIN).replace(1, 2, "A"), "AA");
+        checkSetLength(new StringBuilder(ORIGIN), 1, "A");
+        check(new StringBuilder(ORIGIN).substring(0), "A\uFF21");
+        check(new StringBuilder(ORIGIN).substring(1), "\uFF21");
+        check(new StringBuilder(ORIGIN).substring(0, 1), "A");
+    }
+
+    /*
+     * Tests for "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A"
+     */
+    @Test
+    public void testCompactStringBuilderForDuplicatedMixedA1() {
+        final String ORIGIN = "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A";
+        checkSetLength(new StringBuilder(ORIGIN), 1, "\uFF21");
+        assertEquals(new StringBuilder(ORIGIN).indexOf("A", 5), 5);
+        assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21", 5), 6);
+        assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), 9);
+        assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), 8);
+        assertEquals(new StringBuilder(ORIGIN).lastIndexOf(""), 10);
+        check(new StringBuilder(ORIGIN).substring(9), "A");
+        check(new StringBuilder(ORIGIN).substring(8), "\uFF21A");
+    }
+
+    /*
+     * Tests for "A\uFF21A\uFF21A\uFF21A\uFF21A\uFF21"
+     */
+    @Test
+    public void testCompactStringBuilderForDuplicatedMixedA2() {
+        final String ORIGIN = "A\uFF21A\uFF21A\uFF21A\uFF21A\uFF21";
+        checkSetLength(new StringBuilder(ORIGIN), 1, "A");
+        assertEquals(new StringBuilder(ORIGIN).indexOf("A", 5), 6);
+        assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21", 5), 5);
+        assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), 8);
+        assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), 9);
+        check(new StringBuilder(ORIGIN).substring(9), "\uFF21");
+        check(new StringBuilder(ORIGIN).substring(8), "A\uFF21");
+    }
+
+    /*
+     * Tests for "\uD801\uDC00\uD801\uDC01"
+     */
+    @Test
+    public void testCompactStringForSupplementaryCodePoint() {
+        final String ORIGIN = "\uD801\uDC00\uD801\uDC01";
+        check(new StringBuilder(ORIGIN).append("A"), "\uD801\uDC00\uD801\uDC01A");
+        check(new StringBuilder(ORIGIN).append("\uFF21"),
+                "\uD801\uDC00\uD801\uDC01\uFF21");
+        check(new StringBuilder(ORIGIN).appendCodePoint('A'),
+                "\uD801\uDC00\uD801\uDC01A");
+        check(new StringBuilder(ORIGIN).appendCodePoint('\uFF21'),
+                "\uD801\uDC00\uD801\uDC01\uFF21");
+        assertEquals(new StringBuilder(ORIGIN).charAt(0), '\uD801');
+        assertEquals(new StringBuilder(ORIGIN).codePointAt(0),
+                Character.codePointAt(ORIGIN, 0));
+        assertEquals(new StringBuilder(ORIGIN).codePointAt(1),
+                Character.codePointAt(ORIGIN, 1));
+        assertEquals(new StringBuilder(ORIGIN).codePointBefore(2),
+                Character.codePointAt(ORIGIN, 0));
+        assertEquals(new StringBuilder(ORIGIN).codePointCount(1, 3), 2);
+        check(new StringBuilder(ORIGIN).delete(0, 2), "\uD801\uDC01");
+        check(new StringBuilder(ORIGIN).delete(0, 3), "\uDC01");
+        check(new StringBuilder(ORIGIN).deleteCharAt(1), "\uD801\uD801\uDC01");
+        checkGetChars(new StringBuilder(ORIGIN), 0, 3, new char[] { '\uD801',
+                '\uDC00', '\uD801' });
+        assertEquals(new StringBuilder(ORIGIN).indexOf("\uD801\uDC01"), 2);
+        assertEquals(new StringBuilder(ORIGIN).indexOf("\uDC01"), 3);
+        assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21"), -1);
+        assertEquals(new StringBuilder(ORIGIN).indexOf("A"), -1);
+        check(new StringBuilder(ORIGIN).insert(0, "\uFF21"),
+                "\uFF21\uD801\uDC00\uD801\uDC01");
+        check(new StringBuilder(ORIGIN).insert(1, "\uFF21"),
+                "\uD801\uFF21\uDC00\uD801\uDC01");
+        check(new StringBuilder(ORIGIN).insert(1, "A"),
+                "\uD801A\uDC00\uD801\uDC01");
+        assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uDC00\uD801"), 1);
+        assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uD801"), 2);
+        assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), -1);
+        assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), -1);
+        assertEquals(new StringBuilder(ORIGIN).length(), 4);
+        assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(1, 1), 2);
+        assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(0, 1), 2);
+        check(new StringBuilder(ORIGIN).replace(0, 2, "A"), "A\uD801\uDC01");
+        check(new StringBuilder(ORIGIN).replace(0, 3, "A"), "A\uDC01");
+        check(new StringBuilder(ORIGIN).replace(0, 2, "\uFF21"),
+                "\uFF21\uD801\uDC01");
+        check(new StringBuilder(ORIGIN).replace(0, 3, "\uFF21"), "\uFF21\uDC01");
+        check(new StringBuilder(ORIGIN).reverse(), "\uD801\uDC01\uD801\uDC00");
+        checkSetCharAt(new StringBuilder(ORIGIN), 1, '\uDC01',
+                "\uD801\uDC01\uD801\uDC01");
+        checkSetCharAt(new StringBuilder(ORIGIN), 1, 'A', "\uD801A\uD801\uDC01");
+        checkSetLength(new StringBuilder(ORIGIN), 2, "\uD801\uDC00");
+        checkSetLength(new StringBuilder(ORIGIN), 3, "\uD801\uDC00\uD801");
+        check(new StringBuilder(ORIGIN).substring(1, 3), "\uDC00\uD801");
+    }
+
+    /*
+     * Tests for "A\uD801\uDC00\uFF21"
+     */
+    @Test
+    public void testCompactStringForSupplementaryCodePointMixed1() {
+        final String ORIGIN = "A\uD801\uDC00\uFF21";
+        assertEquals(new StringBuilder(ORIGIN).codePointBefore(3),
+                Character.codePointAt(ORIGIN, 1));
+        assertEquals(new StringBuilder(ORIGIN).codePointBefore(2), '\uD801');
+        assertEquals(new StringBuilder(ORIGIN).codePointBefore(1), 'A');
+        assertEquals(new StringBuilder(ORIGIN).codePointCount(0, 3), 2);
+        assertEquals(new StringBuilder(ORIGIN).codePointCount(0, 4), 3);
+        check(new StringBuilder(ORIGIN).delete(0, 1), "\uD801\uDC00\uFF21");
+        check(new StringBuilder(ORIGIN).delete(0, 1).delete(2, 3),
+                "\uD801\uDC00");
+        check(new StringBuilder(ORIGIN).deleteCharAt(3).deleteCharAt(0),
+                "\uD801\uDC00");
+        assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21"), 3);
+        assertEquals(new StringBuilder(ORIGIN).indexOf("A"), 0);
+        assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), 3);
+        assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), 0);
+        assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(0, 1), 1);
+        assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(1, 1), 3);
+        check(new StringBuilder(ORIGIN).replace(1, 3, "A"), "AA\uFF21");
+        check(new StringBuilder(ORIGIN).replace(1, 4, "A"), "AA");
+        check(new StringBuilder(ORIGIN).replace(1, 4, ""), "A");
+        check(new StringBuilder(ORIGIN).reverse(), "\uFF21\uD801\uDC00A");
+        checkSetLength(new StringBuilder(ORIGIN), 1, "A");
+        check(new StringBuilder(ORIGIN).substring(0, 1), "A");
+    }
+
+    /*
+     * Tests for "\uD801\uDC00\uFF21A"
+     */
+    @Test
+    public void testCompactStringForSupplementaryCodePointMixed2() {
+        final String ORIGIN = "\uD801\uDC00\uFF21A";
+        assertEquals(new StringBuilder(ORIGIN).codePointBefore(3),
+                Character.codePointAt(ORIGIN, 2));
+        assertEquals(new StringBuilder(ORIGIN).codePointBefore(2),
+                Character.codePointAt(ORIGIN, 0));
+        assertEquals(new StringBuilder(ORIGIN).codePointBefore(1), '\uD801');
+        assertEquals(new StringBuilder(ORIGIN).codePointCount(0, 3), 2);
+        assertEquals(new StringBuilder(ORIGIN).codePointCount(0, 4), 3);
+        check(new StringBuilder(ORIGIN).delete(0, 2), "\uFF21A");
+        check(new StringBuilder(ORIGIN).delete(0, 3), "A");
+        check(new StringBuilder(ORIGIN).deleteCharAt(0).deleteCharAt(0)
+                .deleteCharAt(0), "A");
+        assertEquals(new StringBuilder(ORIGIN).indexOf("A"), 3);
+        assertEquals(new StringBuilder(ORIGIN).delete(0, 3).indexOf("A"), 0);
+        assertEquals(new StringBuilder(ORIGIN).replace(0, 3, "B").indexOf("A"),
+                1);
+        assertEquals(new StringBuilder(ORIGIN).substring(3, 4).indexOf("A"), 0);
+        assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(1, 1), 2);
+        assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(0, 1), 2);
+        assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(2, 1), 3);
+        check(new StringBuilder(ORIGIN).replace(0, 3, "B"), "BA");
+        check(new StringBuilder(ORIGIN).reverse(), "A\uFF21\uD801\uDC00");
+    }
+
+    /*
+     * Tests for "\uD801A\uDC00\uFF21"
+     */
+    @Test
+    public void testCompactStringForSupplementaryCodePointMixed3() {
+        final String ORIGIN = "\uD801A\uDC00\uFF21";
+        assertEquals(new StringBuilder(ORIGIN).codePointAt(1), 'A');
+        assertEquals(new StringBuilder(ORIGIN).codePointAt(3), '\uFF21');
+        assertEquals(new StringBuilder(ORIGIN).codePointBefore(1), '\uD801');
+        assertEquals(new StringBuilder(ORIGIN).codePointBefore(2), 'A');
+        assertEquals(new StringBuilder(ORIGIN).codePointBefore(3), '\uDC00');
+        assertEquals(new StringBuilder(ORIGIN).codePointCount(0, 3), 3);
+        assertEquals(new StringBuilder(ORIGIN).codePointCount(1, 3), 2);
+        assertEquals(new StringBuilder(ORIGIN).delete(0, 1).delete(1, 3)
+                .indexOf("A"), 0);
+        assertEquals(
+                new StringBuilder(ORIGIN).replace(0, 1, "B").replace(2, 4, "C")
+                        .indexOf("A"), 1);
+        assertEquals(new StringBuilder(ORIGIN).substring(1, 4).substring(0, 1)
+                .indexOf("A"), 0);
+        assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(0, 1), 1);
+        assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(1, 1), 2);
+        assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(2, 1), 3);
+        check(new StringBuilder(ORIGIN).reverse(), "\uFF21\uDC00A\uD801");
+    }
+
+    /*
+     * Tests for "A\uDC01\uFF21\uD801"
+     */
+    @Test
+    public void testCompactStringForSupplementaryCodePointMixed4() {
+        final String ORIGIN = "A\uDC01\uFF21\uD801";
+        assertEquals(new StringBuilder(ORIGIN).codePointAt(1), '\uDC01');
+        assertEquals(new StringBuilder(ORIGIN).codePointAt(3), '\uD801');
+        assertEquals(new StringBuilder(ORIGIN).codePointBefore(1), 'A');
+        assertEquals(new StringBuilder(ORIGIN).codePointBefore(2), '\uDC01');
+        assertEquals(new StringBuilder(ORIGIN).codePointBefore(3), '\uFF21');
+        assertEquals(new StringBuilder(ORIGIN).codePointCount(0, 3), 3);
+        assertEquals(new StringBuilder(ORIGIN).codePointCount(1, 3), 2);
+        assertEquals(new StringBuilder(ORIGIN).delete(1, 4).indexOf("A"), 0);
+        assertEquals(new StringBuilder(ORIGIN).replace(1, 4, "B").indexOf("A"),
+                0);
+        assertEquals(new StringBuilder(ORIGIN).substring(0, 1).indexOf("A"), 0);
+        assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(0, 1), 1);
+        assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(1, 1), 2);
+        assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(2, 1), 3);
+        check(new StringBuilder(ORIGIN).reverse(), "\uD801\uFF21\uDC01A");
+    }
+
+    private void checkGetChars(StringBuilder sb, int srcBegin, int srcEnd,
+            char expected[]) {
+        char[] dst = new char[srcEnd - srcBegin];
+        sb.getChars(srcBegin, srcEnd, dst, 0);
+        assertTrue(Arrays.equals(dst, expected));
+    }
+
+    private void checkSetCharAt(StringBuilder sb, int index, char ch,
+            String expected) {
+        sb.setCharAt(index, ch);
+        check(sb, expected);
+    }
+
+    private void checkSetLength(StringBuilder sb, int newLength, String expected) {
+        sb.setLength(newLength);
+        check(sb, expected);
+    }
+
+    private void check(StringBuilder sb, String expected) {
+        check(sb.toString(), expected);
+    }
+
+    private void check(String str, String expected) {
+        assertTrue(str.equals(expected), String.format(
+                "Get (%s) but expect (%s), ", escapeNonASCIIs(str),
+                escapeNonASCIIs(expected)));
+    }
+
+    /*
+     * Because right now system default charset in JPRT environment is only
+     * guaranteed to support ASCII characters in log, so we escape them.
+     */
+    private String escapeNonASCIIs(String str) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < str.length(); i++) {
+            char c = str.charAt(i);
+            if (c > 0x7F) {
+                sb.append("\\u").append(Integer.toHexString((int) c));
+            } else {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+}
diff --git a/jdk/test/java/lang/StringBuilder/CompactStringBuilderSerialization.java b/jdk/test/java/lang/StringBuilder/CompactStringBuilderSerialization.java
new file mode 100644
index 0000000..0c622d7
--- /dev/null
+++ b/jdk/test/java/lang/StringBuilder/CompactStringBuilderSerialization.java
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.*;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static jdk.testlibrary.SerializationUtils.*;
+import static org.testng.Assert.*;
+
+/*
+ * @test
+ * @bug 8077559
+ * @library /lib/testlibrary
+ * @build jdk.testlibrary.SerializationUtils
+ * @summary Tests Compact String. This one is testing StringBuilder serialization
+ *          among -XX:+CompactStrings/-XX:-CompactStrings/LegacyStringBuilder
+ * @run testng/othervm -XX:+CompactStrings CompactStringBuilderSerialization
+ * @run testng/othervm -XX:-CompactStrings CompactStringBuilderSerialization
+ */
+
+public class CompactStringBuilderSerialization {
+    @DataProvider
+    public Object[][] provider() {
+        return new Object[][] {
+                // every byte array is serialized from corresponding StringBuffer object
+                // by previous JDK(build 1.8.0_45-b14).
+                new Object[] {
+                        new StringBuilder(""),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105,
+                                108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 0, 117, 114, 0, 2, 91, 67, -80, 38,
+                                102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } },
+                new Object[] {
+                        new StringBuilder("A"),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105,
+                                108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 1, 117, 114, 0, 2, 91, 67, -80, 38,
+                                102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 17, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } },
+                new Object[] {
+                        new StringBuilder("AB"),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105,
+                                108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 2, 117, 114, 0, 2, 91, 67, -80, 38,
+                                102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 18, 0, 65, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } },
+                new Object[] {
+                        new StringBuilder("abcdefghijk"),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105,
+                                108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 11, 117, 114, 0, 2, 91, 67, -80, 38,
+                                102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 27, 0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0, 103, 0, 104, 0,
+                                105, 0, 106, 0, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } },
+                new Object[] {
+                        new StringBuilder("\uff21"),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105,
+                                108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 1, 117, 114, 0, 2, 91, 67, -80, 38,
+                                102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 17, -1, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } },
+                new Object[] {
+                        new StringBuilder("\uff21\uff22"),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105,
+                                108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 2, 117, 114, 0, 2, 91, 67, -80, 38,
+                                102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 18, -1, 33, -1, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } },
+                new Object[] {
+                        new StringBuilder("\uff21A\uff21A\uff21A\uff21A\uff21A"),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105,
+                                108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 10, 117, 114, 0, 2, 91, 67, -80, 38,
+                                102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 26, -1, 33, 0, 65, -1, 33, 0, 65, -1, 33, 0, 65, -1, 33, 0, 65, -1,
+                                33, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } },
+                new Object[] {
+                        new StringBuilder("A\uff21B\uff22C\uff23D\uff24E\uff25F\uff26G\uff27H\uff28"),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105,
+                                108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 16, 117, 114, 0, 2, 91, 67, -80, 38,
+                                102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 32, 0, 65, -1, 33, 0, 66, -1, 34, 0, 67, -1, 35, 0, 68, -1, 36, 0,
+                                69, -1, 37, 0, 70, -1, 38, 0, 71, -1, 39, 0, 72, -1, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } },
+                new Object[] {
+                        new StringBuilder("\uff21A\uff22B\uff23C\uff24D\uff25E\uff26F\uff27G\uff28H"),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105,
+                                108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 16, 117, 114, 0, 2, 91, 67, -80, 38,
+                                102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 32, -1, 33, 0, 65, -1, 34, 0, 66, -1, 35, 0, 67, -1, 36, 0, 68, -1,
+                                37, 0, 69, -1, 38, 0, 70, -1, 39, 0, 71, -1, 40, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                                0, 0, 0, 0, 0, 0, 0, 0, 120 } },
+                new Object[] {
+                        new StringBuilder("\ud801\udc00\ud801\udc01\uff21A"),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105,
+                                108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 6, 117, 114, 0, 2, 91, 67, -80, 38,
+                                102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 22, -40, 1, -36, 0, -40, 1, -36, 1, -1, 33, 0, 65, 0, 0, 0, 0, 0, 0,
+                                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } },
+                new Object[] {
+                        new StringBuilder("\uff21\uff22\uff21\uff22\uff21\uff22\uff21\uff22\uff21\uff22"),
+                        new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105,
+                                108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 10, 117, 114, 0, 2, 91, 67, -80, 38,
+                                102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 26, -1, 33, -1, 34, -1, 33, -1, 34, -1, 33, -1, 34, -1, 33, -1, 34,
+                                -1, 33, -1, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } } };
+    }
+
+    /*
+     * Verify serialization works between Compact StringBuilder/Legacy StringBuilder
+     */
+    @Test(dataProvider = "provider")
+    public void test(StringBuilder sbContent, byte[] baInJDK8) throws Exception {
+        // Serialize a StringBuilder object into byte array.
+        byte[] ba = serialize(sbContent);
+        assertEquals(ba, baInJDK8);
+        // Deserialize a StringBuilder object from byte array which is generated by previous JDK(build 1.8.0_45-b14).
+        Object obj = deserialize(ba);
+        assertEquals(obj.getClass(), StringBuilder.class);
+        assertTrue(equals((StringBuilder)obj, sbContent));
+    }
+
+    boolean equals(StringBuilder sb, StringBuilder expected) {
+        if(sb.length() == expected.length()
+                && sb.capacity() == expected.capacity()
+                && sb.toString().equals(expected.toString())) {
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/jdk/test/java/lang/StringBuilder/Exceptions.java b/jdk/test/java/lang/StringBuilder/Exceptions.java
index e1686b0..bee53bc 100644
--- a/jdk/test/java/lang/StringBuilder/Exceptions.java
+++ b/jdk/test/java/lang/StringBuilder/Exceptions.java
@@ -94,21 +94,21 @@
 
         System.out.println("StringBuilder.replace(int start, int end, String str)");
         tryCatch("  -1, 2, \" \"",
-                 new StringIndexOutOfBoundsException(-1),
+                 new StringIndexOutOfBoundsException("start -1, end 2, length 7"),
                  new Runnable() {
                 public void run() {
                     StringBuilder sb = new StringBuilder("hilbert");
                     sb.replace(-1, 2, " ");
                 }});
         tryCatch("  7, 8, \" \"",
-                 new StringIndexOutOfBoundsException("start > length()"),
+                 new StringIndexOutOfBoundsException("start 7, end 6, length 6"),
                  new Runnable() {
                 public void run() {
                     StringBuilder sb = new StringBuilder("banach");
                     sb.replace(7, 8, " ");
                 }});
         tryCatch("  2, 1, \" \"",
-                 new StringIndexOutOfBoundsException("start > end"),
+                 new StringIndexOutOfBoundsException("start 2, end 1, length 7"),
                  new Runnable() {
                 public void run() {
                     StringBuilder sb = new StringBuilder("riemann");
diff --git a/jdk/test/lib/testlibrary/jdk/testlibrary/SerializationUtils.java b/jdk/test/lib/testlibrary/jdk/testlibrary/SerializationUtils.java
new file mode 100644
index 0000000..3dbc666
--- /dev/null
+++ b/jdk/test/lib/testlibrary/jdk/testlibrary/SerializationUtils.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ *
+ * 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 jdk.testlibrary;
+
+import java.io.*;
+
+/**
+ * Common library for various test serialization utility functions.
+ */
+public final class SerializationUtils {
+    /*
+     * Serialize an object into byte array.
+     */
+    public static byte[] serialize(Object obj) throws Exception {
+        try (ByteArrayOutputStream bs = new ByteArrayOutputStream();
+                ObjectOutputStream out = new ObjectOutputStream(bs);) {
+            out.writeObject(obj);
+            return bs.toByteArray();
+        }
+    }
+
+    /*
+     * Deserialize an object from byte array.
+     */
+    public static Object deserialize(byte[] ba) throws Exception {
+        try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(ba));) {
+            return in.readObject();
+        }
+    }
+}
diff --git a/jdk/test/sun/nio/cs/TestStringCoding.java b/jdk/test/sun/nio/cs/TestStringCoding.java
index f9f7021..4dd85f4 100644
--- a/jdk/test/sun/nio/cs/TestStringCoding.java
+++ b/jdk/test/sun/nio/cs/TestStringCoding.java
@@ -22,7 +22,7 @@
  */
 
 /* @test
- * @bug 6636323 6636319 7040220 7096080 7183053 8080248
+ * @bug 6636323 6636319 7040220 7096080 7183053 8080248 8054307
  * @summary Test if StringCoding and NIO result have the same de/encoding result
  * @modules java.base/sun.nio.cs
  * @run main/othervm/timeout=2000 TestStringCoding
@@ -36,41 +36,61 @@
 public class TestStringCoding {
     public static void main(String[] args) throws Throwable {
 
+        // full bmp first
+        char[] bmp = new char[0x10000];
+        for (int i = 0; i < 0x10000; i++) {
+            bmp[i] = (char)i;
+        }
+        char[] latin = Arrays.copyOf(bmp, 0x100);
+        char[] ascii =  Arrays.copyOf(bmp, 0x80);
+
+        byte[] latinBA = new byte[0x100];
+        for (int i = 0; i < 0x100; i++) {
+            latinBA[i] = (byte)i;
+        }
+        byte[] asciiBA =  Arrays.copyOf(latinBA, 0x80);
+
         for (Boolean hasSM: new boolean[] { false, true }) {
-            if (hasSM)
+            if (hasSM) {
                 System.setSecurityManager(new PermissiveSecurityManger());
+            }
             for (Charset cs:  Charset.availableCharsets().values()) {
                 if ("ISO-2022-CN".equals(cs.name()) ||
                     "x-COMPOUND_TEXT".equals(cs.name()) ||
                     "x-JISAutoDetect".equals(cs.name()))
                     continue;
                 System.out.printf("Testing(sm=%b) " + cs.name() + "....", hasSM);
-                // full bmp first
-                char[] bmpCA = new char[0x10000];
-                for (int i = 0; i < 0x10000; i++) {
-                     bmpCA[i] = (char)i;
-                }
-                byte[] sbBA = new byte[0x100];
-                for (int i = 0; i < 0x100; i++) {
-                    sbBA[i] = (byte)i;
-                }
-                test(cs, bmpCA, sbBA);
+
+                testNewString(cs, testGetBytes(cs, new String(bmp)));
+                testNewString(cs, testGetBytes(cs, new String(latin)));
+                testNewString(cs, testGetBytes(cs, new String(ascii)));
+                testGetBytes(cs, testNewString(cs, latinBA));
+                testGetBytes(cs, testNewString(cs, asciiBA));
+
                 // "randomed" sizes
                 Random rnd = new Random();
                 for (int i = 0; i < 10; i++) {
-                    int clen = rnd.nextInt(0x10000);
-                    int blen = rnd.nextInt(0x100);
                     //System.out.printf("    blen=%d, clen=%d%n", blen, clen);
-                    test(cs, Arrays.copyOf(bmpCA, clen), Arrays.copyOf(sbBA, blen));
+                    char[] bmp0 = Arrays.copyOf(bmp, rnd.nextInt(0x10000));
+                    testNewString(cs, testGetBytes(cs, new String(bmp0)));
                     //add a pair of surrogates
-                    int pos = clen / 2;
-                    if ((pos + 1) < blen) {
-                        bmpCA[pos] = '\uD800';
-                        bmpCA[pos+1] = '\uDC00';
+                    int pos = bmp0.length / 2;
+                    if ((pos + 1) < bmp0.length) {
+                        bmp0[pos] = '\uD800';
+                        bmp0[pos+1] = '\uDC00';
                     }
-                    test(cs, Arrays.copyOf(bmpCA, clen), Arrays.copyOf(sbBA, blen));
-                }
+                    testNewString(cs, testGetBytes(cs, new String(bmp0)));
 
+                    char[] latin0 = Arrays.copyOf(latin, rnd.nextInt(0x100));
+                    char[] ascii0 = Arrays.copyOf(ascii, rnd.nextInt(0x80));
+                    byte[] latinBA0 = Arrays.copyOf(latinBA, rnd.nextInt(0x100));
+                    byte[] asciiBA0 = Arrays.copyOf(asciiBA, rnd.nextInt(0x80));
+                    testNewString(cs, testGetBytes(cs, new String(latin0)));
+                    testNewString(cs, testGetBytes(cs, new String(ascii0)));
+                    testGetBytes(cs, testNewString(cs, latinBA0));
+                    testGetBytes(cs, testNewString(cs, asciiBA0));
+                }
+                testSurrogates(cs);
                 testMixed(cs);
                 System.out.println("done!");
             }
@@ -109,8 +129,9 @@
 
         //getBytes(cs);
         bmpBA = bmpStr.getBytes(cs);
-        if (!Arrays.equals(bmpBA, baNIO))
+        if (!Arrays.equals(bmpBA, baNIO)) {
             throw new RuntimeException("getBytes(cs) failed  -> " + cs.name());
+        }
 
         //new String(csn);
         String strSC = new String(bmpBA, cs.name());
@@ -118,49 +139,61 @@
         if(!strNIO.equals(strSC)) {
             throw new RuntimeException("new String(csn) failed  -> " + cs.name());
         }
-
         //new String(cs);
         strSC = new String(bmpBA, cs);
-        if (!strNIO.equals(strSC))
+        if (!strNIO.equals(strSC)) {
             throw new RuntimeException("new String(cs) failed  -> " + cs.name());
-
+        }
     }
 
-    static void test(Charset cs, char[] bmpCA, byte[] sbBA) throws Throwable {
-        String bmpStr = new String(bmpCA);
-        CharsetDecoder dec = cs.newDecoder()
-            .onMalformedInput(CodingErrorAction.REPLACE)
-            .onUnmappableCharacter(CodingErrorAction.REPLACE);
+    static byte[] getBytes(CharsetEncoder enc, String str) throws Throwable {
+        ByteBuffer bf = enc.reset().encode(CharBuffer.wrap(str.toCharArray()));
+        byte[] ba = new byte[bf.limit()];
+        bf.get(ba, 0, ba.length);
+        return ba;
+    }
+
+    static byte[] testGetBytes(Charset cs, String str) throws Throwable {
         CharsetEncoder enc = cs.newEncoder()
             .onMalformedInput(CodingErrorAction.REPLACE)
             .onUnmappableCharacter(CodingErrorAction.REPLACE);
-
         //getBytes(csn);
-        byte[] baSC = bmpStr.getBytes(cs.name());
-        ByteBuffer bf = enc.reset().encode(CharBuffer.wrap(bmpCA));
-        byte[] baNIO = new byte[bf.limit()];
-        bf.get(baNIO, 0, baNIO.length);
-        if (!Arrays.equals(baSC, baNIO))
+        byte[] baSC = str.getBytes(cs.name());
+        byte[] baNIO = getBytes(enc, str);
+        if (!Arrays.equals(baSC, baNIO)) {
             throw new RuntimeException("getBytes(csn) failed  -> " + cs.name());
-
+        }
         //getBytes(cs);
-        baSC = bmpStr.getBytes(cs);
-        if (!Arrays.equals(baSC, baNIO))
+        baSC = str.getBytes(cs);
+        if (!Arrays.equals(baSC, baNIO)) {
             throw new RuntimeException("getBytes(cs) failed  -> " + cs.name());
+        }
+        return baSC;
+    }
 
+    static String testNewString(Charset cs, byte[] ba) throws Throwable {
+        CharsetDecoder dec = cs.newDecoder()
+            .onMalformedInput(CodingErrorAction.REPLACE)
+            .onUnmappableCharacter(CodingErrorAction.REPLACE);
         //new String(csn);
-        String strSC = new String(sbBA, cs.name());
-        String strNIO = dec.reset().decode(ByteBuffer.wrap(sbBA)).toString();
-
-        if(!strNIO.equals(strSC))
+        String strSC = new String(ba, cs.name());
+        String strNIO = dec.reset().decode(ByteBuffer.wrap(ba)).toString();
+        if(!strNIO.equals(strSC)) {
             throw new RuntimeException("new String(csn) failed  -> " + cs.name());
-
+        }
         //new String(cs);
-        strSC = new String(sbBA, cs);
-        if (!strNIO.equals(strSC))
-            throw new RuntimeException("new String(cs) failed  -> " + cs.name());
+        strSC = new String(ba, cs);
+        if (!strNIO.equals(strSC)) {
+            throw new RuntimeException("new String(cs)/bmp failed  -> " + cs.name());
+        }
+        return strSC;
+    }
 
+    static void testSurrogates(Charset cs) throws Throwable {
         //encode unmappable surrogates
+        CharsetEncoder enc = cs.newEncoder()
+            .onMalformedInput(CodingErrorAction.REPLACE)
+            .onUnmappableCharacter(CodingErrorAction.REPLACE);
         if (enc instanceof sun.nio.cs.ArrayEncoder &&
             cs.contains(Charset.forName("ASCII"))) {
             if (cs.name().equals("UTF-8") ||     // utf8 handles surrogates
diff --git a/jdk/test/sun/nio/cs/TestStringCodingUTF8.java b/jdk/test/sun/nio/cs/TestStringCodingUTF8.java
index b936838..ad07f19 100644
--- a/jdk/test/sun/nio/cs/TestStringCodingUTF8.java
+++ b/jdk/test/sun/nio/cs/TestStringCodingUTF8.java
@@ -22,7 +22,7 @@
  */
 
 /* @test
-   @bug 7040220
+   @bug 7040220 8054307
    @summary Test if StringCoding and NIO result have the same de/encoding result for UTF-8
  * @run main/othervm/timeout=2000 TestStringCodingUTF8
  * @key randomness
@@ -50,6 +50,18 @@
         }
         test(cs, bmp, 0, bmp.length);
 
+        char[] ascii = new char[0x80];
+        for (int i = 0; i < 0x80; i++) {
+            ascii[i] = (char)i;
+        }
+        test(cs, ascii, 0, ascii.length);
+
+        char[] latin1 = new char[0x100];
+        for (int i = 0; i < 0x100; i++) {
+            latin1[i] = (char)i;
+        }
+        test(cs, latin1, 0, latin1.length);
+
         ArrayList<Integer> list = new ArrayList<>(0x20000);
         for (int i = 0; i < 0x20000; i++) {
             list.add(i, i);