8304846: Provide a shared utility to dump generated classes defined via Lookup API

Reviewed-by: jvernee
This commit is contained in:
Mandy Chung 2023-04-04 18:07:02 +00:00
parent 2ee4245105
commit dd59471798
11 changed files with 440 additions and 393 deletions

View file

@ -27,21 +27,16 @@ package java.lang.invoke;
import jdk.internal.misc.CDS;
import jdk.internal.org.objectweb.asm.*;
import jdk.internal.util.ClassFileDumper;
import sun.invoke.util.BytecodeDescriptor;
import sun.invoke.util.VerifyAccess;
import sun.security.action.GetPropertyAction;
import sun.security.action.GetBooleanAction;
import java.io.FilePermission;
import java.io.Serializable;
import java.lang.constant.ConstantDescs;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.nio.file.Path;
import java.util.LinkedHashSet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.PropertyPermission;
import java.util.Set;
import static java.lang.invoke.MethodHandleStatics.CLASSFILE_VERSION;
@ -85,11 +80,8 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
private static final String[] EMPTY_STRING_ARRAY = new String[0];
// Used to ensure that dumped class files for failed definitions have a unique class name
private static final AtomicInteger counter = new AtomicInteger();
// For dumping generated classes to disk, for debugging purposes
private static final ProxyClassesDumper dumper;
private static final ClassFileDumper lambdaProxyClassFileDumper;
private static final boolean disableEagerInitialization;
@ -97,9 +89,11 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
private static final ConstantDynamic implMethodCondy;
static {
final String dumpProxyClassesKey = "jdk.internal.lambda.dumpProxyClasses";
String dumpPath = GetPropertyAction.privilegedGetProperty(dumpProxyClassesKey);
dumper = (null == dumpPath) ? null : ProxyClassesDumper.getInstance(dumpPath);
// To dump the lambda proxy classes, set this system property:
// -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles
// or -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles=true
final String dumpProxyClassesKey = "jdk.invoke.LambdaMetafactory.dumpProxyClassFiles";
lambdaProxyClassFileDumper = ClassFileDumper.getInstance(dumpProxyClassesKey, Path.of("DUMP_LAMBDA_PROXY_CLASS_FILES"));
final String disableEagerInitializationKey = "jdk.internal.lambda.disableEagerInitialization";
disableEagerInitialization = GetBooleanAction.privilegedGetProperty(disableEagerInitializationKey);
@ -363,51 +357,15 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
final byte[] classBytes = cw.toByteArray();
try {
// this class is linked at the indy callsite; so define a hidden nestmate
Lookup lookup = null;
try {
if (useImplMethodHandle) {
lookup = caller.defineHiddenClassWithClassData(classBytes, implementation, !disableEagerInitialization,
NESTMATE, STRONG);
} else {
lookup = caller.defineHiddenClass(classBytes, !disableEagerInitialization, NESTMATE, STRONG);
}
return lookup.lookupClass();
} finally {
// If requested, dump out to a file for debugging purposes
if (dumper != null) {
String name;
if (lookup != null) {
String definedName = lookup.lookupClass().getName();
int suffixIdx = definedName.lastIndexOf('/');
assert suffixIdx != -1;
name = lambdaClassName + '.' + definedName.substring(suffixIdx + 1);
} else {
name = lambdaClassName + ".failed-" + counter.incrementAndGet();
}
doDump(name, classBytes);
}
}
} catch (IllegalAccessException e) {
throw new LambdaConversionException("Exception defining lambda proxy class", e);
var classdata = useImplMethodHandle? implementation : null;
return caller.makeHiddenClassDefiner(lambdaClassName, classBytes, Set.of(NESTMATE, STRONG), lambdaProxyClassFileDumper)
.defineClass(!disableEagerInitialization, classdata);
} catch (Throwable t) {
throw new InternalError(t);
}
}
@SuppressWarnings("removal")
private void doDump(final String className, final byte[] classBytes) {
AccessController.doPrivileged(new PrivilegedAction<>() {
@Override
public Void run() {
dumper.dumpClass(className, classBytes);
return null;
}
}, null,
new FilePermission("<<ALL FILES>>", "read, write"),
// createDirectories may need it
new PropertyPermission("user.dir", "read"));
}
/**
* Generate a static field and a static initializer that sets this field to an instance of the lambda
*/