diff --git a/src/hotspot/share/classfile/classLoaderExt.cpp b/src/hotspot/share/classfile/classLoaderExt.cpp index 78e29f990d7..3cd7dd7cd3b 100644 --- a/src/hotspot/share/classfile/classLoaderExt.cpp +++ b/src/hotspot/share/classfile/classLoaderExt.cpp @@ -213,6 +213,15 @@ void ClassLoaderExt::process_jar_manifest(JavaThread* current, ClassPathEntry* e char sep = os::file_separator()[0]; const char* dir_name = entry->name(); const char* dir_tail = strrchr(dir_name, sep); +#ifdef _WINDOWS + // On Windows, we also support forward slash as the file separator when locating entries in the classpath entry. + const char* dir_tail2 = strrchr(dir_name, '/'); + if (dir_tail == nullptr) { + dir_tail = dir_tail2; + } else if (dir_tail2 != nullptr && dir_tail2 > dir_tail) { + dir_tail = dir_tail2; + } +#endif int dir_len; if (dir_tail == nullptr) { dir_len = 0; diff --git a/test/hotspot/jtreg/runtime/cds/appcds/ClassPathAttr.java b/test/hotspot/jtreg/runtime/cds/appcds/ClassPathAttr.java index f0d797c09ae..560f42d0467 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/ClassPathAttr.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/ClassPathAttr.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2024, 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 @@ -30,6 +30,7 @@ * @run driver/timeout=240 ClassPathAttr */ +import jdk.test.lib.Platform; import jdk.test.lib.cds.CDSTestUtils; import jdk.test.lib.process.OutputAnalyzer; import java.io.File; @@ -82,6 +83,76 @@ public class ClassPathAttr { output.shouldMatch("checking shared classpath entry: .*cpattr2.jar"); output.shouldMatch("checking shared classpath entry: .*cpattr3.jar"); }); + + // Test handling of forward slash ('/') file separator when locating entries + // in the classpath entry on Windows. + // Skip the following test when CDS dynamic dump is enabled due to some + // issue when converting a relative path to real path. + if (Platform.isWindows() && !CDSTestUtils.DYNAMIC_DUMP) { + // Test with relative path + // Find the index to the dir before the jar file. + int idx = jar1.lastIndexOf(File.separator); + idx = jar1.substring(0, idx - 1).lastIndexOf(File.separator); + // Setup jar directory and names. + String jarDir = jar1.substring(0, idx); + String jar1Name = jar1.substring(idx + 1); + String jar4Name = jar4.substring(idx + 1); + String newCp = jar1Name.replace("\\", "/") + File.pathSeparator + jar4Name.replace("\\", "/"); + + OutputAnalyzer out = TestCommon.testDump(jarDir, newCp, classlist, "-Xlog:class+path=info"); + if (i == 1) { + out.shouldMatch("opened:.*cpattr1.jar"); // first jar on -cp + } else { + // first jar on -cp with long Class-Path: attribute + out.shouldMatch("opened:.*cpattr1_long.jar"); + } + // one of the jar in the Class-Path: attribute of cpattr1.jar + out.shouldMatch("opened:.*cpattr2.jar"); + + TestCommon.runWithRelativePath( + jarDir.replace("\\", "/"), + "-Xlog:class+path,class+load", + "-cp", newCp, + "CpAttr1") + .assertNormalExit(output -> { + output.shouldMatch("checking shared classpath entry: .*cpattr2.jar"); + output.shouldMatch("checking shared classpath entry: .*cpattr3.jar"); + }); + + // Go one directory up. + int idx2 = jar1.substring(0, idx - 1).lastIndexOf(File.separator); + if (idx2 != -1) { + // Setup jar directory and names. + jarDir = jar1.substring(0, idx2); + // Set relative path to jar containing '\' and '/' file separators + // e.g. d1\d2/A.jar + jar1Name = jar1.substring(idx2 + 1).replace("\\", "/"); + jar4Name = jar4.substring(idx2 + 1).replace("\\", "/"); + jar1Name = jar1Name.replaceFirst("/", "\\\\"); + jar4Name = jar4Name.replaceFirst("/", "\\\\"); + + newCp = jar1Name + File.pathSeparator + jar4Name; + out = TestCommon.testDump(jarDir, newCp, classlist, "-Xlog:class+path=info"); + if (i == 1) { + out.shouldMatch("opened:.*cpattr1.jar"); // first jar on -cp + } else { + // first jar on -cp with long Class-Path: attribute + out.shouldMatch("opened:.*cpattr1_long.jar"); + } + // one of the jar in the Class-Path: attribute of cpattr1.jar + out.shouldMatch("opened:.*cpattr2.jar"); + + TestCommon.runWithRelativePath( + jarDir.replace("\\", "/"), + "-Xlog:class+path,class+load", + "-cp", newCp, + "CpAttr1") + .assertNormalExit(output -> { + output.shouldMatch("checking shared classpath entry: .*cpattr2.jar"); + output.shouldMatch("checking shared classpath entry: .*cpattr3.jar"); + }); + } + } } // test duplicate jars in the "Class-path" attribute in the jar manifest