Skip to content

Commit 373db91

Browse files
committed
[GR-54852] Fix JDWP unexpected errors relating to array commands.
PullRequest: graal/18904
2 parents 4fb123b + 0da7dd5 commit 373db91

File tree

6 files changed

+172
-181
lines changed

6 files changed

+172
-181
lines changed

espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/JDWPContext.java

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,14 @@ public interface JDWPContext {
155155
int getArrayLength(Object array);
156156

157157
/**
158-
* Returns the TypeTag constant for the input object. The TypeTag will be determined based on
159-
* the declaring class of the object.
158+
* Returns the tag constant for the component type of the input array. Note that all sub
159+
* variants of the OBJECT tag constant is not used, e.g. if the component class of the array is
160+
* j.l.String the generic OBJECT tag is returned and not the STRING tag.
160161
*
161162
* @param array must be a guest language array object
162-
* @return TypeTag for the object
163+
* @return the TypeTag for the component type of the array
163164
*/
164-
byte getTypeTag(Object array);
165+
byte getArrayComponentTag(Object array);
165166

166167
/**
167168
* Returns an unboxed host primitive type array of the array.
@@ -266,14 +267,6 @@ public interface JDWPContext {
266267
*/
267268
boolean isValidClassLoader(Object object);
268269

269-
/**
270-
* Converts an arbitrary host object to the corresponding guest object.
271-
*
272-
* @param object the host object to convert
273-
* @return the guest object
274-
*/
275-
Object toGuest(Object object);
276-
277270
// temporarily needed until we get better exception-type based filtering in the Debug API
278271
Object getGuestException(Throwable exception);
279272

@@ -510,4 +503,6 @@ public interface JDWPContext {
510503
* being reported via stepping).
511504
*/
512505
boolean isSingleSteppingDisabled();
506+
507+
Object allocateInstance(KlassRef klass);
513508
}

espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/api/MethodRef.java

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,30 @@ public interface MethodRef {
123123
LineNumberTableRef getLineNumberTable();
124124

125125
/**
126-
* Invokes the method on the input callee object with input arguments.
126+
* Invokes a non-private, non-constructor instance method on the input callee object with input
127+
* arguments. The first argument must be the self object.
127128
*
128-
* @param callee guest-language object on which to execute the method
129129
* @param args guest-language arguments used when calling the method
130130
* @return the guest-language return value
131131
*/
132-
Object invokeMethod(Object callee, Object[] args);
132+
Object invokeMethodVirtual(Object... args);
133+
134+
/**
135+
* Invokes a static method with input arguments.
136+
*
137+
* @param args guest-language arguments used when calling the method
138+
* @return the guest-language return value
139+
*/
140+
Object invokeMethodStatic(Object... args);
141+
142+
/**
143+
* Invokes a constructor or private instance method with input arguments. The first argument
144+
* must be the self object.
145+
*
146+
* @param args guest-language arguments used when calling the method
147+
* @return the guest-language return value
148+
*/
149+
Object invokeMethodSpecial(Object... args);
133150

134151
/**
135152
* Determines if the declaring class has a source file attribute.
@@ -211,4 +228,18 @@ public interface MethodRef {
211228
* @return last bci
212229
*/
213230
long getLastBCI();
231+
232+
/**
233+
* Determines if the method is a constructor.
234+
*
235+
* @return true if the method is a constructor
236+
*/
237+
boolean isConstructor();
238+
239+
/**
240+
* Determines if the method is a static initializer.
241+
*
242+
* @return true if the method is a static initializer
243+
*/
244+
boolean isClassInitializer();
214245
}

espresso/src/com.oracle.truffle.espresso.jdwp/src/com/oracle/truffle/espresso/jdwp/impl/JDWP.java

Lines changed: 59 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,7 +1125,8 @@ static CommandResult createReply(Packet packet, DebuggerController controller, D
11251125
}
11261126

11271127
MethodRef method = verifyMethodRef(input.readLong(), reply, context);
1128-
if (method == null) {
1128+
if (method == null || !Modifier.isStatic(method.getModifiers()) || method.isClassInitializer()) {
1129+
reply.errorCode(ErrorCodes.INVALID_METHODID);
11291130
return new CommandResult(reply);
11301131
}
11311132

@@ -1160,12 +1161,7 @@ static CommandResult createReply(Packet packet, DebuggerController controller, D
11601161
try {
11611162
// we have to call the method in the correct thread, so post a
11621163
// Callable to the controller and wait for the result to appear
1163-
ThreadJob<Object> job = new ThreadJob<>(thread, new Callable<>() {
1164-
@Override
1165-
public Object call() {
1166-
return method.invokeMethod(null, args);
1167-
}
1168-
}, suspensionStrategy);
1164+
ThreadJob<Object> job = new ThreadJob<>(thread, () -> method.invokeMethodStatic(args), suspensionStrategy);
11691165
controller.postJobForThread(job);
11701166

11711167
// invocation of a method can cause events with possible thread suspension
@@ -1209,6 +1205,10 @@ static CommandResult createReply(Packet packet, DebuggerController controller) {
12091205
if (klass == null) {
12101206
return new CommandResult(reply);
12111207
}
1208+
if (klass.isArray() || klass.isInterface() || Modifier.isAbstract(klass.getModifiers())) {
1209+
reply.errorCode(ErrorCodes.INVALID_CLASS);
1210+
return new CommandResult(reply);
1211+
}
12121212

12131213
Object thread = verifyThread(input.readLong(), reply, context, true);
12141214
if (thread == null) {
@@ -1223,32 +1223,31 @@ static CommandResult createReply(Packet packet, DebuggerController controller) {
12231223
}
12241224

12251225
MethodRef method = verifyMethodRef(input.readLong(), reply, context);
1226-
if (method == null) {
1227-
controller.warning(() -> "not a valid method");
1226+
if (method == null || !method.isConstructor() || method.getDeclaringKlassRef() != klass) {
1227+
reply.errorCode(ErrorCodes.INVALID_METHODID);
12281228
return new CommandResult(reply);
12291229
}
12301230

12311231
controller.fine(() -> "trying to invoke constructor in klass: " + klass.getNameAsString());
12321232

12331233
int arguments = input.readInt();
12341234

1235-
Object[] args = new Object[arguments];
1236-
for (int i = 0; i < arguments; i++) {
1235+
Object[] args = new Object[arguments + 1];
1236+
// we leave room for the allocated object as the first arg
1237+
for (int i = 1; i < args.length; i++) {
12371238
byte valueKind = input.readByte();
12381239
args[i] = readValue(valueKind, input, context);
12391240
}
12401241

12411242
int invocationOptions = input.readInt();
12421243
byte suspensionStrategy = invocationOptions == 1 ? SuspendStrategy.EVENT_THREAD : SuspendStrategy.ALL;
12431244
try {
1244-
// we have to call the method in the correct thread, so post a
1245+
// we have to call the constructor in the correct thread, so post a
12451246
// Callable to the controller and wait for the result to appear
1246-
ThreadJob<?> job = new ThreadJob<>(thread, new Callable<>() {
1247-
1248-
@Override
1249-
public Object call() throws Exception {
1250-
return method.invokeMethod(null, args);
1251-
}
1247+
ThreadJob<?> job = new ThreadJob<>(thread, () -> {
1248+
args[0] = context.allocateInstance(klass);
1249+
method.invokeMethodSpecial(args);
1250+
return args[0];
12521251
}, suspensionStrategy);
12531252
controller.postJobForThread(job);
12541253
ThreadJob<?>.JobResult<?> result = job.getResult();
@@ -1329,7 +1328,7 @@ static CommandResult createReply(Packet packet, DebuggerController controller, D
13291328
return new CommandResult(reply);
13301329
}
13311330

1332-
if (method.getDeclaringKlassRef() != itf) {
1331+
if (method.getDeclaringKlassRef() != itf || !Modifier.isStatic(method.getModifiers()) || method.isClassInitializer()) {
13331332
reply.errorCode(ErrorCodes.INVALID_METHODID);
13341333
return new CommandResult(reply);
13351334
}
@@ -1349,13 +1348,7 @@ static CommandResult createReply(Packet packet, DebuggerController controller, D
13491348
try {
13501349
// we have to call the method in the correct thread, so post a
13511350
// Callable to the controller and wait for the result to appear
1352-
ThreadJob<Object> job = new ThreadJob<>(thread, new Callable<>() {
1353-
1354-
@Override
1355-
public Object call() throws Exception {
1356-
return method.invokeMethod(null, args);
1357-
}
1358-
}, suspensionStrategy);
1351+
ThreadJob<Object> job = new ThreadJob<>(thread, () -> method.invokeMethodStatic(args), suspensionStrategy);
13591352
controller.postJobForThread(job);
13601353
// invocation of a method can cause events with possible thread suspension
13611354
// to happen, e.g. class prepare events for newly loaded classes
@@ -1755,6 +1748,14 @@ static CommandResult createReply(Packet packet, DebuggerController controller, D
17551748
JDWPContext context = controller.getContext();
17561749

17571750
long objectId = input.readLong();
1751+
1752+
Object receiver = context.getIds().fromId((int) objectId);
1753+
if (receiver == null) {
1754+
// object was garbage collected
1755+
reply.errorCode(ErrorCodes.INVALID_OBJECT);
1756+
return new CommandResult(reply);
1757+
}
1758+
17581759
long threadId = input.readLong();
17591760

17601761
Object thread = verifyThread(threadId, reply, context, true);
@@ -1775,42 +1776,38 @@ static CommandResult createReply(Packet packet, DebuggerController controller, D
17751776
}
17761777

17771778
long methodId = input.readLong();
1778-
int arguments = input.readInt();
1779-
1780-
Object[] args = new Object[arguments];
1781-
for (int i = 0; i < arguments; i++) {
1782-
byte valueKind = input.readByte();
1783-
args[i] = readValue(valueKind, input, context);
1784-
}
1785-
1786-
Object callee = context.getIds().fromId((int) objectId);
1787-
if (callee == null) {
1788-
// object was garbage collected
1789-
reply.errorCode(ErrorCodes.INVALID_OBJECT);
1790-
return new CommandResult(reply);
1791-
}
17921779
MethodRef method = verifyMethodRef(methodId, reply, context);
17931780

17941781
if (method == null) {
17951782
return new CommandResult(reply);
17961783
}
17971784

1798-
if (!context.isMemberOf(callee, method.getDeclaringKlassRef())) {
1785+
if (Modifier.isStatic(method.getModifiers()) || method.isConstructor() || !context.isMemberOf(receiver, method.getDeclaringKlassRef())) {
17991786
reply.errorCode(ErrorCodes.INVALID_METHODID);
18001787
return new CommandResult(reply);
18011788
}
18021789

1790+
int arguments = input.readInt();
1791+
1792+
Object[] args = new Object[arguments + 1];
1793+
args[0] = receiver;
1794+
for (int i = 1; i < args.length; i++) {
1795+
byte valueKind = input.readByte();
1796+
args[i] = readValue(valueKind, input, context);
1797+
}
1798+
18031799
controller.fine(() -> "trying to invoke method: " + method.getNameAsString());
18041800

18051801
int invocationOptions = input.readInt();
18061802
byte suspensionStrategy = invocationOptions == 1 ? SuspendStrategy.EVENT_THREAD : SuspendStrategy.ALL;
18071803
try {
18081804
// we have to call the method in the correct thread, so post a
18091805
// Callable to the controller and wait for the result to appear
1810-
ThreadJob<Object> job = new ThreadJob<>(thread, new Callable<>() {
1811-
@Override
1812-
public Object call() throws Exception {
1813-
return method.invokeMethod(callee, args);
1806+
ThreadJob<Object> job = new ThreadJob<>(thread, () -> {
1807+
if (Modifier.isPrivate(method.getModifiers())) {
1808+
return method.invokeMethodSpecial(args);
1809+
} else {
1810+
return method.invokeMethodVirtual(args);
18141811
}
18151812
}, suspensionStrategy);
18161813
controller.postJobForThread(job);
@@ -2539,7 +2536,7 @@ static CommandResult createReply(Packet packet, JDWPContext context) {
25392536
return new CommandResult(reply);
25402537
}
25412538

2542-
byte tag = context.getTypeTag(array);
2539+
byte tag = context.getArrayComponentTag(array);
25432540
boolean isPrimitive = TagConstants.isPrimitive(tag);
25442541

25452542
reply.writeByte(tag);
@@ -2570,64 +2567,28 @@ static CommandResult createReply(Packet packet, JDWPContext context) {
25702567
return new CommandResult(reply);
25712568
}
25722569

2573-
byte tag = context.getTypeTag(array);
2570+
byte tag = context.getArrayComponentTag(array);
25742571

25752572
setArrayValues(context, input, index, values, array, tag);
25762573
return new CommandResult(reply);
25772574
}
25782575

25792576
private static void setArrayValues(JDWPContext context, PacketStream input, int index, int values, Object array, byte tag) {
25802577
for (int i = index; i < index + values; i++) {
2581-
switch (tag) {
2582-
case TagConstants.BOOLEAN:
2583-
boolean bool = input.readBoolean();
2584-
byte[] boolArray = context.getUnboxedArray(array);
2585-
boolArray[i] = bool ? (byte) 1 : (byte) 0;
2586-
break;
2587-
case TagConstants.BYTE:
2588-
byte b = input.readByte();
2589-
byte[] byteArray = context.getUnboxedArray(array);
2590-
byteArray[i] = b;
2591-
break;
2592-
case TagConstants.SHORT:
2593-
short s = input.readShort();
2594-
short[] shortArray = context.getUnboxedArray(array);
2595-
shortArray[i] = s;
2596-
break;
2597-
case TagConstants.CHAR:
2598-
char c = input.readChar();
2599-
char[] charArray = context.getUnboxedArray(array);
2600-
charArray[i] = c;
2601-
break;
2602-
case TagConstants.INT:
2603-
int j = input.readInt();
2604-
int[] intArray = context.getUnboxedArray(array);
2605-
intArray[i] = j;
2606-
break;
2607-
case TagConstants.FLOAT:
2608-
float f = input.readFloat();
2609-
float[] floatArray = context.getUnboxedArray(array);
2610-
floatArray[i] = f;
2611-
break;
2612-
case TagConstants.LONG:
2613-
long l = input.readLong();
2614-
long[] longArray = context.getUnboxedArray(array);
2615-
longArray[i] = l;
2616-
break;
2617-
case TagConstants.DOUBLE:
2618-
double d = input.readDouble();
2619-
double[] doubleArray = context.getUnboxedArray(array);
2620-
doubleArray[i] = d;
2621-
break;
2622-
case TagConstants.ARRAY:
2623-
case TagConstants.STRING:
2624-
case TagConstants.OBJECT:
2625-
Object value = context.getIds().fromId((int) input.readLong());
2626-
context.setArrayValue(array, i, value);
2627-
break;
2628-
default:
2629-
throw new RuntimeException("should not reach here");
2630-
}
2578+
Object value = switch (tag) {
2579+
case TagConstants.BOOLEAN -> input.readBoolean();
2580+
case TagConstants.BYTE -> input.readByte();
2581+
case TagConstants.SHORT -> input.readShort();
2582+
case TagConstants.CHAR -> input.readChar();
2583+
case TagConstants.INT -> input.readInt();
2584+
case TagConstants.FLOAT -> input.readFloat();
2585+
case TagConstants.LONG -> input.readLong();
2586+
case TagConstants.DOUBLE -> input.readDouble();
2587+
case TagConstants.ARRAY, TagConstants.STRING, TagConstants.OBJECT ->
2588+
context.getIds().fromId((int) input.readLong());
2589+
default -> throw new RuntimeException("should not reach here: " + tag);
2590+
};
2591+
context.setArrayValue(array, i, value);
26312592
}
26322593
}
26332594
}
@@ -3084,7 +3045,7 @@ private static void writeMethodResult(PacketStream reply, JDWPContext context, T
30843045
controller.getGCPrevention().setActiveWhileSuspended(thread, guestException);
30853046
}
30863047
} else {
3087-
Object value = context.toGuest(result.getResult());
3048+
Object value = result.getResult();
30883049
if (value != null) {
30893050
byte tag = context.getTag(value);
30903051
writeValue(tag, value, reply, true, context);

0 commit comments

Comments
 (0)