Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import jdk.tools.jlink.internal.plugins.DefaultStripDebugPlugin;
import jdk.tools.jlink.internal.plugins.ExcludeJmodSectionPlugin;
import jdk.tools.jlink.internal.plugins.PluginsResourceBundle;
import jdk.tools.jlink.internal.plugins.StripJavaDebugAttributesPlugin;
import jdk.tools.jlink.plugin.Plugin;
import jdk.tools.jlink.plugin.Plugin.Category;

Expand Down Expand Up @@ -418,6 +419,9 @@ private PluginsConfiguration getPluginsConfig(Path output, Map<String, String> l

List<Plugin> pluginsList = new ArrayList<>();
Set<String> seenPlugins = new HashSet<>();
// reference to the enabled DefaultStripDebugPlugin
DefaultStripDebugPlugin defaultStripDebugPlugin = null;

for (Entry<Plugin, List<Map<String, String>>> entry : pluginToMaps.entrySet()) {
Plugin plugin = entry.getKey();
List<Map<String, String>> argsMaps = entry.getValue();
Expand All @@ -438,6 +442,10 @@ private PluginsConfiguration getPluginsConfig(Path output, Map<String, String> l
}

if (!Utils.isDisabled(plugin)) {
if (plugin instanceof DefaultStripDebugPlugin p) {
defaultStripDebugPlugin = p;
}

// make sure that --strip-debug and --strip-native-debug-symbols
// aren't being used at the same time. --strip-debug invokes --strip-native-debug-symbols on
// platforms that support it, so it makes little sense to allow both at the same time.
Expand All @@ -452,6 +460,11 @@ private PluginsConfiguration getPluginsConfig(Path output, Map<String, String> l
}
}

// disable StripJavaDebugAttributesPlugin within DefaultStripDebug plugin if both enabled
if (seenPlugins.contains(StripJavaDebugAttributesPlugin.NAME) && defaultStripDebugPlugin != null) {
defaultStripDebugPlugin.enableJavaStripPlugin(false);
}

// recreate or postprocessing don't require an output directory.
ImageBuilder builder = null;
if (output != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public final class DefaultStripDebugPlugin extends AbstractPlugin {
private final Plugin javaStripPlugin;
private final NativePluginFactory stripNativePluginFactory;

private boolean isJavaStripPluginEnabled = true;

public DefaultStripDebugPlugin() {
this(new StripJavaDebugAttributesPlugin(),
new DefaultNativePluginFactory());
Expand All @@ -59,21 +61,32 @@ public DefaultStripDebugPlugin(Plugin javaStripPlugin,
this.stripNativePluginFactory = nativeStripPluginFact;
}

public void enableJavaStripPlugin(boolean enableJavaStripPlugin) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method name specifically implies enabling. Maybe "setEnabled..."?

isJavaStripPluginEnabled = enableJavaStripPlugin;
}

@Override
public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
Plugin stripNativePlugin = stripNativePluginFactory.create();
if (stripNativePlugin != null) {
Map<String, String> stripNativeConfig = Map.of(
STRIP_NATIVE_DEBUG_PLUGIN, EXCLUDE_DEBUGINFO);
stripNativePlugin.configure(stripNativeConfig);

if (!isJavaStripPluginEnabled) {
return stripNativePlugin.transform(in, out);
}

ResourcePoolManager outRes =
new ResourcePoolManager(in.byteOrder(),
((ResourcePoolImpl)in).getStringTable());
ResourcePool strippedJava = javaStripPlugin.transform(in,
outRes.resourcePoolBuilder());
return stripNativePlugin.transform(strippedJava, out);
} else {
} else if (isJavaStripPluginEnabled) {
return javaStripPlugin.transform(in, out);
} else {
return in;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,14 @@
*/
public final class StripJavaDebugAttributesPlugin extends AbstractPlugin {
private final Predicate<String> predicate;
public static final String NAME = "strip-java-debug-attributes";

public StripJavaDebugAttributesPlugin() {
this((path) -> false);
}

StripJavaDebugAttributesPlugin(Predicate<String> predicate) {
super("strip-java-debug-attributes");
super(NAME);
this.predicate = predicate;
}

Expand All @@ -69,11 +70,9 @@ public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
ClassFile.LineNumbersOption.DROP_LINE_NUMBERS);
byte[] content = ClassFile.of().transformClass(clm, ClassTransform
.dropping(cle -> cle instanceof SourceFileAttribute
|| cle instanceof SourceDebugExtensionAttribute)
.andThen(ClassTransform.transformingMethods(MethodTransform
.dropping(me -> me instanceof MethodParametersAttribute)
.andThen(MethodTransform
.transformingCode(CodeTransform.ACCEPT_ALL)))));
|| cle instanceof SourceDebugExtensionAttribute)
.andThen(ClassTransform.transformingMethods(MethodTransform
.transformingCode(CodeTransform.ACCEPT_ALL))));
res = resource.copyWithContent(content);
}
}
Expand Down
34 changes: 34 additions & 0 deletions test/jdk/tools/jlink/plugins/DefaultStripDebugPluginTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,40 @@ public void testNoNativeStripPluginPresent() {
}
}

public void testOnlyNativePlugin() {
MockStripPlugin javaPlugin = new MockStripPlugin(false);
MockStripPlugin nativePlugin = new MockStripPlugin(true);
TestNativeStripPluginFactory nativeFactory =
new TestNativeStripPluginFactory(nativePlugin);
DefaultStripDebugPlugin plugin = new DefaultStripDebugPlugin(javaPlugin,
nativeFactory);
plugin.enableJavaStripPlugin(false);

ResourcePoolManager inManager = new ResourcePoolManager();
ResourcePool pool = plugin.transform(inManager.resourcePool(),
inManager.resourcePoolBuilder());
if (pool.findEntry(MockStripPlugin.JAVA_PATH).isPresent() ||
!pool.findEntry(MockStripPlugin.NATIVE_PATH).isPresent()) {
throw new AssertionError("Expected only native to get called");
}
}

public void testNoOperation() {
MockStripPlugin javaPlugin = new MockStripPlugin(false);
TestNativeStripPluginFactory nativeFactory =
new TestNativeStripPluginFactory(null);
DefaultStripDebugPlugin plugin = new DefaultStripDebugPlugin(javaPlugin,
nativeFactory);
plugin.enableJavaStripPlugin(false);
ResourcePoolManager inManager = new ResourcePoolManager();
ResourcePool pool = plugin.transform(inManager.resourcePool(),
inManager.resourcePoolBuilder());
if (pool.findEntry(MockStripPlugin.JAVA_PATH).isPresent() ||
pool.findEntry(MockStripPlugin.NATIVE_PATH).isPresent()) {
throw new AssertionError("Expected both native and java not called");
}
}

public static void main(String[] args) {
DefaultStripDebugPluginTest test = new DefaultStripDebugPluginTest();
test.testNoNativeStripPluginPresent();
Expand Down
228 changes: 228 additions & 0 deletions test/jdk/tools/jlink/plugins/StripParameterNamesTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
/*
* 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.
*/

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.FieldSource;
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.io.IOException;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;
import java.util.spi.ToolProvider;

import tests.JImageGenerator;
import tests.JImageGenerator.InMemorySourceFile;
import tests.Result;

/*
* @test
* @summary Test jlink strip debug plugins handle method parameter names.
* @bug 8347007
* @library ../../lib
* @modules java.base/jdk.internal.jimage
* jdk.jlink/jdk.tools.jlink.internal
* jdk.jlink/jdk.tools.jlink.plugin
* jdk.jlink/jdk.tools.jmod
* jdk.jlink/jdk.tools.jimage
* jdk.compiler
* @build tests.*
* @run junit/othervm StripParameterNamesTest
*/
public class StripParameterNamesTest {
private static final ToolProvider JAVAC_TOOL = ToolProvider.findFirst("javac")
.orElseThrow(() -> new RuntimeException("javac tool not found"));

private static Path src = Paths.get("src").toAbsolutePath();
private static List<Jmod> testJmods = new ArrayList<>();

record Jmod(Path moduleDir, boolean withDebugInfo, boolean withParameterNames) {}

@BeforeAll
public static void setup() throws IOException {
Files.createDirectory(src);
var mainClassSource = new InMemorySourceFile("test", "InspectParameterNames", """
package test;

public class InspectParameterNames {
int add(int a, int b) {
return a + b;
}

public static boolean hasParameterNames() throws NoSuchMethodException {
// Get add method in the class
var method = InspectParameterNames.class.getDeclaredMethod("add", int.class, int.class);

// Get method parameters
var parameters = method.getParameters();

// validate parameter names
return parameters[0].getName().equals("a") && parameters[1].getName().equals("b");
}

public static void main(String[] args) throws NoSuchMethodException {
System.out.println(hasParameterNames());
}
}
""");
var moduleDir = JImageGenerator.generateSources(src, "bug8347007x", List.of(mainClassSource));
JImageGenerator.generateModuleInfo(moduleDir, List.of("test"));
testJmods.add(buildJmod(true, true));
testJmods.add(buildJmod(true, false));
testJmods.add(buildJmod(false, true));
testJmods.add(buildJmod(false, false));
}

@AfterEach
public void cleanup() throws IOException {
rmdir(Paths.get("img"));
}

static void report(String command, List<String> args) {
System.out.println(command + " " + String.join(" ", args));
}

static void javac(List<String> args) {
report("javac", args);
JAVAC_TOOL.run(System.out, System.err, args.toArray(new String[0]));
}
/**
* Recursively remove a Directory
*
* @param dir Directory to delete
* @throws IOException If an error occurs
*/
static void rmdir(Path dir) throws IOException {
// Nothing to do if the file does not exist
if (!Files.exists(dir)) {
return;
}
try (Stream<Path> walk = Files.walk(dir)) {
walk.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
}
}

/**
* Build jmods from the module source path
*/
static Jmod buildJmod(boolean withDebugInfo, boolean withParameterNames) {
String dirName = "jmods";
List<String> options = new ArrayList<>();

if (withDebugInfo) {
options.add("-g");
dirName += "g";
}

if (withParameterNames) {
options.add("-parameters");
dirName += "p";
}

Path moduleDir = Paths.get(dirName).toAbsolutePath();

options.add("-d");
options.add(moduleDir.toString());
options.add("--module-source-path");
options.add(src.toString());
options.add("--module");
options.add("bug8347007x");

javac(options);
return new Jmod(moduleDir, withDebugInfo, withParameterNames);
}

Result buildImage(Path modulePath, Path imageDir, String... options) {
var jlinkTask = JImageGenerator.getJLinkTask()
.modulePath(modulePath.toString())
.output(imageDir);

for (var option: options) {
jlinkTask.option(option);
}

return jlinkTask.addMods("bug8347007x")
.call();
}

void assertHasParameterNames(Path imageDir, boolean expected) throws IOException, InterruptedException {
Path binDir = imageDir.resolve("bin").toAbsolutePath();
Path bin = binDir.resolve("java");

ProcessBuilder processBuilder = new ProcessBuilder(bin.toString(),
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+BytecodeVerificationLocal",
"-m", "bug8347007x/test.InspectParameterNames");
processBuilder.directory(binDir.toFile());
Process process = processBuilder.start();
int exitCode = process.waitFor();
var output = process.inputReader().readLine();
System.out.println(output);
assertEquals(expected, Boolean.parseBoolean(output));
}

Stream<Jmod> provideTestJmods() {
return testJmods.stream();
}

@ParameterizedTest
@FieldSource("testJmods")
public void testDefaultBehavior(Jmod jmod) throws Exception {
var imageDir = Paths.get("img");
buildImage(jmod.moduleDir(), imageDir)
.assertSuccess();
var hasParameter = jmod.withParameterNames();
assertHasParameterNames(imageDir, hasParameter);
}

@ParameterizedTest
@FieldSource("testJmods")
public void testStripDebug(Jmod jmod) throws Exception {
var imageDir = Paths.get("img");
buildImage(jmod.moduleDir(), imageDir,
"--strip-debug")
.assertSuccess();
var hasParameter = jmod.withParameterNames();
assertHasParameterNames(imageDir, hasParameter);
}

@Test
public void testWithoutStripParameterName() throws Exception {
var imageDir = Paths.get("img");
var jmod = testJmods.get(0);
buildImage(jmod.moduleDir(), imageDir,
"--strip-debug", "--strip-java-debug-attributes")
.assertSuccess();
assertHasParameterNames(imageDir, jmod.withParameterNames());
}
}