diff --git a/make/CompileInterimLangtools.gmk b/make/CompileInterimLangtools.gmk index 4a2bbaec5b8..c7d1c3796f6 100644 --- a/make/CompileInterimLangtools.gmk +++ b/make/CompileInterimLangtools.gmk @@ -99,6 +99,7 @@ define SetupInterimModule EXCLUDE_FILES := $(TOPDIR)/src/$1/share/classes/module-info.java \ $(TOPDIR)/src/$1/share/classes/javax/tools/ToolProvider.java \ $(TOPDIR)/src/$1/share/classes/com/sun/tools/javac/launcher/Main.java \ + $(TOPDIR)/src/$1/share/classes/com/sun/tools/javac/launcher/MemoryClassLoader.java \ $(TOPDIR)/src/$1/share/classes/com/sun/tools/javac/launcher/MemoryContext.java \ $(TOPDIR)/src/$1/share/classes/com/sun/tools/javac/launcher/MemoryModuleFinder.java \ $(TOPDIR)/src/$1/share/classes/com/sun/tools/javac/launcher/SourceLauncher.java \ diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryClassLoader.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryClassLoader.java index f87fc88fdff..abfa6a58c30 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryClassLoader.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryClassLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,8 +27,10 @@ package com.sun.tools.javac.launcher; import java.io.ByteArrayInputStream; import java.io.FileNotFoundException; +import java.io.IOError; import java.io.IOException; import java.io.InputStream; +import java.io.UncheckedIOException; import java.lang.module.ModuleDescriptor; import java.net.MalformedURLException; import java.net.URI; @@ -48,6 +50,8 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.function.Function; +import jdk.internal.module.Resources; + /** * An in-memory classloader, that uses an in-memory cache of classes written by * {@link MemoryFileManager}. @@ -149,13 +153,9 @@ final class MemoryClassLoader extends ClassLoader { if (sourceFileClasses.containsKey(toBinaryName(name))) { return findResource(name); } - var programPath = programDescriptor.sourceRootPath().resolve(name); - if (Files.exists(programPath)) { - try { - return programPath.toUri().toURL(); - } catch (MalformedURLException e) { - throw new RuntimeException(e); - } + URL resource = toResourceInRootPath(name); + if (resource != null) { + return resource; } return parentClassLoader.getResource(name); } @@ -233,7 +233,7 @@ final class MemoryClassLoader extends ClassLoader { public URL findResource(String name) { String binaryName = toBinaryName(name); if (binaryName == null || sourceFileClasses.get(binaryName) == null) { - return null; + return toResourceInRootPath(name); } URLStreamHandler handler = this.handler; @@ -271,6 +271,28 @@ final class MemoryClassLoader extends ClassLoader { }; } + /** + * Resolves a "resource name" (as used in the getResource* methods) + * to an existing file relative to source root path, or null otherwise. + * + * @param name the resource name + * @return the URL of the resource, or null + */ + private URL toResourceInRootPath(String name) { + try { + var path = Resources.toFilePath(programDescriptor.sourceRootPath(), name); + return path == null ? null : path.toUri().toURL(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } catch (IOError error) { + Throwable cause = error.getCause(); + if (cause instanceof IOException e) { + throw new UncheckedIOException(e); + } + throw new RuntimeException(cause); + } + } + /** * Converts a "resource name" (as used in the getResource* methods) * to a binary name if the name identifies a class, or null otherwise. diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryModuleFinder.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryModuleFinder.java index d82580c993f..79b0b959c7b 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryModuleFinder.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/launcher/MemoryModuleFinder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -105,7 +105,11 @@ record MemoryModuleFinder(Map classes, try { return Optional.of(path.toUri()); } catch (IOError error) { - throw (IOException) error.getCause(); + Throwable cause = error.getCause(); + if (cause instanceof IOException e) { + throw e; + } + throw new RuntimeException(cause); } } else { return Optional.empty(); diff --git a/test/langtools/tools/javac/launcher/GetResourceTest.java b/test/langtools/tools/javac/launcher/GetResourceTest.java index acdc1e5c429..34bffa5adeb 100644 --- a/test/langtools/tools/javac/launcher/GetResourceTest.java +++ b/test/langtools/tools/javac/launcher/GetResourceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,8 +23,8 @@ /* * @test - * @bug 8210009 8321739 - * @summary Source Launcher classloader should support getResource and getResourceAsStream + * @bug 8210009 8321739 8336470 + * @summary Source Launcher classloader should support getResource/s and getResourceAsStream * @modules jdk.compiler * @library /tools/lib * @build toolbox.JavaTask toolbox.ToolBox @@ -44,12 +44,12 @@ import toolbox.ToolBox; * in order to test the classloader used to launch such programs. */ public class GetResourceTest { - public static void main(String... args) throws Exception { + public static void main(String... args) { GetResourceTest t = new GetResourceTest(); t.run(); } - void run() throws Exception { + void run() { ToolBox tb = new ToolBox(); Path file = Paths.get(tb.testSrc).resolve("src/p/q").resolve("CLTest.java"); new JavaTask(tb) diff --git a/test/langtools/tools/javac/launcher/src/META-INF/services/java.util.spi.ToolProvider b/test/langtools/tools/javac/launcher/src/META-INF/services/java.util.spi.ToolProvider new file mode 100644 index 00000000000..a475bbc4127 --- /dev/null +++ b/test/langtools/tools/javac/launcher/src/META-INF/services/java.util.spi.ToolProvider @@ -0,0 +1 @@ +Tool diff --git a/test/langtools/tools/javac/launcher/src/Tool.java b/test/langtools/tools/javac/launcher/src/Tool.java new file mode 100644 index 00000000000..1082452c113 --- /dev/null +++ b/test/langtools/tools/javac/launcher/src/Tool.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// unnamed package + +import java.io.PrintWriter; +import java.util.spi.ToolProvider; + +public class Tool implements ToolProvider { + @Override + public String name() { + return "Tool"; + } + + @Override + public int run(PrintWriter out, PrintWriter err, String... args) { + out.println("Tool/out"); + err.println("Tool/err"); + return 0; + } +} diff --git a/test/langtools/tools/javac/launcher/src/p/q/CLTest.java b/test/langtools/tools/javac/launcher/src/p/q/CLTest.java index 71840d3f29d..938512e1501 100644 --- a/test/langtools/tools/javac/launcher/src/p/q/CLTest.java +++ b/test/langtools/tools/javac/launcher/src/p/q/CLTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,23 +39,42 @@ import java.net.*; import java.util.*; import java.lang.classfile.ClassModel; import java.lang.classfile.ClassFile; +import java.util.spi.ToolProvider; public class CLTest { public static void main(String... args) throws Exception { try { - new CLTest().run(); + var test = new CLTest(); + test.loadToolProviderByName(); // run first to create Tool.class + test.getGetResources(); } catch (Throwable t) { t.printStackTrace(); System.exit(1); } } - void run() throws Exception { + void loadToolProviderByName() { + ServiceLoader.load(ToolProvider.class).stream() + .map(ServiceLoader.Provider::get) + .filter(toolProvider -> toolProvider.name().equals("Tool")) + .findFirst() + .orElseThrow(); + } + + void getGetResources() throws Exception { String[] names = { + // scheme -> file: + "Tool.java", + "p/q/CLTest.java", + "META-INF/services/java.util.spi.ToolProvider", + // scheme -> sourcelauncher-memoryclassloaderNNN: + "Tool.class", "p/q/CLTest.class", "p/q/CLTest$Inner.class", "p/q/CLTest2.class", + // scheme -> jrt: "java/lang/Object.class", + // no scheme applicable "UNKNOWN.class", "UNKNOWN" }; @@ -102,6 +121,14 @@ public class CLTest { list.add(e.nextElement()); } + if (name.contains("META-INF")) { + if (list.size() == 0) { + error("resource not found: " + name); + } + // one or more resources found, as expected + return; + } + switch (list.size()) { case 0: if (!name.contains("UNKNOWN")) { @@ -150,6 +177,9 @@ public class CLTest { } void checkClass(String name, InputStream in) throws Exception { + if (!name.endsWith(".class")) { + return; // ignore non-class resources + } ClassModel cf = ClassFile.of().parse(in.readAllBytes()); System.err.println(" class " + cf.thisClass().asInternalName()); if (!name.equals(cf.thisClass().asInternalName() + ".class")) {