Update jdt.core to I20191101-0300 for 4.14M2
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java
index 4b39c6b..f9e3b52 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java
@@ -498,6 +498,8 @@
expectedProblemAttributes.put("ExplicitThisParameterNotInLambda", new ProblemAttributes(CategorizedProblem.CAT_SYNTAX));
expectedProblemAttributes.put("ExplicitThisParameterNotBelow18", new ProblemAttributes(CategorizedProblem.CAT_SYNTAX));
expectedProblemAttributes.put("ExplicitlyClosedAutoCloseable", new ProblemAttributes(CategorizedProblem.CAT_CODE_STYLE));
+ expectedProblemAttributes.put("ExportingForeignPackage", new ProblemAttributes(CategorizedProblem.CAT_MODULE));
+ expectedProblemAttributes.put("ExportedPackageDoesNotExistOrIsEmpty", new ProblemAttributes(CategorizedProblem.CAT_MODULE));
expectedProblemAttributes.put("ExpressionShouldBeAVariable", new ProblemAttributes(CategorizedProblem.CAT_SYNTAX));
expectedProblemAttributes.put("ExternalProblemFixable", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL));
expectedProblemAttributes.put("ExternalProblemNotFixable", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL));
@@ -1553,6 +1555,7 @@
expectedProblemAttributes.put("ExplicitThisParameterNotInLambda", SKIP);
expectedProblemAttributes.put("ExplicitThisParameterNotBelow18", SKIP);
expectedProblemAttributes.put("ExplicitlyClosedAutoCloseable", new ProblemAttributes(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE));
+ expectedProblemAttributes.put("ExportingForeignPackage", SKIP);
expectedProblemAttributes.put("ExportedPackageDoesNotExistOrIsEmpty", SKIP);
expectedProblemAttributes.put("ExpressionShouldBeAVariable", SKIP);
expectedProblemAttributes.put("ExternalProblemFixable", SKIP);
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java
index b14ef55..2ca01b8 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericTypeTest.java
@@ -27,8 +27,16 @@
*******************************************************************************/
package org.eclipse.jdt.core.tests.compiler.regression;
+import static java.lang.Math.abs;
+import static java.util.stream.Collectors.toList;
+
import java.io.File;
+import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import java.util.function.IntFunction;
+import java.util.stream.IntStream;
import junit.framework.Test;
@@ -52603,7 +52611,7 @@
*/
public void testBug543480BasedOnTest2FromComment4ToSameSameOptimization() {
if (this.complianceLevel >= ClassFileConstants.JDK1_8) {
- final long durationFor2TypeParameters = compileTimesAfterWarmup(() -> runConformTest(
+ final List<Duration> durationsFor2TypeParameters = compileTimesAfterWarmup(() -> runConformTest(
new String[] {
"Test2_2.java",
"public class Test2_2 {\n" +
@@ -52624,7 +52632,7 @@
"interface R1<T1> {}\n" +
"interface R2<T1, T2> {}\n"
}));
- final long durationFor11TypeParameters = compileTimesAfterWarmup(() -> runConformTest(
+ final List<Duration> durationsFor11TypeParameters = compileTimesAfterWarmup(() -> runConformTest(
new String[] {
"Test2_11.java",
"public class Test2_11 {\n" +
@@ -52674,7 +52682,7 @@
}));
// Time complexity should grow roughly linearly, not O(2^n)
// To make the test robust, it tests for the same order of magnitude only, i.e. factor 10.
- assertCompileTimes(durationFor2TypeParameters, 10, durationFor11TypeParameters);
+ assertCompileTimes(durationsFor2TypeParameters, 10, durationsFor11TypeParameters);
}
}
/**
@@ -52682,7 +52690,7 @@
*/
public void testBug543480WithSameSubSuperOptimization() {
if (this.complianceLevel >= ClassFileConstants.JDK1_8) {
- final long durationFor2TypeParameters = compileTimesAfterWarmup(() -> runConformTest(
+ final List<Duration> durationsFor2TypeParameters = compileTimesAfterWarmup(() -> runConformTest(
new String[] {
"WithParameterizedDependencies_2.java",
"abstract class WithParameterizedDependencies_2 {\n" +
@@ -52698,7 +52706,7 @@
" Type1(final Type1<T1, T2> l) {}" +
"}\n"
}));
- final long durationFor12TypeParameters = compileTimesAfterWarmup(() -> runConformTest(
+ final List<Duration> durationsFor12TypeParameters = compileTimesAfterWarmup(() -> runConformTest(
new String[] {
"WithParameterizedDependencies_12.java",
"abstract class WithParameterizedDependencies_12 {\n" +
@@ -52716,7 +52724,7 @@
}));
// Time complexity should grow roughly linearly, not O(2^n).
// To make the test robust, it tests for the same order of magnitude only, i.e. factor 10.
- assertCompileTimes(durationFor2TypeParameters, 10, durationFor12TypeParameters);
+ assertCompileTimes(durationsFor2TypeParameters, 10, durationsFor12TypeParameters);
}
}
/**
@@ -52746,26 +52754,83 @@
}
}
-protected void assertCompileTimes(final long shortTime, final double factor, final long longTime) {
- assertTrue(longTime + " should be less than " + factor + "x the compile time of " + shortTime,
- longTime < factor*shortTime);
+protected void assertCompileTimes(final List<Duration> shortTimes, final double factor, final List<Duration> longTimes) {
+ final double shortTimesAverage = averageExcludingBoundaries(shortTimes);
+ final double longTimesAverage = averageExcludingBoundaries(longTimes);
+ final String message = "Potential fluctuation of a performance test: average long compile time "
+ + longTimesAverage + "ms should be less than " + factor + "x the average short compile time " + shortTimesAverage +"ms\n"
+ + "long compile times: "+longTimes+"\n"
+ + "short compile times: "+shortTimes;
+ assertTrue(message,longTimesAverage < factor*shortTimesAverage);
+ System.out.println(message);
}
-protected long compileTimesAfterWarmup(final Runnable compileTask) {
+protected double averageExcludingBoundaries(final List<Duration> durations) {
+ return durations.stream()
+ .filter(duration -> !duration.isExcluded)
+ .mapToLong(duration -> duration.durationMs)
+ .average().orElse(-1);
+}
+
+protected List<Duration> compileTimesAfterWarmup(final Runnable compileTask) {
// warm up
duration(compileTask);
- // average
- return (duration(compileTask) + duration(compileTask) +
- duration(compileTask) + duration(compileTask) +
- duration(compileTask) + duration(compileTask)) / 6;
+ runGarbageCollection();
+ // draw samples, exclude boundaries i.e. exclude potential outliers
+ final int numberOfSamples = 10;
+ final int boundarySize = 2;
+ return IntStream.rangeClosed(1, numberOfSamples)
+ .mapToObj(duration(compileTask))
+ .sorted()
+ .peek(markExcludedBoundaries(boundarySize, numberOfSamples))
+ .collect(toList());
}
-protected long duration(final Runnable runnable) {
- final long startMs = System.currentTimeMillis();
- runnable.run();
- final long endMs = System.currentTimeMillis();
- final long duration = endMs-startMs;
- return duration;
+protected static IntFunction<Duration> duration(final Runnable runnable) {
+ return index -> {
+ final long startMs = System.currentTimeMillis();
+ runnable.run();
+ final long endMs = System.currentTimeMillis();
+ final long duration = endMs-startMs;
+ return new Duration(index, duration);
+ };
+}
+
+protected static Consumer<Duration> markExcludedBoundaries(final int boundarySize, final int numberOfSamples) {
+ final AtomicInteger seenSamples = new AtomicInteger(0);
+ return duration -> {
+ final int indexFromBottom = seenSamples.get();
+ final int indexFromTop = abs(seenSamples.get() - numberOfSamples);
+ if(indexFromBottom < boundarySize) {
+ // a sample within the lower boundary
+ duration.isExcluded = true;
+ } else if(indexFromTop <= boundarySize) {
+ // a sample within the upper boundary
+ duration.isExcluded = true;
+ }
+ seenSamples.incrementAndGet();
+ };
+}
+
+protected static class Duration implements Comparable<Duration> {
+ protected final int index;
+ protected final long durationMs;
+ protected boolean isExcluded = false;
+
+ public Duration(final int index, final long durationMs) {
+ this.index = index;
+ this.durationMs = durationMs;
+ }
+
+ @Override
+ public int compareTo(Duration other) {
+ return (int)(this.durationMs - other.durationMs);
+ }
+
+ @Override
+ public String toString() {
+ return "#"+index + " " + durationMs + "ms" + (isExcluded?" (excluded)":"");
+ }
}
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java
index ca24f35..021bc65 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/GenericsRegressionTest.java
@@ -6507,5 +6507,111 @@
};
runner.runConformTest();
}
+public void testBug552388() {
+ runNegativeTest(
+ new String[] {
+ "A.java",
+ "import java.util.ArrayList;\n" +
+ "import java.util.List;\n" +
+ "\n" +
+ "public class A<T extends B> {\n" +
+ " protected List<C> list = new ArrayList<C>();\n" +
+ "\n" +
+ " class C {}\n" +
+ "\n" +
+ " public void createIO() {\n" +
+ " A<? extends B> x = null;\n" +
+ " List<A<? extends B>.C> y = x.list;\n" +
+ " }\n" +
+ "}\n" +
+ "\n" +
+ "class B {\n" +
+ "}\n"
+ },
+ "----------\n" +
+ "1. ERROR in A.java (at line 11)\n" +
+ " List<A<? extends B>.C> y = x.list;\n" +
+ " ^^^^^^\n" +
+ "Type mismatch: cannot convert from List<A<capture#1-of ? extends B>.C> to List<A<? extends B>.C>\n" +
+ "----------\n");
+}
+public void testBug552388b() {
+ String output = this.complianceLevel > ClassFileConstants.JDK1_6 ?
+ "----------\n" +
+ "1. ERROR in A.java (at line 17)\n" +
+ " foo(l);\n" +
+ " ^^^\n" +
+ "The method foo(List<A<?>.C>) in the type A<T> is not applicable for the arguments (List<A<T>.C>)\n" +
+ "----------\n" +
+ "2. ERROR in A.java (at line 33)\n" +
+ " foo2(l); \n" +
+ " ^^^^\n" +
+ "The method foo2(List<A<?>>) in the type A<T> is not applicable for the arguments (List<A<T>>)\n" +
+ "----------\n"
+ :
+ "----------\n" +
+ "1. ERROR in A.java (at line 16)\n" +
+ " List<C> l = new ArrayList<>();\n" +
+ " ^^^^^^^^^\n" +
+ "\'<>\' operator is not allowed for source level below 1.7\n" +
+ "----------\n" +
+ "2. ERROR in A.java (at line 17)\n" +
+ " foo(l);\n" +
+ " ^^^\n" +
+ "The method foo(List<A<?>.C>) in the type A<T> is not applicable for the arguments (List<A<T>.C>)\n" +
+ "----------\n" +
+ "3. ERROR in A.java (at line 32)\n" +
+ " List<A<T>> l = new ArrayList<>();\n" +
+ " ^^^^^^^^^\n" +
+ "\'<>\' operator is not allowed for source level below 1.7\n" +
+ "----------\n" +
+ "4. ERROR in A.java (at line 33)\n" +
+ " foo2(l); \n" +
+ " ^^^^\n" +
+ "The method foo2(List<A<?>>) in the type A<T> is not applicable for the arguments (List<A<T>>)\n" +
+ "----------\n";
+ runNegativeTest(
+ new String[] {
+ "A.java",
+ "import java.util.*;\n" +
+ "class A<T> {\n" +
+ " class C {\n" +
+ " T f;\n" +
+ " }\n" +
+ " C c = new C();\n" +
+ " A(T t) {\n" +
+ " c = new C();\n" +
+ " c.f = t;\n" +
+ " }\n" +
+ " void foo(List<A<?>.C> list) {\n" +
+ " list.add(new A<String>(\"\").c);\n" +
+ " list.add(new A<Number>(3).c);\n" +
+ " }\n" +
+ " T test() {\n" +
+ " List<C> l = new ArrayList<>();\n" +
+ " foo(l);\n" +
+ " return l.get(0).f;\n" +
+ " }\n" +
+ " public static void main(String... args) {\n" +
+ "// Number n = new A<Number>(1).test();\n" +
+ "// System.out.print(n.intValue());\n" +
+ " Number n = new A<Number>(1).test2();\n" +
+ " System.out.print(n.intValue());\n" +
+ " }\n" +
+ " \n" +
+ " void foo2(List<A<?>> list) {\n" +
+ " list.add(new A<String>(\"\"));\n" +
+ " list.add(new A<Number>(3));\n" +
+ " }\n" +
+ " T test2() {\n" +
+ " List<A<T>> l = new ArrayList<>();\n" +
+ " foo2(l); \n" +
+ " return l.get(0).c.f;\n" +
+ " }\n" +
+ " \n" +
+ "}\n"
+ },
+ output);
+}
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakTests.java
index 1df8aa8..8e078ef 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakTests.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakTests.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2011, 2019 GK Software AG and others.
+ * Copyright (c) 2011, 2019 GK Software SE and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -5667,4 +5667,39 @@
"----------\n",
options);
}
+public void testBug486506() {
+ if (this.complianceLevel < ClassFileConstants.JDK1_8) return; // uses switch expression
+ Map options = getCompilerOptions();
+ options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR);
+ options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR);
+ runLeakTest(
+ new String[] {
+ "LogMessage.java",
+ "import java.util.stream.*;\n" +
+ "import java.io.*;\n" +
+ "import java.nio.file.*;\n" +
+ "import java.nio.charset.*;\n" +
+ "class LogMessage {\n" +
+ " LogMessage(Path path, String message) {}\n" +
+ " public static Stream<LogMessage> streamSingleLineLogMessages(Path path) {\n" +
+ " try {\n" +
+ " Stream<String> lineStream = Files.lines(path, StandardCharsets.ISO_8859_1);\n" +
+ " Stream<LogMessage> logMessageStream =\n" +
+ " lineStream.map(message -> new LogMessage(path, message));\n" +
+ " logMessageStream.onClose(lineStream::close);\n" +
+ " return logMessageStream;\n" +
+ " } catch (IOException e) {\n" +
+ " throw new RuntimeException(e);\n" +
+ " }\n" +
+ " }\n" +
+ "}\n"
+ },
+ "----------\n" +
+ "1. ERROR in LogMessage.java (at line 13)\n" +
+ " return logMessageStream;\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "Potential resource leak: \'lineStream\' may not be closed at this location\n" +
+ "----------\n",
+ options);
+}
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/junit/extension/TestCase.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/junit/extension/TestCase.java
index d0510a2..46cdca4 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/junit/extension/TestCase.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/junit/extension/TestCase.java
@@ -778,14 +778,7 @@
super.assertPerformance();
}
-
-/**
- * Clean test before run it.
- * Currently, clean only performs a gc.
- */
-protected void clean() {
- //System.out.println("Clean test "+getName());
- // Run gc
+protected void runGarbageCollection() {
int iterations = 0;
long delta=0, free=0;
for (int i=0; i<MAX_GC; i++) {
@@ -830,7 +823,7 @@
this.first = false;
boolean isFirstTestRun = CURRENT_CLASS == null;
if (isFirstTestRun || CURRENT_CLASS != getClass()) {
- if (CURRENT_CLASS != null && RUN_GC) clean();
+ if (CURRENT_CLASS != null && RUN_GC) runGarbageCollection();
CURRENT_CLASS = getClass();
this.first = true;
CURRENT_CLASS_NAME = getClass().getName();
@@ -839,7 +832,7 @@
// Memory storage if specified
if (STORE_MEMORY != null && MEM_LOG_FILE != null) {
- if (isFirstTestRun) clean();
+ if (isFirstTestRun) runGarbageCollection();
if (ALL_TESTS_LOG && MEM_LOG_FILE.exists()) {
PrintStream stream = new PrintStream(new FileOutputStream(MEM_LOG_FILE, true));
stream.print(CURRENT_CLASS_NAME);
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 21d4e4b..efec12d 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 @@
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 @@
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);
}
@@ -648,4 +682,51 @@
javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, old);
}
}
+
+ public void test0011() throws CoreException {
+ // saw NPE in SwitchExpression.resolveType(SwitchExpression.java:423)
+ if (!isJRE13) {
+ System.err.println("Test "+getName()+" requires a JRE 13");
+ return;
+ }
+ String source =
+ "public class Switch {\n" +
+ " public static void main(String[] args) {\n" +
+ " foo(Day.TUESDAY);\n" +
+ " }\n" +
+ "\n" +
+ " @SuppressWarnings(\"preview\")\n" +
+ " private static void foo(Day day) {\n" +
+ " switch (day) {\n" +
+ " case SUNDAY, MONDAY, FRIDAY -> System.out.println(6);\n" +
+ " case TUESDAY -> System.out.println(7);\n" +
+ " case THURSDAY, SATURDAY -> System.out.println(8);\n" +
+ " }\n" +
+ " }\n" +
+ "}\n" +
+ "\n" +
+ "enum Day {\n" +
+ " MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;\n" +
+ "}\n";
+ this.workingCopy = getWorkingCopy("/Converter13/src/Switch.java", true/*resolve*/);
+ IJavaProject javaProject = this.workingCopy.getJavaProject();
+ String old = javaProject.getOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, true);
+ try {
+ javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.DISABLED);
+ javaProject.setOption(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, JavaCore.IGNORE);
+ try {
+ buildAST(
+ source,
+ this.workingCopy);
+ } catch(UnsupportedOperationException e) {
+ fail("Should not throw UnsupportedOperationException");
+ } catch(AssertionFailedError e) {
+ e.printStackTrace();
+ return;
+ }
+
+ } finally {
+ javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, old);
+ }
+ }
}
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ModuleBuilderTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ModuleBuilderTests.java
index 3ed24c8..4c83d91 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ModuleBuilderTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ModuleBuilderTests.java
@@ -1456,6 +1456,45 @@
deleteProject("com.greetings");
}
}
+ public void test_Exports_foreign_package1() throws CoreException {
+ try {
+ String[] src = new String[] {
+ "src/module-info.java",
+ "module com.greetings {\n" +
+ " exports java.util;\n" +
+ "}"
+ };
+ IJavaProject p2 = setupModuleProject("com.greetings", src);
+ p2.getProject().getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, null);
+ IMarker[] markers = p2.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE);
+ assertMarkers("Unexpected markers",
+ "Cannot export the package java.util which belongs to module java.base", markers);
+ } finally {
+ deleteProject("com.greetings");
+ }
+ }
+ public void test_Exports_foreign_package2() throws CoreException {
+ try {
+ String[] src = new String[] {
+ "src/module-info.java",
+ "module com.greetings {\n" +
+ " exports java.util;\n" +
+ "}",
+ "src/java/util/Wrong.java",
+ "package java.util;\n" +
+ "public class Wrong {}\n"
+ };
+ IJavaProject p2 = setupModuleProject("com.greetings", src);
+ p2.getProject().getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, null);
+ IMarker[] markers = p2.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE);
+ sortMarkers(markers);
+ assertMarkers("Unexpected markers",
+ "The package java.util conflicts with a package accessible from another module: java.base",
+ markers);
+ } finally {
+ deleteProject("com.greetings");
+ }
+ }
public void test_DuplicateExports() throws CoreException {
try {
String[] sources = new String[] {
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java
index a08a75c..ea6f94a 100644
--- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java
+++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java
@@ -2888,7 +2888,7 @@
mode = DEFAULT;
continue;
case INSIDE_RELEASE:
- // If release is < 9, the following are diasllowed:
+ // If release is < 9, the following are disallowed:
// bootclasspath, -Xbootclasspath, -Xbootclasspath/a:, -Xbootclasspath/p:,
// -endorseddirs, -Djava.endorsed.dirs, -extdirs, -Djava.ext.dirs
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java
index 8e0bd49..0ca57f9 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java
@@ -1970,6 +1970,9 @@
int IllegalModifierForModule = ModuleRelated + 1318;
/** @since 3.18 */
int UndefinedModuleAddReads = ModuleRelated + 1319;
+ /** @since 3.20 */
+ int ExportingForeignPackage = ModuleRelated + 1320;
+
/** @since 3.14 */
int DuplicateResource = Internal + 1251;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ModuleDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ModuleDeclaration.java
index 9f9b676..6fa9fab 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ModuleDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ModuleDeclaration.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2015, 2018 IBM Corporation and others.
+ * Copyright (c) 2015, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -314,6 +314,14 @@
continue;
if (pb.hasCompilationUnit(true))
continue;
+ for (ModuleBinding req : this.binding.getAllRequiredModules()) {
+ for (PlainPackageBinding exported : req.getExports()) {
+ if (CharOperation.equals(pb.compoundName, exported.compoundName)) {
+ skope.problemReporter().exportingForeignPackage(stat, req);
+ return;
+ }
+ }
+ }
skope.problemReporter().invalidPackageReference(IProblem.PackageDoesNotExistOrIsEmpty, stat);
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
index d6d044c..cfa3925 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
@@ -533,6 +533,14 @@
}
}
+ if (currentScope.compilerOptions().analyseResourceLeaks) {
+ if (this.haveReceiver && CharOperation.equals(this.selector, TypeConstants.CLOSE)) {
+ FakedTrackingVariable trackingVariable = FakedTrackingVariable.getCloseTrackingVariable(this.lhs, flowInfo, flowContext);
+ if (trackingVariable != null) { // null happens if target is not a local variable or not an AutoCloseable
+ trackingVariable.markClosedInNestedMethod(); // there is a close()-call, but we don't know if it will be invoked
+ }
+ }
+ }
manageSyntheticAccessIfNecessary(currentScope, flowInfo);
return flowInfo;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java
index 21dfd41..1c63824 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.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
@@ -1423,7 +1423,7 @@
if (TypeBinding.notEquals(enclosing, otherEnclosing))
return false;
} else {
- if (!enclosing.isEquivalentTo(otherParamType.enclosingType()))
+ if (!enclosing.isTypeArgumentContainedBy(otherParamType.enclosingType()))
return false;
}
}
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 83f1800..4ea33b4 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
@@ -432,7 +432,7 @@
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() {
@@ -834,6 +834,10 @@
List<char[]> list = new ArrayList<>(lines.length);
for(int i = 0; i < lines.length; i++) {
char[] line = lines[i];
+ if (i + 1 == size && line.length == 0) {
+ list.add(line);
+ break;
+ }
char[][] sub = CharOperation.splitOn('\r', line);
for (char[] cs : sub) {
if (cs.length > 0) {
@@ -860,7 +864,7 @@
}
}
}
- if (!blank) {
+ if (!blank || (i+1 == size)) {
if (prefix < 0 || whitespaces < prefix) {
prefix = whitespaces;
}
@@ -1936,7 +1940,9 @@
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);
@@ -2005,6 +2011,9 @@
}
}
// consume next character
+ if (this.currentPosition >= this.eofPosition) {
+ break;
+ }
this.unicodeAsBackSlash = false;
if (((this.currentCharacter = this.source[this.currentPosition++]) == '\\')
&& (this.source[this.currentPosition] == 'u')) {
@@ -2505,7 +2514,10 @@
case '\n' :
pushLineSeparator();
//$FALL-THROUGH$
- default:
+ default:
+ if (this.currentCharacter == '\\' && this.source[this.currentPosition++] == '"') {
+ this.currentPosition++;
+ }
this.currentCharacter = this.source[this.currentPosition++];
continue Inner;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
index 4368620..8bb2c12 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
@@ -15439,6 +15439,14 @@
ref.pkgRef.sourceStart,
ref.pkgRef.sourceEnd);
}
+public void exportingForeignPackage(PackageVisibilityStatement ref, ModuleBinding enclosingModule) {
+ String[] arguments = new String[] { CharOperation.charToString(ref.pkgName), CharOperation.charToString(enclosingModule.moduleName) };
+ this.handle(IProblem.ExportingForeignPackage,
+ arguments,
+ arguments,
+ ref.pkgRef.sourceStart,
+ ref.pkgRef.sourceEnd);
+}
public void duplicateModuleReference(int problem, ModuleReference ref) {
this.handle(problem,
NoArgument, new String[] { CharOperation.charToString(ref.moduleName) },
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
index b1fb8d0..ebfd8e9 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
@@ -918,6 +918,7 @@
1317 = Invalid service implementation {0}, must be a public class or interface type
1318 = Illegal modifier for module {0}; only open is permitted
1319 = {0} cannot be resolved to a module, it is referenced from an add-reads directive
+1320 = Cannot export the package {0} which belongs to module {1}
#### Java 9
1351 = Variable resource not allowed here for source level below 9
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java
index 9168fb6..8600971 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java
@@ -3572,7 +3572,7 @@
}
public Statement convert(org.eclipse.jdt.internal.compiler.ast.YieldStatement statement) {
- if (this.ast.apiLevel != AST.JLS13_INTERNAL) {
+ if (!this.ast.isPreviewEnabled()) {
return createFakeEmptyStatement(statement);
}
YieldStatement yieldStatement = new YieldStatement(this.ast);
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 a74ed83..38ab6c9 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 @@
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 @@
* @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) {
+ if (start == -1) {
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) {
- throw new IllegalArgumentException();
- }
+ return new String(
+ CharOperation.subarray(escaped, start, len - 3)
+ );
}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IJavaProject.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IJavaProject.java
index f095b8f..0938c2f 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IJavaProject.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IJavaProject.java
@@ -610,7 +610,7 @@
IProject getProject();
/**
- * Returns the <code>IModuleDescription</code> this project represents or
+ * Returns the {@link IModuleDescription} this project represents or
* null if the Java project doesn't represent any named module. A Java
* project is said to represent a module if any of its source package
* fragment roots (see {@link IPackageFragmentRoot#K_SOURCE}) contains a
@@ -620,7 +620,7 @@
* In the latter case the corresponding module description of the
* location referenced by that classpath entry is returned.
*
- * @return the <code>IModule</code> this project represents.
+ * @return the {@link IModuleDescription} this project represents.
* @exception JavaModelException if this element does not exist or if an
* exception occurs while accessing its corresponding resource
* @since 3.14
@@ -628,6 +628,22 @@
IModuleDescription getModuleDescription() throws JavaModelException;
/**
+ * Returns the <code>IModuleDescription</code> owned by this project or
+ * null if the Java project doesn't own a valid Java module descriptor.
+ * This method considers only module descriptions contained in any of the
+ * project's source package fragment roots (see {@link IPackageFragmentRoot#K_SOURCE}).
+ * In particular any {@link IClasspathAttribute#PATCH_MODULE} attribute
+ * is not considered.
+ *
+ * @return the {@link IModuleDescription} this project owns.
+ * @exception JavaModelException if this element does not exist or if an
+ * exception occurs while accessing its corresponding resource
+ * @throws JavaModelException
+ * @since 3.20
+ */
+ IModuleDescription getOwnModuleDescription() throws JavaModelException;
+
+ /**
* Returns the raw classpath for the project, as a list of classpath
* entries. This corresponds to the exact set of entries which were assigned
* using <code>setRawClasspath</code>, in particular such a classpath may
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 2b73170..9479cd8 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 @@
*/
@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>"1.3"</code> or <code>null</code>, occurrences of 'assert' will be reported as identifiers
+ * ({@link ITerminalSymbols#TokenNameIdentifier}), whereas if set to <code>"1.4"</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 b3a9fea..1fe00e1 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
@@ -101,6 +101,11 @@
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;
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java
index 6e692e0..0eb1339 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java
@@ -3890,6 +3890,12 @@
return null;
}
+ @Override
+ public IModuleDescription getOwnModuleDescription() throws JavaModelException {
+ JavaProjectElementInfo info = (JavaProjectElementInfo) getElementInfo();
+ return info.getModule();
+ }
+
public List<String> getPatchedModules(IClasspathEntry cpEntry) {
String patchModules = ClasspathEntry.getExtraAttribute(cpEntry, IClasspathAttribute.PATCH_MODULE);
if (patchModules != null) {
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 1b53bad..e708256 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
@@ -43,6 +43,7 @@
*/
public long sourceLevel;
public long complianceLevel;
+ public boolean previewEnabled;
// 1.4 feature
public boolean useAssertAsAnIndentifier = false;
@@ -201,6 +202,7 @@
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$
@@ -300,6 +302,9 @@
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*/);
}
@@ -313,12 +318,33 @@
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;
@@ -611,6 +637,46 @@
}
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.
@@ -1563,26 +1629,53 @@
}
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;
@@ -1636,22 +1729,39 @@
}
}
// 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
@@ -1669,7 +1779,6 @@
}
throw e; // rethrow
}
- return TokenNameStringLiteral;
case '/' :
if (!this.skipComments) {
int test = getNextChar('/', '*');
@@ -2069,23 +2178,57 @@
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++;
@@ -2129,7 +2272,13 @@
}
}
} 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 '/' :
@@ -4513,6 +4662,7 @@
case TerminalTokens.TokenNameFloatingPointLiteral:
case TerminalTokens.TokenNameDoubleLiteral:
case TerminalTokens.TokenNameStringLiteral:
+ case TerminalTokens.TokenNameTextBlock:
case TerminalTokens.TokenNameCharacterLiteral:
return true;
default: