8327499: MethodHandleStatics.traceLambdaForm includes methods that cannot be generated
Reviewed-by: redestad, iklam
This commit is contained in:
parent
5a8df4106a
commit
adaa509b6e
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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)");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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()))
|
||||
|
Loading…
x
Reference in New Issue
Block a user