8327499: MethodHandleStatics.traceLambdaForm includes methods that cannot be generated

Reviewed-by: redestad, iklam
This commit is contained in:
Chen Liang 2024-05-13 09:11:49 +00:00 committed by Claes Redestad
parent 5a8df4106a
commit adaa509b6e
5 changed files with 140 additions and 34 deletions

View File

@ -28,7 +28,6 @@ package java.lang.invoke;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.Opcodes;
import sun.invoke.util.Wrapper;
import sun.util.logging.PlatformLogger;
import java.util.ArrayList;
import java.util.HashSet;
@ -73,6 +72,7 @@ class GenerateJLIClassesHelper {
private final TreeSet<String> speciesTypes = new TreeSet<>();
private final TreeSet<String> invokerTypes = new TreeSet<>();
private final TreeSet<String> linkerTypes = new TreeSet<>();
private final TreeSet<String> callSiteTypes = new TreeSet<>();
private final Map<String, Set<String>> dmhMethods = new TreeMap<>();
@ -87,6 +87,12 @@ class GenerateJLIClassesHelper {
return this;
}
HolderClassBuilder addLinkerType(String methodType) {
validateMethodType(methodType);
linkerTypes.add(methodType);
return this;
}
HolderClassBuilder addCallSiteType(String csType) {
validateMethodType(csType);
callSiteTypes.add(csType);
@ -130,19 +136,33 @@ class GenerateJLIClassesHelper {
}
}
// The invoker type to ask for is retrieved by removing the first
// The linker type to ask for is retrieved by removing the first
// and the last argument, which needs to be of Object.class
MethodType[] linkerMethodTypes = new MethodType[linkerTypes.size()];
index = 0;
for (String linkerType : linkerTypes) {
MethodType mt = asMethodType(linkerType);
final int lastParam = mt.parameterCount() - 1;
if (!checkLinkerTypeParams(mt)) {
throw new RuntimeException(
"Linker type parameter must start and end with Object: " + linkerType);
}
mt = mt.dropParameterTypes(lastParam, lastParam + 1);
linkerMethodTypes[index] = mt.dropParameterTypes(0, 1);
index++;
}
// The invoker type to ask for is retrieved by removing the first
// argument, which needs to be of Object.class
MethodType[] invokerMethodTypes = new MethodType[invokerTypes.size()];
index = 0;
for (String invokerType : invokerTypes) {
MethodType mt = asMethodType(invokerType);
final int lastParam = mt.parameterCount() - 1;
if (!checkInvokerTypeParams(mt)) {
throw new RuntimeException(
"Invoker type parameter must start and end with Object: " + invokerType);
"Invoker type parameter must start with 2 Objects: " + invokerType);
}
mt = mt.dropParameterTypes(lastParam, lastParam + 1);
invokerMethodTypes[index] = mt.dropParameterTypes(0, 1);
invokerMethodTypes[index] = mt.dropParameterTypes(0, 2);
index++;
}
@ -171,7 +191,7 @@ class GenerateJLIClassesHelper {
DELEGATING_HOLDER, directMethodTypes));
result.put(INVOKERS_HOLDER,
generateInvokersHolderClassBytes(INVOKERS_HOLDER,
invokerMethodTypes, callSiteMethodTypes));
linkerMethodTypes, invokerMethodTypes, callSiteMethodTypes));
result.put(BASIC_FORMS_HOLDER,
generateBasicFormsClassBytes(BASIC_FORMS_HOLDER));
@ -207,6 +227,12 @@ class GenerateJLIClassesHelper {
}
public static boolean checkInvokerTypeParams(MethodType mt) {
return (mt.parameterCount() >= 2 &&
mt.parameterType(0) == Object.class &&
mt.parameterType(1) == Object.class);
}
public static boolean checkLinkerTypeParams(MethodType mt) {
final int lastParam = mt.parameterCount() - 1;
return (mt.parameterCount() >= 2 &&
mt.parameterType(0) == Object.class &&
@ -320,15 +346,11 @@ class GenerateJLIClassesHelper {
if ("linkToTargetMethod".equals(parts[2]) ||
"linkToCallSite".equals(parts[2])) {
builder.addCallSiteType(methodType);
} else if (parts[2].endsWith("nvoker")) {
// MH.exactInvoker exactInvoker MH.invoker invoker
builder.addInvokerType(methodType);
} else {
MethodType mt = HolderClassBuilder.asMethodType(methodType);
// Work around JDK-8327499
if (HolderClassBuilder.checkInvokerTypeParams(mt)) {
builder.addInvokerType(methodType);
} else {
PlatformLogger.getLogger("java.lang.invoke")
.warning("Invalid LF_RESOLVE " + parts[1] + " " + parts[2] + " " + parts[3]);
}
builder.addLinkerType(methodType);
}
} else if (parts[1].contains("DirectMethodHandle")) {
String dmh = parts[2];
@ -465,27 +487,27 @@ class GenerateJLIClassesHelper {
/**
* Returns a {@code byte[]} representation of a class implementing
* the invoker forms for the set of supplied {@code invokerMethodTypes}
* and {@code callSiteMethodTypes}.
* the invoker forms for the set of supplied {@code linkerMethodTypes}
* {@code invokerMethodTypes}, and {@code callSiteMethodTypes}.
*/
static byte[] generateInvokersHolderClassBytes(String className,
MethodType[] invokerMethodTypes, MethodType[] callSiteMethodTypes) {
MethodType[] linkerMethodTypes, MethodType[] invokerMethodTypes,
MethodType[] callSiteMethodTypes) {
HashSet<MethodType> dedupSet = new HashSet<>();
ArrayList<LambdaForm> forms = new ArrayList<>();
ArrayList<String> names = new ArrayList<>();
int[] types = {
MethodTypeForm.LF_EX_LINKER,
int[] invokerTypes = {
MethodTypeForm.LF_EX_INVOKER,
MethodTypeForm.LF_GEN_LINKER,
MethodTypeForm.LF_GEN_INVOKER
MethodTypeForm.LF_GEN_INVOKER,
};
for (int i = 0; i < invokerMethodTypes.length; i++) {
for (MethodType methodType : invokerMethodTypes) {
// generate methods representing invokers of the specified type
if (dedupSet.add(invokerMethodTypes[i])) {
for (int type : types) {
LambdaForm invokerForm = Invokers.invokeHandleForm(invokerMethodTypes[i],
if (dedupSet.add(methodType)) {
for (int type : invokerTypes) {
LambdaForm invokerForm = Invokers.invokeHandleForm(methodType,
/*customized*/false, type);
forms.add(invokerForm);
names.add(invokerForm.kind.defaultLambdaName);
@ -493,6 +515,24 @@ class GenerateJLIClassesHelper {
}
}
int[] linkerTypes = {
MethodTypeForm.LF_EX_LINKER,
MethodTypeForm.LF_GEN_LINKER,
};
dedupSet = new HashSet<>();
for (MethodType methodType : linkerMethodTypes) {
// generate methods representing linkers of the specified type
if (dedupSet.add(methodType)) {
for (int type : linkerTypes) {
LambdaForm linkerForm = Invokers.invokeHandleForm(methodType,
/*customized*/false, type);
forms.add(linkerForm);
names.add(linkerForm.kind.defaultLambdaName);
}
}
}
dedupSet = new HashSet<>();
for (int i = 0; i < callSiteMethodTypes.length; i++) {
// generate methods representing invokers of the specified type

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 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
@ -26,6 +26,9 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
/**
* This is launched from TestLambdaInvokers.
*/
public class CDSLambdaInvoker {
public static void main(String args[]) throws Throwable {
// The following calls trigger the generation of new Species classes
@ -44,6 +47,10 @@ public class CDSLambdaInvoker {
boolean.class, Object.class, long.class, double.class);
MethodHandle mh = lookup.findStatic(CDSLambdaInvoker.class, "callme", mt);
mh.invokeExact(4.0f, 5.0, 6, true, (Object)args, 7L, 8.0);
mh = MethodHandles.dropArguments(MethodHandles.zero(Object.class), 0, Object.class, int.class);
MethodHandle inv = MethodHandles.invoker(mh.type());
invoke(inv, mh, args, 3);
}
private static Object invoke(MethodHandle mh, Object ... args) throws Throwable {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 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
@ -26,7 +26,7 @@
* @test
* @key randomness
* @summary test archive lambda invoker species type in dynamic dump
* @bug 8280767
* @bug 8280767 8327499
* @requires vm.cds
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive
* @compile CDSLambdaInvoker.java
@ -59,6 +59,7 @@ public class TestLambdaInvokers extends DynamicArchiveTestBase {
"-Xlog:cds",
"-Xlog:cds+dynamic=debug",
"-Xlog:class+load",
"-Djava.lang.invoke.MethodHandle.TRACE_RESOLVE=true",
"-cp",
jarFile,
mainClass)
@ -69,6 +70,10 @@ public class TestLambdaInvokers extends DynamicArchiveTestBase {
// java.lang.invoke.BoundMethodHandle$Species_JL is generated from CDSLambdaInvoker and
// stored in the dynamic archive
output.shouldContain("java.lang.invoke.BoundMethodHandle$Species_JL source: shared objects file (top)");
// java.lang.invoke.Invokers$Holder has invoker(Object,Object,Object,int)Object available
// from the archives
output.shouldContain("[LF_RESOLVE] java.lang.invoke.Invokers$Holder invoker L3I_L (success)");
});
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 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
@ -22,6 +22,8 @@
*/
import java.io.IOException;
import java.lang.classfile.ClassFile;
import java.lang.constant.MethodTypeDesc;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
@ -29,6 +31,7 @@ import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.testng.Assert;
import tests.Helper;
import tests.JImageGenerator;
import tests.JImageValidator;
@ -37,9 +40,12 @@ import tests.Result;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
/*
import static java.lang.constant.ConstantDescs.CD_Object;
import static java.lang.constant.ConstantDescs.CD_int;
/*
* @test
* @bug 8252919
* @bug 8252919 8327499
* @library ../../lib
* @summary Test --generate-jli-classes plugin
* @enablePreview
@ -122,6 +128,47 @@ public class GenerateJLIClassesPluginTest {
validateHolderClasses(image);
}
@Test
public static void testInvokers() throws IOException {
var fileString = "[LF_RESOLVE] java.lang.invoke.Invokers$Holder invoker L3I_L (fail)";
Path invokersTrace = Files.createTempFile("invokers", "trace");
Files.writeString(invokersTrace, fileString, Charset.defaultCharset());
Result result = JImageGenerator.getJLinkTask()
.modulePath(helper.defaultModulePath())
.output(helper.createNewImageDir("jli-invokers"))
.option("--generate-jli-classes=@" + invokersTrace.toString())
.addMods("java.base")
.call();
var image = result.assertSuccess();
var targetMtd = MethodTypeDesc.of(CD_Object, CD_Object, CD_Object, CD_Object, CD_int);
validateHolderClasses(image);
JImageValidator.validate(image.resolve("lib").resolve("modules"),
List.of(), List.of(), bytes -> {
var cf = ClassFile.of().parse(bytes);
if (!cf.thisClass().name().equalsString("java/lang/invoke/Invokers$Holder")) {
return;
}
boolean found = false;
for (var m : cf.methods()) {
// LambdaForm.Kind
if (m.methodName().equalsString("invoker") && m.methodTypeSymbol().equals(targetMtd)) {
found = true;
break;
}
}
if (!found) {
var methodsInfo = cf.methods().stream()
.map(m -> m.methodName() + m.methodTypeSymbol().displayDescriptor())
.collect(Collectors.joining("\n"));
Assert.fail("Missing invoker L3I_L in java.lang.invoke.Invokers$Holder, found:\n" + methodsInfo);
}
});
}
private static void validateHolderClasses(Path image) throws IOException {
JImageValidator.validate(image.resolve("lib").resolve("modules"),
List.of("/java.base/java/lang/invoke/DirectMethodHandle$Holder.class",

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -32,6 +32,7 @@ import java.util.List;
import java.util.Properties;
import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassHierarchyResolver;
import java.util.function.Consumer;
import jdk.internal.jimage.BasicImageReader;
import jdk.internal.jimage.ImageLocation;
@ -172,6 +173,11 @@ public class JImageValidator {
public static void validate(Path jimage, List<String> expectedLocations,
List<String> unexpectedPaths) throws IOException {
validate(jimage, expectedLocations, unexpectedPaths, _ -> {});
}
public static void validate(Path jimage, List<String> expectedLocations,
List<String> unexpectedPaths, Consumer<byte[]> classChecker) throws IOException {
BasicImageReader reader = BasicImageReader.open(jimage);
// Validate expected locations
List<String> seenLocations = new ArrayList<>();
@ -195,6 +201,7 @@ public class JImageValidator {
throw new IOException("NULL RESOURCE " + s);
}
readClass(r);
classChecker.accept(r);
} catch (IOException ex) {
System.err.println(s + " ERROR " + ex);
throw ex;
@ -222,7 +229,7 @@ public class JImageValidator {
return moduleExecutionTime;
}
public static void readClass(byte[] clazz) throws IOException{
public static void readClass(byte[] clazz) throws IOException {
var errors = ClassFile.of(
//resolution of all classes as interfaces cancels assignability verification
ClassFile.ClassHierarchyResolverOption.of(cls -> ClassHierarchyResolver.ClassHierarchyInfo.ofInterface()))