From 2d1a2262035ea1459de3b9e679b11a5b72bfeb91 Mon Sep 17 00:00:00 2001 From: Jay Arthanareeswaran Date: Wed, 27 Feb 2019 22:26:24 +0530 Subject: Revert "Revert "Bug 540922 - [12] ct.sym file has been restructured again for older releases"" This reverts commit 6940b0b9166a7ea6c9647fc7c86ed1cd6c67da51. --- .../regression/ModuleCompilationTests.java | 7 +- .../jdt/core/tests/util/AbstractCompilerTest.java | 2 + .../core/tests/model/AbstractJavaModelTests.java | 13 + .../jdt/core/tests/model/ModuleBuilderTests.java | 453 ++++++++++++++++++++- .../internal/compiler/batch/ClasspathJep247.java | 20 +- .../compiler/batch/ClasspathJep247Jdk12.java | 303 ++++++++++++++ .../jdt/internal/compiler/batch/FileSystem.java | 13 +- .../compiler/env/NameEnvironmentAnswer.java | 4 +- .../jdt/internal/compiler/util/JRTUtil.java | 262 +++++++++--- .../jdt/internal/core/builder/ClasspathJrt.java | 182 +++------ .../builder/ClasspathJrtWithReleaseOption.java | 387 ++++++++++++++++++ .../internal/core/builder/ClasspathLocation.java | 8 +- .../jdt/internal/core/builder/JavaBuilder.java | 2 +- .../eclipse/jdt/internal/core/builder/State.java | 28 +- .../search/matching/JavaSearchNameEnvironment.java | 3 +- 15 files changed, 1454 insertions(+), 233 deletions(-) create mode 100644 org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJep247Jdk12.java create mode 100644 org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJrtWithReleaseOption.java diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleCompilationTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleCompilationTests.java index 65587be739..349e89cf8d 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleCompilationTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleCompilationTests.java @@ -40,7 +40,7 @@ import junit.framework.Test; public class ModuleCompilationTests extends AbstractBatchCompilerTest { static { -// TESTS_NAMES = new String[] { "testBug540067e" }; +// TESTS_NAMES = new String[] { "test001" }; // TESTS_NUMBERS = new int[] { 1 }; // TESTS_RANGE = new int[] { 298, -1 }; } @@ -3975,11 +3975,11 @@ public void testBug521362_emptyFile() { "}", }, "\"" + OUTPUT_DIR + File.separator + "X.java\"" - + " --release 6 -d \"" + OUTPUT_DIR + "\"", + + " --release 7 -d \"" + OUTPUT_DIR + "\"", "", "", true); - String expectedOutput = "// Compiled from X.java (version 1.6 : 50.0, super bit)"; + String expectedOutput = "// Compiled from X.java (version 1.7 : 51.0, super bit)"; checkDisassembledClassFile(OUTPUT_DIR + File.separator + "X.class", "X", expectedOutput); } public void testReleaseOption4() throws Exception { @@ -4094,6 +4094,7 @@ public void testBug521362_emptyFile() { true); } public void testReleaseOption10() throws Exception { + if (isJRE12Plus) return; this.runNegativeTest( new String[] { "X.java", diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/AbstractCompilerTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/AbstractCompilerTest.java index 33aa81cb9a..fee9aacbe9 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/AbstractCompilerTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/util/AbstractCompilerTest.java @@ -53,6 +53,7 @@ public class AbstractCompilerTest extends TestCase { protected boolean enableAPT = false; protected static boolean isJRE9Plus = false; // Stop gap, so tests need not be run at 9, but some tests can be adjusted for JRE 9 protected static boolean isJRE11Plus = false; + protected static boolean isJRE12Plus = false; protected static boolean reflectNestedClassUseDollar; /** @@ -147,6 +148,7 @@ public class AbstractCompilerTest extends TestCase { int lessthan9 = F_1_3 | F_1_4 | F_1_5 | F_1_6 | F_1_7 | F_1_8; isJRE9Plus = !isJRELevel(lessthan9); isJRE11Plus = isJRELevel(F_11); + isJRE12Plus = "12".equals(System.getProperty("java.specification.version")); } /** diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java index df6e565b35..72600309b7 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AbstractJavaModelTests.java @@ -74,6 +74,9 @@ public abstract class AbstractJavaModelTests extends SuiteOfTestCases { protected String endChar = ","; protected static boolean isJRE9 = false; + protected static boolean isJRE10 = false; + protected static boolean isJRE11 = false; + protected static boolean isJRE12 = false; protected static String DEFAULT_MODULES = null; static { String javaVersion = System.getProperty("java.version"); @@ -87,6 +90,16 @@ public abstract class AbstractJavaModelTests extends SuiteOfTestCases { } } long jdkLevel = CompilerOptions.versionToJdkLevel(javaVersion.length() > 3 ? javaVersion.substring(0, 3) : javaVersion); + if (jdkLevel >= ClassFileConstants.JDK11) { + if (CompilerOptions.versionToJdkLevel(javaVersion.length() > 3 ? javaVersion.substring(0, 3) : javaVersion, false) == 0) { + // version was capped to 11 during versionToJdkLevel(version, true) + isJRE12 = true; + } + isJRE11 = true; + } + if (jdkLevel >= ClassFileConstants.JDK10) { + isJRE10 = true; + } if (jdkLevel >= ClassFileConstants.JDK9) { isJRE9 = true; if (vmName.contains("HotSpot")) { 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 b80de70c73..ddc65ad408 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 @@ -696,8 +696,14 @@ public class ModuleBuilderTests extends ModifyingResourceTests { public void testConvertToModule() throws CoreException, IOException { Hashtable javaCoreOptions = JavaCore.getOptions(); try { - IJavaProject project = setUpJavaProject("ConvertToModule", "9"); - assertEquals(project.getOption("org.eclipse.jdt.core.compiler.compliance", true), "9"); + IJavaProject project = setUpJavaProject("ConvertToModule"); + Map options = new HashMap<>(); + // Make sure the new options map doesn't reset. + options.put(CompilerOptions.OPTION_Compliance, "9"); + options.put(CompilerOptions.OPTION_Source, "9"); + options.put(CompilerOptions.OPTION_TargetPlatform, "9"); + options.put(CompilerOptions.OPTION_Release, "enabled"); + project.setOptions(options); project.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null); IPackageFragmentRoot[] roots = project.getPackageFragmentRoots(); IPackageFragmentRoot theRoot = null; @@ -709,7 +715,14 @@ public class ModuleBuilderTests extends ModifyingResourceTests { } assertNotNull("should not be null", theRoot); String[] modules = JavaCore.getReferencedModules(project); - assertStringsEqual("incorrect result", new String[]{"java.desktop", "java.rmi", "java.sql"}, modules); + if (isJRE12) + assertStringsEqual("incorrect result", new String[]{"java.desktop", "java.rmi", "java.sql"}, modules); + else if (isJRE11) + assertStringsEqual("incorrect result", new String[]{"java.datatransfer", "java.desktop", "java.net.http", "java.rmi", "java.sql"}, modules); + else if (isJRE10) + assertStringsEqual("incorrect result", new String[]{"java.datatransfer", "java.desktop", "java.rmi", "java.sql"}, modules); + else // 9 + assertStringsEqual("incorrect result", new String[]{"java.desktop", "java.rmi", "java.sql"}, modules); } finally { this.deleteProject("ConvertToModule"); JavaCore.setOptions(javaCoreOptions); @@ -8040,6 +8053,440 @@ public class ModuleBuilderTests extends ModifyingResourceTests { } } + public void testReleaseOption1() throws Exception { + Hashtable options = JavaCore.getOptions(); + IJavaProject p = createJava9Project("p"); + p.setOption(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_7); + p.setOption(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_1_7); + p.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_1_7); + p.setOption(JavaCore.COMPILER_RELEASE, JavaCore.ENABLED); + String outputDirectory = Util.getOutputDirectory(); + try { + String testSource = "public class X {\n" + + "}"; + String mPath = "p/src/X.java"; + createFile(mPath, + testSource); + p.getProject().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null); + waitForAutoBuild(); + IMarker[] markers = p.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE); + assertMarkers("Unexpected markers", + "", markers); + + } finally { + JavaCore.setOptions(options); + deleteProject(p); + File outputDir = new File(outputDirectory); + if (outputDir.exists()) + Util.flushDirectoryContent(outputDir); + } + } + public void testReleaseOption2() throws Exception { + if (!isJRE12) + return; + Hashtable options = JavaCore.getOptions(); + IJavaProject p = createJava9Project("p"); + p.setOption(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_6); + p.setOption(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_1_6); + p.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_1_6); + p.setOption(JavaCore.COMPILER_RELEASE, JavaCore.ENABLED); + String outputDirectory = Util.getOutputDirectory(); + try { + String testSource = "public class X {\n" + + " public java.util.stream.Stream emptyStream() {\n" + + " return null;\n" + + " }\n" + + "}"; + String mPath = "p/src/X.java"; + createFile(mPath, + testSource); + p.getProject().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null); + waitForAutoBuild(); + IMarker[] markers = p.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE); + assertMarkers("Unexpected markers", + "The project was not built due to \"release 6 is not found in the system\". " + + "Fix the problem, then try refreshing this project and building it since it may be inconsistent", markers); + + } finally { + JavaCore.setOptions(options); + deleteProject(p); + File outputDir = new File(outputDirectory); + if (outputDir.exists()) + Util.flushDirectoryContent(outputDir); + } + } + public void testReleaseOption3() throws Exception { + if (isJRE12) + return; + Hashtable options = JavaCore.getOptions(); + IJavaProject p = createJava9Project("p"); + p.setOption(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_7); + p.setOption(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_1_7); + p.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_1_7); + p.setOption(JavaCore.COMPILER_RELEASE, JavaCore.ENABLED); + String outputDirectory = Util.getOutputDirectory(); + try { + String testSource = "public class X {\n" + + " public java.util.stream.Stream emptyStream() {\n" + + " return null;\n" + + " }\n" + + "}"; + String mPath = "p/src/X.java"; + createFile(mPath, + testSource); + p.getProject().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null); + waitForAutoBuild(); + IMarker[] markers = p.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE); + assertMarkers("Unexpected markers", + "java.util.stream.Stream cannot be resolved to a type", markers); + + } finally { + JavaCore.setOptions(options); + deleteProject(p); + File outputDir = new File(outputDirectory); + if (outputDir.exists()) + Util.flushDirectoryContent(outputDir); + } + } + public void testReleaseOption4() throws Exception { + Hashtable options = JavaCore.getOptions(); + IJavaProject p = createJava9Project("p"); + p.setOption(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_8); + p.setOption(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_1_8); + p.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_1_8); + p.setOption(JavaCore.COMPILER_RELEASE, JavaCore.ENABLED); + String outputDirectory = Util.getOutputDirectory(); + try { + String testSource = "public class X {\n" + + " public java.util.stream.Stream emptyStream() {\n" + + " return null;\n" + + " }\n" + + "}"; + String mPath = "p/src/X.java"; + createFile(mPath, + testSource); + p.getProject().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null); + waitForAutoBuild(); + IMarker[] markers = p.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE); + assertMarkers("Unexpected markers", + "", markers); + + } finally { + JavaCore.setOptions(options); + deleteProject(p); + File outputDir = new File(outputDirectory); + if (outputDir.exists()) + Util.flushDirectoryContent(outputDir); + } + } + public void testReleaseOption5() throws Exception { + Hashtable options = JavaCore.getOptions(); + IJavaProject p = createJava9Project("p"); + p.setOption(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_7); + p.setOption(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_1_7); + p.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_1_7); + p.setOption(JavaCore.COMPILER_RELEASE, JavaCore.ENABLED); + String outputDirectory = Util.getOutputDirectory(); + try { + String testSource = "public class X {\n" + + " public java.util.stream.Stream emptyStream() {\n" + + " return null;\n" + + " }\n" + + "}"; + String mPath = "p/src/X.java"; + createFile(mPath, + testSource); + p.getProject().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null); + waitForAutoBuild(); + IMarker[] markers = p.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE); + assertMarkers("Unexpected markers", + "java.util.stream.Stream cannot be resolved to a type", markers); + + } finally { + JavaCore.setOptions(options); + deleteProject(p); + File outputDir = new File(outputDirectory); + if (outputDir.exists()) + Util.flushDirectoryContent(outputDir); + } + } + public void testReleaseOption6() throws Exception { + Hashtable options = JavaCore.getOptions(); + IJavaProject p = createJava9Project("p"); + p.setOption(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_7); + p.setOption(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_1_7); + p.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_1_7); + p.setOption(JavaCore.COMPILER_RELEASE, JavaCore.ENABLED); + String outputDirectory = Util.getOutputDirectory(); + try { + String testSource = "interface I {\n" + + " int add(int x, int y);\n" + + "}\n" + + "public class X {\n" + + " public static void main(String[] args) {\n" + + " I i = (x, y) -> {\n" + + " return x + y;\n" + + " };\n" + + " }\n" + + "}\n"; + String mPath = "p/src/X.java"; + createFile(mPath, + testSource); + p.getProject().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null); + waitForAutoBuild(); + IMarker[] markers = p.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE); + assertMarkers("Unexpected markers", + "Lambda expressions are allowed only at source level 1.8 or above", markers); + + } finally { + JavaCore.setOptions(options); + deleteProject(p); + File outputDir = new File(outputDirectory); + if (outputDir.exists()) + Util.flushDirectoryContent(outputDir); + } + } + public void testReleaseOption7() throws Exception { + if (isJRE12) + return; + Hashtable options = JavaCore.getOptions(); + IJavaProject p = createJava9Project("p"); + p.setOption(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_6); + p.setOption(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_1_6); + p.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_1_6); + p.setOption(JavaCore.COMPILER_RELEASE, JavaCore.ENABLED); + String outputDirectory = Util.getOutputDirectory(); + try { + String testSource = "import java.io.*;\n" + + "public class X {\n" + + " public static void main(String[] args) {\n" + + " try {\n" + + " System.out.println();\n" + + " Reader r = new FileReader(args[0]);\n" + + " r.read();\n" + + " } catch(IOException | FileNotFoundException e) {\n" + + " e.printStackTrace();\n" + + " }\n" + + " }\n" + + "}"; + String mPath = "p/src/X.java"; + createFile(mPath, + testSource); + p.getProject().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null); + waitForAutoBuild(); + IMarker[] markers = p.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE); + sortMarkers(markers); + assertMarkers("Unexpected markers", + "Multi-catch parameters are not allowed for source level below 1.7\n" + + "The exception FileNotFoundException is already caught by the alternative IOException", markers); + + } finally { + JavaCore.setOptions(options); + deleteProject(p); + File outputDir = new File(outputDirectory); + if (outputDir.exists()) + Util.flushDirectoryContent(outputDir); + } + } + public void testReleaseOption8() throws Exception { + Hashtable options = JavaCore.getOptions(); + IJavaProject p = createJava9Project("p"); + p.setOption(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_9); + p.setOption(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_9); + p.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_9); + p.setOption(JavaCore.COMPILER_RELEASE, JavaCore.ENABLED); + String outputDirectory = Util.getOutputDirectory(); + try { + String testSource = "module mod.one { \n" + + " requires java.base;\n" + + "}"; + String mPath = "p/src/module-info.java"; + createFile(mPath, + testSource); + p.getProject().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null); + waitForAutoBuild(); + IMarker[] markers = p.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE); + assertMarkers("Unexpected markers", + "", markers); + + } finally { + JavaCore.setOptions(options); + deleteProject(p); + File outputDir = new File(outputDirectory); + if (outputDir.exists()) + Util.flushDirectoryContent(outputDir); + } + } + public void testReleaseOption9() throws Exception { + if (!isJRE10) return; + Hashtable options = JavaCore.getOptions(); + IJavaProject p = createJava9Project("p"); + p.setOption(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_10); + p.setOption(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_10); + p.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_10); + p.setOption(JavaCore.COMPILER_RELEASE, JavaCore.ENABLED); + String outputDirectory = Util.getOutputDirectory(); + try { + String testSource = "module mod.one { \n" + + " requires java.base;\n" + + "}"; + String mPath = "p/src/module-info.java"; + createFile(mPath, + testSource); + p.getProject().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null); + waitForAutoBuild(); + IMarker[] markers = p.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE); + assertMarkers("Unexpected markers", + "", markers); + + } finally { + JavaCore.setOptions(options); + deleteProject(p); + File outputDir = new File(outputDirectory); + if (outputDir.exists()) + Util.flushDirectoryContent(outputDir); + } + } + public void testReleaseOption10() throws Exception { + Hashtable options = JavaCore.getOptions(); + IJavaProject p = createJava9Project("p"); + p.setOption(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_8); + p.setOption(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_1_8); + p.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_1_8); + p.setOption(JavaCore.COMPILER_RELEASE, JavaCore.ENABLED); + String outputDirectory = Util.getOutputDirectory(); + try { + String testSource = "module mod.one { \n" + + " requires java.base;\n" + + "}"; + String mPath = "p/src/module-info.java"; + createFile(mPath, + testSource); + p.getProject().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null); + waitForAutoBuild(); + IMarker[] markers = p.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE); + sortMarkers(markers); + String expected = + "Syntax error on token \"module\", package expected\n" + + "Syntax error on token(s), misplaced construct(s)\n" + + "Syntax error on token \".\", , expected\n" + + "Syntax error on token \"}\", delete this token"; + assertMarkers("Unexpected markers", + expected, markers); + + } finally { + JavaCore.setOptions(options); + deleteProject(p); + File outputDir = new File(outputDirectory); + if (outputDir.exists()) + Util.flushDirectoryContent(outputDir); + } + } + public void testReleaseOption11() throws Exception { + Hashtable options = JavaCore.getOptions(); + IJavaProject p = createJava9Project("p"); + p.setOption(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_8); + p.setOption(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_1_8); + p.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_1_8); + p.setOption(JavaCore.COMPILER_RELEASE, JavaCore.ENABLED); + String outputDirectory = Util.getOutputDirectory(); + try { + createFolder("p/src/foo"); + createFile( + "p/src/foo/Module.java", + "package foo;\n" + + "public class Module {}\n"); + createFile( + "p/src/foo/X.java", + "package foo;\n" + + "public class X { \n" + + " public Module getModule(String name) {\n" + + " return null;\n" + + " }\n" + + "}"); + p.getProject().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null); + waitForAutoBuild(); + IMarker[] markers = p.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE); + assertMarkers("Unexpected markers", + "", markers); + + } finally { + JavaCore.setOptions(options); + deleteProject(p); + File outputDir = new File(outputDirectory); + if (outputDir.exists()) + Util.flushDirectoryContent(outputDir); + } + } + public void testReleaseOption12() throws Exception { + if (!isJRE12) + return; + Hashtable options = JavaCore.getOptions(); + IJavaProject p = createJava9Project("p"); + p.setOption(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_7); + p.setOption(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_1_7); + p.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_1_7); + p.setOption(JavaCore.COMPILER_RELEASE, JavaCore.ENABLED); + String outputDirectory = Util.getOutputDirectory(); + try { + String testSource = "import java.io.*;\n" + + "\n" + + "public class X {\n" + + " public static void main(String[] args) {\n" + + " String str = Integer.toUnsignedString(1, 1);\n" + + " }\n" + + "}"; + String mPath = "p/src/X.java"; + createFile(mPath, + testSource); + p.getProject().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null); + waitForAutoBuild(); + IMarker[] markers = p.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE); + assertMarkers("Unexpected markers", + "The method toUnsignedString(int, int) is undefined for the type Integer", markers); + + } finally { + JavaCore.setOptions(options); + deleteProject(p); + File outputDir = new File(outputDirectory); + if (outputDir.exists()) + Util.flushDirectoryContent(outputDir); + } + } + public void testReleaseOption13() throws Exception { + if (!isJRE12) + return; + Hashtable options = JavaCore.getOptions(); + IJavaProject p = createJava9Project("p"); + p.setOption(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_8); + p.setOption(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_1_8); + p.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_1_8); + p.setOption(JavaCore.COMPILER_RELEASE, JavaCore.ENABLED); + String outputDirectory = Util.getOutputDirectory(); + try { + String testSource = "\n" + + "public class X {\n" + + " public static void main(String[] args) {\n" + + " Integer.toUnsignedString(1, 1);\n" + + " }\n" + + "}"; + String mPath = "p/src/X.java"; + createFile(mPath, + testSource); + p.getProject().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null); + waitForAutoBuild(); + IMarker[] markers = p.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE); + assertMarkers("Unexpected markers", + "", markers); + + } finally { + JavaCore.setOptions(options); + deleteProject(p); + File outputDir = new File(outputDirectory); + if (outputDir.exists()) + Util.flushDirectoryContent(outputDir); + } + } protected void assertNoErrors() throws CoreException { for (IProject p : getWorkspace().getRoot().getProjects()) { int maxSeverity = p.findMaxProblemSeverity(null, true, IResource.DEPTH_INFINITE); diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJep247.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJep247.java index 7a39271115..8aad54669b 100644 --- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJep247.java +++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJep247.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018 IBM Corporation. + * Copyright (c) 2018, 2019 IBM Corporation. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -42,15 +42,15 @@ import org.eclipse.jdt.internal.compiler.util.Util; public class ClasspathJep247 extends ClasspathJrt { - private java.nio.file.FileSystem fs = null; - private String compliance = null; - private long jdklevel; - private String releaseInHex = null; - private String[] subReleases = null; - private Path releasePath = null; - private Set packageCache; - File jdkHome; - String modulePath = null; + protected java.nio.file.FileSystem fs = null; + protected String compliance = null; + protected long jdklevel; + protected String releaseInHex = null; + protected String[] subReleases = null; + protected Path releasePath = null; + protected Set packageCache; + protected File jdkHome; + protected String modulePath = null; public ClasspathJep247(File jdkHome, String release, AccessRuleSet accessRuleSet) { super(jdkHome, false, accessRuleSet, null); diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJep247Jdk12.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJep247Jdk12.java new file mode 100644 index 0000000000..52a83987cf --- /dev/null +++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJep247Jdk12.java @@ -0,0 +1,303 @@ +/******************************************************************************* + * Copyright (c) 2019 IBM Corporation. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.compiler.batch; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.DirectoryStream; +import java.nio.file.FileSystemNotFoundException; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitResult; +import java.nio.file.FileVisitor; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; +import org.eclipse.jdt.internal.compiler.env.AccessRuleSet; +import org.eclipse.jdt.internal.compiler.env.IModule; +import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; +import org.eclipse.jdt.internal.compiler.util.JRTUtil; +import org.eclipse.jdt.internal.compiler.util.Util; + +public class ClasspathJep247Jdk12 extends ClasspathJep247 { + + Map modules; + + public ClasspathJep247Jdk12(File jdkHome, String release, AccessRuleSet accessRuleSet) { + super(jdkHome, release, accessRuleSet); + } + @Override + public List fetchLinkedJars(FileSystem.ClasspathSectionProblemReporter problemReporter) { + return null; + } + @Override + public NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageName, String moduleName, String qualifiedBinaryFileName) { + return findClass(typeName, qualifiedPackageName, moduleName, qualifiedBinaryFileName, false); + } + @Override + public NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageName, String moduleName, String qualifiedBinaryFileName, boolean asBinaryOnly) { + if (!isPackage(qualifiedPackageName, moduleName)) + return null; // most common case + + try { + ClassFileReader reader = null; + byte[] content = null; + qualifiedBinaryFileName = qualifiedBinaryFileName.replace(".class", ".sig"); //$NON-NLS-1$ //$NON-NLS-2$ + if (this.subReleases != null && this.subReleases.length > 0) { + done: for (String rel : this.subReleases) { + if (moduleName == null) { + Path p = this.fs.getPath(rel); + try (DirectoryStream stream = Files.newDirectoryStream(p)) { + for (final java.nio.file.Path subdir: stream) { + Path f = this.fs.getPath(rel, subdir.getFileName().toString(), qualifiedBinaryFileName); + if (Files.exists(f)) { + content = JRTUtil.safeReadBytes(f); + if (content != null) + break done; + } + } + } + } else { + Path p = this.fs.getPath(rel, moduleName, qualifiedBinaryFileName); + if (Files.exists(p)) { + content = JRTUtil.safeReadBytes(p); + if (content != null) + break; + } + } + } + } else { + content = JRTUtil.safeReadBytes(this.fs.getPath(this.releaseInHex, qualifiedBinaryFileName)); + } + if (content != null) { + reader = new ClassFileReader(content, qualifiedBinaryFileName.toCharArray()); + return new NameEnvironmentAnswer(reader, fetchAccessRestriction(qualifiedBinaryFileName), null); + } + } catch(ClassFormatException e) { + // Continue + } catch (IOException e) { + // continue + } + return null; + } + + @Override + public void initialize() throws IOException { + if (this.compliance == null) { + return; + } + if (this.fs != null) { + super.initialize(); + return; + } + this.releaseInHex = Integer.toHexString(Integer.parseInt(this.compliance)).toUpperCase(); + Path filePath = this.jdkHome.toPath().resolve("lib").resolve("ct.sym"); //$NON-NLS-1$ //$NON-NLS-2$ + URI t = filePath.toUri(); + if (!Files.exists(filePath)) { + return; + } + URI uri = URI.create("jar:file:" + t.getRawPath()); //$NON-NLS-1$ + try { + this.fs = FileSystems.getFileSystem(uri); + } catch(FileSystemNotFoundException fne) { + // Ignore and move on + } + if (this.fs == null) { + HashMap env = new HashMap<>(); + this.fs = FileSystems.newFileSystem(uri, env); + } + this.releasePath = this.fs.getPath("/"); //$NON-NLS-1$ + if (!Files.exists(this.fs.getPath(this.releaseInHex))) { + throw new IllegalArgumentException("release " + this.compliance + " is not found in the system"); //$NON-NLS-1$//$NON-NLS-2$ + } + List sub = new ArrayList<>(); + try (DirectoryStream stream = Files.newDirectoryStream(this.releasePath)) { + for (final java.nio.file.Path subdir: stream) { + String rel = subdir.getFileName().toString(); + if (rel.contains(this.releaseInHex)) + sub.add(rel); + } + this.subReleases = sub.toArray(new String[sub.size()]); + } catch (IOException e) { + //e.printStackTrace(); + } + super.initialize(); + } + @Override + public void loadModules() { + // Modules below level 8 are not dealt with here. Leave it to ClasspathJrt + if (this.jdklevel <= ClassFileConstants.JDK1_8) { + super.loadModules(); + return; + } + final Path modPath = this.fs.getPath(this.releaseInHex); + this.modulePath = this.file.getPath() + "|" + modPath.toString(); //$NON-NLS-1$ + this.modules = ModulesCache.get(this.modulePath); + if (this.modules == null) { + try (DirectoryStream stream = Files.newDirectoryStream(this.releasePath)) { + for (final java.nio.file.Path subdir: stream) { + String rel = subdir.getFileName().toString(); + if (!rel.contains(this.releaseInHex)) { + continue; + } + Files.walkFileTree(subdir, Collections.EMPTY_SET, 2, new FileVisitor() { + + @Override + public FileVisitResult preVisitDirectory(java.nio.file.Path dir, BasicFileAttributes attrs) + throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(java.nio.file.Path f, BasicFileAttributes attrs) throws IOException { + if (attrs.isDirectory() || f.getNameCount() < 3) + return FileVisitResult.CONTINUE; + byte[] content = null; + if (Files.exists(f)) { + content = JRTUtil.safeReadBytes(f); + if (content == null) + return FileVisitResult.CONTINUE; + Path m = f.subpath(1, f.getNameCount() - 1); + ClasspathJep247Jdk12.this.acceptModule(m.getFileName().toString(), content); + ClasspathJep247Jdk12.this.moduleNamesCache.add(m.getFileName().toString()); + } + return FileVisitResult.SKIP_SIBLINGS; + } + + @Override + public FileVisitResult visitFileFailed(java.nio.file.Path f, IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(java.nio.file.Path dir, IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + }); + } + } catch (IOException e) { + e.printStackTrace(); + } + } else { + this.moduleNamesCache.addAll(this.modules.keySet()); + } + } + @Override + public Collection getModuleNames(Collection limitModule, Function getModule) { + return selectModules(this.moduleNamesCache, limitModule, getModule); + } + @Override + public IModule getModule(char[] moduleName) { + if (this.modules != null) { + return this.modules.get(String.valueOf(moduleName)); + } + return null; + } + void acceptModule(String name, byte[] content) { + if (content == null) + return; + + if (this.modules != null) { + if (this.modules.containsKey(name)) + return; + } + + ClassFileReader reader = null; + try { + reader = new ClassFileReader(content, IModule.MODULE_INFO_CLASS.toCharArray()); + } catch (ClassFormatException e) { + e.printStackTrace(); + } + if (reader != null) { + acceptModule(reader); + } + } + @Override + void acceptModule(ClassFileReader reader) { + // Modules below level 8 are not dealt with here. Leave it to ClasspathJrt + if (this.jdklevel <= ClassFileConstants.JDK1_8) { + super.acceptModule(reader); + return; + } + if (reader != null) { + IModule moduleDecl = reader.getModuleDeclaration(); + if (moduleDecl != null) { + if (this.modules == null) { + ModulesCache.put(this.modulePath, this.modules = new HashMap()); + } + this.modules.put(String.valueOf(moduleDecl.name()), moduleDecl); + } + } + } + @Override + public synchronized char[][] getModulesDeclaringPackage(String qualifiedPackageName, String moduleName) { + // Ignore moduleName as this has nothing to do with modules (as of now) + if (this.packageCache != null) + return singletonModuleNameIf(this.packageCache.contains(qualifiedPackageName)); + + this.packageCache = new HashSet<>(41); + this.packageCache.add(Util.EMPTY_STRING); + try (DirectoryStream stream = Files.newDirectoryStream(this.releasePath)) { + for (final java.nio.file.Path subdir: stream) { + String rel = subdir.getFileName().toString(); + if (!rel.contains(this.releaseInHex)) { + continue; + } + try (DirectoryStream stream2 = Files.newDirectoryStream(subdir)) { + for (final java.nio.file.Path subdir2: stream2) { + Files.walkFileTree(subdir2, new FileVisitor() { + @Override + public FileVisitResult preVisitDirectory(java.nio.file.Path dir, BasicFileAttributes attrs) throws IOException { + if (dir.getNameCount() <= 2) + return FileVisitResult.CONTINUE; + Path relative = dir.subpath(2, dir.getNameCount()); + addToPackageCache(relative.toString(), false); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(java.nio.file.Path f, BasicFileAttributes attrs) throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(java.nio.file.Path f, IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(java.nio.file.Path dir, IOException exc) throws IOException { + return FileVisitResult.CONTINUE; + } + }); + } + } + } + } catch (IOException e) { + e.printStackTrace(); + // Rethrow + } + return singletonModuleNameIf(this.packageCache.contains(qualifiedPackageName)); + } +} diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/FileSystem.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/FileSystem.java index 1a81699b44..060351e20a 100644 --- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/FileSystem.java +++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/FileSystem.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 @@ -164,11 +164,14 @@ public class FileSystem implements IModuleAwareNameEnvironment, SuffixConstants Set knownFileNames; protected boolean annotationsFromClasspath; // should annotation files be read from the classpath (vs. explicit separate path)? private static HashMap JRT_CLASSPATH_CACHE = null; - protected Map moduleLocations = new HashMap<>(); /** Tasks resulting from --add-reads or --add-exports command line options. */ Map moduleUpdates = new HashMap<>(); + static final boolean isJRE12Plus; + static { + isJRE12Plus = "12".equals(System.getProperty("java.specification.version")); //$NON-NLS-1$ //$NON-NLS-2$ + } /* classPathNames is a collection is Strings representing the full path of each class path @@ -205,6 +208,8 @@ protected FileSystem(Classpath[] paths, String[] initialFileNames, boolean annot final Classpath classpath = paths[i]; try { classpath.initialize(); + for (String moduleName : classpath.getModuleNames(limitedModules)) + this.moduleLocations.put(moduleName, classpath); this.classpaths[counter++] = classpath; } catch(IOException | InvalidPathException exception) { // JRE 9 could throw an IAE if the linked JAR paths have invalid chars, such as ":" @@ -256,7 +261,9 @@ public static Classpath getJrtClasspath(String jdkHome, String encoding, AccessR return new ClasspathJrt(new File(convertPathSeparators(jdkHome)), true, accessRuleSet, null); } public static Classpath getOlderSystemRelease(String jdkHome, String release, AccessRuleSet accessRuleSet) { - return new ClasspathJep247(new File(convertPathSeparators(jdkHome)), release, accessRuleSet); + return isJRE12Plus ? + new ClasspathJep247Jdk12(new File(convertPathSeparators(jdkHome)), release, accessRuleSet) : + new ClasspathJep247(new File(convertPathSeparators(jdkHome)), release, accessRuleSet); } public static Classpath getClasspath(String classpathName, String encoding, boolean isSourceOnly, AccessRuleSet accessRuleSet, diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/NameEnvironmentAnswer.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/NameEnvironmentAnswer.java index bb3bfb757c..b11078dfe5 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/NameEnvironmentAnswer.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/NameEnvironmentAnswer.java @@ -32,9 +32,7 @@ public class NameEnvironmentAnswer { String externalAnnotationPath; // should be an absolute file system path public NameEnvironmentAnswer(IBinaryType binaryType, AccessRestriction accessRestriction) { - this.binaryType = binaryType; - this.accessRestriction = accessRestriction; - this.moduleName = binaryType.getModule(); + this(binaryType, accessRestriction, binaryType.getModule()); } public NameEnvironmentAnswer(IBinaryType binaryType, AccessRestriction accessRestriction, char[] module) { diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/JRTUtil.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/JRTUtil.java index 481bbef429..f6903f814f 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/JRTUtil.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/JRTUtil.java @@ -22,6 +22,7 @@ import java.net.URLClassLoader; import java.nio.channels.ClosedByInterruptException; import java.nio.file.DirectoryStream; import java.nio.file.FileSystem; +import java.nio.file.FileSystemNotFoundException; import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; import java.nio.file.FileVisitor; @@ -59,7 +60,7 @@ public class JRTUtil { public static int NOTIFY_ALL = NOTIFY_FILES | NOTIFY_PACKAGES | NOTIFY_MODULES; // TODO: Java 9 Think about clearing the cache too. - private static Map images = null; + private static Map images = null; private static final Object lock = new Object(); @@ -100,7 +101,11 @@ public class JRTUtil { } public static JrtFileSystem getJrtSystem(File image) { - Map i = images; + return getJrtSystem(image, null); + } + + public static JrtFileSystem getJrtSystem(File image, String release) { + Map i = images; if (images == null) { synchronized (lock) { i = images; @@ -110,10 +115,12 @@ public class JRTUtil { } } JrtFileSystem system = null; + String key = image.toString(); + if (release != null) key = key + "|" + release; //$NON-NLS-1$ synchronized(i) { - if ((system = images.get(image)) == null) { + if ((system = images.get(key)) == null) { try { - images.put(image, system = new JrtFileSystem(image)); + images.put(key, system = JrtFileSystem.getNewJrtFileSystem(image, release)); } catch (IOException e) { e.printStackTrace(); // Needs better error handling downstream? But for now, make sure @@ -138,8 +145,8 @@ public class JRTUtil { * /modules/$MODULE/$PATH * /packages/$PACKAGE/$MODULE * The latter provides quick look up of the module that contains a particular package. However, - * this method only notifies its clients of the entries within the modules sub-directory. The - * clients can decide which notifications they want to receive. See {@link JRTUtil#NOTIFY_ALL}, + * this method only notifies its clients of the entries within the modules (latter) sub-directory. + * Clients can decide which notifications they want to receive. See {@link JRTUtil#NOTIFY_ALL}, * {@link JRTUtil#NOTIFY_FILES}, {@link JRTUtil#NOTIFY_PACKAGES} and {@link JRTUtil#NOTIFY_MODULES}. * * @param image a java.io.File handle to the JRT image. @@ -148,7 +155,10 @@ public class JRTUtil { * @throws IOException */ public static void walkModuleImage(File image, final JRTUtil.JrtFileVisitor visitor, int notify) throws IOException { - getJrtSystem(image).walkModuleImage(visitor, false, notify); + getJrtSystem(image, null).walkModuleImage(visitor, notify); + } + public static void walkModuleImage(File image, String release, final JRTUtil.JrtFileVisitor visitor, int notify) throws IOException { + getJrtSystem(image, release).walkModuleImage(visitor, notify); } public static InputStream getContentFromJrt(File jrt, String fileName, String module) throws IOException { @@ -187,13 +197,138 @@ public class JRTUtil { } } } +class JrtFileSystemWithOlderRelease extends JrtFileSystem { + final String release; + String releaseInHex = null; + //private Path releasePath = null; + private String[] subReleases = null; + protected Path modulePath = null; + + /** + * The jrt file system is based on the location of the JRE home whose libraries + * need to be loaded. + * + * @param jrt the path to the root of the JRE whose libraries we are interested in. + * @param release the older release where classes and modules should be searched for. + * @throws IOException + */ + JrtFileSystemWithOlderRelease(File jrt, String release) throws IOException { + super(jrt); + this.release = release; + initialize(jrt, release); + } + @Override + void initialize(File jdk) throws IOException { + // Just to make sure we don't do anything in super.initialize() + // before setting this.release + } + void initialize(File jdk, String rel) throws IOException { + super.initialize(jdk); + this.fs = null;// reset and proceed, TODO: this is crude and need to be removed. + this.releaseInHex = Integer.toHexString(Integer.parseInt(this.release)).toUpperCase(); + Path ct = Paths.get(this.jdkHome, "lib", "ct.sym"); //$NON-NLS-1$ //$NON-NLS-2$ + if (!Files.exists(ct)) { + return; + } + URI uri = URI.create("jar:file:" + ct.toUri().getRawPath()); //$NON-NLS-1$ + try { + this.fs = FileSystems.getFileSystem(uri); + } catch(FileSystemNotFoundException fne) { + // Ignore and move on + } + if (this.fs == null) { + HashMap env = new HashMap<>(); + try { + this.fs = FileSystems.newFileSystem(uri, env); + } catch (IOException e) { + return; + } + } + Path releasePath = this.fs.getPath("/"); //$NON-NLS-1$ + if (!Files.exists(this.fs.getPath(this.releaseInHex)) + || Files.exists(this.fs.getPath(this.releaseInHex, "system-modules"))) { //$NON-NLS-1$ + this.fs = null; + } + if (this.release != null) { + List sub = new ArrayList<>(); + try (DirectoryStream stream = Files.newDirectoryStream(releasePath)) { + for (final java.nio.file.Path subdir: stream) { + String r = subdir.getFileName().toString(); + if (r.contains(this.releaseInHex)) { + sub.add(r); + } else { + continue; + } + } + } catch (IOException e) { + e.printStackTrace(); + // Rethrow? + } + this.subReleases = sub.toArray(new String[sub.size()]); + } + // Ensure walkJrtForModules() is not called + } + @Override + void walkModuleImage(final JRTUtil.JrtFileVisitor visitor, final int notify) throws IOException { + if (this.subReleases != null && this.subReleases.length > 0) { + for (String rel : this.subReleases) { + Path p = this.fs.getPath(rel); + Files.walkFileTree(p, new JRTUtil.AbstractFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(java.nio.file.Path dir, BasicFileAttributes attrs) + throws IOException { + int count = dir.getNameCount(); + if (count == 1) { + return FileVisitResult.CONTINUE; + } + if (count == 2) { + // e.g. /9A/java.base + java.nio.file.Path mod = dir.getName(1); + if ((JRTUtil.MODULE_TO_LOAD != null && JRTUtil.MODULE_TO_LOAD.length() > 0 + && JRTUtil.MODULE_TO_LOAD.indexOf(mod.toString()) == -1)) { + return FileVisitResult.SKIP_SUBTREE; + } + return ((notify & JRTUtil.NOTIFY_MODULES) == 0) ? FileVisitResult.CONTINUE + : visitor.visitModule(dir); + } + if ((notify & JRTUtil.NOTIFY_PACKAGES) == 0) { + // client is not interested in packages + return FileVisitResult.CONTINUE; + } + return visitor.visitPackage(dir.subpath(2, count), dir.getName(1), attrs); + } + + @Override + public FileVisitResult visitFile(java.nio.file.Path file, BasicFileAttributes attrs) + throws IOException { + if ((notify & JRTUtil.NOTIFY_FILES) == 0) { + return FileVisitResult.CONTINUE; + } + // This happens when a file in a default package is present. E.g. /modules/some.module/file.name + if (file.getNameCount() == 3) { + cachePackage(JRTUtil.DEFAULT_PACKAGE, file.getName(1).toString()); + } + return visitor.visitFile(file.subpath(2, file.getNameCount()), file.getName(1), attrs); + } + }); + } + } + } + +} class JrtFileSystem { private final Map packageToModule = new HashMap(); private final Map> packageToModules = new HashMap>(); - FileSystem jrtSystem = null; - + FileSystem fs = null; + Path modRoot = null; + String jdkHome = null; + public static JrtFileSystem getNewJrtFileSystem(File jrt, String release) throws IOException { + return (release == null) ? new JrtFileSystem(jrt) : + new JrtFileSystemWithOlderRelease(jrt, release); + + } /** * The jrt file system is based on the location of the JRE home whose libraries * need to be loaded. @@ -201,18 +336,18 @@ class JrtFileSystem { * @param jrt the path to the root of the JRE whose libraries we are interested in. * @throws IOException */ - public JrtFileSystem(File jrt) throws IOException { + JrtFileSystem(File jrt) throws IOException { initialize(jrt); } void initialize(File jrt) throws IOException { URL jrtPath = null; - String jdkHome = null; + this.jdkHome = null; if (jrt.toString().endsWith(JRTUtil.JRT_FS_JAR)) { jrtPath = jrt.toPath().toUri().toURL(); - jdkHome = jrt.getParentFile().getParent(); + this.jdkHome = jrt.getParentFile().getParent(); } else { - jdkHome = jrt.toPath().toString(); - jrtPath = Paths.get(jdkHome, "lib", JRTUtil.JRT_FS_JAR).toUri().toURL(); //$NON-NLS-1$ + this.jdkHome = jrt.toPath().toString(); + jrtPath = Paths.get(this.jdkHome, "lib", JRTUtil.JRT_FS_JAR).toUri().toURL(); //$NON-NLS-1$ } JRTUtil.MODULE_TO_LOAD = System.getProperty("modules.to.load"); //$NON-NLS-1$ @@ -220,13 +355,15 @@ class JrtFileSystem { if (javaVersion != null && javaVersion.startsWith("1.8")) { //$NON-NLS-1$ URLClassLoader loader = new URLClassLoader(new URL[] { jrtPath }); HashMap env = new HashMap<>(); - this.jrtSystem = FileSystems.newFileSystem(JRTUtil.JRT_URI, env, loader); + this.fs = FileSystems.newFileSystem(JRTUtil.JRT_URI, env, loader); } else { HashMap env = new HashMap<>(); - env.put("java.home", jdkHome); //$NON-NLS-1$ - this.jrtSystem = FileSystems.newFileSystem(JRTUtil.JRT_URI, env); + env.put("java.home", this.jdkHome); //$NON-NLS-1$ + this.fs = FileSystems.newFileSystem(JRTUtil.JRT_URI, env); } - walkModuleImage(null, true, 0 /* doesn't matter */); + this.modRoot = this.fs.getPath(JRTUtil.MODULES_SUBDIR); + // Set up the root directory wherere modules are located + walkJrtForModules(); } public List getModulesDeclaringPackage(String qualifiedPackageName, String moduleName) { @@ -279,7 +416,7 @@ class JrtFileSystem { String knownModule = this.packageToModule.get(qualifiedPackageName); if (knownModule == null || (knownModule != JRTUtil.MULTIPLE && !knownModule.equals(module))) return false; - Path packagePath = this.jrtSystem.getPath(JRTUtil.MODULES_SUBDIR, module, qualifiedPackageName); + Path packagePath = this.fs.getPath(JRTUtil.MODULES_SUBDIR, module, qualifiedPackageName); if (!Files.exists(packagePath)) return false; // iterate files: @@ -294,11 +431,11 @@ class JrtFileSystem { public InputStream getContentFromJrt(String fileName, String module) throws IOException { if (module != null) { - return Files.newInputStream(this.jrtSystem.getPath(JRTUtil.MODULES_SUBDIR, module, fileName)); + return Files.newInputStream(this.fs.getPath(JRTUtil.MODULES_SUBDIR, module, fileName)); } String[] modules = getModules(fileName); for (String mod : modules) { - return Files.newInputStream(this.jrtSystem.getPath(JRTUtil.MODULES_SUBDIR, mod, fileName)); + return Files.newInputStream(this.fs.getPath(JRTUtil.MODULES_SUBDIR, mod, fileName)); } return null; } @@ -309,7 +446,7 @@ class JrtFileSystem { for (String mod : modules) { if (moduleNameFilter != null && !moduleNameFilter.test(mod)) continue; - content = JRTUtil.safeReadBytes(this.jrtSystem.getPath(JRTUtil.MODULES_SUBDIR, mod, fileName)); + content = JRTUtil.safeReadBytes(this.fs.getPath(JRTUtil.MODULES_SUBDIR, mod, fileName)); if (content != null) { module = mod; break; @@ -330,7 +467,7 @@ class JrtFileSystem { } else { String[] modules = getModules(fileName); for (String mod : modules) { - content = JRTUtil.safeReadBytes(this.jrtSystem.getPath(JRTUtil.MODULES_SUBDIR, mod, fileName)); + content = JRTUtil.safeReadBytes(this.fs.getPath(JRTUtil.MODULES_SUBDIR, mod, fileName)); if (content != null) { break; } @@ -339,7 +476,7 @@ class JrtFileSystem { return content; } private byte[] getClassfileBytes(String fileName, String module) throws IOException, ClassFormatException { - return JRTUtil.safeReadBytes(this.jrtSystem.getPath(JRTUtil.MODULES_SUBDIR, module, fileName)); + return JRTUtil.safeReadBytes(this.fs.getPath(JRTUtil.MODULES_SUBDIR, module, fileName)); } public ClassFileReader getClassfile(String fileName, String module, Predicate moduleNameFilter) throws IOException, ClassFormatException { ClassFileReader reader = null; @@ -367,47 +504,12 @@ class JrtFileSystem { return reader; } - void walkModuleImage(final JRTUtil.JrtFileVisitor visitor, boolean visitPackageMapping, final int notify) throws IOException { - Iterable roots = this.jrtSystem.getRootDirectories(); + void walkJrtForModules() throws IOException { + Iterable roots = this.fs.getRootDirectories(); for (java.nio.file.Path path : roots) { try (DirectoryStream stream = Files.newDirectoryStream(path)) { for (final java.nio.file.Path subdir: stream) { - if (subdir.toString().equals(JRTUtil.MODULES_SUBDIR)) { - if (visitPackageMapping) continue; - Files.walkFileTree(subdir, new JRTUtil.AbstractFileVisitor() { - @Override - public FileVisitResult preVisitDirectory(java.nio.file.Path dir, BasicFileAttributes attrs) throws IOException { - int count = dir.getNameCount(); - if (count == 2) { - // e.g. /modules/java.base - java.nio.file.Path mod = dir.getName(1); - if ((JRTUtil.MODULE_TO_LOAD != null && JRTUtil.MODULE_TO_LOAD.length() > 0 && - JRTUtil.MODULE_TO_LOAD.indexOf(mod.toString()) == -1)) { - return FileVisitResult.SKIP_SUBTREE; - } - return ((notify & JRTUtil.NOTIFY_MODULES) == 0) ? - FileVisitResult.CONTINUE : visitor.visitModule(mod); - } - if (dir == subdir || count < 3 || (notify & JRTUtil.NOTIFY_PACKAGES) == 0) { - // We are dealing with a module or not client is not interested in packages - return FileVisitResult.CONTINUE; - } - return visitor.visitPackage(dir.subpath(2, count), dir.getName(1), attrs); - } - - @Override - public FileVisitResult visitFile(java.nio.file.Path file, BasicFileAttributes attrs) throws IOException { - if ((notify & JRTUtil.NOTIFY_FILES) == 0) - return FileVisitResult.CONTINUE; - int count = file.getNameCount(); - // This happens when a file in a default package is present. E.g. /modules/some.module/file.name - if (count == 3) { - cachePackage(JRTUtil.DEFAULT_PACKAGE, file.getName(1).toString()); - } - return visitor.visitFile(file.subpath(2, file.getNameCount()), file.getName(1), attrs); - } - }); - } else if (visitPackageMapping) { + if (!subdir.toString().equals(JRTUtil.MODULES_SUBDIR)) { Files.walkFileTree(subdir, new JRTUtil.AbstractFileVisitor() { @Override public FileVisitResult visitFile(java.nio.file.Path file, BasicFileAttributes attrs) throws IOException { @@ -424,6 +526,42 @@ class JrtFileSystem { } } } + void walkModuleImage(final JRTUtil.JrtFileVisitor visitor, final int notify) throws IOException { + Files.walkFileTree(this.modRoot, new JRTUtil.AbstractFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(java.nio.file.Path dir, BasicFileAttributes attrs) throws IOException { + int count = dir.getNameCount(); + if (count == 1) return FileVisitResult.CONTINUE; + if (count == 2) { + // e.g. /modules/java.base + java.nio.file.Path mod = dir.getName(1); + if ((JRTUtil.MODULE_TO_LOAD != null && JRTUtil.MODULE_TO_LOAD.length() > 0 && + JRTUtil.MODULE_TO_LOAD.indexOf(mod.toString()) == -1)) { + return FileVisitResult.SKIP_SUBTREE; + } + return ((notify & JRTUtil.NOTIFY_MODULES) == 0) ? + FileVisitResult.CONTINUE : visitor.visitModule(mod); + } + if ((notify & JRTUtil.NOTIFY_PACKAGES) == 0) { + // We are dealing with a module or not client is not interested in packages + return FileVisitResult.CONTINUE; + } + return visitor.visitPackage(dir.subpath(2, count), dir.getName(1), attrs); + } + + @Override + public FileVisitResult visitFile(java.nio.file.Path file, BasicFileAttributes attrs) throws IOException { + if ((notify & JRTUtil.NOTIFY_FILES) == 0) + return FileVisitResult.CONTINUE; + int count = file.getNameCount(); + // This happens when a file in a default package is present. E.g. /modules/some.module/file.name + if (count == 3) { + cachePackage(JRTUtil.DEFAULT_PACKAGE, file.getName(1).toString()); + } + return visitor.visitFile(file.subpath(2, file.getNameCount()), file.getName(1), attrs); + } + }); + } void cachePackage(String packageName, String module) { packageName = packageName.intern(); diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJrt.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJrt.java index 34ab941c95..50cfd275f1 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJrt.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJrt.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2016, 2018 IBM Corporation and others. + * Copyright (c) 2016, 2019 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -15,16 +15,9 @@ package org.eclipse.jdt.internal.core.builder; import java.io.File; import java.io.IOException; -import java.net.URI; -import java.nio.file.DirectoryStream; -import java.nio.file.FileSystemNotFoundException; -import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; -import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -35,7 +28,6 @@ import java.util.function.Predicate; import java.util.zip.ZipFile; import org.eclipse.core.runtime.IPath; -import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; @@ -50,36 +42,29 @@ import org.eclipse.jdt.internal.compiler.util.JRTUtil; import org.eclipse.jdt.internal.compiler.util.SimpleSet; import org.eclipse.jdt.internal.compiler.util.SuffixConstants; import org.eclipse.jdt.internal.core.JavaProject; -import org.eclipse.jdt.internal.core.util.Util; public class ClasspathJrt extends ClasspathLocation implements IMultiModuleEntry { //private HashMap packagesInModule = null; -private static HashMap> PackageCache = new HashMap<>(); -private static HashMap> ModulesCache = new HashMap<>(); +protected static HashMap> PackageCache = new HashMap<>(); +protected static HashMap> ModulesCache = new HashMap<>(); String externalAnnotationPath; -private ZipFile annotationZipFile; +protected ZipFile annotationZipFile; String zipFilename; // keep for equals AccessRuleSet accessRuleSet; -String release = null; -String releaseInHex = null; -private Path releasePath = null; -private String[] subReleases = null; -private java.nio.file.FileSystem fs = null; static final Set NO_LIMIT_MODULES = new HashSet<>(); -public ClasspathJrt(String zipFilename, AccessRuleSet accessRuleSet, IPath externalAnnotationPath, String release) { +/* + * Only for use from ClasspathJrtWithOlderRelease + */ +protected ClasspathJrt() { +} +public ClasspathJrt(String zipFilename, AccessRuleSet accessRuleSet, IPath externalAnnotationPath) { this.zipFilename = zipFilename; this.accessRuleSet = accessRuleSet; if (externalAnnotationPath != null) this.externalAnnotationPath = externalAnnotationPath.toString(); - if (release != null && release.length() == 0) { - this.release = null; - } else { - this.release = release; - } - initialize(); loadModules(this); } /** @@ -89,7 +74,7 @@ public ClasspathJrt(String zipFilename, AccessRuleSet accessRuleSet, IPath exter */ static HashMap findPackagesInModules(final ClasspathJrt jrt) { String zipFileName = jrt.zipFilename; - HashMap cache = PackageCache.get(zipFileName); + HashMap cache = PackageCache.get(jrt.getKey()); if (cache != null) { return cache; } @@ -97,7 +82,7 @@ static HashMap findPackagesInModules(final ClasspathJrt jrt) PackageCache.put(zipFileName, packagesInModule); try { final File imageFile = new File(zipFileName); - org.eclipse.jdt.internal.compiler.util.JRTUtil.walkModuleImage(imageFile, + org.eclipse.jdt.internal.compiler.util.JRTUtil.walkModuleImage(imageFile, new org.eclipse.jdt.internal.compiler.util.JRTUtil.JrtFileVisitor() { SimpleSet packageSet = null; @Override @@ -132,12 +117,11 @@ static HashMap findPackagesInModules(final ClasspathJrt jrt) } public static void loadModules(final ClasspathJrt jrt) { - String zipFileName = jrt.zipFilename; - Set cache = ModulesCache.get(zipFileName); + Set cache = ModulesCache.get(jrt.getKey()); if (cache == null) { try { - final File imageFile = new File(zipFileName); + final File imageFile = new File(jrt.zipFilename); org.eclipse.jdt.internal.compiler.util.JRTUtil.walkModuleImage(imageFile, new org.eclipse.jdt.internal.compiler.util.JRTUtil.JrtFileVisitor() { SimpleSet packageSet = null; @@ -174,70 +158,11 @@ public static void loadModules(final ClasspathJrt jrt) { // } } } -public void initialize() { - if (this.release == null) { - return; - } - this.release = getReleaseOptionFromCompliance(this.release); - this.releaseInHex = Integer.toHexString(Integer.parseInt(this.release)).toUpperCase(); - Path lib = Paths.get(this.zipFilename).getParent(); - Path filePath = Paths.get(lib.toString(), "ct.sym"); //$NON-NLS-1$ - URI t = filePath.toUri(); - if (!Files.exists(filePath)) { - return; - } - URI uri = URI.create("jar:file:" + t.getRawPath()); //$NON-NLS-1$ - try { - this.fs = FileSystems.getFileSystem(uri); - } catch(FileSystemNotFoundException fne) { - // Ignore and move on - } - if (this.fs == null) { - HashMap env = new HashMap<>(); - try { - this.fs = FileSystems.newFileSystem(uri, env); - } catch (IOException e) { - this.release = null; - return; - } - } - this.releasePath = this.fs.getPath("/"); //$NON-NLS-1$ - if (!Files.exists(this.fs.getPath(this.releaseInHex)) - || Files.exists(this.fs.getPath(this.releaseInHex, "system-modules"))) { //$NON-NLS-1$ - this.release = null; - } - if (this.release != null) { - List sub = new ArrayList<>(); - try (DirectoryStream stream = Files.newDirectoryStream(this.releasePath)) { - for (final java.nio.file.Path subdir: stream) { - String rel = subdir.getFileName().toString(); - if (rel.contains(this.releaseInHex)) { - sub.add(rel); - } else { - continue; - } - } - } catch (IOException e) { - e.printStackTrace(); - // Rethrow - } - this.subReleases = sub.toArray(new String[sub.size()]); - } -} -private String getReleaseOptionFromCompliance(String comp) { - if (JavaCore.compareJavaVersions(comp, JavaCore.VERSION_1_5) <= 0) { - // For a JDK 9 and above, the minimum release we support is "6" - return "6"; //$NON-NLS-1$ - } - int index = comp.indexOf("1."); //$NON-NLS-1$ - if (index != -1) { - return comp.substring(index + 2, comp.length()); - } else { - return comp; - } +protected String getKey() { + return this.zipFilename; } void acceptModule(byte[] content) { - if (content == null) + if (content == null) return; ClassFileReader reader = null; try { @@ -246,11 +171,12 @@ void acceptModule(byte[] content) { e.printStackTrace(); } if (reader != null) { + String key = getKey(); IModule moduleDecl = reader.getModuleDeclaration(); if (moduleDecl != null) { - Set cache = ModulesCache.get(this.zipFilename); + Set cache = ModulesCache.get(key); if (cache == null) { - ModulesCache.put(this.zipFilename, cache = new HashSet()); + ModulesCache.put(key, cache = new HashSet()); } cache.add(moduleDecl); } @@ -265,7 +191,6 @@ public void cleanup() { } this.annotationZipFile = null; } - this.fs = null; } @Override @@ -273,9 +198,6 @@ public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ClasspathJrt)) return false; ClasspathJrt jar = (ClasspathJrt) o; - if (!Util.equalOrNull(this.release, jar.release)) { - return false; - } if (this.accessRuleSet != jar.accessRuleSet) if (this.accessRuleSet == null || !this.accessRuleSet.equals(jar.accessRuleSet)) return false; @@ -288,43 +210,31 @@ public NameEnvironmentAnswer findClass(String binaryFileName, String qualifiedPa if (!isPackage(qualifiedPackageName, moduleName)) return null; // most common case try { - IBinaryType reader = null; - byte[] content = null; String fileNameWithoutExtension = qualifiedBinaryFileName.substring(0, qualifiedBinaryFileName.length() - SuffixConstants.SUFFIX_CLASS.length); - if (this.subReleases != null && this.subReleases.length > 0) { - qualifiedBinaryFileName = qualifiedBinaryFileName.replace(".class", ".sig"); //$NON-NLS-1$ //$NON-NLS-2$ - for (String rel : this.subReleases) { - Path p = this.fs.getPath(rel, qualifiedBinaryFileName); - if (Files.exists(p)) { - content = JRTUtil.safeReadBytes(p); - if (content != null) { - reader = new ClassFileReader(content, qualifiedBinaryFileName.toCharArray()); - break; - } - } - } - } else { - reader = ClassFileReader.readFromModule(new File(this.zipFilename), moduleName, qualifiedBinaryFileName, moduleNameFilter); - } - if (reader != null) { - if (this.externalAnnotationPath != null) { - try { - if (this.annotationZipFile == null) { - this.annotationZipFile = ExternalAnnotationDecorator.getAnnotationZipFile(this.externalAnnotationPath, null); - } - reader = ExternalAnnotationDecorator.create(reader, this.externalAnnotationPath, fileNameWithoutExtension, this.annotationZipFile); - } catch (IOException e) { - // don't let error on annotations fail class reading + IBinaryType reader = ClassFileReader.readFromModule(new File(this.zipFilename), moduleName, qualifiedBinaryFileName, moduleNameFilter); + return createAnswer(fileNameWithoutExtension, reader); + } catch (ClassFormatException e) { // treat as if class file is missing + } catch (IOException e) { // treat as if class file is missing + } + return null; +} +protected NameEnvironmentAnswer createAnswer(String fileNameWithoutExtension, IBinaryType reader) { + if (reader != null) { + if (this.externalAnnotationPath != null) { + try { + if (this.annotationZipFile == null) { + this.annotationZipFile = ExternalAnnotationDecorator.getAnnotationZipFile(this.externalAnnotationPath, null); } + reader = ExternalAnnotationDecorator.create(reader, this.externalAnnotationPath, fileNameWithoutExtension, this.annotationZipFile); + } catch (IOException e) { + // don't let error on annotations fail class reading } - if (this.accessRuleSet == null) - return new NameEnvironmentAnswer(reader, null, reader.getModule()); - return new NameEnvironmentAnswer(reader, - this.accessRuleSet.getViolatedRestriction(fileNameWithoutExtension.toCharArray()), - reader.getModule()); } - } catch (ClassFormatException e) { // treat as if class file is missing - } catch (IOException e) { // treat as if class file is missing + if (this.accessRuleSet == null) + return new NameEnvironmentAnswer(reader, null, reader.getModule()); + return new NameEnvironmentAnswer(reader, + this.accessRuleSet.getViolatedRestriction(fileNameWithoutExtension.toCharArray()), + reader.getModule()); } return null; } @@ -341,7 +251,7 @@ public int hashCode() { @Override public char[][] getModulesDeclaringPackage(String qualifiedPackageName, String moduleName) { List moduleNames = JRTUtil.getModulesDeclaringPackage(new File(this.zipFilename), qualifiedPackageName, moduleName); - return CharOperation.toCharArrays(moduleNames); + return CharOperation.toCharArrays(moduleNames); } @Override public boolean hasCompilationUnit(String qualifiedPackageName, String moduleName) { @@ -374,7 +284,7 @@ public boolean hasModule() { } @Override public IModule getModule(char[] moduleName) { - Set modules = ModulesCache.get(this.zipFilename); + Set modules = ModulesCache.get(getKey()); if (modules != null) { for (IModule mod : modules) { if (CharOperation.equals(mod.name(), moduleName)) @@ -391,7 +301,7 @@ public Collection getModuleNames(Collection limitModules) { return Collections.emptyList(); } -private Collection selectModules(Set keySet, Collection limitModules) { +protected Collection selectModules(Set keySet, Collection limitModules) { Collection rootModules; if (limitModules == NO_LIMIT_MODULES) { rootModules = new HashSet<>(keySet); @@ -408,7 +318,7 @@ private Collection selectModules(Set keySet, Collection return allModules; } -private void addRequired(String mod, Set allModules) { +protected void addRequired(String mod, Set allModules) { IModule iMod = getModule(mod.toCharArray()); for (IModuleReference requiredRef : iMod.requires()) { IModule reqMod = getModule(requiredRef.name()); @@ -421,7 +331,7 @@ private void addRequired(String mod, Set allModules) { } @Override public NameEnvironmentAnswer findClass(String typeName, String qualifiedPackageName, String moduleName, String qualifiedBinaryFileName) { - // + // return findClass(typeName, qualifiedPackageName, moduleName, qualifiedBinaryFileName, false, null); } /** TEST ONLY */ diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJrtWithReleaseOption.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJrtWithReleaseOption.java new file mode 100644 index 0000000000..a5d8446c37 --- /dev/null +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJrtWithReleaseOption.java @@ -0,0 +1,387 @@ +/******************************************************************************* + * Copyright (c) 2016, 2019 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.builder; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.DirectoryStream; +import java.nio.file.FileSystemNotFoundException; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitResult; +import java.nio.file.FileVisitor; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; +import org.eclipse.jdt.internal.compiler.env.AccessRuleSet; +import org.eclipse.jdt.internal.compiler.env.IBinaryType; +import org.eclipse.jdt.internal.compiler.env.IModule; +import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; +import org.eclipse.jdt.internal.compiler.util.JRTUtil; +import org.eclipse.jdt.internal.compiler.util.SimpleSet; +import org.eclipse.jdt.internal.compiler.util.SuffixConstants; +import org.eclipse.jdt.internal.core.util.Util; + +public class ClasspathJrtWithReleaseOption extends ClasspathJrt { + + final String release; + String releaseInHex; + private String[] subReleases; + private java.nio.file.FileSystem fs; + protected Path modulePath; + private String modPathString; + private boolean isJRE12Plus; + + public ClasspathJrtWithReleaseOption(String zipFilename, AccessRuleSet accessRuleSet, IPath externalAnnotationPath, + String release) throws CoreException { + super(); + if (release == null || release.equals("")) { //$NON-NLS-1$ + throw new IllegalArgumentException("--release argument can not be null"); //$NON-NLS-1$ + } + this.zipFilename = zipFilename; + this.accessRuleSet = accessRuleSet; + if (externalAnnotationPath != null) + this.externalAnnotationPath = externalAnnotationPath.toString(); + this.release = getReleaseOptionFromCompliance(release); + initialize(); + loadModules(this); + } + /* + * JDK 11 doesn't contain release 5. Hence + * if the compliance is below 6, we simply return the lowest supported + * release, which is 6. + */ + private String getReleaseOptionFromCompliance(String comp) { + if (JavaCore.compareJavaVersions(comp, JavaCore.VERSION_1_5) <= 0) { + return "6"; //$NON-NLS-1$ + } + int index = comp.indexOf("1."); //$NON-NLS-1$ + if (index != -1) { + return comp.substring(index + 2, comp.length()); + } else { + return comp; + } + } + private boolean isJRE12Plus(Path path) { + try (DirectoryStream stream = Files.newDirectoryStream(path)) { + for (final java.nio.file.Path subdir : stream) { + String rel = subdir.getFileName().toString(); + if (Files.exists(this.fs.getPath(rel, "system-modules"))) { //$NON-NLS-1$ + int parseInt = Integer.parseInt(rel, 16); + return (parseInt > 11); + } + } + } catch (IOException e) { + this.fs = null; + } + return false; + } + /* + * Set up the paths where modules and regular classes need to be read. We need to deal with two different kind of + * formats of cy.sym: Post JDK 12: ct.sym -> 9 -> java/ -> lang/* 9-modules -> java.base -> module-info.sig + * + * From JDK 12 onward: ct.sym -> 9 -> java.base -> module-info.sig java/ -> lang/* Notably, 1) in JDK 12 modules + * classes and ordinary classes are located in the same location 2) in JDK 12, ordinary classes are found inside + * their respective modules + * + */ + protected void initialize() throws CoreException { + this.releaseInHex = Integer.toHexString(Integer.parseInt(this.release)).toUpperCase(); + Path lib = Paths.get(this.zipFilename).getParent(); + Path filePath = Paths.get(lib.toString(), "ct.sym"); //$NON-NLS-1$ + URI t = filePath.toUri(); + if (!Files.exists(filePath)) { + return; + } + URI uri = URI.create("jar:file:" + t.getRawPath()); //$NON-NLS-1$ + try { + this.fs = FileSystems.getFileSystem(uri); + } catch (FileSystemNotFoundException fne) { + // Ignore and move on + } + if (this.fs == null) { + HashMap env = new HashMap<>(); + try { + this.fs = FileSystems.newFileSystem(uri, env); + } catch (IOException e) { + return; + } + } + Path releasePath = this.fs.getPath("/"); //$NON-NLS-1$ + this.isJRE12Plus = isJRE12Plus(releasePath); + Path modPath = this.fs.getPath(this.releaseInHex + (this.isJRE12Plus ? "" : "-modules")); //$NON-NLS-1$ //$NON-NLS-2$ + if (Files.exists(modPath)) { + this.modulePath = modPath; + this.modPathString = this.zipFilename + "|"+ modPath.toString(); //$NON-NLS-1$ + } + + if (!Files.exists(releasePath.resolve(this.releaseInHex))) { + Exception e = new IllegalArgumentException("release " + this.release + " is not found in the system"); //$NON-NLS-1$//$NON-NLS-2$ + throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, e.getMessage(), e)); + } + if (Files.exists(this.fs.getPath(this.releaseInHex, "system-modules"))) { //$NON-NLS-1$ + this.fs = null; // Fallback to default version + return; + } + if (this.release != null) { + List sub = new ArrayList<>(); + try (DirectoryStream stream = Files.newDirectoryStream(releasePath)) { + for (final java.nio.file.Path subdir : stream) { + String rel = subdir.getFileName().toString(); + if (rel.contains(this.releaseInHex)) { + sub.add(rel); + } else { + continue; + } + } + } catch (IOException e) { + this.fs = null; // Fallback to default version + } + this.subReleases = sub.toArray(new String[sub.size()]); + } + } + + static HashMap findPackagesInModules(final ClasspathJrtWithReleaseOption jrt) { + // In JDK 11 and before, classes are not listed under their respective modules + // Hence, we simply go to the default module system for package-module mapping + if (jrt.fs == null || !jrt.isJRE12Plus) { + return ClasspathJrt.findPackagesInModules(jrt); + } + String zipFileName = jrt.zipFilename; + HashMap cache = PackageCache.get(jrt.modPathString); + if (cache != null) { + return cache; + } + final HashMap packagesInModule = new HashMap<>(); + PackageCache.put(jrt.modPathString, packagesInModule); + try { + final File imageFile = new File(zipFileName); + org.eclipse.jdt.internal.compiler.util.JRTUtil.walkModuleImage(imageFile, jrt.release, + new org.eclipse.jdt.internal.compiler.util.JRTUtil.JrtFileVisitor() { + SimpleSet packageSet = null; + + @Override + public FileVisitResult visitPackage(Path dir, Path mod, BasicFileAttributes attrs) + throws IOException { + ClasspathJar.addToPackageSet(this.packageSet, dir.toString(), true); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, Path mod, BasicFileAttributes attrs) + throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitModule(Path mod) throws IOException { + String name = mod.getName(1).toString(); + this.packageSet = new SimpleSet(41); + this.packageSet.add(""); //$NON-NLS-1$ + packagesInModule.put(name, this.packageSet); + return FileVisitResult.CONTINUE; + } + }, JRTUtil.NOTIFY_PACKAGES | JRTUtil.NOTIFY_MODULES); + } catch (IOException e) { + // return empty handed + } + return packagesInModule; + } + + public static void loadModules(final ClasspathJrtWithReleaseOption jrt) { + if (jrt.fs == null || !jrt.isJRE12Plus) { + ClasspathJrt.loadModules(jrt); + return; + } + if (jrt.modPathString == null) + return; + Set cache = ModulesCache.get(jrt.modPathString); + if (cache == null) { + try (DirectoryStream stream = Files.newDirectoryStream(jrt.modulePath)) { + for (final java.nio.file.Path subdir : stream) { + + Files.walkFileTree(subdir, Collections.EMPTY_SET, 1, new FileVisitor() { + @Override + public FileVisitResult preVisitDirectory(java.nio.file.Path dir, BasicFileAttributes attrs) + throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(java.nio.file.Path f, BasicFileAttributes attrs) + throws IOException { + byte[] content = null; + if (Files.exists(f)) { + content = JRTUtil.safeReadBytes(f); + if (content == null) + return FileVisitResult.CONTINUE; + jrt.acceptModule(content); + } + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(java.nio.file.Path f, IOException exc) + throws IOException { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(java.nio.file.Path dir, IOException exc) + throws IOException { + return FileVisitResult.CONTINUE; + } + }); + } + } catch (IOException e) { + // Nothing much to do + } + } + } + + + @Override + public NameEnvironmentAnswer findClass(String binaryFileName, String qualifiedPackageName, String moduleName, + String qualifiedBinaryFileName, boolean asBinaryOnly, Predicate moduleNameFilter) { + + if (this.fs == null) { + return super.findClass(binaryFileName, qualifiedPackageName, moduleName, qualifiedBinaryFileName, + asBinaryOnly, moduleNameFilter); + } + if (!isPackage(qualifiedPackageName, moduleName)) + return null; // most common case + + try { + IBinaryType reader = null; + byte[] content = null; + String fileNameWithoutExtension = qualifiedBinaryFileName.substring(0, + qualifiedBinaryFileName.length() - SuffixConstants.SUFFIX_CLASS.length); + if (this.subReleases != null && this.subReleases.length > 0) { + qualifiedBinaryFileName = qualifiedBinaryFileName.replace(".class", ".sig"); //$NON-NLS-1$ //$NON-NLS-2$ + outer: for (String rel : this.subReleases) { + Path p = null; + inner: if (this.isJRE12Plus) { + if (moduleName != null) { + p = this.fs.getPath(rel, moduleName, qualifiedBinaryFileName); + } + else { + try (DirectoryStream stream = Files + .newDirectoryStream(this.fs.getPath(rel))) { + for (final java.nio.file.Path subdir : stream) { + p = subdir.resolve(qualifiedBinaryFileName); + if (Files.exists(p)) { + if (subdir.getNameCount() == 2 ) { + moduleName = subdir.getName(1).toString(); + } + break inner; + } + } + } + } + } else { + p = this.fs.getPath(rel, qualifiedBinaryFileName); + } + if (Files.exists(p)) { + content = JRTUtil.safeReadBytes(p); + if (content != null) { + reader = new ClassFileReader(content, qualifiedBinaryFileName.toCharArray()); + if (moduleName != null) + ((ClassFileReader) reader).moduleName = moduleName.toCharArray(); + break outer; + } + } + } + } else { + reader = ClassFileReader.readFromModule(new File(this.zipFilename), moduleName, qualifiedBinaryFileName, + moduleNameFilter); + } + return createAnswer(fileNameWithoutExtension, reader); + } catch (ClassFormatException e) { + // treat as if class file is missing + } catch (IOException e) { + // treat as if class file is missing + } + return null; + } + + @Override + public Collection getModuleNames(Collection limitModules) { + HashMap cache = findPackagesInModules(this); + if (cache != null) + return selectModules(cache.keySet(), limitModules); + return Collections.emptyList(); + } + + @Override + public void cleanup() { + try { + super.reset(); + } finally { + // The same file system is also used in JRTUtil, so don't close it here. + this.fs = null; + } + } + + @Override + public boolean hasModule() { + return this.modPathString != null; + } + + @Override + protected String getKey() { + return this.modPathString; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof ClasspathJrtWithReleaseOption)) + return false; + ClasspathJrtWithReleaseOption jar = (ClasspathJrtWithReleaseOption) o; + if (!Util.equalOrNull(this.release, jar.release)) { + return false; + } + return super.equals(o); + } + + @Override + public int hashCode() { + int hash = this.zipFilename == null ? super.hashCode() : this.zipFilename.hashCode(); + return Util.combineHashCodes(hash, this.release.hashCode()); + } + + @Override + public String toString() { + String start = "Classpath jrt file " + this.zipFilename + " with --release option " + this.release; //$NON-NLS-1$ //$NON-NLS-2$ + return start; + } + +} diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathLocation.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathLocation.java index d501e436cf..52cd7e0c31 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathLocation.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathLocation.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 @@ -23,6 +23,7 @@ import java.util.zip.ZipFile; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.env.AccessRuleSet; @@ -141,8 +142,9 @@ static ClasspathLocation forLibrary(String libraryPathname, new ClasspathMultiReleaseJar(libraryPathname, lastModified, accessRuleSet, annotationsPath, autoModule, compliance)); } -static ClasspathJrt forJrtSystem(String jrtPath, AccessRuleSet accessRuleSet, IPath annotationsPath, String release) { - return new ClasspathJrt(jrtPath, accessRuleSet, annotationsPath, release); +public static ClasspathJrt forJrtSystem(String jrtPath, AccessRuleSet accessRuleSet, IPath annotationsPath, String release) throws CoreException { + return (release == null || release.equals("")) ? new ClasspathJrt(jrtPath, accessRuleSet, annotationsPath) : //$NON-NLS-1$ + new ClasspathJrtWithReleaseOption(jrtPath, accessRuleSet, annotationsPath, release); } public static ClasspathLocation forLibrary(String libraryPathname, AccessRuleSet accessRuleSet, IPath annotationsPath, diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/JavaBuilder.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/JavaBuilder.java index 269e064480..abf0ebc7a7 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/JavaBuilder.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/JavaBuilder.java @@ -152,7 +152,7 @@ public static void removeProblemsAndTasksFor(IResource resource) { } } -public static State readState(IProject project, DataInputStream in) throws IOException { +public static State readState(IProject project, DataInputStream in) throws IOException, CoreException { return State.read(project, in); } diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.java index dd240bcacb..a57cc107e6 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.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 @@ -56,7 +56,7 @@ private long previousStructuralBuildTime; private StringSet structurallyChangedTypes; public static int MaxStructurallyChangedTypes = 100; // keep track of ? structurally changed types, otherwise consider all to be changed -public static final byte VERSION = 0x0021; +public static final byte VERSION = 0x0022; static final byte SOURCE_FOLDER = 1; static final byte BINARY_FOLDER = 2; @@ -233,7 +233,7 @@ void removeQualifiedTypeName(String qualifiedTypeNameToRemove) { this.typeLocators.removeKey(qualifiedTypeNameToRemove); } -static State read(IProject project, DataInputStream in) throws IOException { +static State read(IProject project, DataInputStream in) throws IOException, CoreException { if (JavaBuilder.DEBUG) System.out.println("About to read state " + project.getName()); //$NON-NLS-1$ if (VERSION != in.readByte()) { @@ -568,13 +568,20 @@ void write(DataOutputStream out) throws IOException { out.writeBoolean(jar.isOnModulePath); out.writeUTF(jar.compliance == null ? "" : jar.compliance); //$NON-NLS-1$ - } else { + } else if (c instanceof ClasspathJrt) { ClasspathJrt jrt = (ClasspathJrt) c; out.writeByte(EXTERNAL_JAR); out.writeUTF(jrt.zipFilename); writeRestriction(jrt.accessRuleSet, out); out.writeUTF(jrt.externalAnnotationPath != null ? jrt.externalAnnotationPath : ""); //$NON-NLS-1$ - out.writeUTF(jrt.release != null ? jrt.release : ""); //$NON-NLS-1$ + out.writeUTF(""); //$NON-NLS-1$ + } else { + ClasspathJrtWithReleaseOption jrt = (ClasspathJrtWithReleaseOption) c; + out.writeByte(EXTERNAL_JAR); + out.writeUTF(jrt.zipFilename); + writeRestriction(jrt.accessRuleSet, out); + out.writeUTF(jrt.externalAnnotationPath != null ? jrt.externalAnnotationPath : ""); //$NON-NLS-1$ + out.writeUTF(jrt.release); } char[] patchName = c.patchModuleName == null ? CharOperation.NO_CHAR : c.patchModuleName.toCharArray(); writeName(patchName, out); @@ -680,13 +687,20 @@ void write(DataOutputStream out) throws IOException { out.writeUTF(jar.externalAnnotationPath != null ? jar.externalAnnotationPath : ""); //$NON-NLS-1$ out.writeBoolean(jar.isOnModulePath); out.writeUTF(jar.compliance != null ? jar.compliance : ""); //$NON-NLS-1$ - } else { + } else if (c instanceof ClasspathJrt) { ClasspathJrt jrt = (ClasspathJrt) c; out.writeByte(EXTERNAL_JAR); out.writeUTF(jrt.zipFilename); writeRestriction(jrt.accessRuleSet, out); out.writeUTF(jrt.externalAnnotationPath != null ? jrt.externalAnnotationPath : ""); //$NON-NLS-1$ - out.writeUTF(jrt.release != null ? jrt.release : ""); //$NON-NLS-1$ + out.writeUTF(""); //$NON-NLS-1$ + } else { + ClasspathJrtWithReleaseOption jrt = (ClasspathJrtWithReleaseOption) c; + out.writeByte(EXTERNAL_JAR); + out.writeUTF(jrt.zipFilename); + writeRestriction(jrt.accessRuleSet, out); + out.writeUTF(jrt.externalAnnotationPath != null ? jrt.externalAnnotationPath : ""); //$NON-NLS-1$ + out.writeUTF(jrt.release); } } diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/JavaSearchNameEnvironment.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/JavaSearchNameEnvironment.java index a61e40abd2..db613ca5a3 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/JavaSearchNameEnvironment.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/JavaSearchNameEnvironment.java @@ -49,7 +49,6 @@ import org.eclipse.jdt.internal.core.JavaProject; import org.eclipse.jdt.internal.core.JrtPackageFragmentRoot; import org.eclipse.jdt.internal.core.NameLookup; import org.eclipse.jdt.internal.core.PackageFragmentRoot; -import org.eclipse.jdt.internal.core.builder.ClasspathJrt; import org.eclipse.jdt.internal.core.builder.ClasspathLocation; import org.eclipse.jdt.internal.core.util.Util; @@ -162,7 +161,7 @@ private ClasspathLocation mapToClassPathLocation(JavaModelManager manager, Packa IJavaProject project = (IJavaProject) root.getParent(); String compliance = project.getOption(JavaCore.COMPILER_COMPLIANCE, true); cp = (root instanceof JrtPackageFragmentRoot) ? - new ClasspathJrt(path.toOSString(), rawClasspathEntry.getAccessRuleSet(), + ClasspathLocation.forJrtSystem(path.toOSString(), rawClasspathEntry.getAccessRuleSet(), ClasspathEntry.getExternalAnnotationPath(rawClasspathEntry, project.getProject(), true), compliance) : ClasspathLocation.forLibrary(manager.getZipFile(path), rawClasspathEntry.getAccessRuleSet(), ClasspathEntry.getExternalAnnotationPath(rawClasspathEntry, -- cgit v1.2.3