-
Notifications
You must be signed in to change notification settings - Fork 5.8k
8304846: Provide a shared utility to dump generated classes defined via Lookup API #13182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
👋 Welcome back mchung! A progress list of the required criteria for merging this PR into |
Webrevs
|
src/java.base/share/classes/java/lang/invoke/ClassFileDumper.java
Outdated
Show resolved
Hide resolved
/label remove security |
@mlchung |
On a side note, can the class dumper be moved to jdk.internal so infrastructure like j.l.r.Proxy can use it too? |
Mailing list message from Brian Goetz on core-libs-dev: Since LMF goes through Lookup::defineHiddenClass, does this mean that Now that there is a shared implementation, perhaps we should migrate use -Djava.lang.invoke.MethodHandle.DUMP_LAMBDA_PROXY_CLASS_FILES=true and retire the separate dumpProxyClasses flag. On 3/24/2023 4:49 PM, Mandy Chung wrote:
-------------- next part -------------- |
No, only once. The old dumping code in LMF is removed.
+1. I think it's simpler to use a boolean property and dump to a known directory. As this is implementation-specific, I will rename the property name with |
My thought on the property names:
LMF and LambdaForms (and other method handle internal classes) are special cases to dump via a different system property respectively: Thoughts/comments? |
Is there any other place in JDK where classfile dumping is needed but isn't covered by this new utility? I know j.l.r.Proxy for one, but how it declares classes is incompatible with lookups. |
j.l.r.Proxy needs to define the class file with null protection domain; that's why it can't be converted to use It's okay to move |
private static String encodeForFilename(String className) { | ||
final int len = className.length(); | ||
StringBuilder sb = new StringBuilder(len); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be fewer lines of adhoc conversions, the replacements are hex encoding of the characters, so the replacement array is not needed.
java.util.HexFormat should be used for the hex encoding. The formatHex
methods append to a StringBuilder.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This old code was from ProxyClassesDumper. Yes it can be converted to use HexFormat.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @ASISBusiness, thanks for making a comment in an OpenJDK project!
All comments and discussions in the OpenJDK Community must be made available under the OpenJDK Terms of Use. If you already are an OpenJDK Author, Committer or Reviewer, please click here to open a new issue so that we can record that fact. Please Use "Add GitHub user ASISBusiness for the summary.
If you are not an OpenJDK Author, Committer or Reviewer, simply check the box below to accept the OpenJDK Terms of Use for your comments.
- I agree to the OpenJDK Terms of Use for all comments I make in a project in the OpenJDK GitHub organization.
Your comment will be automatically restored once you have accepted the OpenJDK Terms of Use.
} | ||
} | ||
private static final HashMap<String,Integer> DUMP_CLASS_FILES_COUNTERS = | ||
dumper().isEnabled() ? new HashMap<>(): null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dumper().isEnabled() ? new HashMap<>(): null; | |
dumper().isEnabled() ? new HashMap<>(): null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks mostly good.
public Path run() { | ||
if (!Files.exists(path)) { | ||
try { | ||
Files.createDirectory(path); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this could use createDirectories
instead, in case the path is nested. dumpClass
also uses that.
if (dumper.isEnabled() && !path.equals(dumper.dumpPath())) { | ||
throw new IllegalArgumentException("mismatched dump path for " + key); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see how this exception case could ever occur, given that dumper.dumpPath
is directly coming from dir
, and validateDumpDir
doesn't return a modified path either.
Can this check be removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is to check if two callers get the ClassFileDumper
of the same key but different path.
ClassFileDumper.getInstance("jdk.foo.dump", "DUMP_DIR");
ClassFileDumper.getInstance("jdk.foo.dump", "DUMP_NEW_DIR"); <--- IAE
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I see.
public Path pathname(String internalName) { | ||
return dumpDir.resolve(encodeForFilename(internalName) + ".class"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be private
I think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's public in case the caller who invokes ClassDefiner::defineClass
wants to print the path to which the classfile is written.
@@ -89,17 +85,19 @@ | |||
private static final AtomicInteger counter = new AtomicInteger(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This counter could be removed now, it looks like.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for catching it. will fix.
assert ((classFlags & HIDDEN_CLASS) == 0); | ||
} | ||
} else { | ||
name += ".failed-" + dumper.incrementAndGetCounter(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it makes more sense to move the counter to ClassDefiner
. It is not used by ClassFileDumper
itself.
(make it static
if it's possible to have multiple definer instances with the same class name)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see your point. If it were a static counter for all dumpers, multiple.failed-xxx
dumped by a single dumper may not be in sequence if other dumpers have .failed-xxx
class files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the counter has to be per dumper, maybe it makes sense to push the logic that derives the file name into ClassFileDumper too. e.g. have a dumpClass(name, Class<?>, bytes)
and dumpFailed(name, bytes)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's a good idea. Updated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Thanks.
@mlchung This change now passes all automated pre-integration checks. ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details. After integration, the commit message for the final commit will be:
You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been 113 new commits pushed to the
As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details. ➡️ To integrate this PR with the above commit message to the |
return AccessController.doPrivileged(new PrivilegedAction<>() { | ||
@Override | ||
public Path run() { | ||
if (!Files.exists(path)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not think this is necessary, Files.createDirectories() already only create directories if they do not exist.
Moreover, someone can change the state of the files in between the two calls (Files.exists and Files.createDirectories) given that those two calls are not atomic from the filesystem POV.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's right. updated
}); | ||
} | ||
|
||
private static HexFormat HEX = HexFormat.of().withUpperCase(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'final' ?
char c = className.charAt(i); | ||
// control characters | ||
if (c <= 31 || BAD_CHARS.contains(c)) { | ||
sb.append("%"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sb.append('%') // using a character instead of a String
/integrate |
Going to push as commit dd59471.
Your commit was automatically rebased without conflicts. |
@@ -570,22 +566,9 @@ S loadSpecies(S speciesData) { | |||
@SuppressWarnings("removal") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This @SuppressWarnings
is no longer needed:
@SuppressWarnings("removal") |
This implements a shared utility to dump generated classes defined as normal/hidden classes via
Lookup
API. This replaces the implementation inLambdaMetaFactory
and method handle implementation that dumps the hidden class bytes on disk for debugging.For classes defined via
Lookup::defineClass
,Lookup::defineHiddenClass
andLookup::defineHiddenClassWithClassData
, by default they will be dumped to the path specified in-Djava.lang.invoke.Lookup.dumpClasses=<dumpDir>
The hidden classes generated for lambdas,
LambdaForms
and method handle implementation use non-default dumper so that they can be controlled via a separate system property and path as in the current implementation.To dump lambda proxy classes, set this system property:
-Djdk.internal.lambda.dumpProxyClasses=
To dump LambdaForms and method handle implementation, set this system property:
-Djava.lang.invoke.MethodHandle.DUMP_CLASS_FILES=true
P.S.
ProxyClassesDumper
is renamed toClassFileDumper
but for some reason, it's not shown as rename.Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/13182/head:pull/13182
$ git checkout pull/13182
Update a local copy of the PR:
$ git checkout pull/13182
$ git pull https://git.openjdk.org/jdk.git pull/13182/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 13182
View PR using the GUI difftool:
$ git pr show -t 13182
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/13182.diff
Webrev
Link to Webrev Comment