Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJay Arthanareeswaran2019-04-29 07:20:44 +0000
committerJay Arthanareeswaran2019-10-24 06:24:35 +0000
commitf178ea0c32cc749d2aa2615414008a6a522da301 (patch)
tree83b380597a4460cf245dce1d90e64e957be1b700
parent9ef03eee1438718368e8d3a7fe69a47ee6a51ef0 (diff)
downloadeclipse.jdt.core-f178ea0c32cc749d2aa2615414008a6a522da301.tar.gz
eclipse.jdt.core-f178ea0c32cc749d2aa2615414008a6a522da301.tar.xz
eclipse.jdt.core-f178ea0c32cc749d2aa2615414008a6a522da301.zip
Bug 552339: [13] Add support for text block in PublicScanner
Change-Id: Ia43f06569f4c8e9df3fd3294375f825638816af4 Signed-off-by: Jay Arthanareeswaran <jarthana@in.ibm.com>
-rw-r--r--org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter13Test.java34
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java15
-rw-r--r--org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/TextBlock.java42
-rw-r--r--org.eclipse.jdt.core/model/org/eclipse/jdt/core/ToolFactory.java41
-rw-r--r--org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/ITerminalSymbols.java8
-rw-r--r--org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/PublicScanner.java208
6 files changed, 284 insertions, 64 deletions
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter13Test.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter13Test.java
index 17592cbbeb..efec12db6e 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter13Test.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter13Test.java
@@ -526,6 +526,16 @@ public class ASTConverter13Test extends ConverterTestSetup {
assertTrue("String should not be empty", escapedValue.length() != 0);
assertTrue("String should start with \"\"\"", escapedValue.startsWith("\"\"\""));
+
+ String literal = ((TextBlock) initializer).getLiteralValue();
+ assertEquals("literal value not correct",
+ " <html>\n" +
+ " <body>\n" +
+ " <p>Hello, world</p>\n" +
+ " </body>\n" +
+ " </html>\n" +
+ " ",
+ literal);
} finally {
javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, old);
@@ -607,6 +617,30 @@ public class ASTConverter13Test extends ConverterTestSetup {
ITypeBinding binding = initializer.resolveTypeBinding();
assertNotNull("No binding", binding);
assertEquals("Wrong qualified name", "java.lang.String", binding.getQualifiedName());
+
+ String escapedValue = ((TextBlock) initializer).getEscapedValue();
+
+ assertTrue("String should not be empty", escapedValue.length() != 0);
+ assertTrue("String should start with \"\"\"", escapedValue.startsWith("\"\"\""));
+ assertEquals("escaped value not correct",
+ "\"\"\"\n" +
+ " <html>\n" +
+ " <body>\n" +
+ " <p>Hello, world</p>\n" +
+ " </body>\n" +
+ " </html>\n" +
+ " \"\"\"",
+ escapedValue);
+
+ String literal = ((TextBlock) initializer).getLiteralValue();
+ assertEquals("literal value not correct",
+ " <html>\n" +
+ " <body>\n" +
+ " <p>Hello, world</p>\n" +
+ " </body>\n" +
+ " </html>\n" +
+ " ",
+ literal);
} finally {
javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, old);
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java
index 82a5bcc103..54ea9a91d2 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java
@@ -237,7 +237,7 @@ public class Scanner implements TerminalTokens {
public static final int HIGH_SURROGATE_MAX_VALUE = 0xDBFF;
public static final int LOW_SURROGATE_MAX_VALUE = 0xDFFF;
- // raw string support - 11
+ // text block support - 13
/* package */ int rawStart = -1;
public Scanner() {
@@ -1684,7 +1684,9 @@ protected int getNextToken0() throws InvalidInputException {
lastQuotePos = this.currentPosition;
// look for text block delimiter
if (scanForTextBlockClose()) {
- if (this.source[this.currentPosition + 2] == '"') {
+ // Account for just the snippet being passed around
+ // If already at the EOF, bail out.
+ if (this.currentPosition + 2 < this.source.length && this.source[this.currentPosition + 2] == '"') {
terminators++;
if (terminators > 2)
throw new InvalidInputException(UNTERMINATED_TEXT_BLOCK);
@@ -1753,6 +1755,9 @@ protected int getNextToken0() throws InvalidInputException {
}
}
// consume next character
+ if (this.currentPosition >= this.eofPosition) {
+ break;
+ }
this.unicodeAsBackSlash = false;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
@@ -2246,12 +2251,10 @@ public final void jumpOverMethodBody() {
case '\n' :
pushLineSeparator();
//$FALL-THROUGH$
- case '\\':
- if (this.source[this.currentPosition++] == '"') {
+ default:
+ if (this.currentCharacter == '\\' && this.source[this.currentPosition++] == '"') {
this.currentPosition++;
}
- //$FALL-THROUGH$
- default:
this.currentCharacter = this.source[this.currentPosition++];
continue Inner;
}
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/TextBlock.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/TextBlock.java
index a74ed8307e..38ab6c9d79 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/TextBlock.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/TextBlock.java
@@ -17,6 +17,7 @@ package org.eclipse.jdt.core.dom;
import java.util.ArrayList;
import java.util.List;
+import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.internal.compiler.parser.Scanner;
import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
@@ -240,45 +241,34 @@ public class TextBlock extends Expression {
* @exception IllegalArgumentException if the literal value cannot be converted
*/
public String getLiteralValue() {
- String s = getEscapedValue();
- int len = s.length();
- if (len < 2 || s.indexOf("\"\"\"") != 0 || !s.substring(len-3, len).equals("\"\"\"") ) { //$NON-NLS-1$ //$NON-NLS-2$
+ char[] escaped = getEscapedValue().toCharArray();
+ int len = escaped.length;
+ if (len < 7) {
throw new IllegalArgumentException();
}
- boolean newLineFound = false;
- for (int i = 3; i < s.length(); i++) {
- char c = s.charAt(i);
- while (ScannerHelper.isWhitespace(c)) {
+ int start = -1;
+ loop: for (int i = 3; i < len; i++) {
+ char c = escaped[i];
+ if (ScannerHelper.isWhitespace(c)) {
switch (c) {
case 10 : /* \ u000a: LINE FEED */
case 13 : /* \ u000d: CARRIAGE RETURN */
- newLineFound = true;
- break;
+ start = i + 1;
+ break loop;
default:
break;
}
+ } else {
+ break loop;
}
}
- if (!newLineFound) {
- throw new IllegalArgumentException();
- }
-
- Scanner scanner = this.ast.scanner;
- char[] source = s.toCharArray();
- scanner.setSource(source);
- scanner.resetTo(0, source.length);
- try {
- int tokenType = scanner.getNextToken();
- switch(tokenType) {
- case TerminalTokens.TokenNameTextBlock:
- return scanner.getCurrentStringLiteral();
- default:
- throw new IllegalArgumentException();
- }
- } catch(InvalidInputException e) {
+ if (start == -1) {
throw new IllegalArgumentException();
}
+ return new String(
+ CharOperation.subarray(escaped, start, len - 3)
+ );
}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/ToolFactory.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/ToolFactory.java
index 2b7317085b..9479cd8a34 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/ToolFactory.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/ToolFactory.java
@@ -547,12 +547,51 @@ public class ToolFactory {
*/
@SuppressWarnings("javadoc") // references deprecated TokenNameIdentifier
public static IScanner createScanner(boolean tokenizeComments, boolean tokenizeWhiteSpace, boolean recordLineSeparator, String sourceLevel, String complianceLevel) {
+ return createScanner(tokenizeComments, tokenizeWhiteSpace, recordLineSeparator, sourceLevel, complianceLevel, true);
+ }
+ /**
+ * Create a scanner, indicating the level of detail requested for tokenizing. The scanner can then be
+ * used to tokenize some source in a Java aware way.
+ * Here is a typical scanning loop:
+ *
+ * <pre>
+ * <code>
+ * IScanner scanner = ToolFactory.createScanner(false, false, false, false);
+ * scanner.setSource("int i = 0;".toCharArray());
+ * while (true) {
+ * int token = scanner.getNextToken();
+ * if (token == ITerminalSymbols.TokenNameEOF) break;
+ * System.out.println(token + " : " + new String(scanner.getCurrentTokenSource()));
+ * }
+ * </code>
+ * </pre>
+ *
+ * @param tokenizeComments if set to <code>false</code>, comments will be silently consumed
+ * @param tokenizeWhiteSpace if set to <code>false</code>, white spaces will be silently consumed,
+ * @param recordLineSeparator if set to <code>true</code>, the scanner will record positions of encountered line
+ * separator ends. In case of multi-character line separators, the last character position is considered. These positions
+ * can then be extracted using {@link IScanner#getLineEnds()}. Only non-unicode escape sequences are
+ * considered as valid line separators.
+ * @param sourceLevel if set to <code>&quot;1.3&quot;</code> or <code>null</code>, occurrences of 'assert' will be reported as identifiers
+ * ({@link ITerminalSymbols#TokenNameIdentifier}), whereas if set to <code>&quot;1.4&quot;</code>, it
+ * would report assert keywords ({@link ITerminalSymbols#TokenNameassert}). Java 1.4 has introduced
+ * a new 'assert' keyword.
+ * @param complianceLevel This is used to support the Unicode 4.0 character sets. if set to 1.5 or above,
+ * the Unicode 4.0 is supported, otherwise Unicode 3.0 is supported.
+ * @param enablePreview specify whether the scanner should look for preview language features for the specified compliance level
+ * @return a scanner
+ * @see org.eclipse.jdt.core.compiler.IScanner
+ *
+ * @since 3.14
+ */
+ @SuppressWarnings("javadoc") // references deprecated TokenNameIdentifier
+ public static IScanner createScanner(boolean tokenizeComments, boolean tokenizeWhiteSpace, boolean recordLineSeparator, String sourceLevel, String complianceLevel, boolean enablePreview) {
PublicScanner scanner = null;
long sourceLevelValue = CompilerOptions.versionToJdkLevel(sourceLevel);
if (sourceLevelValue == 0) sourceLevelValue = ClassFileConstants.JDK1_3; // fault-tolerance
long complianceLevelValue = CompilerOptions.versionToJdkLevel(complianceLevel);
if (complianceLevelValue == 0) complianceLevelValue = ClassFileConstants.JDK1_4; // fault-tolerance
- scanner = new PublicScanner(tokenizeComments, tokenizeWhiteSpace, false/*nls*/,sourceLevelValue /*sourceLevel*/, complianceLevelValue, null/*taskTags*/, null/*taskPriorities*/, true/*taskCaseSensitive*/);
+ scanner = new PublicScanner(tokenizeComments, tokenizeWhiteSpace, false/*nls*/,sourceLevelValue /*sourceLevel*/, complianceLevelValue, null/*taskTags*/, null/*taskPriorities*/, true/*taskCaseSensitive*/, enablePreview);
scanner.recordLineSeparator = recordLineSeparator;
return scanner;
}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/ITerminalSymbols.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/ITerminalSymbols.java
index 0ab6738990..52fa9a24cd 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/ITerminalSymbols.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/ITerminalSymbols.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2018 IBM Corporation and others.
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -99,6 +99,11 @@ public interface ITerminalSymbols {
int TokenNameDoubleLiteral = 43;
int TokenNameCharacterLiteral = 44;
int TokenNameStringLiteral = 45;
+ /**
+ * @since 3.20
+ * @noreference This class is not intended to be referenced by clients as it is a part of Java preview feature.
+ */
+ int TokenNameTextBlock = 46;
int TokenNamePLUS_PLUS = 1;
int TokenNameMINUS_MINUS = 2;
int TokenNameEQUAL_EQUAL = 35;
@@ -185,5 +190,4 @@ public interface ITerminalSymbols {
*/
int TokenNameCOLON_COLON = 406;
-
}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/PublicScanner.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/PublicScanner.java
index e7084cd927..13c78fd9ff 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/PublicScanner.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/PublicScanner.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2018 IBM Corporation and others.
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -41,6 +41,7 @@ public class PublicScanner implements IScanner, ITerminalSymbols {
*/
public long sourceLevel;
public long complianceLevel;
+ public boolean previewEnabled;
// 1.4 feature
public boolean useAssertAsAnIndentifier = false;
@@ -114,6 +115,7 @@ public class PublicScanner implements IScanner, ITerminalSymbols {
public static final String NULL_SOURCE_STRING = "Null_Source_String"; //$NON-NLS-1$
public static final String UNTERMINATED_STRING = "Unterminated_String"; //$NON-NLS-1$
+ public static final String UNTERMINATED_TEXT_BLOCK = "Unterminated_Text_Block"; //$NON-NLS-1$
public static final String UNTERMINATED_COMMENT = "Unterminated_Comment"; //$NON-NLS-1$
public static final String INVALID_CHAR_IN_STRING = "Invalid_Char_In_String"; //$NON-NLS-1$
public static final String INVALID_DIGIT = "Invalid_Digit"; //$NON-NLS-1$
@@ -201,6 +203,9 @@ public class PublicScanner implements IScanner, ITerminalSymbols {
public static final int HIGH_SURROGATE_MAX_VALUE = 0xDBFF;
public static final int LOW_SURROGATE_MAX_VALUE = 0xDFFF;
+ // text block support - 13
+ /* package */ int rawStart = -1;
+
public PublicScanner() {
this(false /*comment*/, false /*whitespace*/, false /*nls*/, ClassFileConstants.JDK1_3 /*sourceLevel*/, null/*taskTag*/, null/*taskPriorities*/, true /*taskCaseSensitive*/);
}
@@ -214,12 +219,33 @@ public PublicScanner(
char[][] taskTags,
char[][] taskPriorities,
boolean isTaskCaseSensitive) {
+ this(tokenizeComments,
+ tokenizeWhiteSpace,
+ checkNonExternalizedStringLiterals,
+ sourceLevel,
+ complianceLevel,
+ taskTags,
+ taskPriorities,
+ isTaskCaseSensitive,
+ true);
+}
+public PublicScanner(
+ boolean tokenizeComments,
+ boolean tokenizeWhiteSpace,
+ boolean checkNonExternalizedStringLiterals,
+ long sourceLevel,
+ long complianceLevel,
+ char[][] taskTags,
+ char[][] taskPriorities,
+ boolean isTaskCaseSensitive,
+ boolean previewEnabled) {
this.eofPosition = Integer.MAX_VALUE;
this.tokenizeComments = tokenizeComments;
this.tokenizeWhiteSpace = tokenizeWhiteSpace;
this.sourceLevel = sourceLevel;
this.complianceLevel = complianceLevel;
+ this.previewEnabled = previewEnabled;
this.checkNonExternalizedStringLiterals = checkNonExternalizedStringLiterals;
if (taskTags != null) {
int taskTagsLength = taskTags.length;
@@ -512,6 +538,46 @@ public char[] getCurrentTokenSourceString() {
}
return result;
}
+protected final boolean scanForTextBlockBeginning() {
+ if (this.complianceLevel < ClassFileConstants.JDK13 || !this.previewEnabled) {
+ return false;
+ }
+ try {
+ // Don't change the position and current character unless we are certain
+ // to be dealing with a text block. For producing all errors like before
+ // in case of a valid """ but missing \r or \n, just return false and not
+ // throw any error.
+ int temp = this.currentPosition;
+ if ((this.source[temp++] == '\"' && this.source[temp++] == '\"')) {
+ char c = this.source[temp++];
+ while (ScannerHelper.isWhitespace(c)) {
+ switch (c) {
+ case 10 : /* \ u000a: LINE FEED */
+ case 13 : /* \ u000d: CARRIAGE RETURN */
+ this.currentCharacter = c;
+ this.currentPosition = temp;
+ return true;
+ default:
+ break;
+ }
+ c = this.source[temp++];
+ }
+ }
+ } catch(IndexOutOfBoundsException e) {
+ //let it return false;
+ }
+ return false;
+}
+protected final boolean scanForTextBlockClose() throws InvalidInputException {
+ try {
+ if (this.source[this.currentPosition] == '\"' && this.source[this.currentPosition + 1] == '\"') {
+ return true;
+ }
+ } catch(IndexOutOfBoundsException e) {
+ //let it return false;
+ }
+ return false;
+}
public final String getCurrentStringLiteral() {
//return the token REAL source (aka unicodes are precomputed).
//REMOVE the two " that are at the beginning and the end.
@@ -1423,26 +1489,53 @@ public int getNextToken() throws InvalidInputException {
}
throw new InvalidInputException(INVALID_CHARACTER_CONSTANT);
case '"' :
+ boolean isTextBlock = false;
+ int lastQuotePos = 0;
try {
// consume next character
this.unicodeAsBackSlash = false;
boolean isUnicode = false;
- if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
- && (this.source[this.currentPosition] == 'u')) {
- getNextUnicodeChar();
- isUnicode = true;
- } else {
- if (this.withoutUnicodePtr != 0) {
- unicodeStore();
+ isTextBlock = scanForTextBlockBeginning();
+ if (!isTextBlock) {
+ if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
+ && (this.source[this.currentPosition] == 'u')) {
+ getNextUnicodeChar();
+ isUnicode = true;
+ } else {
+ if (this.withoutUnicodePtr != 0) {
+ unicodeStore();
+ }
}
}
-
- while (this.currentCharacter != '"') {
- if (this.currentPosition >= this.eofPosition) {
- throw new InvalidInputException(UNTERMINATED_STRING);
+ this.rawStart = this.currentPosition - this.startPosition;
+ int terminators = 0;
+ while (this.currentPosition <= this.eofPosition) {
+ if (this.currentCharacter == '"') {
+ if (!isTextBlock) {
+ return ITerminalSymbols.TokenNameStringLiteral;
+ }
+ lastQuotePos = this.currentPosition;
+ // look for text block delimiter
+ if (scanForTextBlockClose()) {
+ // Account for just the snippet being passed around
+ // If already at the EOF, bail out.
+ if (this.currentPosition + 2 < this.source.length && this.source[this.currentPosition + 2] == '"') {
+ terminators++;
+ if (terminators > 2)
+ throw new InvalidInputException(UNTERMINATED_TEXT_BLOCK);
+ } else {
+ this.currentPosition += 2;
+ return ITerminalSymbols.TokenNameTextBlock;
+ }
+ }
+ if (this.withoutUnicodePtr != 0) {
+ unicodeStore();
+ }
+ } else {
+ terminators = 0;
}
/**** \r and \n are not valid in string literals ****/
- if ((this.currentCharacter == '\n') || (this.currentCharacter == '\r')) {
+ if (!isTextBlock && (this.currentCharacter == '\n') || (this.currentCharacter == '\r')) {
// relocate if finding another quote fairly close: thus unicode '/u000D' will be fully consumed
if (isUnicode) {
int start = this.currentPosition;
@@ -1496,22 +1589,39 @@ public int getNextToken() throws InvalidInputException {
}
}
// consume next character
+ if (this.currentPosition >= this.eofPosition) {
+ break;
+ }
this.unicodeAsBackSlash = false;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
- && (this.source[this.currentPosition] == 'u')) {
+ && (this.source[this.currentPosition] == 'u')) {
getNextUnicodeChar();
isUnicode = true;
} else {
isUnicode = false;
+ if (isTextBlock && this.currentCharacter == '"')
+ continue;
if (this.withoutUnicodePtr != 0) {
unicodeStore();
}
}
-
+ }
+ if (isTextBlock) {
+ if (lastQuotePos > 0)
+ this.currentPosition = lastQuotePos;
+ this.currentPosition = (lastQuotePos > 0) ? lastQuotePos : this.startPosition + this.rawStart;
+ throw new InvalidInputException(UNTERMINATED_TEXT_BLOCK);
+ } else {
+ throw new InvalidInputException(UNTERMINATED_STRING);
}
} catch (IndexOutOfBoundsException e) {
- this.currentPosition--;
- throw new InvalidInputException(UNTERMINATED_STRING);
+ if (isTextBlock) {
+ this.currentPosition = (lastQuotePos > 0) ? lastQuotePos : this.startPosition + this.rawStart;
+ throw new InvalidInputException(UNTERMINATED_TEXT_BLOCK);
+ } else {
+ this.currentPosition--;
+ throw new InvalidInputException(UNTERMINATED_STRING);
+ }
} catch (InvalidInputException e) {
if (e.getMessage().equals(INVALID_ESCAPE)) {
// relocate if finding another quote fairly close: thus unicode '/u000D' will be fully consumed
@@ -1529,7 +1639,6 @@ public int getNextToken() throws InvalidInputException {
}
throw e; // rethrow
}
- return TokenNameStringLiteral;
case '/' :
if (!this.skipComments) {
int test = getNextChar('/', '*');
@@ -1929,23 +2038,57 @@ public final void jumpOverMethodBody() {
break NextToken;
}
case '"' :
+ boolean isTextBlock = false;
+ int firstClosingBrace = 0;
try {
try { // consume next character
- this.unicodeAsBackSlash = false;
- if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
- && (this.source[this.currentPosition] == 'u')) {
- getNextUnicodeChar();
- } else {
- if (this.withoutUnicodePtr != 0) {
- unicodeStore();
+ isTextBlock = scanForTextBlockBeginning();
+ if (!isTextBlock) {
+ this.unicodeAsBackSlash = false;
+ if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
+ && (this.source[this.currentPosition] == 'u')) {
+ getNextUnicodeChar();
+ } else {
+ if (this.withoutUnicodePtr != 0) {
+ unicodeStore();
+ }
}
}
} catch (InvalidInputException ex) {
// ignore
}
- while (this.currentCharacter != '"') {
- if (this.currentPosition >= this.eofPosition) {
- return;
+ Inner: while (this.currentPosition <= this.eofPosition) {
+ if (isTextBlock) {
+ switch (this.currentCharacter) {
+ case '"':
+ // look for text block delimiter
+ if (scanForTextBlockClose()) {
+ this.currentPosition += 2;
+ this.currentCharacter = this.source[this.currentPosition];
+ isTextBlock = false;
+ break Inner;
+ }
+ break;
+ case '}':
+ if (firstClosingBrace == 0)
+ firstClosingBrace = this.currentPosition;
+ break;
+ case '\r' :
+ if (this.source[this.currentPosition] == '\n')
+ this.currentPosition++;
+ //$FALL-THROUGH$
+ case '\n' :
+ pushLineSeparator();
+ //$FALL-THROUGH$
+ default:
+ if (this.currentCharacter == '\\' && this.source[this.currentPosition++] == '"') {
+ this.currentPosition++;
+ }
+ this.currentCharacter = this.source[this.currentPosition++];
+ continue Inner;
+ }
+ } else if (this.currentCharacter == '"') {
+ break Inner;
}
if (this.currentCharacter == '\r'){
if (this.source[this.currentPosition] == '\n') this.currentPosition++;
@@ -1989,7 +2132,13 @@ public final void jumpOverMethodBody() {
}
}
} catch (IndexOutOfBoundsException e) {
- return;
+ if(isTextBlock) {
+ // Pull it back to the first closing brace after the beginning
+ // of the unclosed text block and let recovery take over.
+ if (firstClosingBrace > 0) {
+ this.currentPosition = firstClosingBrace - 1;
+ }
+ }
}
break NextToken;
case '/' :
@@ -4076,6 +4225,7 @@ public static boolean isLiteral(int token) {
case TerminalTokens.TokenNameFloatingPointLiteral:
case TerminalTokens.TokenNameDoubleLiteral:
case TerminalTokens.TokenNameStringLiteral:
+ case TerminalTokens.TokenNameTextBlock:
case TerminalTokens.TokenNameCharacterLiteral:
return true;
default:

Back to the top