mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 15:24:43 +02:00
8247536: Support for pre-generated java.lang.invoke classes in CDS static archive
Reviewed-by: iklam, mchung
This commit is contained in:
parent
7ec9c8eac7
commit
e4469d2c8c
22 changed files with 537 additions and 20 deletions
|
@ -25,6 +25,7 @@
|
|||
|
||||
package java.lang.invoke;
|
||||
|
||||
import jdk.internal.misc.CDS;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
@ -32,6 +33,7 @@ import sun.invoke.util.Wrapper;
|
|||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
@ -58,12 +60,18 @@ class GenerateJLIClassesHelper {
|
|||
shortenSignature(basicTypeSignature(type)) +
|
||||
(resolvedMember != null ? " (success)" : " (fail)"));
|
||||
}
|
||||
if (CDS.isDumpingClassList()) {
|
||||
CDS.traceLambdaFormInvoker(LF_RESOLVE, holder.getName(), name, shortenSignature(basicTypeSignature(type)));
|
||||
}
|
||||
}
|
||||
|
||||
static void traceSpeciesType(String cn, Class<?> salvage) {
|
||||
if (TRACE_RESOLVE) {
|
||||
System.out.println(SPECIES_RESOLVE + " " + cn + (salvage != null ? " (salvaged)" : " (generated)"));
|
||||
}
|
||||
if (CDS.isDumpingClassList()) {
|
||||
CDS.traceSpeciesType(SPECIES_RESOLVE, cn);
|
||||
}
|
||||
}
|
||||
|
||||
// Map from DirectMethodHandle method type name to index to LambdForms
|
||||
|
@ -310,13 +318,14 @@ class GenerateJLIClassesHelper {
|
|||
* jlink phase.
|
||||
*/
|
||||
static Map<String, byte[]> generateHolderClasses(Stream<String> traces) {
|
||||
Objects.requireNonNull(traces);
|
||||
HolderClassBuilder builder = new HolderClassBuilder();
|
||||
traces.map(line -> line.split(" "))
|
||||
.forEach(parts -> {
|
||||
switch (parts[0]) {
|
||||
case SPECIES_RESOLVE:
|
||||
// Allow for new types of species data classes being resolved here
|
||||
assert parts.length == 3;
|
||||
assert parts.length >= 2;
|
||||
if (parts[1].startsWith(BMH_SPECIES_PREFIX)) {
|
||||
String species = parts[1].substring(BMH_SPECIES_PREFIX.length());
|
||||
if (!"L".equals(species)) {
|
||||
|
|
|
@ -25,7 +25,29 @@
|
|||
|
||||
package jdk.internal.misc;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.access.JavaLangInvokeAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
|
||||
public class CDS {
|
||||
private static final boolean isDumpingClassList;
|
||||
static {
|
||||
isDumpingClassList = isDumpingClassList0();
|
||||
}
|
||||
|
||||
/**
|
||||
* indicator for dumping class list.
|
||||
*/
|
||||
public static boolean isDumpingClassList() {
|
||||
return isDumpingClassList;
|
||||
}
|
||||
private static native boolean isDumpingClassList0();
|
||||
private static native void logLambdaFormInvoker(String line);
|
||||
|
||||
/**
|
||||
* Initialize archived static fields in the given Class using archived
|
||||
* values from CDS dump time. Also initialize the classes of objects in
|
||||
|
@ -59,4 +81,110 @@ public class CDS {
|
|||
* Check if sharing is enabled via the UseSharedSpaces flag.
|
||||
*/
|
||||
public static native boolean isSharingEnabled();
|
||||
|
||||
/**
|
||||
* log lambda form invoker holder, name and method type
|
||||
*/
|
||||
public static void traceLambdaFormInvoker(String prefix, String holder, String name, String type) {
|
||||
if (isDumpingClassList) {
|
||||
logLambdaFormInvoker(prefix + " " + holder + " " + name + " " + type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* log species
|
||||
*/
|
||||
public static void traceSpeciesType(String prefix, String cn) {
|
||||
if (isDumpingClassList) {
|
||||
logLambdaFormInvoker(prefix + " " + cn);
|
||||
}
|
||||
}
|
||||
|
||||
static final String DIRECT_HOLDER_CLASS_NAME = "java.lang.invoke.DirectMethodHandle$Holder";
|
||||
static final String DELEGATING_HOLDER_CLASS_NAME = "java.lang.invoke.DelegatingMethodHandle$Holder";
|
||||
static final String BASIC_FORMS_HOLDER_CLASS_NAME = "java.lang.invoke.LambdaForm$Holder";
|
||||
static final String INVOKERS_HOLDER_CLASS_NAME = "java.lang.invoke.Invokers$Holder";
|
||||
|
||||
private static boolean isValidHolderName(String name) {
|
||||
return name.equals(DIRECT_HOLDER_CLASS_NAME) ||
|
||||
name.equals(DELEGATING_HOLDER_CLASS_NAME) ||
|
||||
name.equals(BASIC_FORMS_HOLDER_CLASS_NAME) ||
|
||||
name.equals(INVOKERS_HOLDER_CLASS_NAME);
|
||||
}
|
||||
|
||||
private static boolean isBasicTypeChar(char c) {
|
||||
return "LIJFDV".indexOf(c) >= 0;
|
||||
}
|
||||
|
||||
private static boolean isValidMethodType(String type) {
|
||||
String[] typeParts = type.split("_");
|
||||
// check return type (second part)
|
||||
if (typeParts.length != 2 || typeParts[1].length() != 1
|
||||
|| !isBasicTypeChar(typeParts[1].charAt(0))) {
|
||||
return false;
|
||||
}
|
||||
// first part
|
||||
if (!isBasicTypeChar(typeParts[0].charAt(0))) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 1; i < typeParts[0].length(); i++) {
|
||||
char c = typeParts[0].charAt(i);
|
||||
if (!isBasicTypeChar(c)) {
|
||||
if (!(c >= '0' && c <= '9')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Throw exception on invalid input
|
||||
private static void validateInputLines(String[] lines) {
|
||||
for (String s: lines) {
|
||||
// There might be a trailing '\f' for line in ExtraClassListFile, do trim first.
|
||||
String line = s.trim();
|
||||
if (!line.startsWith("[LF_RESOLVE]") && !line.startsWith("[SPECIES_RESOLVE]")) {
|
||||
throw new IllegalArgumentException("Wrong prefix: " + line);
|
||||
}
|
||||
|
||||
String[] parts = line.split(" ");
|
||||
boolean isLF = line.startsWith("[LF_RESOLVE]");
|
||||
|
||||
if (isLF) {
|
||||
if (parts.length != 4) {
|
||||
throw new IllegalArgumentException("Incorrect number of items in the line: " + parts.length);
|
||||
}
|
||||
if (!isValidHolderName(parts[1])) {
|
||||
throw new IllegalArgumentException("Invalid holder class name: " + parts[1]);
|
||||
}
|
||||
if (!isValidMethodType(parts[3])) {
|
||||
throw new IllegalArgumentException("Invalid method type: " + parts[3]);
|
||||
}
|
||||
} else {
|
||||
if (parts.length != 2) {
|
||||
throw new IllegalArgumentException("Incorrect number of items in the line: " + parts.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* called from vm to generate MethodHandle holder classes
|
||||
* @return {@code Object[]} if holder classes can be generated.
|
||||
* @param lines in format of LF_RESOLVE or SPECIES_RESOLVE output
|
||||
*/
|
||||
private static Object[] generateLambdaFormHolderClasses(String[] lines) {
|
||||
Objects.requireNonNull(lines);
|
||||
validateInputLines(lines);
|
||||
Stream<String> lineStream = Arrays.stream(lines).map(String::trim);
|
||||
Map<String, byte[]> result = SharedSecrets.getJavaLangInvokeAccess().generateHolderClasses(lineStream);
|
||||
int size = result.size();
|
||||
Object[] retArray = new Object[size * 2];
|
||||
int index = 0;
|
||||
for (Map.Entry<String, byte[]> entry : result.entrySet()) {
|
||||
retArray[index++] = entry.getKey();
|
||||
retArray[index++] = entry.getValue();
|
||||
};
|
||||
return retArray;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue