Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/java.se/share/data/jdwp/jdwp.spec
Original file line number Diff line number Diff line change
Expand Up @@ -1801,6 +1801,33 @@ JDWP "Java(tm) Debug Wire Protocol"
(Error VM_DEAD)
)
)
(Command IsSameObject=11
"Determines whether two objects refer to the same Java object."
(Out
(object object1 "The object ID")
(object object2 "The object ID")
)
(Reply
(boolean isSameObject "true if the objects refer to the same Java object; false otherwise")
)
(ErrorSet
(Error INVALID_OBJECT)
(Error VM_DEAD)
)
)
(Command ObjectHashCode=12
"Returns hash code for an object."
(Out
(object object "The object ID")
)
(Reply
(int hashCode "hash code value for the object")
)
(ErrorSet
(Error INVALID_OBJECT)
(Error VM_DEAD)
)
)
)

(CommandSet StringReference=10
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,16 @@ public boolean vmNotSuspended(VMAction action) {

public boolean equals(Object obj) {
if (obj instanceof ObjectReferenceImpl other) {
return (ref() == other.ref()) &&
super.equals(obj);
if (ref() == other.ref() && super.equals(obj)) {
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 wondering if the super.equals(obj) call is necessary. The way JDI is implemetned, there is a 1-to-1 relationship between the ObjectID and the ObjectReference. If the ObjectIDs match, then the ObjectRefernces instances should be one in the same. So either one of these checks should be sufficient. I don't think both are needed.

Copy link
Author

Choose a reason for hiding this comment

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

I agree that super.equals call is not necessary, just kept it as it was

return true;
}
// We can get equal value objects with different IDs.
// TODO: do it only for value objects.
try {
return JDWP.ObjectReference.IsSameObject.process(vm, this, other).isSameObject;
} catch (JDWPException exc) {
throw exc.toJDIException();
}
Comment on lines +155 to +159
Copy link
Contributor

@plummercj plummercj Dec 18, 2025

Choose a reason for hiding this comment

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

So the downside here is that when we don't have equality w.r.t. ObjectIDs, we do the heavyweight call over JDWP. A while back I did a scan of users of equals() in the JDI implementation and jdb, and it seemed it was always done on known subtypes (ClassLoaderReference, ThreadReference, ClassReference, etc). So I doubt in practice you would ever see two value types being compared, unless the debugger was doing it for some reason. So if you did limit this JDWP call to value types, probably it would be rare to non-existent that it would ever be called.

Copy link
Author

Choose a reason for hiding this comment

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

agreed. Do I understand correctly that I cannot modify existing command (jdwp protocol shoul be backward compatible?) to get classes and need a new one to check if the class is value class?

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 sure what your backward compatible question is. I think we need an API to determine if a ClassType is a value type or not. This could be expensive to maintain. Probably we need to only look it up when needed and can keep it cached with the ClassType. Another option is to only try to avoid the IsSameObject callback when we are not dealing with an ObjectReferenceImpl subclass. We can make a check something like obj.getClass() == ObjectReferenceImpl.class. When they are not equal, then we are dealing with a subclass that we know can't be a value type.

} else {
return false;
}
Expand Down
61 changes: 60 additions & 1 deletion src/jdk.jdwp.agent/share/native/libjdwp/ObjectReferenceImpl.c
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,63 @@ referringObjects(PacketInputStream *in, PacketOutputStream *out)
return JNI_TRUE;
}

static jboolean
isSameObjectImpl(PacketInputStream *in, PacketOutputStream *out)
{
jlong id1;
jlong id2;
jobject ref1;
jobject ref2;
JNIEnv *env;

env = getEnv();
id1 = inStream_readObjectID(in);
id2 = inStream_readObjectID(in);
if (inStream_error(in)) {
return JNI_TRUE;
}

if (id1 == NULL_OBJECT_ID || id2 == NULL_OBJECT_ID) {
outStream_setError(out, JDWP_ERROR(INVALID_OBJECT));
return JNI_TRUE;
}

ref1 = commonRef_idToRef(env, id1);
ref2 = commonRef_idToRef(env, id2);
(void)outStream_writeBoolean(out, isSameObject(env, ref1, ref2));

commonRef_idToRef_delete(env, ref1);
commonRef_idToRef_delete(env, ref2);

return JNI_TRUE;
}

static jboolean
objectHashCodeImpl(PacketInputStream *in, PacketOutputStream *out)
{
jlong id;
jobject ref;
JNIEnv *env;

env = getEnv();
id = inStream_readObjectID(in);
if (inStream_error(in)) {
return JNI_TRUE;
}

if (id == NULL_OBJECT_ID) {
outStream_setError(out, JDWP_ERROR(INVALID_OBJECT));
return JNI_TRUE;
}

ref = commonRef_idToRef(env, id);
(void)outStream_writeInt(out, objectHashCode(ref));
Copy link
Contributor

Choose a reason for hiding this comment

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

Interesting that the debug agent already has an objectHashCode() but there were no users of it.

Copy link
Author

Choose a reason for hiding this comment

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

ObjectReferenceImpl.hashCode() calculates it by itself from objectID (it should call objectHashCode)


commonRef_idToRef_delete(env, ref);

return JNI_TRUE;
}

Command ObjectReference_Commands[] = {
{referenceType, "ReferenceType"},
{getValues, "GetValues"},
Expand All @@ -367,7 +424,9 @@ Command ObjectReference_Commands[] = {
{disableCollection, "DisableCollection"},
{enableCollection, "EnableCollection"},
{isCollected, "IsCollected"},
{referringObjects, "ReferringObjects"}
{referringObjects, "ReferringObjects"},
{isSameObjectImpl, "IsSameObject"},
{objectHashCodeImpl, "ObjectHashCode"}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd suggest to keep current naming convention (just first letter is changed to capital):
referenceType <=> ReferenceType
disableCollection <=> DisableCollection
referringObjects <=> ReferringObjects
isSameObjectIs <=> SameObject
objectHashCode <=> ObjectHashCode
Then the existing objectHashCode needs to be renamed to something else.

Copy link
Author

@alexmenkov alexmenkov Dec 18, 2025

Choose a reason for hiding this comment

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

We have isSameObject too (and it's used in many places)


DEBUG_DISPATCH_DEFINE_CMDSET(ObjectReference)
Loading