8247536: Support for pre-generated java.lang.invoke classes in CDS static archive

Reviewed-by: iklam, mchung
This commit is contained in:
Yumin Qi 2020-10-10 02:06:52 +00:00
parent 7ec9c8eac7
commit e4469d2c8c
22 changed files with 537 additions and 20 deletions

View file

@ -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)) {

View file

@ -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;
}
}