merge in mnc-release history after reset to mnc-dev
diff --git a/jdwp/src/test/java/org/apache/harmony/jpda/tests/framework/jdwp/VmMirror.java b/jdwp/src/test/java/org/apache/harmony/jpda/tests/framework/jdwp/VmMirror.java
index 3ffa6df..67c0aa0 100644
--- a/jdwp/src/test/java/org/apache/harmony/jpda/tests/framework/jdwp/VmMirror.java
+++ b/jdwp/src/test/java/org/apache/harmony/jpda/tests/framework/jdwp/VmMirror.java
@@ -218,6 +218,19 @@
* @return requestID id of request
*/
public int setBreakpointAtMethodBegin(long classID, String methodName) {
+ return setBreakpointAtMethodBegin(classID, methodName, JDWPConstants.SuspendPolicy.ALL);
+ }
+
+ /**
+ * Sets breakpoint at the beginning of method with name <i>methodName</i>.
+ *
+ * @param classID
+ * id of class with required method
+ * @param methodName
+ * name of required method
+ * @return requestID id of request
+ */
+ public int setBreakpointAtMethodBegin(long classID, String methodName, byte suspendPolicy) {
long methodID = getMethodID(classID, methodName);
ReplyPacket lineTableReply = getLineTable(classID, methodID);
@@ -245,7 +258,7 @@
Location breakpointLocation = new Location(JDWPConstants.TypeTag.CLASS,
classID, methodID, lineCodeIndex);
- ReplyPacket reply = setBreakpoint(breakpointLocation);
+ ReplyPacket reply = setBreakpoint(breakpointLocation, suspendPolicy);
checkReply(reply);
return reply.getNextValueAsInt();
@@ -733,6 +746,22 @@
}
/**
+ * Returns suspend count for specified <code>threadID</code>.
+ *
+ * @param threadID
+ * thread ID
+ * @return thread's suspend count
+ */
+ public int getThreadSuspendCount(long threadID) {
+ CommandPacket commandPacket = new CommandPacket(
+ JDWPCommands.ThreadReferenceCommandSet.CommandSetID,
+ JDWPCommands.ThreadReferenceCommandSet.SuspendCountCommand);
+ commandPacket.setNextValueAsThreadID(threadID);
+ ReplyPacket replyPacket = checkReply(performCommand(commandPacket));
+ return replyPacket.getNextValueAsInt();
+ }
+
+ /**
* Returns name of thread group for specified <code>groupID</code>
*
* @param groupID
@@ -2349,6 +2378,20 @@
}
/**
+ * Returns the value of one static field of the reference type
+ *
+ * @param refTypeID
+ * The reference type ID.
+ * @param fieldID
+ * ID of field to get
+ * @return A Value object representing the field's value
+ */
+ public final Value getReferenceTypeValue(long refTypeID, long fieldID) {
+ Value[] values = getReferenceTypeValues(refTypeID, new long[]{fieldID});
+ return values[0];
+ }
+
+ /**
* Returns the value of the 'this' reference for this frame
*
* @param threadID
diff --git a/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/ClassType/InvokeMethodWithSuspensionTest.java b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/ClassType/InvokeMethodWithSuspensionTest.java
new file mode 100644
index 0000000..d1d7efe
--- /dev/null
+++ b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/ClassType/InvokeMethodWithSuspensionTest.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.jpda.tests.jdwp.ClassType;
+
+import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
+import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.TaggedObject;
+import org.apache.harmony.jpda.tests.framework.jdwp.Value;
+import org.apache.harmony.jpda.tests.jdwp.share.JDWPInvokeMethodWithSuspensionTestCase;
+import org.apache.harmony.jpda.tests.jdwp.share.debuggee.InvokeMethodWithSuspensionDebuggee;
+
+/**
+ * JDWP unit test for ClassType.InvokeCommand command with a thread suspension.
+ */
+public class InvokeMethodWithSuspensionTest extends JDWPInvokeMethodWithSuspensionTestCase {
+ public void testInvokeWithMultipleEvents001() {
+ runInvokeMethodTest(InvokeMethodWithSuspensionDebuggee.STATIC_METHOD_NAME);
+ }
+
+ @Override
+ protected CommandPacket buildInvokeCommand(long threadId, long classID,
+ long methodId, int invoke_options) {
+ CommandPacket command = new CommandPacket(
+ JDWPCommands.ClassTypeCommandSet.CommandSetID,
+ JDWPCommands.ClassTypeCommandSet.InvokeMethodCommand);
+ command.setNextValueAsClassID(classID);
+ command.setNextValueAsThreadID(threadId);
+ command.setNextValueAsMethodID(methodId);
+ command.setNextValueAsInt(0);
+ command.setNextValueAsInt(invoke_options);
+ return command;
+ }
+
+ @Override
+ protected String getInvokeCommandName() {
+ return "ClassType.InvokeCommand";
+ }
+
+ @Override
+ protected void checkInvokeReply(ReplyPacket reply) {
+ // Check result is 'void'
+ Value invokeResult = reply.getNextValueAsValue();
+ assertNull("Expect null result value for 'void'", invokeResult);
+
+ // Check exception is null.
+ TaggedObject invokeException = reply.getNextValueAsTaggedObject();
+ assertEquals("Invalid exception object id", 0, invokeException.objectID);
+ assertAllDataRead(reply);
+
+ }
+
+}
diff --git a/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/ClassType/NewInstanceWithSuspensionTest.java b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/ClassType/NewInstanceWithSuspensionTest.java
new file mode 100644
index 0000000..26509d3
--- /dev/null
+++ b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/ClassType/NewInstanceWithSuspensionTest.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.jpda.tests.jdwp.ClassType;
+
+import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
+import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.TaggedObject;
+import org.apache.harmony.jpda.tests.jdwp.share.JDWPInvokeMethodWithSuspensionTestCase;
+
+/**
+ * JDWP unit test for ClassType.NewInstance command with a thread suspension.
+ */
+public class NewInstanceWithSuspensionTest extends JDWPInvokeMethodWithSuspensionTestCase {
+ public void testInvokeWithMultipleEvents001() {
+ runInvokeMethodTest("<init>");
+ }
+
+ @Override
+ protected CommandPacket buildInvokeCommand(long threadId, long classID,
+ long methodId, int invoke_options) {
+ CommandPacket command = new CommandPacket(
+ JDWPCommands.ClassTypeCommandSet.CommandSetID,
+ JDWPCommands.ClassTypeCommandSet.NewInstanceCommand);
+ command.setNextValueAsClassID(classID);
+ command.setNextValueAsThreadID(threadId);
+ command.setNextValueAsMethodID(methodId);
+ command.setNextValueAsInt(0);
+ command.setNextValueAsInt(invoke_options);
+ return command;
+ }
+
+ @Override
+ protected String getInvokeCommandName() {
+ return "ClassType.NewInstance";
+ }
+
+ @Override
+ protected void checkInvokeReply(ReplyPacket reply) {
+ // Check result is 'void'
+ TaggedObject invokeNewObject = reply.getNextValueAsTaggedObject();
+ assertEquals(JDWPConstants.Tag.OBJECT_TAG, invokeNewObject.tag);
+ assertFalse("Invalid exception object id", invokeNewObject.objectID == 0);
+
+ // Check exception is null.
+ TaggedObject invokeException = reply.getNextValueAsTaggedObject();
+ assertEquals(JDWPConstants.Tag.OBJECT_TAG, invokeException.tag);
+ assertEquals("Invalid exception object id", 0, invokeException.objectID);
+
+ assertAllDataRead(reply);
+
+ }
+
+}
diff --git a/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/ObjectReference/InvokeMethodWithSuspensionTest.java b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/ObjectReference/InvokeMethodWithSuspensionTest.java
new file mode 100644
index 0000000..578d446
--- /dev/null
+++ b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/ObjectReference/InvokeMethodWithSuspensionTest.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.jpda.tests.jdwp.ObjectReference;
+
+import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
+import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.TaggedObject;
+import org.apache.harmony.jpda.tests.framework.jdwp.Value;
+import org.apache.harmony.jpda.tests.jdwp.share.JDWPInvokeMethodWithSuspensionTestCase;
+import org.apache.harmony.jpda.tests.jdwp.share.debuggee.InvokeMethodWithSuspensionDebuggee;
+
+/**
+ * JDWP unit test for ObjectReference.InvokeCommand command with a thread suspension.
+ */
+public class InvokeMethodWithSuspensionTest extends JDWPInvokeMethodWithSuspensionTestCase {
+ public void testInvokeWithMultipleEvents001() {
+ runInvokeMethodTest(InvokeMethodWithSuspensionDebuggee.INSTANCE_METHOD_NAME);
+ }
+
+ @Override
+ protected CommandPacket buildInvokeCommand(long threadId, long classID,
+ long methodId, int invoke_options) {
+ // We must first find the 'this' object of the top frame.
+ ReplyPacket replyPacket = debuggeeWrapper.vmMirror.getThreadFrames(threadId, 0, 1);
+ int framesCount = replyPacket.getNextValueAsInt();
+ assertEquals("Invalid frame count:", 1, framesCount);
+ long topFrameId = replyPacket.getNextValueAsFrameID();
+ replyPacket.getNextValueAsLocation(); // consume 'location'
+ assertAllDataRead(replyPacket);
+
+ long receiverId = debuggeeWrapper.vmMirror.getThisObject(threadId, topFrameId);
+
+ CommandPacket command = new CommandPacket(
+ JDWPCommands.ObjectReferenceCommandSet.CommandSetID,
+ JDWPCommands.ObjectReferenceCommandSet.InvokeMethodCommand);
+ command.setNextValueAsThreadID(receiverId);
+ command.setNextValueAsThreadID(threadId);
+ command.setNextValueAsClassID(classID);
+ command.setNextValueAsMethodID(methodId);
+ command.setNextValueAsInt(0);
+ command.setNextValueAsInt(invoke_options);
+ return command;
+ }
+
+ @Override
+ protected String getInvokeCommandName() {
+ return "ObjectReference.InvokeCommand";
+ }
+
+ @Override
+ protected void checkInvokeReply(ReplyPacket reply) {
+ // Check result is 'void'
+ Value invokeResult = reply.getNextValueAsValue();
+ assertNull("Expect null result value for 'void'", invokeResult);
+
+ // Check exception is null.
+ TaggedObject invokeException = reply.getNextValueAsTaggedObject();
+ assertEquals("Invalid exception object id", 0, invokeException.objectID);
+ assertAllDataRead(reply);
+
+ }
+
+}
diff --git a/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/VirtualMachine/DisposeDuringInvokeDebuggee.java b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/VirtualMachine/DisposeDuringInvokeDebuggee.java
new file mode 100644
index 0000000..7acc3a5
--- /dev/null
+++ b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/VirtualMachine/DisposeDuringInvokeDebuggee.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.jpda.tests.jdwp.VirtualMachine;
+
+import org.apache.harmony.jpda.tests.framework.TestErrorException;
+import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
+import org.apache.harmony.jpda.tests.share.SyncDebuggee;
+
+/**
+ * Debuggee for {@link DisposeDuringInvokeTest}.
+ */
+public class DisposeDuringInvokeDebuggee extends SyncDebuggee {
+ public static final String INVOKED_METHOD_NAME = "invokeMethodWithSynchronization";
+ public static final String BREAKPOINT_METHOD_NAME = "breakpointMethod";
+ public static final String THIS_FIELD_NAME = "thisObject";
+
+ // This field holds the receiver to invoke the invokeMethodWithSynchronization method.
+ public static DisposeDuringInvokeDebuggee thisObject;
+
+ private class DebuggeeThread extends Thread {
+ public DebuggeeThread() {
+ super("DebuggeeThread");
+ }
+
+ public void run() {
+ breakpointMethod();
+ }
+ }
+
+ /**
+ * The method used to suspend the DebuggeeThread on a breakpoint.
+ */
+ private void breakpointMethod() {
+ }
+
+ /**
+ * The method called by the DebuggeeThread through JDWP.
+ */
+ private void invokeMethodWithSynchronization() {
+ logWriter.println("#START invokeMethodWithSynchronization");
+
+ // Tell the test we are invoking the requested method.
+ synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_READY);
+
+ // Wait for the test to send a VirtualMachine.Dispose command and resume us.
+ synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
+
+ logWriter.println("#END invokeMethodWithSynchronization");
+ }
+
+ @Override
+ public void run() {
+ thisObject = this;
+
+ DebuggeeThread thrd = new DebuggeeThread();
+ synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_READY);
+
+ synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
+
+ logWriter.println("Start DebuggeeThread");
+ thrd.start();
+
+ logWriter.println("Main thread waits for DebuggeeThread ...");
+ try {
+ thrd.join();
+ } catch (InterruptedException e) {
+ throw new TestErrorException(e);
+ }
+ logWriter.println("DebuggeeThread is finished");
+
+ // Tell the test we successfully waited for the end of DebuggeeThread.
+ synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_READY);
+
+ // Wait for the test to resume us.
+ synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
+ }
+
+ public static void main(String [] args) {
+ runDebuggee(DisposeDuringInvokeDebuggee.class);
+ }
+}
diff --git a/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/VirtualMachine/DisposeDuringInvokeTest.java b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/VirtualMachine/DisposeDuringInvokeTest.java
new file mode 100644
index 0000000..7046d4e
--- /dev/null
+++ b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/VirtualMachine/DisposeDuringInvokeTest.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.jpda.tests.jdwp.VirtualMachine;
+
+import org.apache.harmony.jpda.tests.framework.TestErrorException;
+import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
+import org.apache.harmony.jpda.tests.framework.jdwp.Value;
+import org.apache.harmony.jpda.tests.jdwp.share.JDWPSyncTestCase;
+import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
+
+import java.io.IOException;
+
+/**
+ * JDWP Unit test for VirtualMachine.Dispose command while a thread is invoking a method.
+ */
+public class DisposeDuringInvokeTest extends JDWPSyncTestCase {
+
+ @Override
+ protected String getDebuggeeClassName() {
+ return DisposeDuringInvokeDebuggee.class.getName();
+ }
+
+ /**
+ * This testcase exercises VirtualMachine.Dispose command when a thread, suspended by an
+ * event, is still invoking a method.
+ * <BR>At first the test starts DisposeDuringInvokeDebuggee debuggee.
+ * <BR>Then the test sets a breakpoint so that the tested thread (DebuggeeThread) gets suspended
+ * by an event. Once this thread is suspended, we send it an ObjectReference.InvokeMethod
+ * command to initiate a method invocation executing in that thread. The method will synchronize
+ * with the test, waiting for a signal to continue its execution.
+ * <BR>While the tested thread waits for the signal, we send a VirtualMachine.Dispose command to
+ * the debuggee and sends the expected signal so the tested thread completes the method
+ * invocation.
+ * <BR>Finally, we wait for the debuggee's main thread to signal us when the tested thread has
+ * normally terminated after we dispose the JDWP connection.
+ */
+ public void testDisposeDuringInvoke() {
+ synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_READY);
+
+ // Set breakpoint so the DebuggeeThread suspends itself only.
+ long classID = getClassIDBySignature(getDebuggeeClassSignature());
+ long invokedMethodId = getMethodID(classID,
+ DisposeDuringInvokeDebuggee.INVOKED_METHOD_NAME);
+ int breakpointID = debuggeeWrapper.vmMirror.setBreakpointAtMethodBegin(classID,
+ DisposeDuringInvokeDebuggee.BREAKPOINT_METHOD_NAME,
+ JDWPConstants.SuspendPolicy.EVENT_THREAD);
+ long thisObjectId = getReceiverObjectId(classID);
+
+ // Continue debuggee.
+ synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
+
+ // Wait for the DebuggeeThread to suspend on the breakpoint.
+ long threadID = debuggeeWrapper.vmMirror.waitForBreakpoint(breakpointID);
+
+ // Send ObjectReference.InvokeMethod command.
+ CommandPacket command = new CommandPacket(
+ JDWPCommands.ObjectReferenceCommandSet.CommandSetID,
+ JDWPCommands.ObjectReferenceCommandSet.InvokeMethodCommand);
+ command.setNextValueAsThreadID(thisObjectId);
+ command.setNextValueAsThreadID(threadID);
+ command.setNextValueAsClassID(classID);
+ command.setNextValueAsMethodID(invokedMethodId);
+ command.setNextValueAsInt(0);
+ command.setNextValueAsInt(0);
+ try {
+ debuggeeWrapper.vmMirror.sendCommand(command);
+ } catch (IOException e) {
+ throw new TestErrorException("Failed to send ObjectReference.InvokeMethod command", e);
+ }
+
+ // Wait for the DebuggeeThread to start method invocation.
+ synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_READY);
+
+ // Detach from debuggee with a VirtualMachine.Dispose command.
+ debuggeeWrapper.vmMirror.dispose();
+
+ // Signal DebuggeeThread to continue so it completes method invocation.
+ synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
+
+ // Wait for the DebuggeeThread to terminate. The debuggee's main thread waits for it
+ // (using Thread.join) before signaling us.
+ synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_READY);
+
+ // The test is a success: resume the debuggee to finish
+ synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
+ }
+
+ /**
+ * Returns the object ID of the debuggee object to issue the ObjectReference.InvokeMethod
+ * command.
+ *
+ * @param classID
+ * the debuggee class ID.
+ * @return the object ID of the debuggee
+ */
+ private long getReceiverObjectId(long classID) {
+ long thisObjectFieldID = checkField(classID, DisposeDuringInvokeDebuggee.THIS_FIELD_NAME);
+ Value thisObjectValue =
+ debuggeeWrapper.vmMirror.getReferenceTypeValue(classID, thisObjectFieldID);
+ assertEquals("Invalid value tag:", JDWPConstants.Tag.OBJECT_TAG, thisObjectValue.getTag());
+ return thisObjectValue.getLongValue();
+ }
+}
diff --git a/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/share/JDWPInvokeMethodWithSuspensionTestCase.java b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/share/JDWPInvokeMethodWithSuspensionTestCase.java
new file mode 100644
index 0000000..cbb0107
--- /dev/null
+++ b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/share/JDWPInvokeMethodWithSuspensionTestCase.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.jpda.tests.jdwp.share;
+
+import org.apache.harmony.jpda.tests.framework.TestErrorException;
+import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket;
+import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants;
+import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket;
+import org.apache.harmony.jpda.tests.jdwp.share.debuggee.InvokeMethodWithSuspensionDebuggee;
+import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
+
+import java.io.IOException;
+
+/**
+ * Base class for tests checking invoke command with thread suspension.
+ */
+public abstract class JDWPInvokeMethodWithSuspensionTestCase extends JDWPSyncTestCase {
+
+ @Override
+ protected final String getDebuggeeClassName() {
+ return InvokeMethodWithSuspensionDebuggee.class.getName();
+ }
+
+ /**
+ * This methods runs the {@link InvokeMethodWithSuspensionDebuggee} then sets up the
+ * following breakpoints:
+ * - breakpoint #1 to suspend the current thread only when the debuggee starts
+ * - breakpoint #2 to suspend all threads from a new thread started by a method called through
+ * JDWP.
+ * When we receive the event for breakpoint #1, we issue a request to invoke a method (or a
+ * constructor) in the event thread (suspended on the breakpoint). The event thread starts
+ * another child thread and loops as long as the child is running. However, we do not read the
+ * reply of the invoke yet.
+ * Next, we wait for the new thread to hit breakpoint #2. We resume all threads in the debuggee
+ * and read the reply of the invoke.
+ * Finally, we resume the thread that completed the invoke.
+ *
+ * @param invokedMethodName
+ * the name of the method to invoke
+ */
+ protected void runInvokeMethodTest(String invokedMethodName) {
+ // Wait for debuggee to start.
+ synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_READY);
+
+ long classID = getClassIDBySignature(getDebuggeeClassSignature());
+ long invokeMethodID = getMethodID(classID, invokedMethodName);
+
+ // Set breakpoint with EVENT_THREAD suspend policy so only the event thread is suspended.
+ // We will invoke the method in this thread.
+ int breakpointEventThread = debuggeeWrapper.vmMirror.setBreakpointAtMethodBegin(classID,
+ InvokeMethodWithSuspensionDebuggee.BREAKPOINT_EVENT_THREAD_METHOD_NAME,
+ JDWPConstants.SuspendPolicy.EVENT_THREAD);
+
+ // Set breakpoint with ALL suspend policy to suspend all threads. The thread started
+ // during the invoke will suspend all threads, including the thread executing the invoke.
+ int breakpointAllThreads = debuggeeWrapper.vmMirror.setBreakpointAtMethodBegin(classID,
+ InvokeMethodWithSuspensionDebuggee.BREAKPOINT_ALL_THREADS_METHOD_NAME,
+ JDWPConstants.SuspendPolicy.ALL);
+
+ // Tell the debuggee to continue.
+ synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
+
+ // Wait for breakpoint and get id of suspended thread.
+ long eventThreadOne = debuggeeWrapper.vmMirror.waitForBreakpoint(breakpointEventThread);
+ int suspendCount = debuggeeWrapper.vmMirror.getThreadSuspendCount(eventThreadOne);
+ assertEquals("Invalid suspend count:", 1, suspendCount);
+
+ // Send command but does not read reply now. That invoked method starts another thread
+ // that is going to hit a breakpoint and suspend all threads, including the thread invoking
+ // the method. The invoke can only complete when that new thread terminates, which requires
+ // we send a VirtualMachine.Resume command.
+ final int invoke_options = 0; // resume/suspend all threads before/after the invoke.
+ CommandPacket invokeMethodCommand = buildInvokeCommand(eventThreadOne, classID,
+ invokeMethodID, invoke_options);
+
+ String commandName = getInvokeCommandName();
+ logWriter.println("Send " + commandName);
+ int invokeMethodCommandID = -1;
+ try {
+ invokeMethodCommandID = debuggeeWrapper.vmMirror.sendCommand(invokeMethodCommand);
+ } catch (IOException e) {
+ logWriter.printError("Failed to send " + commandName, e);
+ fail();
+ }
+
+ // Wait for 2nd breakpoint to hit.
+ long eventThreadTwo = debuggeeWrapper.vmMirror.waitForBreakpoint(breakpointAllThreads);
+ suspendCount = debuggeeWrapper.vmMirror.getThreadSuspendCount(eventThreadTwo);
+ assertEquals("Invalid suspend count:", 1, suspendCount);
+
+ // At this point, the event thread #1 must have been suspended by event thread #2. Since
+ // the invoke has resumed it too, its suspend count must remain 1.
+ suspendCount = debuggeeWrapper.vmMirror.getThreadSuspendCount(eventThreadOne);
+ assertEquals("Invalid suspend count:", 1, suspendCount);
+
+ // Test that sending another invoke command in the same thread returns an error.
+ CommandPacket anotherInvokeMethodCommand = buildInvokeCommand(eventThreadOne, classID,
+ invokeMethodID, invoke_options);
+ ReplyPacket anotherInvokeMethodReply =
+ debuggeeWrapper.vmMirror.performCommand(anotherInvokeMethodCommand);
+ // The RI returns INVALID_THREAD error while ART returns ALREADY_INVOKING error which is
+ // more accurate. We only test we get an error so we can run the test with both runtimes.
+ assertTrue("Expected an error",
+ anotherInvokeMethodReply.getErrorCode() != JDWPConstants.Error.NONE);
+
+ // Send a VirtualMachine.Resume to resume all threads. This will unblock the event thread
+ // with the invoke in-progress.
+ logWriter.println("Resume all threads");
+ resumeDebuggee();
+
+ // Now we can read the invoke reply.
+ ReplyPacket invokeMethodReply = null;
+ try {
+ logWriter.println("Receiving reply for command " + invokeMethodCommandID + " ...");
+ invokeMethodReply = debuggeeWrapper.vmMirror.receiveReply(invokeMethodCommandID);
+ } catch (Exception e) {
+ throw new TestErrorException("Did not receive invoke reply", e);
+ }
+ checkReplyPacket(invokeMethodReply, commandName + " command");
+ logWriter.println("Received reply for command " + invokeMethodCommandID + " OK");
+
+ checkInvokeReply(invokeMethodReply);
+
+ // The invoke is complete but the thread is still suspended: let's resume it now.
+ suspendCount = debuggeeWrapper.vmMirror.getThreadSuspendCount(eventThreadOne);
+ assertEquals("Invalid suspend count:", 1, suspendCount);
+
+ logWriter.println("Resume event thread #1");
+ debuggeeWrapper.vmMirror.resumeThread(eventThreadOne);
+ }
+
+ /**
+ * Builds the packed for the tested JDWP command.
+ *
+ * @param threadId
+ * the id of the thread that will invoke the method
+ * @param classID
+ * the class ID of the invoked method
+ * @param methodId
+ * the ID of the invoke method
+ * @param invokeOptions
+ * options for the invoke
+ * @return a command
+ */
+ protected abstract CommandPacket buildInvokeCommand(long threadId, long classID,
+ long methodId, int invokeOptions);
+
+ /**
+ * Returns the name of the command returned by {@link #buildInvokeCommand} for printing.
+ *
+ * @return the name of the invoke command sent to the debuggee
+ */
+ protected abstract String getInvokeCommandName();
+
+ /**
+ * Checks the reply for the tested JDWP command.
+ *
+ * @param reply the reply of the invoke
+ */
+ protected abstract void checkInvokeReply(ReplyPacket reply);
+
+}
diff --git a/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/share/debuggee/InvokeMethodWithSuspensionDebuggee.java b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/share/debuggee/InvokeMethodWithSuspensionDebuggee.java
new file mode 100644
index 0000000..ce7327c
--- /dev/null
+++ b/jdwp/src/test/java/org/apache/harmony/jpda/tests/jdwp/share/debuggee/InvokeMethodWithSuspensionDebuggee.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.jpda.tests.jdwp.share.debuggee;
+
+import org.apache.harmony.jpda.tests.jdwp.share.JDWPInvokeMethodWithSuspensionTestCase;
+import org.apache.harmony.jpda.tests.share.JPDADebuggeeSynchronizer;
+import org.apache.harmony.jpda.tests.share.SyncDebuggee;
+
+/**
+ * Debuggee for subclasses of {@link JDWPInvokeMethodWithSuspensionTestCase}.
+ */
+public class InvokeMethodWithSuspensionDebuggee extends SyncDebuggee {
+ // Information for the test.
+ public static final String STATIC_METHOD_NAME = "invokedStaticMethod";
+ public static final String INSTANCE_METHOD_NAME = "invokedInstanceMethod";
+ public static final String BREAKPOINT_EVENT_THREAD_METHOD_NAME = "breakpointEventThread";
+ public static final String BREAKPOINT_ALL_THREADS_METHOD_NAME = "breakpointAllThreads";
+
+ private static volatile boolean testThreadFinished = false;
+ private static Thread testThread = null;
+ private static boolean enableThreadSuspensionForTesting = false;
+
+ private class TestThread extends Thread {
+ public TestThread() {
+ super("TestThread");
+ }
+
+ @Override
+ public void run() {
+ logWriter.println("TestThread starts");
+
+ // We're going to suspend all threads in the method below.
+ logWriter.println("Breakpoint for event thread #2");
+ breakpointAllThreads();
+
+ // The test needs to resume us so the invoke in progress in event thread #1 can
+ // complete.
+ testThreadFinished = true;
+
+ logWriter.println("TestThread ends");
+ }
+ }
+
+ // Invoked to suspend main thread (event thread #1) on a breakpoint.
+ public void breakpointEventThread() {
+ }
+
+ // Invoked to suspend test thread (event thread #2) and all others threads on a breakpoint.
+ public void breakpointAllThreads() {
+ }
+
+ // Invoked in event thread #1. This will unblock event thread #2 that will suspend all threads
+ // including event thread #1. This helps us check that the debugger is not blocked waiting for
+ // this invoke.
+ private static void causeEventThreadSuspension() {
+ if (enableThreadSuspensionForTesting) {
+ // Start event thread #2. It's going to hit a breakpoint and suspend us.
+ testThread.start();
+
+ // We don't use wait/notify pattern to be sure our loop is active.
+ while (!testThreadFinished) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ // Static method to test ClassType.InvokeMethod.
+ public static void invokedStaticMethod() {
+ causeEventThreadSuspension();
+ }
+
+ // Constructor to test ClassType.NewInstance.
+ public InvokeMethodWithSuspensionDebuggee() {
+ causeEventThreadSuspension();
+ }
+
+ // Instance method to test ObjectReference.InvokeMethod.
+ public void invokedInstanceMethod() {
+ causeEventThreadSuspension();
+ }
+
+ @Override
+ public void run() {
+ logWriter.println("InvokeMethodWithThreadSuspensionDebuggee starts");
+
+ synchronizer.sendMessage(JPDADebuggeeSynchronizer.SGNL_READY);
+
+ // Create test thread but do not start it now. It will be started by the invoke from
+ // the test through JDWP.
+ testThread = new TestThread();
+
+ synchronizer.receiveMessage(JPDADebuggeeSynchronizer.SGNL_CONTINUE);
+
+ enableThreadSuspensionForTesting = true;
+
+ // We want to suspend the main thread on a breakpoint.
+ logWriter.println("Breakpoint for event thread #1");
+ breakpointEventThread();
+
+ // Ensure tested thread is finished.
+ try {
+ testThread.join();
+ } catch (InterruptedException e) {
+ logWriter.printError("Failed to join tested thread", e);
+ }
+ testThread = null;
+
+ logWriter.println("InvokeMethodWithThreadSuspensionDebuggee ends");
+ }
+
+ public static void main(String[] args) {
+ runDebuggee(InvokeMethodWithSuspensionDebuggee.class);
+ }
+}
diff --git a/jdwp/src/test/java/org/apache/harmony/jpda/tests/share/AllTests.java b/jdwp/src/test/java/org/apache/harmony/jpda/tests/share/AllTests.java
index 3936605..e38b3d0 100644
--- a/jdwp/src/test/java/org/apache/harmony/jpda/tests/share/AllTests.java
+++ b/jdwp/src/test/java/org/apache/harmony/jpda/tests/share/AllTests.java
@@ -62,9 +62,11 @@
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ClassObjectReference.ReflectedTypeTest.class);
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ClassType.InvokeMethod002Test.class);
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ClassType.InvokeMethod003Test.class);
+ suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ClassType.InvokeMethodWithSuspensionTest.class);
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ClassType.InvokeMethodTest.class);
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ClassType.NewInstance002Test.class);
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ClassType.NewInstanceTest.class);
+ suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ClassType.NewInstanceWithSuspensionTest.class);
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ClassType.SetValues002Test.class);
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ClassType.SetValuesTest.class);
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ClassType.SuperClassTest.class);
@@ -125,6 +127,7 @@
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ObjectReference.InvokeMethod002Test.class);
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ObjectReference.InvokeMethod003Test.class);
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ObjectReference.InvokeMethodTest.class);
+ suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ObjectReference.InvokeMethodWithSuspensionTest.class);
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ObjectReference.IsCollectedTest.class);
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ObjectReference.MonitorInfoTest.class);
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.ObjectReference.ReferenceTypeTest.class);
@@ -198,6 +201,7 @@
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.VirtualMachine.ClassesBySignatureTest.class);
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.VirtualMachine.ClassPathsTest.class);
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.VirtualMachine.CreateStringTest.class);
+ suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.VirtualMachine.DisposeDuringInvokeTest.class);
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.VirtualMachine.DisposeTest.class);
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.VirtualMachine.DisposeObjectsTest.class);
suite.addTestSuite(org.apache.harmony.jpda.tests.jdwp.VirtualMachine.ExitTest.class);