From e0ef39536ddd75096e61dc7469790c8e33048421 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Sun, 10 Jul 2022 11:45:04 +0200 Subject: [PATCH 1/3] [NETBEANS-4044] Attempting to fix handling of patches for modular libraries. --- .../parsing/PatchModuleFileManager.java | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/java/java.source.base/src/org/netbeans/modules/java/source/parsing/PatchModuleFileManager.java b/java/java.source.base/src/org/netbeans/modules/java/source/parsing/PatchModuleFileManager.java index aed1075b0d05..ed9f6f920050 100644 --- a/java/java.source.base/src/org/netbeans/modules/java/source/parsing/PatchModuleFileManager.java +++ b/java/java.source.base/src/org/netbeans/modules/java/source/parsing/PatchModuleFileManager.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; @@ -29,6 +30,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -43,6 +45,7 @@ import org.netbeans.api.annotations.common.CheckForNull; import org.netbeans.api.annotations.common.NonNull; import org.netbeans.api.java.classpath.ClassPath; +import org.netbeans.api.java.queries.BinaryForSourceQuery; import org.netbeans.modules.java.source.indexing.JavaIndex; import org.netbeans.modules.java.source.util.Iterators; import org.netbeans.spi.java.classpath.support.ClassPathSupport; @@ -64,6 +67,7 @@ final class PatchModuleFileManager implements JavaFileManager { private final Map> patches; private final Map roots; private Set moduleLocations; + private String overrideModuleName; PatchModuleFileManager( @NonNull final JavaFileManager binDelegate, @@ -162,7 +166,8 @@ public boolean handleOption(String head, Iterator tail) { return true; } } else if (head.startsWith(JavacParser.NB_X_MODULE)) { - addModulePatches(head.substring(JavacParser.NB_X_MODULE.length()), + overrideModuleName = head.substring(JavacParser.NB_X_MODULE.length()); + addModulePatches(overrideModuleName, src.entries().stream().map(e -> e.getURL()).collect(Collectors.toList())); return true; } @@ -214,6 +219,8 @@ public ClassLoader getClassLoader(Location location) { @Override public Iterable list(Location location, String packageName, Set kinds, boolean recurse) throws IOException { + location = fixLocation(location); + if (PatchLocation.isInstance(location)) { final PatchLocation pl = PatchLocation.cast(location); final ModuleLocation bin = pl.getBin(); @@ -249,6 +256,8 @@ public String inferBinaryName(Location location, JavaFileObject file) { @Override public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind) throws IOException { + location = fixLocation(location); + if (PatchLocation.isInstance(location)) { final PatchLocation pl = PatchLocation.cast(location); final ModuleLocation bin = pl.getBin(); @@ -285,6 +294,14 @@ public FileObject getFileForOutput(Location location, String packageName, String } // + private Location fixLocation(Location input) throws IOException { + if (input == StandardLocation.CLASS_OUTPUT && overrideModuleName != null) { + return this.moduleLocations.stream().filter(pl -> overrideModuleName.equals(pl.getModuleName())).map(pl -> (Location) pl).findAny().orElse(input); + } else { + return input; + } + } + private Set moduleLocations(final Location baseLocation) throws IOException { if (baseLocation != StandardLocation.PATCH_MODULE_PATH) { throw new IllegalStateException(baseLocation.toString()); @@ -292,7 +309,7 @@ private Set moduleLocations(final Location baseLocation) throws I if (moduleLocations == null) { Set res = new HashSet<>(); for (Map.Entry> patch : patches.entrySet()) { - res.add(createPatchLocation(patch.getKey(), patch.getValue())); + res.add(createPatchLocation(patch.getKey(), patch.getValue(), patch.getKey().equals(overrideModuleName))); } moduleLocations = Collections.unmodifiableSet(res); } @@ -302,7 +319,8 @@ private Set moduleLocations(final Location baseLocation) throws I @NonNull private static PatchLocation createPatchLocation( @NonNull final String modName, - @NonNull final List roots) throws IOException { + @NonNull final List roots, + final boolean sourceOverride) throws IOException { Collection bin = new ArrayList<>(roots.size()); Collection src = new ArrayList<>(roots.size()); for (URL root : roots) { @@ -310,7 +328,11 @@ private static PatchLocation createPatchLocation( src.add(root); bin.add(FileUtil.urlForArchiveOrDir(JavaIndex.getClassFolder(root))); } else { - bin.add(root); + if (sourceOverride) { + bin.addAll(Arrays.asList(BinaryForSourceQuery.findBinaryRoots(root).getRoots())); + } else { + bin.add(root); + } } } return new PatchLocation( From 67f4bac97ae43e18bd7f9359ed9007e33979a193 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Sun, 17 Jul 2022 08:21:01 +0200 Subject: [PATCH 2/3] Adding test. --- .../java/source/parsing/PatchModuleTest.java | 148 ++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 java/java.source.base/test/unit/src/org/netbeans/modules/java/source/parsing/PatchModuleTest.java diff --git a/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/parsing/PatchModuleTest.java b/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/parsing/PatchModuleTest.java new file mode 100644 index 000000000000..518e65bcc409 --- /dev/null +++ b/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/parsing/PatchModuleTest.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.netbeans.modules.java.source.parsing; + +import com.sun.tools.javac.Main; +import java.io.File; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import javax.swing.event.ChangeListener; +import static junit.framework.TestCase.assertEquals; +import org.netbeans.api.java.queries.BinaryForSourceQuery; +import org.netbeans.api.java.source.CompilationController; +import org.netbeans.api.java.source.JavaSource; +import org.netbeans.api.java.source.JavaSource.Phase; +import org.netbeans.api.java.source.SourceUtilsTestUtil; +import org.netbeans.api.java.source.Task; +import org.netbeans.api.java.source.TestUtilities; +import org.netbeans.junit.NbTestCase; +import org.netbeans.modules.java.source.tasklist.CompilerSettings; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.netbeans.spi.java.queries.BinaryForSourceQueryImplementation; + +public class PatchModuleTest extends NbTestCase { + + public PatchModuleTest(String name) { + super(name); + } + + private FileObject sourceRoot; + private FileObject classRoot; + private FileObject cp; + + protected void setUp() throws Exception { + SourceUtilsTestUtil.prepareTest(new String[0], new Object[] {settings, binaryForSource}); + clearWorkDir(); + prepareTest(); + } + + public void testNETBEANS_4044() throws Exception { + settings.commandLine = "-Xnb-Xmodule:patch"; + binaryForSource.result = new BinaryForSourceQuery.Result() { + @Override + public URL[] getRoots() { + return new URL[] {classRoot.toURL()}; + } + @Override + public void addChangeListener(ChangeListener l) {} + @Override + public void removeChangeListener(ChangeListener l) {} + }; + FileObject sourceModule = createFile("module-info.java", "module patch {}"); SourceUtilsTestUtil.setSourceLevel(sourceModule, "11"); + FileObject source1 = createFile("patch/Patch.java", "package patch; class Patch { Dep dep; }"); SourceUtilsTestUtil.setSourceLevel(source1, "11"); + FileObject source2 = createFile("patch/Dep.java", "package patch; class Dep { }"); SourceUtilsTestUtil.setSourceLevel(source2, "11"); + File classDir = FileUtil.toFile(classRoot); + List options = new ArrayList<>(); + options.add("-d"); + options.add(classDir.getAbsolutePath()); + Enumeration en = sourceRoot.getChildren(true); + while (en.hasMoreElements()) { + FileObject f = en.nextElement(); + if (f.isData()) { + options.add(FileUtil.toFile(f).getAbsolutePath()); + } + } + assertEquals(0, Main.compile(options.toArray(new String[0]))); + JavaSource js = JavaSource.forFileObject(source1); + + js.runUserActionTask(new Task() { + public void run(CompilationController parameter) throws Exception { + assertTrue(Phase.RESOLVED.compareTo(parameter.toPhase(Phase.RESOLVED)) <= 0); + assertEquals(parameter.getDiagnostics().toString(), 0, parameter.getDiagnostics().size()); + } + }, true); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + settings.commandLine = null; + binaryForSource.result = null; + } + + private FileObject createFile(String path, String content) throws Exception { + FileObject file = FileUtil.createData(sourceRoot, path); + TestUtilities.copyStringToFile(file, content); + + return file; + } + + private void prepareTest() throws Exception { + File work = getWorkDir(); + FileObject workFO = FileUtil.toFileObject(work); + + assertNotNull(workFO); + + sourceRoot = workFO.createFolder("src"); + classRoot = workFO.createFolder("class"); + + FileObject buildRoot = workFO.createFolder("build"); + FileObject cache = workFO.createFolder("cache"); + + SourceUtilsTestUtil.prepareTest(sourceRoot, buildRoot, cache, new FileObject[] {cp}); + } + + private static final CompilerSettingsImpl settings = new CompilerSettingsImpl(); + + private static final class CompilerSettingsImpl extends CompilerSettings { + private String commandLine; + @Override + protected String buildCommandLine(FileObject file) { + return commandLine; + } + + } + + private static final BinaryForSourceQueryImpl binaryForSource = new BinaryForSourceQueryImpl(); + + private static final class BinaryForSourceQueryImpl implements BinaryForSourceQueryImplementation { + + private BinaryForSourceQuery.Result result; + + @Override + public BinaryForSourceQuery.Result findBinaryRoots(URL sourceRoot) { + return result; + } + + } +} From be419c3032a6cbebc1be5c8b48b6ccd49c8fc8ae Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Sun, 17 Jul 2022 22:24:33 +0200 Subject: [PATCH 3/3] Attempting to fix test data compilation on JDK 8. --- .../java/source/parsing/PatchModuleTest.java | 56 +++++++++++++++---- 1 file changed, 45 insertions(+), 11 deletions(-) diff --git a/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/parsing/PatchModuleTest.java b/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/parsing/PatchModuleTest.java index 518e65bcc409..0dfcf604c106 100644 --- a/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/parsing/PatchModuleTest.java +++ b/java/java.source.base/test/unit/src/org/netbeans/modules/java/source/parsing/PatchModuleTest.java @@ -19,15 +19,26 @@ package org.netbeans.modules.java.source.parsing; -import com.sun.tools.javac.Main; +import com.sun.tools.javac.api.JavacTool; import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import javax.swing.event.ChangeListener; +import javax.tools.ForwardingJavaFileManager; +import javax.tools.JavaFileManager; +import javax.tools.JavaFileManager.Location; +import javax.tools.JavaFileObject; +import javax.tools.JavaFileObject.Kind; +import javax.tools.SimpleJavaFileObject; import static junit.framework.TestCase.assertEquals; import org.netbeans.api.java.queries.BinaryForSourceQuery; +import org.netbeans.api.java.source.ClasspathInfo; import org.netbeans.api.java.source.CompilationController; import org.netbeans.api.java.source.JavaSource; import org.netbeans.api.java.source.JavaSource.Phase; @@ -36,6 +47,7 @@ import org.netbeans.api.java.source.TestUtilities; import org.netbeans.junit.NbTestCase; import org.netbeans.modules.java.source.tasklist.CompilerSettings; +import org.netbeans.modules.java.source.usages.ClasspathInfoAccessor; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.netbeans.spi.java.queries.BinaryForSourceQueryImplementation; @@ -71,18 +83,19 @@ public void removeChangeListener(ChangeListener l) {} FileObject sourceModule = createFile("module-info.java", "module patch {}"); SourceUtilsTestUtil.setSourceLevel(sourceModule, "11"); FileObject source1 = createFile("patch/Patch.java", "package patch; class Patch { Dep dep; }"); SourceUtilsTestUtil.setSourceLevel(source1, "11"); FileObject source2 = createFile("patch/Dep.java", "package patch; class Dep { }"); SourceUtilsTestUtil.setSourceLevel(source2, "11"); - File classDir = FileUtil.toFile(classRoot); - List options = new ArrayList<>(); - options.add("-d"); - options.add(classDir.getAbsolutePath()); - Enumeration en = sourceRoot.getChildren(true); - while (en.hasMoreElements()) { - FileObject f = en.nextElement(); - if (f.isData()) { - options.add(FileUtil.toFile(f).getAbsolutePath()); + ClasspathInfo cpInfo = ClasspathInfo.create(sourceModule); + try (JavaFileManager fm = ClasspathInfoAccessor.getINSTANCE().createFileManager(cpInfo, "11"); + JavaFileManager output = new OutputFileManager(fm)) { + List files = new ArrayList<>(); + Enumeration en = sourceRoot.getChildren(true); + while (en.hasMoreElements()) { + FileObject f = en.nextElement(); + if (f.isData()) { + files.add(FileObjects.fileObjectFileObject(f, sourceRoot, null, null)); + } } + assertTrue(JavacTool.create().getTask(null, output, null, null, null, files).call()); } - assertEquals(0, Main.compile(options.toArray(new String[0]))); JavaSource js = JavaSource.forFileObject(source1); js.runUserActionTask(new Task() { @@ -145,4 +158,25 @@ public BinaryForSourceQuery.Result findBinaryRoots(URL sourceRoot) { } } + + private class OutputFileManager extends ForwardingJavaFileManager { + + public OutputFileManager(JavaFileManager fileManager) { + super(fileManager); + } + + @Override + public JavaFileObject getJavaFileForOutput(Location location, String className, Kind kind, javax.tools.FileObject sibling) throws IOException { + try { + return new SimpleJavaFileObject(new URI("mem://" + className + kind.extension), kind) { + @Override + public OutputStream openOutputStream() throws IOException { + return FileUtil.createData(classRoot, className.replace(".", "/") + kind.extension).getOutputStream(); + } + }; + } catch (URISyntaxException ex) { + throw new IOException(ex); + } + } + } }