Skip to content

Commit dd59471

Browse files
author
Mandy Chung
committed
8304846: Provide a shared utility to dump generated classes defined via Lookup API
Reviewed-by: jvernee
1 parent 2ee4245 commit dd59471

11 files changed

+440
-393
lines changed

src/java.base/share/classes/java/lang/invoke/ClassSpecializer.java

+4-21
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -25,7 +25,6 @@
2525

2626
package java.lang.invoke;
2727

28-
import jdk.internal.access.SharedSecrets;
2928
import jdk.internal.loader.BootLoader;
3029
import jdk.internal.org.objectweb.asm.ClassWriter;
3130
import jdk.internal.org.objectweb.asm.FieldVisitor;
@@ -36,9 +35,6 @@
3635
import java.lang.reflect.Constructor;
3736
import java.lang.reflect.Field;
3837
import java.lang.reflect.Modifier;
39-
import java.security.AccessController;
40-
import java.security.PrivilegedAction;
41-
import java.security.ProtectionDomain;
4238
import java.util.ArrayList;
4339
import java.util.Collections;
4440
import java.util.List;
@@ -570,22 +566,9 @@ S loadSpecies(S speciesData) {
570566
@SuppressWarnings("removal")
571567
Class<? extends T> generateConcreteSpeciesCode(String className, ClassSpecializer<T,K,S>.SpeciesData speciesData) {
572568
byte[] classFile = generateConcreteSpeciesCodeFile(className, speciesData);
573-
574-
// load class
575-
InvokerBytecodeGenerator.maybeDump(classBCName(className), classFile);
576-
ClassLoader cl = topClass.getClassLoader();
577-
ProtectionDomain pd = null;
578-
if (cl != null) {
579-
pd = AccessController.doPrivileged(
580-
new PrivilegedAction<>() {
581-
@Override
582-
public ProtectionDomain run() {
583-
return topClass().getProtectionDomain();
584-
}
585-
});
586-
}
587-
Class<?> speciesCode = SharedSecrets.getJavaLangAccess()
588-
.defineClass(cl, className, classFile, pd, "_ClassSpecializer_generateConcreteSpeciesCode");
569+
var lookup = new MethodHandles.Lookup(topClass);
570+
Class<?> speciesCode = lookup.makeClassDefiner(classBCName(className), classFile, dumper())
571+
.defineClass(false);
589572
return speciesCode.asSubclass(topClass());
590573
}
591574

src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java

+12-54
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,16 @@
2727

2828
import jdk.internal.misc.CDS;
2929
import jdk.internal.org.objectweb.asm.*;
30+
import jdk.internal.util.ClassFileDumper;
3031
import sun.invoke.util.BytecodeDescriptor;
3132
import sun.invoke.util.VerifyAccess;
32-
import sun.security.action.GetPropertyAction;
3333
import sun.security.action.GetBooleanAction;
3434

35-
import java.io.FilePermission;
3635
import java.io.Serializable;
3736
import java.lang.constant.ConstantDescs;
38-
import java.lang.invoke.MethodHandles.Lookup;
3937
import java.lang.reflect.Modifier;
40-
import java.security.AccessController;
41-
import java.security.PrivilegedAction;
38+
import java.nio.file.Path;
4239
import java.util.LinkedHashSet;
43-
import java.util.concurrent.atomic.AtomicInteger;
44-
import java.util.PropertyPermission;
4540
import java.util.Set;
4641

4742
import static java.lang.invoke.MethodHandleStatics.CLASSFILE_VERSION;
@@ -85,21 +80,20 @@
8580

8681
private static final String[] EMPTY_STRING_ARRAY = new String[0];
8782

88-
// Used to ensure that dumped class files for failed definitions have a unique class name
89-
private static final AtomicInteger counter = new AtomicInteger();
90-
9183
// For dumping generated classes to disk, for debugging purposes
92-
private static final ProxyClassesDumper dumper;
84+
private static final ClassFileDumper lambdaProxyClassFileDumper;
9385

9486
private static final boolean disableEagerInitialization;
9587

9688
// condy to load implMethod from class data
9789
private static final ConstantDynamic implMethodCondy;
9890

9991
static {
100-
final String dumpProxyClassesKey = "jdk.internal.lambda.dumpProxyClasses";
101-
String dumpPath = GetPropertyAction.privilegedGetProperty(dumpProxyClassesKey);
102-
dumper = (null == dumpPath) ? null : ProxyClassesDumper.getInstance(dumpPath);
92+
// To dump the lambda proxy classes, set this system property:
93+
// -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles
94+
// or -Djdk.invoke.LambdaMetafactory.dumpProxyClassFiles=true
95+
final String dumpProxyClassesKey = "jdk.invoke.LambdaMetafactory.dumpProxyClassFiles";
96+
lambdaProxyClassFileDumper = ClassFileDumper.getInstance(dumpProxyClassesKey, Path.of("DUMP_LAMBDA_PROXY_CLASS_FILES"));
10397

10498
final String disableEagerInitializationKey = "jdk.internal.lambda.disableEagerInitialization";
10599
disableEagerInitialization = GetBooleanAction.privilegedGetProperty(disableEagerInitializationKey);
@@ -363,51 +357,15 @@ else if (accidentallySerializable)
363357
final byte[] classBytes = cw.toByteArray();
364358
try {
365359
// this class is linked at the indy callsite; so define a hidden nestmate
366-
Lookup lookup = null;
367-
try {
368-
if (useImplMethodHandle) {
369-
lookup = caller.defineHiddenClassWithClassData(classBytes, implementation, !disableEagerInitialization,
370-
NESTMATE, STRONG);
371-
} else {
372-
lookup = caller.defineHiddenClass(classBytes, !disableEagerInitialization, NESTMATE, STRONG);
373-
}
374-
return lookup.lookupClass();
375-
} finally {
376-
// If requested, dump out to a file for debugging purposes
377-
if (dumper != null) {
378-
String name;
379-
if (lookup != null) {
380-
String definedName = lookup.lookupClass().getName();
381-
int suffixIdx = definedName.lastIndexOf('/');
382-
assert suffixIdx != -1;
383-
name = lambdaClassName + '.' + definedName.substring(suffixIdx + 1);
384-
} else {
385-
name = lambdaClassName + ".failed-" + counter.incrementAndGet();
386-
}
387-
doDump(name, classBytes);
388-
}
389-
}
390-
} catch (IllegalAccessException e) {
391-
throw new LambdaConversionException("Exception defining lambda proxy class", e);
360+
var classdata = useImplMethodHandle? implementation : null;
361+
return caller.makeHiddenClassDefiner(lambdaClassName, classBytes, Set.of(NESTMATE, STRONG), lambdaProxyClassFileDumper)
362+
.defineClass(!disableEagerInitialization, classdata);
363+
392364
} catch (Throwable t) {
393365
throw new InternalError(t);
394366
}
395367
}
396368

397-
@SuppressWarnings("removal")
398-
private void doDump(final String className, final byte[] classBytes) {
399-
AccessController.doPrivileged(new PrivilegedAction<>() {
400-
@Override
401-
public Void run() {
402-
dumper.dumpClass(className, classBytes);
403-
return null;
404-
}
405-
}, null,
406-
new FilePermission("<<ALL FILES>>", "read, write"),
407-
// createDirectories may need it
408-
new PropertyPermission("user.dir", "read"));
409-
}
410-
411369
/**
412370
* Generate a static field and a static initializer that sets this field to an instance of the lambda
413371
*/

src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java

+10-70
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -34,11 +34,7 @@
3434
import sun.invoke.util.VerifyAccess;
3535
import sun.invoke.util.VerifyType;
3636
import sun.invoke.util.Wrapper;
37-
import sun.reflect.misc.ReflectUtil;
3837

39-
import java.io.File;
40-
import java.io.FileOutputStream;
41-
import java.io.IOException;
4238
import java.lang.reflect.Modifier;
4339
import java.util.ArrayList;
4440
import java.util.Arrays;
@@ -125,7 +121,7 @@ private InvokerBytecodeGenerator(LambdaForm lambdaForm, int localsMapSize,
125121
name = invokerName.substring(0, p);
126122
invokerName = invokerName.substring(p + 1);
127123
}
128-
if (DUMP_CLASS_FILES) {
124+
if (dumper().isEnabled()) {
129125
name = makeDumpableClassName(name);
130126
}
131127
this.name = name;
@@ -173,58 +169,8 @@ private InvokerBytecodeGenerator(String name, LambdaForm form, MethodType invoke
173169
}
174170

175171
/** instance counters for dumped classes */
176-
private static final HashMap<String,Integer> DUMP_CLASS_FILES_COUNTERS;
177-
/** debugging flag for saving generated class files */
178-
private static final File DUMP_CLASS_FILES_DIR;
179-
180-
static {
181-
if (DUMP_CLASS_FILES) {
182-
DUMP_CLASS_FILES_COUNTERS = new HashMap<>();
183-
try {
184-
File dumpDir = new File("DUMP_CLASS_FILES");
185-
if (!dumpDir.exists()) {
186-
dumpDir.mkdirs();
187-
}
188-
DUMP_CLASS_FILES_DIR = dumpDir;
189-
System.out.println("Dumping class files to "+DUMP_CLASS_FILES_DIR+"/...");
190-
} catch (Exception e) {
191-
throw newInternalError(e);
192-
}
193-
} else {
194-
DUMP_CLASS_FILES_COUNTERS = null;
195-
DUMP_CLASS_FILES_DIR = null;
196-
}
197-
}
198-
199-
private void maybeDump(final byte[] classFile) {
200-
if (DUMP_CLASS_FILES) {
201-
maybeDump(className, classFile);
202-
}
203-
}
204-
205-
// Also used from BoundMethodHandle
206-
@SuppressWarnings("removal")
207-
static void maybeDump(final String className, final byte[] classFile) {
208-
if (DUMP_CLASS_FILES) {
209-
java.security.AccessController.doPrivileged(
210-
new java.security.PrivilegedAction<>() {
211-
public Void run() {
212-
try {
213-
String dumpName = className.replace('.','/');
214-
File dumpFile = new File(DUMP_CLASS_FILES_DIR, dumpName+".class");
215-
System.out.println("dump: " + dumpFile);
216-
dumpFile.getParentFile().mkdirs();
217-
FileOutputStream file = new FileOutputStream(dumpFile);
218-
file.write(classFile);
219-
file.close();
220-
return null;
221-
} catch (IOException ex) {
222-
throw newInternalError(ex);
223-
}
224-
}
225-
});
226-
}
227-
}
172+
private static final HashMap<String,Integer> DUMP_CLASS_FILES_COUNTERS =
173+
dumper().isEnabled() ? new HashMap<>(): null;
228174

229175
private static String makeDumpableClassName(String className) {
230176
Integer ctr;
@@ -271,7 +217,7 @@ String classData(Object arg) {
271217

272218
// unique static variable name
273219
String name;
274-
if (DUMP_CLASS_FILES) {
220+
if (dumper().isEnabled()) {
275221
Class<?> c = arg.getClass();
276222
while (c.isArray()) {
277223
c = c.getComponentType();
@@ -299,7 +245,7 @@ private static String debugString(Object arg) {
299245
* Extract the MemberName of a newly-defined method.
300246
*/
301247
private MemberName loadMethod(byte[] classFile) {
302-
Class<?> invokerClass = LOOKUP.makeHiddenClassDefiner(className, classFile, Set.of())
248+
Class<?> invokerClass = LOOKUP.makeHiddenClassDefiner(className, classFile, Set.of(), dumper())
303249
.defineClass(true, classDataValues());
304250
return resolveInvokerMember(invokerClass, invokerName, invokerType);
305251
}
@@ -809,9 +755,7 @@ private byte[] generateCustomizedCodeBytes() {
809755
clinit(cw, className, classData);
810756
bogusMethod(lambdaForm);
811757

812-
final byte[] classFile = toByteArray();
813-
maybeDump(classFile);
814-
return classFile;
758+
return toByteArray();
815759
}
816760

817761
void setClassWriter(ClassWriter cw) {
@@ -1898,9 +1842,7 @@ private byte[] generateLambdaFormInterpreterEntryPointBytes() {
18981842
clinit(cw, className, classData);
18991843
bogusMethod(invokerType);
19001844

1901-
final byte[] classFile = cw.toByteArray();
1902-
maybeDump(classFile);
1903-
return classFile;
1845+
return cw.toByteArray();
19041846
}
19051847

19061848
/**
@@ -1967,17 +1909,15 @@ private byte[] generateNamedFunctionInvokerImpl(MethodTypeForm typeForm) {
19671909
clinit(cw, className, classData);
19681910
bogusMethod(dstType);
19691911

1970-
final byte[] classFile = cw.toByteArray();
1971-
maybeDump(classFile);
1972-
return classFile;
1912+
return cw.toByteArray();
19731913
}
19741914

19751915
/**
19761916
* Emit a bogus method that just loads some string constants. This is to get the constants into the constant pool
19771917
* for debugging purposes.
19781918
*/
19791919
private void bogusMethod(Object os) {
1980-
if (DUMP_CLASS_FILES) {
1920+
if (dumper().isEnabled()) {
19811921
mv = cw.visitMethod(Opcodes.ACC_STATIC, "dummy", "()V", null, null);
19821922
mv.visitLdcInsn(os.toString());
19831923
mv.visitInsn(Opcodes.POP);

src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java

+3-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2008, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -1101,8 +1101,9 @@ private static Class<?> makeInjectedInvoker(Class<?> targetClass) {
11011101
// use the original class name
11021102
name = name.replace('/', '_');
11031103
}
1104+
name = name.replace('.', '/');
11041105
Class<?> invokerClass = new Lookup(targetClass)
1105-
.makeHiddenClassDefiner(name, INJECTED_INVOKER_TEMPLATE, Set.of(NESTMATE))
1106+
.makeHiddenClassDefiner(name, INJECTED_INVOKER_TEMPLATE, Set.of(NESTMATE), dumper())
11061107
.defineClass(true, targetClass);
11071108
assert checkInjectedInvoker(targetClass, invokerClass);
11081109
return invokerClass;
@@ -1655,12 +1656,6 @@ public MethodHandle reflectiveInvoker(Class<?> caller) {
16551656
return BindCaller.reflectiveInvoker(caller);
16561657
}
16571658

1658-
@Override
1659-
public Lookup defineHiddenClassWithClassData(Lookup caller, String name, byte[] bytes, Object classData, boolean initialize) {
1660-
// skip name and access flags validation
1661-
return caller.makeHiddenClassDefiner(name, bytes, Set.of()).defineClassAsLookup(initialize, classData);
1662-
}
1663-
16641659
@Override
16651660
public Class<?>[] exceptionTypes(MethodHandle handle) {
16661661
return VarHandles.exceptionTypes(handle);

0 commit comments

Comments
 (0)