changeset 55916:6650d8850de0 string-tapas

Align code with JEP
author jlaskey
date Wed, 08 May 2019 09:36:43 -0300
parents 1077401aa2eb
children 67f14114d955
files src/java.base/share/classes/java/lang/MalformedEscapeException.java src/java.base/share/classes/java/lang/String.java src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java src/jdk.compiler/share/classes/com/sun/tools/javac/parser/UnicodeReader.java src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties test/langtools/tools/javac/MultilineStringLiteralAPI.java test/langtools/tools/javac/MultilineStringLiteralLang.java
diffstat 7 files changed, 557 insertions(+), 213 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/MalformedEscapeException.java	Wed May 08 09:36:43 2019 -0300
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2019, 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;
+
+/**
+ * Unchecked exception thrown when a string escape sequence does not
+ * adhere to escape rules defined in section 3.10.6 of the
+ * <cite>The Java&trade; Language Specification</cite>.
+ *
+ * @since 13
+ *
+ * //@deprecated  Preview feature associated with Multi-line String Literals.
+ *              Use at your own risk.
+ */
+// @Deprecated(forRemoval=true, since="13")
+public class MalformedEscapeException extends IllegalArgumentException {
+
+    private static final long serialVersionUID = 0L;
+
+    /*
+     * String index of malformed escape sequence.
+     */
+    private int index;
+
+    /**
+     * Constructs an <code>MalformedEscapeException</code> with no
+     * detail message and the index of malformed escape sequence.
+     *
+     * @param index index of malformed escape sequence
+     */
+    public MalformedEscapeException(int index) {
+        this.index = index;
+    }
+
+    /**
+     * Returns the string index of malformed escape sequence.
+     *
+     * @return index of malformed escape sequence
+     */
+    public int getIndex() {
+        return index;
+    }
+}
--- a/src/java.base/share/classes/java/lang/String.java	Fri Apr 19 13:08:29 2019 -0300
+++ b/src/java.base/share/classes/java/lang/String.java	Wed May 08 09:36:43 2019 -0300
@@ -2852,7 +2852,7 @@
         return indentStream(lines(), n).collect(Collectors.joining("\n", "", "\n"));
     }
 
-    private Stream<String> indentStream(Stream<String> stream, int n) {
+    private static Stream<String> indentStream(Stream<String> stream, int n) {
         if (n > 0) {
             final String spaces = " ".repeat(n);
             return stream.map(s -> spaces + s);
@@ -2875,9 +2875,8 @@
     }
 
     /**
-     * Removes vertical and horizontal white space margins from around the
-     * essential body of a multi-line string, while preserving relative
-     * indentation.
+     * Removes horizontal white space margins from the essential body of a
+     * multi-line string, while preserving relative indentation.
      * <p>
      * This string is first conceptually separated into lines as if by
      * {@link String#lines()}.
@@ -2895,8 +2894,6 @@
      * particular, the tab character {@code "\t"} (U+0009) is considered a
      * single character; it is not expanded.
      * <p>
-     * The first leading and last trailing lines, if blank, are removed.
-     * <p>
      * The trailing white space of each line is also removed.
      * </p>
      * <p>
@@ -2913,96 +2910,258 @@
      *
      * Example:
      * <blockquote><pre>
-     * String s = """
-     *            This is the first line
-     *                This is the second line
-     *            """.align();
+     * String s = ("   This is the first line\n" +
+     *             "       This is the second line\n").stripIndent();
      *
      * returns
      * This is the first line
      *     This is the second line
-     *
-     * String t = """
-     *                This is the first line
-     *                    This is the second line
-     *            """.align();
+     * </pre></blockquote>
+     *
+     * @return string with margins removed and line terminators normalized
+     *
+     * @see String#lines()
+     * @see String#isBlank()
+     * @see String#indent(int)
+     * @see Character#isWhitespace(int)
+     *
+     * @since 13
+     *
+     * @deprecated  Preview feature associated with Multi-line String Literals.
+     *              Use at your own risk.
+     */
+    @Deprecated(forRemoval=true, since="13")
+    public String stripIndent() {
+        return stripIndent(0);
+    }
+
+    /**
+     * Removes white space margins from around the essential body of a
+     * multi-line string, while preserving relative indentation and with
+     * optional indentation adjustment.
+     *
+     * @apiNote
+     * Examples:
+     * <blockquote><pre>
+     * String s = ("This is the first line\n" +
+     *             "    This is the second line\n").stripIndent(4);
      *
      * returns
      *     This is the first line
      *         This is the second line
      * </pre></blockquote>
      *
-     * @return string with margins removed and line terminators normalized
-     *
-     * @see String#lines()
-     * @see String#isBlank()
-     * @see String#indent(int)
-     * @see Character#isWhitespace(int)
+     * @param n  number of leading white space characters
+     *           to add or remove
+     *
+     * @return string with margins removed, indentation adjusted and
+     *         line terminators normalized
+     *
+     * @see String#stripIndent()
      *
      * @since 13
+     *
+     * @deprecated  Preview feature associated with Multi-line String Literals.
+     *              Use at your own risk.
      */
-    public String align() {
-        return align(0);
+    @Deprecated(forRemoval=true, since="13")
+    public String stripIndent(int n) {
+        if (isEmpty()) {
+            return "";
+        }
+        int outdent = Integer.MAX_VALUE;
+        boolean isNewLine = true;
+        int whitespaceCount = 0;
+        for (int i = 0; i < length(); i++) {
+            char ch = charAt(i);
+            if (ch == '\n' || ch == '\r') {
+                whitespaceCount = 0;
+                isNewLine = true;
+            } else if (Character.isWhitespace(ch)) {
+                whitespaceCount++;
+            } else {
+                if (isNewLine) {
+                    outdent = Math.min(outdent, whitespaceCount);
+                }
+                isNewLine = false;
+                whitespaceCount = 0;
+            }
+        }
+        if (isNewLine) {
+            outdent = Math.min(outdent, whitespaceCount);
+        }
+        return indentStream(lines(), n - outdent)
+                    .map(s -> s.stripTrailing())
+                    .collect(Collectors.joining("\n", "",
+                        isNewLine && whitespaceCount == 0 ? "\n" : ""));
     }
 
     /**
-     * Removes vertical and horizontal white space margins from around the
-     * essential body of a multi-line string, while preserving relative
-     * indentation and with optional indentation adjustment.
-     *
-     * @apiNote
-     * Examples:
-     * <blockquote><pre>
-     * String s = """
-     *            This is the first line
-     *                This is the second line
-     *            """.align();
-     *
-     * returns
-     * This is the first line
-     *     This is the second line
-     *
-     *
-     * String t = """
-     *            This is the first line
-     *                This is the second line
-     *            """.align(4);
-     *
-     * returns
-     *     This is the first line
-     *         This is the second line
-     * </pre></blockquote>
-     *
-     * @param n  number of leading white space characters
-     *           to add or remove
-     *
-     * @return string with margins removed, indentation adjusted and
-     *         line terminators normalized
-     *
-     * @see String#align()
+     * Translates all escape sequences in the string into characters
+     * represented by those escapes specified in section
+     * 3.10.6 of the <cite>The Java&trade; Language Specification</cite>.
+     * <p>
+     * Escape sequences are translated as follows;
+     * <table class="plain">
+     *   <caption style="display:none">Escape sequences</caption>
+     *   <thead>
+     *   <tr>
+     *     <th scope="col">Escape</th>
+     *     <th scope="col">Name</th>
+     *     <th scope="col">Unicode/action</th>
+     *   </tr>
+     *   </thead>
+     *   <tr>
+     *     <td>{@code \u005Cb}</td>
+     *     <td>backspace</td>
+     *     <td>{@code \u005Cu0008}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@code \u005Ct}</td>
+     *     <td>horizontal tab</td>
+     *     <td>{@code \u005Cu0009}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@code \u005Cn}</td>
+     *     <td>line feed</td>
+     *     <td>{@code \u005Cu000A}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@code \u005Cf}</td>
+     *     <td>form feed</td>
+     *     <td>{@code \u005Cu000C}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@code \u005Cr}</td>
+     *     <td>carriage return</td>
+     *     <td>{@code \u005Cu000D}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@code \u005C"}</td>
+     *     <td>double quote</td>
+     *     <td>{@code \u005Cu0022}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@code \u005C'}</td>
+     *     <td>single quote</td>
+     *     <td>{@code \u005Cu0027}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@code \u005C\u005C}</td>
+     *     <td>backslash</td>
+     *     <td>{@code \u005Cu005C}</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@code \u005C0 - \u005C377}</td>
+     *     <td>octal escape</td>
+     *     <td>code point equivalents</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@code \u005C<space>}</td>
+     *     <td>continue after here</td>
+     *     <td>consumes prior white space</td>
+     *   </tr>
+     *   <tr>
+     *     <td>{@code \u005C<lineterminator>}</td>
+     *     <td>continue on next line</td>
+     *     <td>consumes line terminator</td>
+     *   </tr>
+     * </table>
+     * <p>
+     * Octal escapes \u005C0 - \u005C377 are translated to their code
+     * point equivalents.
+     *
+     * @throws MalformedEscapeException when an escape sequence is malformed.
+     *
+     * @return String with all escapes translated.
      *
      * @since 13
+     *
+     * @deprecated  Preview feature associated with Multi-line String Literals.
+     *              Use at your own risk.
      */
-    public String align(int n) {
+    @Deprecated(forRemoval=true, since="13")
+    public String translateEscapes() throws MalformedEscapeException {
         if (isEmpty()) {
             return "";
         }
-        long count = lines().count();
-        if (count == 1) {
-            return strip();
+        char[] chars = toCharArray();
+        int length = chars.length;
+        int from = 0;
+        int to = 0;
+        int whitespaceStart = 0;
+        while (from < length) {
+            char ch = chars[from++];
+            if (ch == '\\') {
+                ch = from < length ? chars[from++] : '\0';
+                switch (ch) {
+                case 'b':
+                    ch = '\b';
+                    break;
+                case 'f':
+                    ch = '\f';
+                    break;
+                case 'n':
+                    ch = '\n';
+                    break;
+                case 'r':
+                    ch = '\r';
+                    break;
+                case 't':
+                    ch = '\t';
+                    break;
+                case '\'':
+                    ch = '\'';
+                    break;
+                case '\"':
+                    ch = '\"';
+                    break;
+                case '\\':
+                    // ch = '\\';
+                    break;
+                case '0': case '1': case '2': case '3':
+                case '4': case '5': case '6': case '7':
+                    int limit = ch <= '3' ? 2 : 1;
+                    int code = Character.digit(ch, 8);
+                    for (int i = 0; i < limit && from < length; i++) {
+                        ch = chars[from];
+                        int digit = Character.digit(ch, 8);
+                        if (digit < 0) {
+                            break;
+                        }
+                        from++;
+                        code = code << 3 | digit;
+                    }
+                    if (0377 < code) {
+                        throw new MalformedEscapeException(from);
+                    }
+                    ch = (char)code;
+                    break;
+                case ' ':
+                    to = whitespaceStart;
+                    continue;
+                case '\n':
+                    whitespaceStart = to;
+                    continue;
+                case '\r':
+                    if (from < length && chars[from] == '\n') {
+                        from++;
+                    }
+                    whitespaceStart = to;
+                    continue;
+                default:
+                    throw new MalformedEscapeException(from);
+                }
+            }
+
+            if (ch == '\n' || ch == '\r' || !Character.isWhitespace(ch)) {
+                whitespaceStart = to + 1;
+            }
+
+            chars[to++] = ch;
         }
-        String last = lines().skip(count - 1).findFirst().orElse("");
-        int outdent = lines().skip(1)
-                             .filter(not(String::isBlank))
-                             .mapToInt(String::indexOfNonWhitespace)
-                             .min()
-                             .orElse(0);
-        boolean lastIsBlank = last.isBlank();
-        if (lastIsBlank) {
-            outdent = Integer.min(outdent, last.length());
-        }
-        return indentStream(lines(1, 1), n - outdent).map(s -> s.stripTrailing())
-                                                     .collect(Collectors.joining("\n", "", lastIsBlank ? "\n" : ""));
+
+        return new String(chars, 0, to);
     }
 
     /**
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java	Fri Apr 19 13:08:29 2019 -0300
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavaTokenizer.java	Wed May 08 09:36:43 2019 -0300
@@ -85,9 +85,13 @@
      */
     protected UnicodeReader reader;
 
-    /** Is string auto-aligned?
+    /** Should the string stripped of indentation?
      */
-    protected boolean isAutoAligned = false;
+    protected boolean shouldStripIndent;
+
+    /** Should the string's escapes be translated?
+     */
+    protected boolean shouldTranslateEscapes;
 
     protected ScannerFactory fac;
 
@@ -205,6 +209,192 @@
         }
     }
 
+    /** Read next character in character or string literal and copy into sbuf
+     * without translating escapes.
+     */
+    private void scanLitCharRaw(int pos) {
+        if (reader.ch == '\\') {
+            if (reader.peekChar() == '\\' && !reader.isUnicode()) {
+                reader.skipChar();
+                reader.putChar('\\', false);
+                reader.putChar('\\', true);
+            } else {
+                reader.putChar('\\', true);
+                switch (reader.ch) {
+                case '0': case '1': case '2': case '3':
+                case '4': case '5': case '6': case '7':
+                    char leadch = reader.ch;
+                    reader.putChar(true);
+                    if ('0' <= reader.ch && reader.ch <= '7') {
+                        reader.putChar(true);
+                        if (leadch <= '3' && '0' <= reader.ch && reader.ch <= '7') {
+                            reader.putChar(true);
+                        }
+                    }
+                    break;
+                case 'b':
+                case 't':
+                case 'n':
+                case 'f':
+                case 'r':
+                case '\'':
+                case '\"':
+                case '\\':
+                case ' ':
+                case '\n':
+                case '\r':
+                    reader.putChar(true); break;
+                default:
+                    lexError(reader.bp, Errors.IllegalEscChar);
+                }
+            }
+        } else if (reader.bp != reader.buflen) {
+            reader.putChar(true);
+        }
+    }
+
+    static class MultilineStringSupport {
+        private static final Method stripIndent;
+        private static final Method translateEscapes;
+        private static final boolean hasSupport;
+
+        private static Method getStringMethod(String name) {
+            try {
+                return String.class.getMethod(name);
+            } catch (Exception ex) {
+                // Bootstrapping issue, do nothing.
+            }
+            return null;
+        }
+
+        static {
+            stripIndent = getStringMethod("stripIndent");
+            translateEscapes = getStringMethod("translateEscapes");
+            hasSupport = stripIndent != null && translateEscapes != null;
+        }
+
+        static boolean hasSupport() {
+            return hasSupport;
+        }
+
+        static String stripIndent(String string) {
+            try {
+                string = (String)stripIndent.invoke(string);
+            } catch (Exception ex) {
+                // Bootstrapping issue, do nothing.
+            }
+            return string;
+        }
+
+        static String translateEscapes(String string) {
+            try {
+                string = (String)translateEscapes.invoke(string);
+            } catch (Exception ex) {
+                // Bootstrapping issue, do nothing.
+            }
+            return string;
+        }
+    }
+
+    /** Test for EOLN.
+     */
+    private boolean isEOLN() {
+        return reader.ch == LF || reader.ch == CR;
+    }
+
+    /** Test for CRLF.
+     */
+    private boolean isCRLF() {
+        return reader.ch == CR && reader.peekChar() == LF;
+    }
+
+    /** Count and skip repeated occurances of the specified character.
+     */
+    private int countChar(char ch, int max) {
+        int count = 0;
+        for ( ; count < max && reader.bp < reader.buflen && reader.ch == ch; count++) {
+            reader.scanChar();
+        }
+        return count;
+    }
+
+    /** Scan a string literal.
+     */
+    private void scanStringLiteral(int pos) {
+        boolean hasMultilineStringSupport = MultilineStringSupport.hasSupport();
+        int firstEOLN = -1;
+        int openCount = countChar('\"', 3);
+        switch (openCount) {
+        case 1: // traditional string
+            break;
+        case 2: // empty string
+            reader.reset(pos);
+            openCount = countChar('\"', 1);
+            break;
+        case 3: // multi-line string
+            // checkSourceLevel(pos, Feature.MULTILINE_STRING_LITERALS);
+            if (hasMultilineStringSupport) {
+                shouldStripIndent = true;
+                boolean hasOpenEOLN = false;
+                while (reader.bp < reader.buflen && Character.isWhitespace(reader.ch)) {
+                    hasOpenEOLN = isEOLN();
+                    if (hasOpenEOLN) {
+                        break;
+                    }
+                    reader.scanChar();
+                }
+                if (!hasOpenEOLN) {
+                    lexError(reader.bp, Errors.IllegalMultilineStringLitOpen);
+                    return;
+                }
+                if (isCRLF()) {
+                    reader.scanChar();
+                }
+                reader.scanChar();
+            } else {
+                reader.reset(pos);
+                openCount = countChar('\"', 1);
+            }
+            break;
+        }
+        while (reader.bp < reader.buflen) {
+            if (reader.ch == '\"') {
+                int closeCount = countChar('\"', openCount);
+                if (openCount == closeCount) {
+                    tk = Tokens.TokenKind.STRINGLITERAL;
+                    return;
+                }
+                reader.repeat('\"', closeCount);
+            } else if (isEOLN()) {
+                if (openCount == 1) {
+                    break;
+                }
+                if (firstEOLN == -1) {
+                    firstEOLN = reader.bp;
+                }
+                int start = reader.bp;
+                if (isCRLF()) {
+                    reader.scanChar();
+                }
+                reader.putChar('\n', true);
+                processLineTerminator(start, reader.bp);
+            } else if (reader.ch == '\\') {
+                if (hasMultilineStringSupport) {
+                    shouldTranslateEscapes = true;
+                    scanLitCharRaw(pos);
+                } else {
+                    scanLitChar(pos);
+                }
+            } else {
+                reader.putChar(true);
+            }
+        }
+        lexError(pos, Errors.UnclosedStrLit);
+        if (firstEOLN  != -1) {
+            reader.reset(firstEOLN);
+        }
+    }
+
     private void scanDigits(int pos, int digitRadix) {
         char saveCh;
         int savePos;
@@ -464,79 +654,6 @@
         }
     }
 
-    private static String align(String string) {
-        try {
-            Method align = String.class.getMethod("align");
-            string = (String) align.invoke(string);
-        } catch (Exception ex) {
-            // Bootstrapping situation, do nothing.
-        }
-        return string;
-    }
-
-    /** Scan a string literal.
-     */
-    private void scanStringLiteral(int pos) {
-        boolean align = true;
-        int firstEOLN = -1;
-        int rescan = reader.bp;
-        int openCount = countChar('\"', 3);
-        if (openCount == 2) {
-            reader.reset(rescan);
-            openCount = countChar('\"', 1);
-        }
-        while (reader.bp < reader.buflen) {
-            if (reader.ch == '\"') {
-                int closeCount = countChar('\"', openCount);
-                rescan = reader.bp;
-                if (openCount == closeCount) {
-                    tk = Tokens.TokenKind.STRINGLITERAL;
-                    isAutoAligned = align && openCount == 3;
-                    return;
-                }
-                reader.repeat('\"', closeCount);
-                reader.reset(rescan);
-            } else if (reader.ch == LF || reader.ch == CR) {
-                if (openCount == 1) {
-                    break;
-                }
-                int start = reader.bp;
-                if (firstEOLN == -1) {
-                    firstEOLN = start;
-                }
-                if (reader.ch == CR && reader.peekChar() == LF) {
-                    reader.scanChar();
-                }
-                reader.putChar('\n', true);
-                processLineTerminator(start, reader.bp);
-            } else if (reader.ch == '\\') {
-                if (reader.peekChar() == '~') {
-                    reader.scanChar();
-                    reader.scanChar();
-                    align = false;
-                } else {
-                    scanLitChar(pos);
-                }
-            } else {
-                 reader.putChar(true);
-            }
-        }
-        if (firstEOLN  != -1) {
-            reader.reset(firstEOLN);
-        }
-        lexError(pos, Errors.UnclosedStrLit);
-    }
-
-    /** Count and skip repeated occurances of the specified character.
-     */
-    private int countChar(char ch, int max) {
-        int count = 0;
-        for ( ; count < max && reader.bp < reader.buflen && reader.ch == ch; count++) {
-            reader.scanChar();
-        }
-        return count;
-    }
-
     /** Read token.
      */
     public Token readToken() {
@@ -702,7 +819,7 @@
                         lexError(pos, Errors.EmptyCharLit);
                         reader.scanChar();
                     } else {
-                        if (reader.ch == CR || reader.ch == LF)
+                        if (isEOLN())
                             lexError(pos, Errors.IllegalLineEndInCharLit);
                         scanLitChar(pos);
                         if (reader.ch == '\'') {
@@ -766,7 +883,15 @@
                 case DEFAULT: return new Token(tk, pos, endPos, comments);
                 case NAMED: return new NamedToken(tk, pos, endPos, name, comments);
                 case STRING: {
-                    String string = isAutoAligned ? align(reader.chars()) : reader.chars();
+                    String string = reader.chars();
+                    if (shouldStripIndent) {
+                        string = MultilineStringSupport.stripIndent(string);
+                        shouldStripIndent = false;
+                    }
+                    if (shouldTranslateEscapes) {
+                        string = MultilineStringSupport.translateEscapes(string);
+                        shouldTranslateEscapes = false;
+                    }
                     return new StringToken(tk, pos, endPos, string, comments);
                 }
                 case NUMERIC: return new NumericToken(tk, pos, endPos, reader.chars(), radix, comments);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/UnicodeReader.java	Fri Apr 19 13:08:29 2019 -0300
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/UnicodeReader.java	Wed May 08 09:36:43 2019 -0300
@@ -64,10 +64,6 @@
      */
     protected int unicodeConversionBp = -1;
 
-    /** Control conversion of unicode characters
-     */
-    protected boolean unicodeConversion = true;
-
     protected Log log;
     protected Names names;
 
@@ -169,17 +165,11 @@
         scanChar();
     }
 
-    protected boolean setUnicodeConversion(boolean newState) {
-        boolean oldState = unicodeConversion;
-        unicodeConversion = newState;
-        return oldState;
-    }
-
     /** Convert unicode escape; bp points to initial '\' character
      *  (Spec 3.3).
      */
     protected void convertUnicode() {
-        if (ch == '\\' && unicodeConversion && unicodeConversionBp != bp ) {
+        if (ch == '\\' && unicodeConversionBp != bp ) {
             bp++; ch = buf[bp];
             if (ch == 'u') {
                 do {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Fri Apr 19 13:08:29 2019 -0300
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Wed May 08 09:36:43 2019 -0300
@@ -623,6 +623,9 @@
 compiler.err.illegal.line.end.in.char.lit=\
     illegal line end in character literal
 
+compiler.err.illegal.multiline.string.lit.open=\
+    illegal multi-line string literal open delimiter sequence, missing line terminator
+
 compiler.err.illegal.nonascii.digit=\
     illegal non-ASCII digit
 
--- a/test/langtools/tools/javac/MultilineStringLiteralAPI.java	Fri Apr 19 13:08:29 2019 -0300
+++ b/test/langtools/tools/javac/MultilineStringLiteralAPI.java	Wed May 08 09:36:43 2019 -0300
@@ -50,13 +50,16 @@
      * Check that correct/incorrect syntax is properly detected
      */
     static void test1() {
-        String[] s = new String[] { "a", "ab", "abc", "\u2022", "*".repeat(1000), "*".repeat(10000) };
-        for (String a : s)  {
+        for (String lineterminators : new String[] { "\n", "\r", "\r\n" })
+        for (String whitespace : new String[] { "", "   ", "\t", "\u2002" })
+        for (String content : new String[] { "a", "ab", "abc", "\u2022", "*".repeat(1000), "*".repeat(10000) })  {
             String code =
                     "public class CorrectTest {\n" +
                             "    public static void main(String... args) {\n" +
                             "        String xxx = " +
-                            "\"\"\"" + a + "\"\"\";\n" +
+                            "\"\"\"" + whitespace + lineterminators +
+                            content +
+                            "\"\"\";\n" +
                             "    }\n" +
                             "}\n";
             compPass(code);
@@ -69,7 +72,7 @@
     static void test2() {
         compPass("public class UnicodeDelimiterTest {\n" +
                 "    public static void main(String... args) {\n" +
-                "        String xxx = \\u0022\\u0022\\u0022abc\\u0022\\u0022\\u0022;\n" +
+                "        String xxx = \\u0022\\u0022\\u0022\nabc\n\\u0022\\u0022\\u0022;\n" +
                 "    }\n" +
                 "}\n");
     }
@@ -80,22 +83,22 @@
     static void test3() {
         compFail("public class EndTest {\n" +
                 "    public static void main(String... args) {\n" +
-                "        String xxx = \"\"\"abc\"\"\"");
-        compFail("public class MultilineStringLiteralTest {\n" +
+                "        String xxx = \"\"\"\nabc\"\"\"");
+        compFail("public class TwoQuoteClose {\n" +
                 "    public static void main(String... args) {\n" +
-                "        String xxx = \"\"\"abc\"\"");
-        compFail("public class MultilineStringLiteralTest {\n" +
+                "        String xxx = \"\"\"\nabc\"\"");
+        compFail("public class OneQuoteClose {\n" +
                 "    public static void main(String... args) {\n" +
-                "        String xxx = \"\"\"abc\"");
-        compFail("public class MultilineStringLiteralTest {\n" +
+                "        String xxx = \"\"\"\nabc\"");
+        compFail("public class NoClose {\n" +
                 "    public static void main(String... args) {\n" +
-                "        String xxx = \"\"\"abc");
-        compFail("public class MultilineStringLiteralTest {\n" +
+                "        String xxx = \"\"\"\nabc");
+        compFail("public class ZeroTerminator {\n" +
                 "    public static void main(String... args) {\n" +
-                "        String xxx = \"\"\"abc\\u0000");
-        compFail("public class MultilineStringLiteralTest {\n" +
+                "        String xxx = \"\"\"\nabc\\u0000");
+        compFail("public class NonBreakingSpace {\n" +
                 "    public static void main(String... args) {\n" +
-                "        String xxx = \"\"\"abc\\u001A");
+                "        String xxx = \"\"\"\nabc\\u001A");
     }
 
     /*
--- a/test/langtools/tools/javac/MultilineStringLiteralLang.java	Fri Apr 19 13:08:29 2019 -0300
+++ b/test/langtools/tools/javac/MultilineStringLiteralLang.java	Wed May 08 09:36:43 2019 -0300
@@ -36,52 +36,50 @@
 public class MultilineStringLiteralLang {
     public static void main(String... args) {
         test1();
-        test2();
     }
 
     /*
      * Test basic string functionality.
      */
     static void test1() {
-        EQ("""abc""", "abc");
-        EQ("""""", "");
-        EQ("""abc"def""", "abc\"def");
-        EQ("""abc""def""", "abc\"\"def");
-        EQ("""abc\"""def""", "abc\"\"\"def");
-        EQ("""abc"\""def""", "abc\"\"\"def");
-        EQ("""abc""\"def""", "abc\"\"\"def");
-        EQ("""abc\ndef""", "abc\ndef");
-        EQ("""abc\u0020def""", "abc\u0020def");
-        EQ("""abc•def""", "abc•def");
+        EQ("""
+            abc\
+            """, "abc");
+        EQ("""
+            \
+            """, "");
+        EQ("""
+            abc
+            """, "abc\n");
+        EQ("""
 
-        LENGTH("""abc""", 3);
+            """, "\n");
+        EQ("""
+            "
+            """, "\"\n");
+        EQ("""
+            ""
+            """, "\"\"\n");
+         EQ("""
+            \"""
+            """, "\"\"\"\n");
+         EQ("""
+            "\""
+            """, "\"\"\"\n");
+        EQ("""
+            \r
+            """, "\r\n");
+        EQ("""
+            \u2022
+            """, "\u2022\n");
+        EQ("""
+            •
+            """, "\u2022\n");
+        LENGTH("""
+            abc
+            """, 4);
     }
 
-    /*
-     * Test multiline string functionality.
-     */
-    static void test2() {
-        EQ(
-"""abc
-def
-ghi""", "abc\ndef\nghi");
-        EQ(
-"""abc
-def
-ghi
-""", "abc\ndef\nghi\n");
-        EQ(
-"""
-abc
-def
-ghi""", "abc\ndef\nghi");
-        EQ(
-"""
-abc
-def
-ghi
-""", "abc\ndef\nghi\n");
-    }
 
     /*
      * Raise an exception if the string is not the expected length.