Skip to content
Closed
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
37 changes: 37 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,43 @@ JDWP "Java(tm) Debug Wire Protocol"
(Error VM_DEAD)
)
)
(Command IsSameObject=11
"<b>IsSameObject is a preview API of the Java platform.</b> "
"<em>Preview features may be removed in a future release, or upgraded to "
"permanent features of the Java platform.</em>"
"<p>"
"Determines whether two objects refer to the same Java object."
"<p>Since JDWP version 27."
(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
"<b>ObjectHashCode is a preview API of the Java platform.</b> "
"<em>Preview features may be removed in a future release, or upgraded to "
"permanent features of the Java platform.</em>"
"<p>"
"Returns hash code for an object."
"<p>Since JDWP version 27."
(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
10 changes: 10 additions & 0 deletions src/jdk.jdi/share/classes/com/sun/tools/jdi/ClassTypeImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,16 @@ public boolean isEnum() {
return false;
}

public boolean isValueClass() {
if (!vm.supportsValueClasses()) {
return false;
}
if (modifiers == -1) {
getModifiers();
}
return (modifiers & VMModifiers.IDENTITY) == 0;
}

public void setValue(Field field, Value value)
throws InvalidTypeException, ClassNotLoadedException {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,17 +145,42 @@ public boolean vmNotSuspended(VMAction action) {
}
}

boolean isValueObject() {
if (referenceType() instanceof ClassTypeImpl classType) {
return classType.isValueClass();
}
return false;
}

public boolean equals(Object obj) {
if (obj instanceof ObjectReferenceImpl other) {
return (ref() == other.ref()) &&
super.equals(obj);
} else {
return false;
if (!super.equals(obj)) { // checks if the references belong to the same VM
return false;
}
if (ref() == other.ref()) {
return true;
}
// We can get equal value objects with different IDs.
if (isValueObject()) {
try {
return JDWP.ObjectReference.IsSameObject.process(vm, this, other).isSameObject;
} catch (JDWPException exc) {
throw exc.toJDIException();
}
}
}
return false;
}

@Override
public int hashCode() {
if (isValueObject()) {
try {
return JDWP.ObjectReference.ObjectHashCode.process(vm, this).hashCode;
} catch (JDWPException exc) {
throw exc.toJDIException();
}
}
return Long.hashCode(ref());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public interface VMModifiers {
int STATIC = 0x00000008; /* instance variable is static */
int FINAL = 0x00000010; /* no further subclassing, overriding */
int SYNCHRONIZED = 0x00000020; /* wrap method call in monitor lock */
int IDENTITY = 0x0020; /* identity (not value) class */
int VOLATILE = 0x00000040; /* can cache in registers */
int BRIDGE = 0x00000040; /* Bridge method generated by compiler */
int TRANSIENT = 0x00000080; /* not persistent */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,10 @@ boolean mayCreateVirtualThreads() {
return versionInfo().jdwpMajor >= 19;
}

public boolean supportsValueClasses() {
Copy link
Contributor

Choose a reason for hiding this comment

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

What happens if not running in preview mode? Value classes are not supported in that case, but as implemented this API will say they are supported, leading to isValueClass() looking at modifiers in a way that is not compatible when value classes are not supported.

JDI didn't really run into this type of problem with virtual thread support. It did modify a couple of new public APIs with:

@PreviewFeature(feature = PreviewFeature.Feature.VIRTUAL_THREADS)

However, I don't see how that helps us here. It seems we need to query the debug agent to find out if value classes are supported. There's no good way to do that. Is there any way for JDI to somewhat detect that value class support is supported (preview mode is enabled).

Copy link
Contributor

Choose a reason for hiding this comment

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

What happens if not running in preview mode? Value classes are not supported in that case, but as implemented this API will say they are supported, leading to isValueClass() looking at modifiers in a way that is not compatible when value classes are not supported.

It should work correctly. The IDENTITY modifier bit is not used for other purposes in JDK 27 (Alex, please, fix me if it is wrong). So, the check for version >= 27 is needed. If preview is not enabled in target VM, so it does not support value classes then the IDENTITY modifier bit will be always set which results in isValueClass() to always return false.

Copy link
Contributor

Choose a reason for hiding this comment

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

So in JDK 27, with or without enable preview, the IDENTITY modifier bit is accurate and can be looked at, but in previous JDK versions it might be used for something else?

Copy link
Author

Choose a reason for hiding this comment

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

So in JDK 27, with or without enable preview, the IDENTITY modifier bit is accurate and can be looked at, but in previous JDK versions it might be used for something else?

Correct

return versionInfo().jdwpMajor >= 27;
}

public void setDebugTraceMode(int traceFlags) {
validateVM();
this.traceFlags = traceFlags;
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));

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"}
};

DEBUG_DISPATCH_DEFINE_CMDSET(ObjectReference)
Loading