Skip to content

8378908: [lworld] Serializations warnings for value classes that have private serialization methods#2187

Open
vicente-romero-oracle wants to merge 15 commits intoopenjdk:lworldfrom
vicente-romero-oracle:JDK-8378908
Open

8378908: [lworld] Serializations warnings for value classes that have private serialization methods#2187
vicente-romero-oracle wants to merge 15 commits intoopenjdk:lworldfrom
vicente-romero-oracle:JDK-8378908

Conversation

@vicente-romero-oracle
Copy link
Copy Markdown
Contributor

@vicente-romero-oracle vicente-romero-oracle commented Mar 2, 2026

This PR is adding a new serialization warning for the case when a value class declares one of the following serialization related methods:

  • readObject
  • writeObject
  • readObjectNoData

They have no effect if declared in value classes, fact that should be notified to the user,

TIA


Progress

  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue
  • Change must be properly reviewed (1 review required, with at least 1 Committer)

Issue

  • JDK-8378908: [lworld] Serializations warnings for value classes that have private serialization methods (Bug - P4)

Reviewers

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/valhalla.git pull/2187/head:pull/2187
$ git checkout pull/2187

Update a local copy of the PR:
$ git checkout pull/2187
$ git pull https://git.openjdk.org/valhalla.git pull/2187/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 2187

View PR using the GUI difftool:
$ git pr show -t 2187

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/valhalla/pull/2187.diff

Using Webrev

Link to Webrev Comment

@vicente-romero-oracle vicente-romero-oracle marked this pull request as draft March 2, 2026 20:58
@vicente-romero-oracle vicente-romero-oracle marked this pull request as ready for review March 2, 2026 21:47
case "readObject" -> checkReadObject(tree,e, method);
case "readObjectNoData" -> checkReadObjectNoData(tree, e, method);
case "readResolve" -> checkReadResolve(tree, e, method);
case "writeObject" -> isSerialMethodCorrect[0] = hasAppropriateWriteObject(tree, e, method);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some changes in this method and in the methods invoked here were not strictly necessary but were done for the sake of uniformity

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we convert this to a switch expression, like:

isSerialMethodCorrect[0] = switch (name) {
    case "writeObject"      -> hasAppropriateWriteObject(tree, e, method);

and so on.

@bridgekeeper
Copy link
Copy Markdown

bridgekeeper bot commented Mar 3, 2026

👋 Welcome back vromero! A progress list of the required criteria for merging this PR into lworld will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link
Copy Markdown

openjdk bot commented Mar 3, 2026

@vicente-romero-oracle 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:

8378908: [lworld] Serializations warnings for value classes that have private serialization methods

Reviewed-by: liach

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 no new commits pushed to the lworld branch. If another commit should be pushed before you perform the /integrate command, your PR will be automatically rebased. If you prefer to avoid any potential automatic rebasing, please check the documentation for the /integrate command for further details.

➡️ To integrate this PR with the above commit message to the lworld branch, type /integrate in a new comment.

@openjdk openjdk bot added the rfr Pull request is ready for review label Mar 3, 2026
@mlbridge
Copy link
Copy Markdown

mlbridge bot commented Mar 3, 2026

case "readObject" -> checkReadObject(tree,e, method);
case "readObjectNoData" -> checkReadObjectNoData(tree, e, method);
case "readResolve" -> checkReadResolve(tree, e, method);
case "writeObject" -> isSerialMethodCorrect[0] = hasAppropriateWriteObject(tree, e, method);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we convert this to a switch expression, like:

isSerialMethodCorrect[0] = switch (name) {
    case "writeObject"      -> hasAppropriateWriteObject(tree, e, method);

and so on.

vicente-romero-oracle and others added 2 commits March 4, 2026 06:52
…java

Co-authored-by: Chen Liang <liach@openjdk.org>
…java

Co-authored-by: Chen Liang <liach@openjdk.org>
* @throws InvalidObjectException always
*/
@java.io.Serial
@SuppressWarnings("serial")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other uses of @SuppressSerial in the JDK tend to have a comment explaining why the warning is being suppressed.

// fields
final boolean[] hasWriteReplace = {false};
final Map<String, Symbol> declaredSerialMethodNames = new HashMap<>();
final boolean[] isSerialMethodCorrect = new boolean[] { false };
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is a one-element boolean array being used as opposed to a (non-final) boolean?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because this is passed to a lambda expression, which requires final local variables.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable is no longer used outside of runUnderLint, we can convert it to a local at the switch where it is assigned.

LintWarnings.SerializableValueClassWithoutWriteReplace2);
}
}
if (c.isValueClass()) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a short comment here explaining the rules for serializable value classes would be helpful.


// Check for missing serialVersionUID; check *not* done
// for enums or records.
VarSymbol svuidSym = null;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do value classes need any distinct serialVersionUID handling?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure I get your question. Why should they?

Copy link
Copy Markdown
Collaborator

@RogerRiggs RogerRiggs Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @RogerRiggs, 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 RogerRiggs" 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.

Your comment will be automatically restored once you have accepted the OpenJDK Terms of Use.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure I get your question. Why should they?

I don't know if they do; that's why I was asking :-)

I think my concerns would be addressed by a "here's how serial value classes work."

Copy link
Copy Markdown
Contributor Author

@vicente-romero-oracle vicente-romero-oracle Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, thanks @RogerRiggs for your answer. I didn't know that specific about migrated value classes. I will add a comment

Copy link
Copy Markdown
Collaborator

@RogerRiggs RogerRiggs Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @RogerRiggs, 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 RogerRiggs" 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.

Your comment will be automatically restored once you have accepted the OpenJDK Terms of Use.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do value classes need any distinct serialVersionUID handling?

Value classes have the same requirements (non-requirements) as identity classes.
They are used to confirm the match between the class and the serialized stream contents.
Migrated value classes especially need the value class and identity class to have the same SVUID.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, thanks @RogerRiggs for your answer. I didn't know that specific about migrated value classes. I will add a comment

Formally, use of the proxy pattern is recommended for value classes. Where-in, the writeReplace method is invoked to provide the proxy object to be serialized. A record is recommended since it has a stable nominative serialized form. When the proxy object is deserialized, its readReplace is invoked to construct the object.
The Valhalla ObjectOutputStream and ObjectInputStream describe the process. (Maybe more words are needed).

For migrated java.base classes there is a private protocol implemented in ObjectInputStream to use a constructor of each class to create the value objects from the field values extracted from the serialized form.
The spec in this area is weak and vague, since it is not intended for general use. More work is needed in this area of the spec.

@jddarcy
Copy link
Copy Markdown
Member

jddarcy commented Mar 12, 2026

@vicente-romero-oracle, updates generally looks good; I can given a final re-review after JavaOne.

Copy link
Copy Markdown
Member

@liach liach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks all good otherwise.

// fields
final boolean[] hasWriteReplace = {false};
final Map<String, Symbol> declaredSerialMethodNames = new HashMap<>();
final boolean[] isSerialMethodCorrect = new boolean[] { false };
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable is no longer used outside of runUnderLint, we can convert it to a local at the switch where it is assigned.

vicente-romero-oracle and others added 5 commits April 3, 2026 10:29
Co-authored-by: Chen Liang <liach@openjdk.org>
Co-authored-by: Chen Liang <liach@openjdk.org>
Co-authored-by: Chen Liang <liach@openjdk.org>
@openjdk
Copy link
Copy Markdown

openjdk bot commented Apr 3, 2026

⚠️ @vicente-romero-oracle This pull request contains merges that bring in commits not present in the target repository. Since this is not a "merge style" pull request, these changes will be squashed when this pull request in integrated. If this is your intention, then please ignore this message. If you want to preserve the commit structure, you must change the title of this pull request to Merge <project>:<branch> where <project> is the name of another project in the OpenJDK organization (for example Merge jdk:master).

Copy link
Copy Markdown
Member

@liach liach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to go!

@openjdk openjdk bot added the ready Pull request is ready to be integrated label Apr 3, 2026
* @throws InvalidObjectException always
*/
@java.io.Serial
@SuppressWarnings("serial") // this method is not invoked for value classes
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps as future work, it would be technically possible for the serial lint checks to look inside the body of a method like this, notice the method always throws an exception, and elide the warning in that case. (It is a little unfortunate to "penalize" the java.time method for being robust in their serial handing by requiring new suppress warnings.)

Various known enhancements to the serial checking envision that deep inspection of lint checking, as in JDK-7019074.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @jddarcy, I have created: https://bugs.openjdk.org/browse/JDK-8381637 to address this issue. Are you OK with the PR?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, would it be easy for Javac to determine a method alwys finishes exceptionally?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready Pull request is ready to be integrated rfr Pull request is ready for review

Development

Successfully merging this pull request may close these issues.

4 participants