Skip to content

Conversation

@alexmenkov
Copy link

@alexmenkov alexmenkov commented Dec 18, 2025

Updated implementation of ObjectReference.equals and ObjectReference.hashCode to comply the spec for value objects.
Added the test for value object ctor debugging, the test verifies the behaviour is expected.
There is an issue with instance filter, it till be fixed separately (it's not yet clear how it would be better to fix it)

testing: tier1..4, hs-tier5-svc


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-8373202: [lworld] ObjectReference.equals should follow == semantics for value objects (Bug - P3)

Reviewing

Using git

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

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

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 1834

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

Using diff file

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

Using Webrev

Link to Webrev Comment

@alexmenkov alexmenkov marked this pull request as draft December 18, 2025 20:00
@bridgekeeper
Copy link

bridgekeeper bot commented Dec 18, 2025

👋 Welcome back amenkov! 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

openjdk bot commented Dec 18, 2025

❗ This change is not yet ready to be integrated.
See the Progress checklist in the description for automated requirements.

@openjdk
Copy link

openjdk bot commented Jan 10, 2026

⚠️ @alexmenkov 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).

@alexmenkov alexmenkov changed the title Ctor debug 8373202: [lworld] ObjectReference.equals should follow == semantics for value objects Jan 12, 2026
@alexmenkov alexmenkov marked this pull request as ready for review January 12, 2026 21:49
@openjdk openjdk bot added the rfr Pull request is ready for review label Jan 12, 2026
@mlbridge
Copy link

mlbridge bot commented Jan 12, 2026

Webrevs

(Error VM_DEAD)
)
)
(Command IsSameObject=11
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you going to add "preview API" warnings to these two APIs?

Copy link
Author

Choose a reason for hiding this comment

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

I'm not sure it makes much sense.
They are quite generic, I think about adding them to mainline in jdk 27 (but don't use by ObjectReferenceImpl.equals/hashCode)

Copy link
Contributor

Choose a reason for hiding this comment

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

They might be generic, but they serve no useful purpose outside of valhalla support.

}

// Inline type is a concrete value class.
boolean isInlined() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this called isInlined()? Shouldn't it be isValueClass()? Do we want to expose it to users through ReferenceType? Should we also consider exposing a new isIdentity() API?

Copy link
Contributor

@sspitsyn sspitsyn Jan 14, 2026

Choose a reason for hiding this comment

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

Why is this called isInlined()? Shouldn't it be isValueClass()?

I had the same question. The isInlined() matches the obsolete approach. The isValueClass() looks better.

Do we want to expose it to users through ReferenceType?

If the JDI implementation needed it then there are some chances debuggers may need it as well. We have some time to decide. Preview allows to change the API specs in the future. I have a minor preference to expose it now. It is pretty simple and clear.

Should we also consider exposing a new isIdentity() API?

It would be consistent with other modifier bits.

Copy link
Author

Choose a reason for hiding this comment

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

isInlined name (and its implementation) is consistent with hotspot code.
This API if required by ObjectReferenceImpl to determine if the reference is a value object (so the class cannot be abstract or interface).
To me IsIdentity (I think isValue is better) should be in ClassType, I'm updating ClassTypeImpl, it can be added to ClassType interface later if debuggers really need it).

Copy link
Contributor

@plummercj plummercj Jan 15, 2026

Choose a reason for hiding this comment

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

isInlined name (and its implementation) is consistent with hotspot code.

But in the case of JDI we are talking about a potentially external API.

To me IsIdentity (I think isValue is better) should be in ClassType

We need to be careful of the use of "value" in JDI since we already have com.sun.jdi.Value, which peculates into all sorts of APIs like ObjectReference.getValue(Field) and BooleanValue. It doesn't help any that we also have Value.type().

Copy link
Author

Choose a reason for hiding this comment

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

ClassType laready has isEnum(), so isValue looks more consistent.
Maybe isValueClass? Anyway it's not too important now (while it's internal API)

return JDWP.ObjectReference.ObjectHashCode.process(vm, this).hashCode;
} catch (JDWPException exc) {
throw exc.toJDIException();
if (isValueObject() && vm.canUseIsSameObject()) {
Copy link
Contributor

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 logic can be correct for value objects if vm.canUseIsSameObject() returns false. We still have a value object in that case. Is using Long.hashCode(ref()) the right thing to do? Same thing applies to equals() above. I think if a JVM supports value objects then the debug agent has to support IsSameObject and HashCode. The version check should really only be in regard to whether or not value objects are supported in general by the JVM, because if they are they have to be supported by the debug agent.

Copy link
Author

Choose a reason for hiding this comment

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

We should execute jdwp command only when it's available (vm.canUseIsSameObject returns true) and the object is value object.
Note that isValueObject() may not work for old releases, so we need to check both conditions here or move version check to ClassTypeImpl.isValueClass

Copy link
Contributor

Choose a reason for hiding this comment

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

If the JVM supports value objects, the debug agent has to support IsSameObject and ObjectHashCode. I don't see how we can have a functional JDWP and JDI otherwise. The logic above is proving that. It allows for the impossible to support situation where isValueObject() can return true, yet IsSameObject is not supported, meaning we can't properly compute the hash code.

If IsValueObject returns true, the invariant should be that IsSameObject is supported.

(Error VM_DEAD)
)
)
(Command IsSameObject=11
Copy link
Contributor

Choose a reason for hiding this comment

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

They might be generic, but they serve no useful purpose outside of valhalla support.

return JDWP.ObjectReference.ObjectHashCode.process(vm, this).hashCode;
} catch (JDWPException exc) {
throw exc.toJDIException();
if (isValueObject() && vm.canUseIsSameObject()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If the JVM supports value objects, the debug agent has to support IsSameObject and ObjectHashCode. I don't see how we can have a functional JDWP and JDI otherwise. The logic above is proving that. It allows for the impossible to support situation where isValueObject() can return true, yet IsSameObject is not supported, meaning we can't properly compute the hash code.

If IsValueObject returns true, the invariant should be that IsSameObject is supported.

Comment on lines +70 to +84
The test reproduces scenarios when ObjectReference for value object is fetched during the object construction
and the object content is changed later. When "this" ObjectReference for value object is requested,
JDI returns existing reference if there are other references to the equal value object or create a new one otherwise.
The test debugs "new Value(3,6)" statement by setting breakpoints in Value class constructor at locations
when the object being constructed is (0,0), (3,0) and (3,6).
Test scenarios are defined by test arguments; TestScaffold passes them creating debuggee process (TargetApp class).
Debugsee initializes static fields with value objects (0,0), (3,0), (3,6) depending on the passed arguments.
Debugger gets ObjectReferences for the fields before testing.
Tested scenarios:
- no existing references;
- all 3 references exists;
- there are references for 1 and 3 breakpoints.
Copy link
Contributor

Choose a reason for hiding this comment

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

These comment lines should all start with a "*".

(Error INVALID_OBJECT)
(Error VM_DEAD)
)
)
Copy link
Contributor

Choose a reason for hiding this comment

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

As we discussed, "since" is needed and with possible preview comment (check what we had for Loom preview).

public boolean canUseIsSameObject() {
return versionInfo().jdwpMajor >= 27;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: We may want to generalize this with something like 'maySupportValueClasses()'.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not so sure this API is really even needed. I don't believe the current calls to it are valid.

} catch (JDWPException exc) {
throw exc.toJDIException();
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: The lines 164, 177 can be simplified a little bit with move of the renamed vm.canUseIsSameObject() check equivalent to the isValueClass().

Copy link
Contributor

Choose a reason for hiding this comment

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

If Valhalla is not supported on the target VM, there is no way for isValueClass() or isValueObject() to ever return true. If Valhalla is supported, they can return true, and we have to assume that JDWP and the debug agent support Valhalla also. We can't have Valhalla support in the JVM and not also have it in JDWP and the debug agent. The debugger/JDI will break if isValueObject() returns false for a value object.

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

Labels

rfr Pull request is ready for review

Development

Successfully merging this pull request may close these issues.

3 participants