Return-Path: Delivered-To: apmail-harmony-commits-archive@www.apache.org Received: (qmail 61108 invoked from network); 28 Nov 2006 17:51:48 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 28 Nov 2006 17:51:48 -0000 Received: (qmail 56143 invoked by uid 500); 28 Nov 2006 17:51:50 -0000 Delivered-To: apmail-harmony-commits-archive@harmony.apache.org Received: (qmail 56087 invoked by uid 500); 28 Nov 2006 17:51:50 -0000 Mailing-List: contact commits-help@harmony.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@harmony.apache.org Delivered-To: mailing list commits@harmony.apache.org Received: (qmail 55970 invoked by uid 99); 28 Nov 2006 17:51:50 -0000 Received: from herse.apache.org (HELO herse.apache.org) (140.211.11.133) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 28 Nov 2006 09:51:50 -0800 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 28 Nov 2006 09:51:29 -0800 Received: by eris.apache.org (Postfix, from userid 65534) id 659FE1A9880; Tue, 28 Nov 2006 09:50:00 -0800 (PST) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r480141 [20/38] - in /harmony/enhanced/jdktools/trunk/modules/jpda: ./ doc/ doc/images/ make/ src/ src/common/ src/common/other/ src/common/other/jpda/ src/common/other/jpda/jdwp/ src/common/other/jpda/jdwp/agent/ src/common/other/jpda/jdwp... Date: Tue, 28 Nov 2006 17:49:31 -0000 To: commits@harmony.apache.org From: geirm@apache.org X-Mailer: svnmailer-1.1.0 Message-Id: <20061128175000.659FE1A9880@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Added: harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/framework/jdwp/VmMirror.java URL: http://svn.apache.org/viewvc/harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/framework/jdwp/VmMirror.java?view=auto&rev=480141 ============================================================================== --- harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/framework/jdwp/VmMirror.java (added) +++ harmony/enhanced/jdktools/trunk/modules/jpda/test/common/unit/org/apache/harmony/jpda/tests/framework/jdwp/VmMirror.java Tue Nov 28 09:49:08 2006 @@ -0,0 +1,2600 @@ +/* + * Copyright 2005-2006 The Apache Software Foundation or its licensors, as applicable. + * + * Licensed 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. + */ + +/** + * @author Vitaly A. Provodin + * @version $Revision: 1.7 $ + */ + +package org.apache.harmony.jpda.tests.framework.jdwp; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.harmony.jpda.tests.framework.Breakpoint; +import org.apache.harmony.jpda.tests.framework.LogWriter; +import org.apache.harmony.jpda.tests.framework.TestErrorException; +import org.apache.harmony.jpda.tests.framework.TestOptions; +import org.apache.harmony.jpda.tests.framework.jdwp.Capabilities; +import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket; +import org.apache.harmony.jpda.tests.framework.jdwp.Event; +import org.apache.harmony.jpda.tests.framework.jdwp.EventMod; +import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants; +import org.apache.harmony.jpda.tests.framework.jdwp.Location; +import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket; +import org.apache.harmony.jpda.tests.framework.jdwp.TransportWrapper; +import org.apache.harmony.jpda.tests.framework.jdwp.TypesLengths; +import org.apache.harmony.jpda.tests.framework.jdwp.Frame.Variable; +import org.apache.harmony.jpda.tests.framework.jdwp.exceptions.ReplyErrorCodeException; +import org.apache.harmony.jpda.tests.framework.jdwp.exceptions.TimeoutException; + +/** + * This class provides convenient way for communicating with debuggee VM using + * JDWP packets. + *

+ * Most methods can throw ReplyErrorCodeException if error occurred in execution + * of corresponding JDWP command or TestErrorException if any other error + * occurred. + */ +public class VmMirror { + + /** Target VM Capabilities. */ + public Capabilities targetVMCapabilities; + + /** Transport used to sent and receive packets. */ + private TransportWrapper connection; + + /** PacketDispatcher thread used for asynchronous reading packets. */ + private PacketDispatcher packetDispatcher; + + /** Test run options. */ + protected TestOptions config; + + /** Log to write messages. */ + protected LogWriter logWriter; + + /** + * Creates new VmMirror instance for given test run options. + * + * @param config test run options + * @param logWriter log writer + */ + public VmMirror(TestOptions config, LogWriter logWriter) { + connection = null; + this.config = config; + this.logWriter = logWriter; + } + + /** + * Checks error code of given reply packet and throws + * ReplyErrorCodeException if any error detected. + * + * @param reply + * reply packet to check + * @return ReplyPacket unchanged reply packet + */ + public ReplyPacket checkReply(ReplyPacket reply) { + if (reply.getErrorCode() != JDWPConstants.Error.NONE) + throw new ReplyErrorCodeException(reply.getErrorCode()); + return reply; + } + + /** + * Sets breakpoint to given location. + * + * @param typeTag + * @param breakpoint + * @return ReplyPacket for corresponding command + */ + public ReplyPacket setBreakpoint(byte typeTag, Breakpoint breakpoint) { + + return setBreakpoint(typeTag, breakpoint, + JDWPConstants.SuspendPolicy.ALL); + } + + /** + * Sets breakpoint to given location. + * + * @param typeTag + * @param breakpoint + * @param suspendPolicy + * Suspend policy for a breakpoint being created + * @return ReplyPacket for corresponding command + */ + public ReplyPacket setBreakpoint(byte typeTag, Breakpoint breakpoint, + byte suspendPolicy) { + // Get Class reference ID + long typeID = getTypeID(breakpoint.className, typeTag); + + // Get Method reference ID + long methodID = getMethodID(typeID, breakpoint.methodName); + + // Fill location + Location location = new Location(typeTag, typeID, methodID, + breakpoint.index); + + // Set breakpoint + return setBreakpoint(location, suspendPolicy); + } + + /** + * Sets breakpoint to given location. + * + * @param location + * Location of breakpoint + * @return ReplyPacket for corresponding command + */ + public ReplyPacket setBreakpoint(Location location) { + + return setBreakpoint(location, JDWPConstants.SuspendPolicy.ALL); + } + + /** + * Sets breakpoint to given location + * + * @param location + * Location of breakpoint + * @param suspendPolicy + * Suspend policy for a breakpoint being created + * @return ReplyPacket for corresponding command + */ + public ReplyPacket setBreakpoint(Location location, byte suspendPolicy) { + // Prepare corresponding event + byte eventKind = JDWPConstants.EventKind.BREAKPOINT; + + // EventMod[] mods = new EventMod[1]; + EventMod[] mods = new EventMod[] { new EventMod() }; + + mods[0].loc = location; + mods[0].modKind = EventMod.ModKind.LocationOnly; + Event event = new Event(eventKind, suspendPolicy, mods); + + // Set breakpoint + return setEvent(event); + } + + /** + * Sets breakpoint that triggers only on a certain occurrence to a given + * location + * + * @param typeTag + * @param breakpoint + * @param suspendPolicy + * Suspend policy for a breakpoint being created + * @param count + * Limit the requested event to be reported at most once after a + * given number of occurrences + * @return ReplyPacket for corresponding command + */ + public ReplyPacket setCountableBreakpoint(byte typeTag, + Breakpoint breakpoint, byte suspendPolicy, int count) { + long typeID = getTypeID(breakpoint.className, typeTag); + + // Get Method reference ID + long methodID = getMethodID(typeID, breakpoint.methodName); + + byte eventKind = JDWPConstants.EventKind.BREAKPOINT; + + EventMod mod1 = new EventMod(); + mod1.modKind = EventMod.ModKind.LocationOnly; + mod1.loc = new Location(typeTag, typeID, methodID, breakpoint.index); + + EventMod mod2 = new EventMod(); + mod2.modKind = EventMod.ModKind.Count; + mod2.count = count; + + EventMod[] mods = new EventMod[] { mod1, mod2 }; + Event event = new Event(eventKind, suspendPolicy, mods); + + // Set breakpoint + return setEvent(event); + } + + /** + * Sets breakpoint at the beginning of method with name methodName. + * + * @param classID + * id of class with required method + * @param methodName + * name of required method + * @return requestID id of request + */ + public long setBreakpointAtMethodBegin(long classID, String methodName) { + long requestID; + + long methodID = getMethodID(classID, methodName); + + ReplyPacket lineTableReply = getLineTable(classID, methodID); + if (lineTableReply.getErrorCode() != JDWPConstants.Error.NONE) { + throw new TestErrorException( + "Command getLineTable returned error code: " + + lineTableReply.getErrorCode() + + " - " + + JDWPConstants.Error.getName(lineTableReply + .getErrorCode())); + } + + lineTableReply.getNextValueAsLong(); + // Lowest valid code index for the method + + lineTableReply.getNextValueAsLong(); + // Highest valid code index for the method + + // int numberOfLines = + lineTableReply.getNextValueAsInt(); + + long lineCodeIndex = lineTableReply.getNextValueAsLong(); + + // set breakpoint inside checked method + Location breakpointLocation = new Location(JDWPConstants.TypeTag.CLASS, + classID, methodID, lineCodeIndex); + + ReplyPacket reply = setBreakpoint(breakpointLocation); + checkReply(reply); + + requestID = reply.getNextValueAsInt(); + + return requestID; + } + + /** + * Waits for stop on breakpoint and gets id of thread where it stopped. + * + * @param requestID + * id of request for breakpoint + * @return threadID id of thread, where we stop on breakpoint + */ + public long waitForBreakpoint(long requestID) { + // receive event + CommandPacket event = null; + event = receiveEvent(); + + event.getNextValueAsByte(); + // suspendPolicy - is not used here + + // int numberOfEvents = + event.getNextValueAsInt(); + + long breakpointThreadID = 0; + ParsedEvent[] eventParsed = ParsedEvent.parseEventPacket(event); + + if (eventParsed.length != 1) { + throw new TestErrorException("Received " + eventParsed.length + + " events instead of 1 BREAKPOINT_EVENT"); + } + + // check if received event is for breakpoint + if (eventParsed[0].getEventKind() == JDWPConstants.EventKind.BREAKPOINT) { + breakpointThreadID = ((ParsedEvent.Event_BREAKPOINT) eventParsed[0]) + .getThreadID(); + + } else { + throw new TestErrorException( + "Kind of received event is not BREAKPOINT_EVENT: " + + eventParsed[0].getEventKind()); + + } + + if (eventParsed[0].getRequestID() != requestID) { + throw new TestErrorException( + "Received BREAKPOINT_EVENT with another requestID: " + + eventParsed[0].getRequestID()); + } + + return breakpointThreadID; + } + + /** + * Removes breakpoint according to specified requestID. + * + * @param requestID + * for given breakpoint + * @return ReplyPacket for corresponding command + */ + public ReplyPacket clearBreakpoint(int requestID) { + + // Create new command packet + CommandPacket commandPacket = new CommandPacket(); + + // Set command. "2" - is ID of Clear command in EventRequest Command Set + commandPacket + .setCommand(JDWPCommands.EventRequestCommandSet.ClearCommand); + + // Set command set. "15" - is ID of EventRequest Command Set + commandPacket + .setCommandSet(JDWPCommands.EventRequestCommandSet.CommandSetID); + + // Set outgoing data + // Set eventKind + commandPacket.setNextValueAsByte(JDWPConstants.EventKind.BREAKPOINT); + + // Set suspendPolicy + commandPacket.setNextValueAsInt(requestID); + + // Send packet + return checkReply(performCommand(commandPacket)); + } + + /** + * Removes all breakpoints. + * + * @return ReplyPacket for corresponding command + */ + public ReplyPacket ClearAllBreakpoints() { + + // Create new command packet + CommandPacket commandPacket = new CommandPacket(); + + // Set command. "3" - is ID of ClearAllBreakpoints command in + // EventRequest Command Set + commandPacket + .setCommand(JDWPCommands.EventRequestCommandSet.ClearAllBreakpointsCommand); + + // Set command set. "15" - is ID of EventRequest Command Set + commandPacket + .setCommandSet(JDWPCommands.EventRequestCommandSet.CommandSetID); + + // Send packet + return checkReply(performCommand(commandPacket)); + } + + /** + * Requests debuggee VM capabilities. Function parses reply packet of + * VirtualMachine::CapabilitiesNew command, creates and fills class Capabilities with + * returned info. + * + * @return ReplyPacket useless, already parsed reply packet. + */ + public ReplyPacket capabilities() { + + // Create new command packet + CommandPacket commandPacket = new CommandPacket(); + + // Set command. "17" - is ID of CapabilitiesNew command in + // VirtualMachine Command Set + commandPacket + .setCommand(JDWPCommands.VirtualMachineCommandSet.CapabilitiesNewCommand); + + // Set command set. "1" - is ID of VirtualMachine Command Set + commandPacket + .setCommandSet(JDWPCommands.VirtualMachineCommandSet.CommandSetID); + + // Send packet + ReplyPacket replyPacket = checkReply(performCommand(commandPacket)); + + targetVMCapabilities = new Capabilities(); + + // Set capabilities + targetVMCapabilities.canWatchFieldModification = replyPacket + .getNextValueAsBoolean(); + targetVMCapabilities.canWatchFieldAccess = replyPacket + .getNextValueAsBoolean(); + targetVMCapabilities.canGetBytecodes = replyPacket + .getNextValueAsBoolean(); + targetVMCapabilities.canGetSyntheticAttribute = replyPacket + .getNextValueAsBoolean(); + targetVMCapabilities.canGetOwnedMonitorInfo = replyPacket + .getNextValueAsBoolean(); + targetVMCapabilities.canGetCurrentContendedMonitor = replyPacket + .getNextValueAsBoolean(); + targetVMCapabilities.canGetMonitorInfo = replyPacket + .getNextValueAsBoolean(); + targetVMCapabilities.canRedefineClasses = replyPacket + .getNextValueAsBoolean(); + targetVMCapabilities.canAddMethod = replyPacket.getNextValueAsBoolean(); + targetVMCapabilities.canUnrestrictedlyRedefineClasses = replyPacket + .getNextValueAsBoolean(); + targetVMCapabilities.canPopFrames = replyPacket.getNextValueAsBoolean(); + targetVMCapabilities.canUseInstanceFilters = replyPacket + .getNextValueAsBoolean(); + targetVMCapabilities.canGetSourceDebugExtension = replyPacket + .getNextValueAsBoolean(); + targetVMCapabilities.canRequestVMDeathEvent = replyPacket + .getNextValueAsBoolean(); + targetVMCapabilities.canSetDefaultStratum = replyPacket + .getNextValueAsBoolean(); + targetVMCapabilities.reserved16 = replyPacket.getNextValueAsBoolean(); + targetVMCapabilities.reserved17 = replyPacket.getNextValueAsBoolean(); + targetVMCapabilities.reserved18 = replyPacket.getNextValueAsBoolean(); + targetVMCapabilities.reserved19 = replyPacket.getNextValueAsBoolean(); + targetVMCapabilities.reserved20 = replyPacket.getNextValueAsBoolean(); + targetVMCapabilities.reserved21 = replyPacket.getNextValueAsBoolean(); + targetVMCapabilities.reserved22 = replyPacket.getNextValueAsBoolean(); + targetVMCapabilities.reserved23 = replyPacket.getNextValueAsBoolean(); + targetVMCapabilities.reserved24 = replyPacket.getNextValueAsBoolean(); + targetVMCapabilities.reserved25 = replyPacket.getNextValueAsBoolean(); + targetVMCapabilities.reserved26 = replyPacket.getNextValueAsBoolean(); + targetVMCapabilities.reserved27 = replyPacket.getNextValueAsBoolean(); + targetVMCapabilities.reserved28 = replyPacket.getNextValueAsBoolean(); + targetVMCapabilities.reserved29 = replyPacket.getNextValueAsBoolean(); + targetVMCapabilities.reserved30 = replyPacket.getNextValueAsBoolean(); + targetVMCapabilities.reserved31 = replyPacket.getNextValueAsBoolean(); + targetVMCapabilities.reserved32 = replyPacket.getNextValueAsBoolean(); + + return replyPacket; + } + + /** + * Resumes debuggee VM. + * + * @return ReplyPacket for corresponding command + */ + public ReplyPacket resume() { + CommandPacket commandPacket = new CommandPacket( + JDWPCommands.VirtualMachineCommandSet.CommandSetID, + JDWPCommands.VirtualMachineCommandSet.ResumeCommand); + + return checkReply(performCommand(commandPacket)); + } + + /** + * Resumes specified thread on target Virtual Machine + * + * @return ReplyPacket for corresponding command + */ + public ReplyPacket resumeThread(long threadID) { + CommandPacket commandPacket = new CommandPacket( + JDWPCommands.ThreadReferenceCommandSet.CommandSetID, + JDWPCommands.ThreadReferenceCommandSet.ResumeCommand); + + commandPacket.setNextValueAsThreadID(threadID); + return checkReply(performCommand(commandPacket)); + } + + /** + * Suspends debuggee VM. + * + * @return ReplyPacket for corresponding command + */ + public ReplyPacket suspend() { + CommandPacket commandPacket = new CommandPacket( + JDWPCommands.VirtualMachineCommandSet.CommandSetID, + JDWPCommands.VirtualMachineCommandSet.SuspendCommand); + + return checkReply(performCommand(commandPacket)); + } + + /** + * Suspends specified thread in debuggee VM. + * + * @return ReplyPacket for corresponding command + */ + public ReplyPacket suspendThread(long threadID) { + CommandPacket commandPacket = new CommandPacket( + JDWPCommands.ThreadReferenceCommandSet.CommandSetID, + JDWPCommands.ThreadReferenceCommandSet.SuspendCommand); + + commandPacket.setNextValueAsThreadID(threadID); + return checkReply(performCommand(commandPacket)); + } + + /** + * Disposes connection to debuggee VM. + * + * @return ReplyPacket for corresponding command + */ + public ReplyPacket dispose() { + // Create new command packet + CommandPacket commandPacket = new CommandPacket(); + commandPacket + .setCommand(JDWPCommands.VirtualMachineCommandSet.DisposeCommand); + commandPacket + .setCommandSet(JDWPCommands.VirtualMachineCommandSet.CommandSetID); + + // Send packet + return checkReply(performCommand(commandPacket)); + } + + /** + * Exits debuggee VM process. + * + * @return ReplyPacket for corresponding command + */ + public ReplyPacket exit(int exitCode) { + // Create new command packet + CommandPacket commandPacket = new CommandPacket(); + commandPacket + .setCommand(JDWPCommands.VirtualMachineCommandSet.ExitCommand); + commandPacket + .setCommandSet(JDWPCommands.VirtualMachineCommandSet.CommandSetID); + commandPacket.setNextValueAsInt(exitCode); + + // Send packet + return checkReply(performCommand(commandPacket)); + } + + /** + * Adjusts lengths for all VM-specific types. + * + * @return ReplyPacket for corresponding command + */ + public ReplyPacket adjustTypeLength() { + // Create new command packet + CommandPacket commandPacket = new CommandPacket(); + commandPacket + .setCommand(JDWPCommands.VirtualMachineCommandSet.IDSizesCommand); + commandPacket + .setCommandSet(JDWPCommands.VirtualMachineCommandSet.CommandSetID); + + // Send packet + ReplyPacket replyPacket = checkReply(performCommand(commandPacket)); + + // Get FieldIDSize from ReplyPacket + TypesLengths.setTypeLength(TypesLengths.FIELD_ID, replyPacket + .getNextValueAsInt()); + + // Get MethodIDSize from ReplyPacket + TypesLengths.setTypeLength(TypesLengths.METHOD_ID, replyPacket + .getNextValueAsInt()); + + // Get ObjectIDSize from ReplyPacket + TypesLengths.setTypeLength(TypesLengths.OBJECT_ID, replyPacket + .getNextValueAsInt()); + + // Get ReferenceTypeIDSize from ReplyPacket + TypesLengths.setTypeLength(TypesLengths.REFERENCE_TYPE_ID, replyPacket + .getNextValueAsInt()); + + // Get FrameIDSize from ReplyPacket + TypesLengths.setTypeLength(TypesLengths.FRAME_ID, replyPacket + .getNextValueAsInt()); + + // Adjust all other types lengths + TypesLengths.setTypeLength(TypesLengths.ARRAY_ID, TypesLengths + .getTypeLength(TypesLengths.OBJECT_ID)); + TypesLengths.setTypeLength(TypesLengths.STRING_ID, TypesLengths + .getTypeLength(TypesLengths.OBJECT_ID)); + TypesLengths.setTypeLength(TypesLengths.THREAD_ID, TypesLengths + .getTypeLength(TypesLengths.OBJECT_ID)); + TypesLengths.setTypeLength(TypesLengths.THREADGROUP_ID, TypesLengths + .getTypeLength(TypesLengths.OBJECT_ID)); + TypesLengths.setTypeLength(TypesLengths.LOCATION_ID, TypesLengths + .getTypeLength(TypesLengths.OBJECT_ID)); + TypesLengths.setTypeLength(TypesLengths.CLASS_ID, TypesLengths + .getTypeLength(TypesLengths.OBJECT_ID)); + TypesLengths.setTypeLength(TypesLengths.CLASSLOADER_ID, TypesLengths + .getTypeLength(TypesLengths.OBJECT_ID)); + TypesLengths.setTypeLength(TypesLengths.CLASSOBJECT_ID, TypesLengths + .getTypeLength(TypesLengths.OBJECT_ID)); + return replyPacket; + } + + /** + * Gets TypeID for specified type signature and type tag. + * + * @param typeSignature + * type signature + * @param classTypeTag + * type tag + * @return received TypeID + */ + public long getTypeID(String typeSignature, byte classTypeTag) { + int classes = 0; + byte refTypeTag = 0; + long typeID = -1; + + // Request referenceTypeID for exception + ReplyPacket classReference = getClassBySignature(typeSignature); + + // Get referenceTypeID from received packet + classes = classReference.getNextValueAsInt(); + for (int i = 0; i < classes; i++) { + refTypeTag = classReference.getNextValueAsByte(); + if (refTypeTag == classTypeTag) { + typeID = classReference.getNextValueAsReferenceTypeID(); + classReference.getNextValueAsInt(); + break; + } else { + classReference.getNextValueAsReferenceTypeID(); + classReference.getNextValueAsInt(); + refTypeTag = 0; + } + } + return typeID; + } + + /** + * Gets ClassID for specified class signature. + * + * @param classSignature + * class signature + * @return received ClassID + */ + public long getClassID(String classSignature) { + return getTypeID(classSignature, JDWPConstants.TypeTag.CLASS); + } + + /** + * Gets ThreadID for specified thread name. + * + * @param threadName + * thread name + * @return received ThreadID + */ + public long getThreadID(String threadName) { + ReplyPacket request = null; + long threadID = -1; + long thread = -1; + String name = null; + int threads = -1; + + // Get All Threads IDs + request = getAllThreadID(); + + // Get thread ID for threadName + threads = request.getNextValueAsInt(); + for (int i = 0; i < threads; i++) { + thread = request.getNextValueAsThreadID(); + name = getThreadName(thread); + if (threadName.equals(name)) { + threadID = thread; + break; + } + } + + return threadID; + } + + /** + * Returns all running thread IDs. + * + * @return received reply packet + */ + public ReplyPacket getAllThreadID() { + // Create new command packet + CommandPacket commandPacket = new CommandPacket( + JDWPCommands.VirtualMachineCommandSet.CommandSetID, + JDWPCommands.VirtualMachineCommandSet.AllThreadsCommand); + + return checkReply(performCommand(commandPacket)); + } + + /** + * Gets class signature for specified class ID. + * + * @param classID + * class ID + * @return received class signature + */ + public String getClassSignature(long classID) { + // Create new command packet + CommandPacket commandPacket = new CommandPacket( + JDWPCommands.ReferenceTypeCommandSet.CommandSetID, + JDWPCommands.ReferenceTypeCommandSet.SignatureCommand); + commandPacket.setNextValueAsReferenceTypeID(classID); + ReplyPacket replyPacket = checkReply(performCommand(commandPacket)); + return replyPacket.getNextValueAsString(); + } + + /** + * Returns thread name for specified threadID. + * + * @param threadID + * thread ID + * @return thread name + */ + public String getThreadName(long threadID) { + // Create new command packet + CommandPacket commandPacket = new CommandPacket( + JDWPCommands.ThreadReferenceCommandSet.CommandSetID, + JDWPCommands.ThreadReferenceCommandSet.NameCommand); + commandPacket.setNextValueAsThreadID(threadID); + ReplyPacket replyPacket = checkReply(performCommand(commandPacket)); + return replyPacket.getNextValueAsString(); + } + + /** + * Returns thread status for specified threadID. + * + * @param threadID + * thread ID + * @return thread status + */ + public int getThreadStatus(long threadID) { + CommandPacket commandPacket = new CommandPacket( + JDWPCommands.ThreadReferenceCommandSet.CommandSetID, + JDWPCommands.ThreadReferenceCommandSet.StatusCommand); + commandPacket.setNextValueAsThreadID(threadID); + ReplyPacket replyPacket = checkReply(performCommand(commandPacket)); + return replyPacket.getNextValueAsInt(); + } + + /** + * Returns name of thread group for specified groupID + * + * @param groupID + * thread group ID + * + * @return name of thread group + */ + public String getThreadGroupName(long groupID) { + // Create new command packet + CommandPacket commandPacket = new CommandPacket( + JDWPCommands.ThreadGroupReferenceCommandSet.CommandSetID, + JDWPCommands.ThreadGroupReferenceCommandSet.NameCommand); + commandPacket.setNextValueAsReferenceTypeID(groupID); + ReplyPacket replyPacket = checkReply(performCommand(commandPacket)); + return replyPacket.getNextValueAsString(); + } + + /** + * Gets InterfaceID for specified interface signature. + * + * @param interfaceSignature + * interface signature + * @return received ClassID + */ + public long getInterfaceID(String interfaceSignature) { + return getTypeID(interfaceSignature, JDWPConstants.TypeTag.INTERFACE); + } + + /** + * Gets ArrayID for specified array signature. + * + * @param arraySignature + * array signature + * @return received ArrayID + */ + public long getArrayID(String arraySignature) { + return getTypeID(arraySignature, JDWPConstants.TypeTag.INTERFACE); + } + + /** + * Gets RequestID from specified ReplyPacket. + * + * @param request + * ReplyPacket with RequestID + * @return received RequestID + */ + public int getRequestID(ReplyPacket request) { + return request.getNextValueAsInt(); + } + + /** + * Returns FieldID for specified class and field name. + * + * @param classID + * ClassID to find field + * @param fieldName + * field name + * @return received FieldID + */ + public long getFieldID(long classID, String fieldName) { + ReplyPacket reply = getFieldsInClass(classID); + return getFieldID(reply, fieldName); + } + + /** + * Gets FieldID from ReplyPacket. + * + * @param request + * ReplyPacket for request + * @param field + * field name to get ID for + * @return received FieldID + */ + public long getFieldID(ReplyPacket request, String field) { + long fieldID = -1; + String fieldName; + // Get fieldID from received packet + int count = request.getNextValueAsInt(); + for (int i = 0; i < count; i++) { + fieldID = request.getNextValueAsFieldID(); + fieldName = request.getNextValueAsString(); + if (field.equals(fieldName)) { + request.getNextValueAsString(); + request.getNextValueAsInt(); + break; + } else { + request.getNextValueAsString(); + request.getNextValueAsInt(); + fieldID = 0; + fieldName = null; + } + } + return fieldID; + } + + /** + * Gets Method ID for specified class and method name. + * + * @param classID + * class to find method + * @param methodName + * method name + * @return received MethodID + */ + public long getMethodID(long classID, String methodName) { + ReplyPacket reply; + int declared = 0; + String method = null; + long methodID = -1; + + // Get Method reference ID + reply = getMethods(classID); + + // Get methodID from received packet + declared = reply.getNextValueAsInt(); + for (int i = 0; i < declared; i++) { + methodID = reply.getNextValueAsMethodID(); + method = reply.getNextValueAsString(); + if (methodName.equals(method)) { + // If this method name is the same as requested + reply.getNextValueAsString(); + reply.getNextValueAsInt(); + break; + } else { + // If this method name is not the requested one + reply.getNextValueAsString(); + reply.getNextValueAsInt(); + methodID = -1; + method = null; + } + } + return methodID; + } + + /** + * Returns method name for specified pair of classID and methodID. + * + * @param classID + * @param methodID + * @return method name + */ + public String getMethodName(long classID, long methodID) { + CommandPacket packet = new CommandPacket( + JDWPCommands.ReferenceTypeCommandSet.CommandSetID, + JDWPCommands.ReferenceTypeCommandSet.MethodsCommand); + packet.setNextValueAsReferenceTypeID(classID); + ReplyPacket reply = performCommand(packet); + + int declared = reply.getNextValueAsInt(); + long mID; + String value = null; + String methodName = ""; + for (int i = 0; i < declared; i++) { + mID = reply.getNextValueAsMethodID(); + methodName = reply.getNextValueAsString(); + reply.getNextValueAsString(); + reply.getNextValueAsInt(); + if (mID == methodID) { + value = methodName; + break; + } + } + return value; + } + + /** + * Sets ClassPrepare event request for given class name pattern. + * + * @param classRegexp + * Required class pattern. Matches are limited to exact matches + * of the given class pattern and matches of patterns that begin + * or end with '*'; for example, "*.Foo" or "java.*". + * @return ReplyPacket for setting request. + */ + public ReplyPacket setClassPrepared(String classRegexp) { + // Prepare corresponding event + byte eventKind = JDWPConstants.EventKind.CLASS_PREPARE; + byte suspendPolicy = JDWPConstants.SuspendPolicy.ALL; + // EventMod[] mods = new EventMod[1]; + EventMod[] mods = new EventMod[] { new EventMod() }; + mods[0].classPattern = classRegexp; + mods[0].modKind = EventMod.ModKind.ClassMatch; + Event event = new Event(eventKind, suspendPolicy, mods); + + // Set event + return setEvent(event); + } + + /** + * Set ClassPrepare event request for given class ID. + * + * @param referenceTypeID + * class referenceTypeID + * @return ReplyPacket for setting request + */ + public ReplyPacket setClassPrepared(long referenceTypeID) { + // Prepare corresponding event + byte eventKind = JDWPConstants.EventKind.CLASS_PREPARE; + byte suspendPolicy = JDWPConstants.SuspendPolicy.ALL; + // EventMod[] mods = new EventMod[1]; + EventMod[] mods = new EventMod[] { new EventMod() }; + mods[0].clazz = referenceTypeID; + mods[0].modKind = EventMod.ModKind.ClassOnly; + Event event = new Event(eventKind, suspendPolicy, mods); + + // Set event + return setEvent(event); + } + + /** + * Sets ClassUnload event request for given class signature. + * + * @param classSignature + * class signature + * @return ReplyPacket for setting request + */ + public ReplyPacket setClassUnload(String classSignature) { + long typeID; + + // Request referenceTypeID for class + typeID = getClassID(classSignature); + + // Set corresponding event + return setClassUnload(typeID); + } + + /** + * Set ClassUnload event request for given class ID. + * + * @param referenceTypeID + * class referenceTypeID + * @return ReplyPacket for setting request + */ + public ReplyPacket setClassUnload(long referenceTypeID) { + // Prepare corresponding event + byte eventKind = JDWPConstants.EventKind.CLASS_UNLOAD; + byte suspendPolicy = JDWPConstants.SuspendPolicy.ALL; + // EventMod[] mods = new EventMod[1]; + EventMod[] mods = new EventMod[] { new EventMod() }; + mods[0].clazz = referenceTypeID; + mods[0].modKind = EventMod.ModKind.ClassOnly; + Event event = new Event(eventKind, suspendPolicy, mods); + + // Set event + return setEvent(event); + } + + /** + * Sets ClassLoad event request for given class signature. + * + * @param classSignature + * class signature + * @return ReplyPacket for setting request + */ + public ReplyPacket setClassLoad(String classSignature) { + long typeID; + + // Request referenceTypeID for class + typeID = getClassID(classSignature); + + // Set corresponding event + return setClassLoad(typeID); + } + + /** + * Set ClassLoad event request for given class ID. + * + * @param referenceTypeID + * class referenceTypeID + * @return ReplyPacket for setting request + */ + public ReplyPacket setClassLoad(long referenceTypeID) { + // Prepare corresponding event + byte eventKind = JDWPConstants.EventKind.CLASS_LOAD; + byte suspendPolicy = JDWPConstants.SuspendPolicy.ALL; + EventMod[] mods = new EventMod[] { new EventMod() }; + mods[0].clazz = referenceTypeID; + mods[0].modKind = EventMod.ModKind.ClassOnly; + Event event = new Event(eventKind, suspendPolicy, mods); + + // Set event + return setEvent(event); + } + + /** + * Set event request for given event. + * + * @param event + * event to set request for + * @return ReplyPacket for setting request + */ + public ReplyPacket setEvent(Event event) { + // Create new command packet + CommandPacket commandPacket = new CommandPacket( + JDWPCommands.EventRequestCommandSet.CommandSetID, + JDWPCommands.EventRequestCommandSet.SetCommand); + + // Set eventKind + commandPacket.setNextValueAsByte(event.eventKind); + // Set suspendPolicy + commandPacket.setNextValueAsByte(event.suspendPolicy); + + // Set modifiers + commandPacket.setNextValueAsInt(event.modifiers); + + for (int i = 0; i < event.modifiers; i++) { + + commandPacket.setNextValueAsByte(event.mods[i].modKind); + + switch (event.mods[i].modKind) { + case EventMod.ModKind.Count: { + // Case Count + commandPacket.setNextValueAsInt(event.mods[i].count); + break; + } + case EventMod.ModKind.Conditional: { + // Case Conditional + commandPacket.setNextValueAsInt(event.mods[i].exprID); + break; + } + case EventMod.ModKind.ThreadOnly: { + // Case ThreadOnly + commandPacket.setNextValueAsThreadID(event.mods[i].thread); + break; + } + case EventMod.ModKind.ClassOnly: { + // Case ClassOnly + commandPacket + .setNextValueAsReferenceTypeID(event.mods[i].clazz); + break; + } + case EventMod.ModKind.ClassMatch: { + // Case ClassMatch + commandPacket.setNextValueAsString(event.mods[i].classPattern); + break; + } + case EventMod.ModKind.ClassExclude: { + // Case ClassExclude + commandPacket.setNextValueAsString(event.mods[i].classPattern); + break; + } + case EventMod.ModKind.LocationOnly: { + // Case LocationOnly + commandPacket.setNextValueAsLocation(event.mods[i].loc); + break; + } + case EventMod.ModKind.ExceptionOnly: + // Case ExceptionOnly + commandPacket + .setNextValueAsReferenceTypeID(event.mods[i].exceptionOrNull); + commandPacket.setNextValueAsBoolean(event.mods[i].caught); + commandPacket.setNextValueAsBoolean(event.mods[i].uncaught); + break; + case EventMod.ModKind.FieldOnly: { + // Case FieldOnly + commandPacket + .setNextValueAsReferenceTypeID(event.mods[i].declaring); + commandPacket.setNextValueAsFieldID(event.mods[i].fieldID); + break; + } + case EventMod.ModKind.Step: { + // Case Step + commandPacket.setNextValueAsThreadID(event.mods[i].thread); + commandPacket.setNextValueAsInt(event.mods[i].size); + commandPacket.setNextValueAsInt(event.mods[i].depth); + break; + } + case EventMod.ModKind.InstanceOnly: { + // Case InstanceOnly + commandPacket.setNextValueAsObjectID(event.mods[i].instance); + break; + } + } + } + + // Send packet + return checkReply(performCommand(commandPacket)); + } + + /** + * Gets method reference by signature. + * + * @param classReferenceTypeID + * class referenceTypeID. + * @return ReplyPacket for corresponding command + */ + public ReplyPacket getMethods(long classReferenceTypeID) { + // Create new command packet + CommandPacket commandPacket = new CommandPacket(); + + // Set command. "5" - is ID of Methods command in ReferenceType Command + // Set + commandPacket + .setCommand(JDWPCommands.ReferenceTypeCommandSet.MethodsCommand); + + // Set command set. "2" - is ID of ReferenceType Command Set + commandPacket + .setCommandSet(JDWPCommands.ReferenceTypeCommandSet.CommandSetID); + + // Set outgoing data + // Set referenceTypeID + commandPacket.setNextValueAsObjectID(classReferenceTypeID); + + // Send packet + return checkReply(performCommand(commandPacket)); + } + + /** + * Gets class reference by signature. + * + * @param classSignature + * class signature. + * @return ReplyPacket for corresponding command + */ + public ReplyPacket getClassBySignature(String classSignature) { + CommandPacket commandPacket = new CommandPacket( + JDWPCommands.VirtualMachineCommandSet.CommandSetID, + JDWPCommands.VirtualMachineCommandSet.ClassesBySignatureCommand); + commandPacket.setNextValueAsString(classSignature); + return checkReply(performCommand(commandPacket)); + } + + /** + * Gets class fields by class referenceTypeID. + * + * @param referenceTypeID + * class referenceTypeID. + * @return ReplyPacket for corresponding command + */ + public ReplyPacket getFieldsInClass(long referenceTypeID) { + CommandPacket commandPacket = new CommandPacket( + JDWPCommands.ReferenceTypeCommandSet.CommandSetID, + JDWPCommands.ReferenceTypeCommandSet.FieldsCommand); + commandPacket.setNextValueAsReferenceTypeID(referenceTypeID); + return checkReply(performCommand(commandPacket)); + } + + /** + * Sets exception event request for given exception class signature. + * + * @param exceptionSignature + * exception signature. + * @param caught + * is exception caught + * @param uncaught + * is exception uncaught + * @return ReplyPacket for corresponding command + */ + public ReplyPacket setException(String exceptionSignature, boolean caught, + boolean uncaught) { + // Request referenceTypeID for exception + long typeID = getClassID(exceptionSignature); + return setException(typeID, caught, uncaught); + } + + /** + * Sets exception event request for given exception class ID. + * + * @param exceptionID + * exception referenceTypeID. + * @param caught + * is exception caught + * @param uncaught + * is exception uncaught + * @return ReplyPacket for corresponding command + */ + public ReplyPacket setException(long exceptionID, boolean caught, + boolean uncaught) { + // Prepare corresponding event + byte eventKind = JDWPConstants.EventKind.EXCEPTION; + byte suspendPolicy = JDWPConstants.SuspendPolicy.ALL; + EventMod[] mods = new EventMod[1]; + mods[0] = new EventMod(); + mods[0].modKind = EventMod.ModKind.ExceptionOnly; + mods[0].caught = caught; + mods[0].uncaught = uncaught; + mods[0].exceptionOrNull = exceptionID; + Event event = new Event(eventKind, suspendPolicy, mods); + + return setEvent(event); + } + + /** + * Sets exception event request for given exception class signature. + * + * @param exceptionSignature + * exception signature. + * @param caught + * is exception caught + * @param uncaught + * is exception uncaught + * @param count + * Limit the requested event to be reported at most once after a + * given number of occurrences + * @return ReplyPacket for corresponding command + */ + public ReplyPacket setCountableException(String exceptionSignature, + boolean caught, boolean uncaught, int count) { + // Request referenceTypeID for exception + long exceptionID = getClassID(exceptionSignature); + byte eventKind = JDWPConstants.EventKind.EXCEPTION; + byte suspendPolicy = JDWPConstants.SuspendPolicy.ALL; + EventMod[] mods = new EventMod[2]; + mods[0] = new EventMod(); + mods[0].modKind = EventMod.ModKind.ExceptionOnly; + mods[0].caught = caught; + mods[0].uncaught = uncaught; + mods[0].exceptionOrNull = exceptionID; + + mods[1] = new EventMod(); + mods[1].modKind = EventMod.ModKind.Count; + mods[1].count = count; + Event event = new Event(eventKind, suspendPolicy, mods); + + return setEvent(event); + } + + /** + * Sets METHOD_ENTRY event request for specified class name pattern. + * + * @param classRegexp + * class name pattern or null for no pattern + * + * @return ReplyPacket for corresponding command + */ + public ReplyPacket setMethodEntry(String classRegexp) { + // Prepare corresponding event + byte eventKind = JDWPConstants.EventKind.METHOD_ENTRY; + byte suspendPolicy = JDWPConstants.SuspendPolicy.ALL; + EventMod[] mods = null; + if (classRegexp == null) { + mods = new EventMod[0]; + } else { + mods = new EventMod[1]; + mods[0] = new EventMod(); + mods[0].modKind = EventMod.ModKind.ClassMatch; + mods[0].classPattern = classRegexp; + } + Event event = new Event(eventKind, suspendPolicy, mods); + + return setEvent(event); + } + + /** + * Sets METHOD_ENTRY event request for specified class name pattern. + * + * @param classRegexp + * class name pattern or null for no pattern + * @param count + * Limit the requested event to be reported at most once after a + * given number of occurrences + * @return ReplyPacket for corresponding command + */ + public ReplyPacket setCountableMethodEntry(String classRegexp, int count) { + byte eventKind = JDWPConstants.EventKind.METHOD_ENTRY; + byte suspendPolicy = JDWPConstants.SuspendPolicy.ALL; + EventMod[] mods = null; + if (classRegexp == null) { + mods = new EventMod[] { new EventMod() }; + mods[0].modKind = EventMod.ModKind.Count; + mods[0].count = count; + } else { + mods = new EventMod[2]; + mods[0] = new EventMod(); + mods[0].modKind = EventMod.ModKind.ClassMatch; + mods[0].classPattern = classRegexp; + + mods[1] = new EventMod(); + mods[1].modKind = EventMod.ModKind.Count; + mods[1].count = count; + } + Event event = new Event(eventKind, suspendPolicy, mods); + + return setEvent(event); + } + + /** + * Sets METHOD_EXIT event request for specified class name pattern. + * + * @param classRegexp + * class name pattern or null for no pattern + * + * @return ReplyPacket for corresponding command + */ + public ReplyPacket setMethodExit(String classRegexp) { + // Prepare corresponding event + byte eventKind = JDWPConstants.EventKind.METHOD_EXIT; + byte suspendPolicy = JDWPConstants.SuspendPolicy.ALL; + EventMod[] mods = null; + if (classRegexp == null) { + mods = new EventMod[0]; + } else { + mods = new EventMod[1]; + mods[0] = new EventMod(); + mods[0].modKind = EventMod.ModKind.ClassMatch; + mods[0].classPattern = classRegexp; + } + Event event = new Event(eventKind, suspendPolicy, mods); + + return setEvent(event); + } + + /** + * Sets METHOD_EXIT event request for specified class name pattern. + * + * @param classRegexp + * classRegexp class name pattern or null for no pattern + * @param count + * Limit the requested event to be reported at most once after a + * given number of occurrences + * @return ReplyPacket for corresponding command + */ + public ReplyPacket setCountableMethodExit(String classRegexp, int count) { + byte eventKind = JDWPConstants.EventKind.METHOD_EXIT; + byte suspendPolicy = JDWPConstants.SuspendPolicy.ALL; + EventMod[] mods = null; + if (classRegexp == null) { + mods = new EventMod[] { new EventMod() }; + mods[0].modKind = EventMod.ModKind.Count; + mods[0].count = count; + } else { + mods = new EventMod[2]; + mods[0] = new EventMod(); + mods[0].modKind = EventMod.ModKind.ClassMatch; + mods[0].classPattern = classRegexp; + + mods[1] = new EventMod(); + mods[1].modKind = EventMod.ModKind.Count; + mods[1].count = count; + } + Event event = new Event(eventKind, suspendPolicy, mods); + + return setEvent(event); + + } + + /** + * Sets field access event request for specified class signature and field + * name. + * + * @param classTypeTag + * class Type Tag (class/interface/array) + * @param classSignature + * class signature + * @param fieldName + * field name + * @return ReplyPacket if breakpoint is set + * @throws ReplyErrorCodeException + */ + public ReplyPacket setFieldAccess(String classSignature, byte classTypeTag, + String fieldName) throws ReplyErrorCodeException { + ReplyPacket request = null; + long typeID = -1; + long fieldID = -1; + + // Request referenceTypeID for class + typeID = getClassID(classSignature); + + // Request fields in class + request = getFieldsInClass(typeID); + + // Get fieldID from received packet + fieldID = getFieldID(request, fieldName); + + // Prepare corresponding event + byte eventKind = JDWPConstants.EventKind.FIELD_ACCESS; + byte suspendPolicy = JDWPConstants.SuspendPolicy.ALL; + // EventMod[] mods = new EventMod[1]; + EventMod[] mods = new EventMod[] { new EventMod() }; + mods[0].fieldID = fieldID; + mods[0].declaring = typeID; + mods[0].modKind = EventMod.ModKind.FieldOnly; + Event event = new Event(eventKind, suspendPolicy, mods); + + // Set exception + return setEvent(event); + } + + /** + * Sets field modification event request for specified class signature and + * field name. + * + * @param classTypeTag + * class Type Tag (class/interface/array) + * @param classSignature + * class signature + * @param fieldName + * field name + * @return ReplyPacket for corresponding command + * @throws ReplyErrorCodeException + */ + public ReplyPacket setFieldModification(String classSignature, + byte classTypeTag, String fieldName) throws ReplyErrorCodeException { + ReplyPacket request = null; + long typeID = -1; + long fieldID = -1; + + // Request referenceTypeID for class + typeID = getClassID(classSignature); + + // Request fields in class + request = getFieldsInClass(typeID); + + // Get fieldID from received packet + fieldID = getFieldID(request, fieldName); + + // Prepare corresponding event + byte eventKind = JDWPConstants.EventKind.FIELD_MODIFICATION; + byte suspendPolicy = JDWPConstants.SuspendPolicy.ALL; + // EventMod[] mods = new EventMod[1]; + EventMod[] mods = new EventMod[] { new EventMod() }; + mods[0].fieldID = fieldID; + mods[0].declaring = typeID; + mods[0].modKind = EventMod.ModKind.FieldOnly; + Event event = new Event(eventKind, suspendPolicy, mods); + + // Set event + return setEvent(event); + } + + /** + * Sets step event request for given thread name. + * + * @param threadName + * thread name + * @param stepSize + * @param stepDepth + * @return ReplyPacket for corresponding command + */ + public ReplyPacket setStep(String threadName, int stepSize, int stepDepth) { + long typeID = -1; + + // Request referenceTypeID for class + typeID = getThreadID(threadName); + + // Prepare corresponding event + byte eventKind = JDWPConstants.EventKind.SINGLE_STEP; + byte suspendPolicy = JDWPConstants.SuspendPolicy.ALL; + // EventMod[] mods = new EventMod[1]; + EventMod[] mods = new EventMod[] { new EventMod() }; + mods[0].thread = typeID; + mods[0].modKind = EventMod.ModKind.Step; + mods[0].size = stepSize; + mods[0].depth = stepDepth; + Event event = new Event(eventKind, suspendPolicy, mods); + + // Set event + return setEvent(event); + } + + /** + * Sets SINGLE_STEP event request for classes whose name does not match the + * given restricted regular expression + * + * @param classRegexp + * Disallowed class patterns. Matches are limited to exact + * matches of the given class pattern and matches of patterns + * that begin or end with '*'; for example, "*.Foo" or "java.*". + * @param stepSize + * @param stepDepth + * @return ReplyPacket for setting request. + */ + public ReplyPacket setStep(String[] classRegexp, long threadID, + int stepSize, int stepDepth) { + // Prepare corresponding event + byte eventKind = JDWPConstants.EventKind.SINGLE_STEP; + byte suspendPolicy = JDWPConstants.SuspendPolicy.ALL; + int modsSize = classRegexp.length + 1; + EventMod[] mods = new EventMod[modsSize]; + for (int i = 0; i < classRegexp.length; i++) { + mods[i] = new EventMod(); + mods[i].classPattern = classRegexp[i]; + mods[i].modKind = EventMod.ModKind.ClassExclude; + } + + int index = modsSize - 1; + mods[index] = new EventMod(); + mods[index].modKind = EventMod.ModKind.Step; + mods[index].thread = threadID; + mods[index].size = stepSize; + mods[index].depth = stepDepth; + + Event event = new Event(eventKind, suspendPolicy, mods); + + // Set event + return setEvent(event); + } + + /** + * Sets THREAD_START event request. + * + * @return ReplyPacket for corresponding command + */ + public ReplyPacket setThreadStart() { + // Prepare corresponding event + byte eventKind = JDWPConstants.EventKind.THREAD_START; + byte suspendPolicy = JDWPConstants.SuspendPolicy.ALL; + EventMod[] mods = new EventMod[0]; + Event event = new Event(eventKind, suspendPolicy, mods); + + return setEvent(event); + } + + /** + * Sets THREAD_END event request. + * + * @return ReplyPacket for corresponding command + */ + public ReplyPacket setThreadEnd() { + // Prepare corresponding event + byte eventKind = JDWPConstants.EventKind.THREAD_END; + byte suspendPolicy = JDWPConstants.SuspendPolicy.ALL; + EventMod[] mods = new EventMod[0]; + Event event = new Event(eventKind, suspendPolicy, mods); + + return setEvent(event); + } + + /** + * Clear an event request for specified request ID. + * + * @param eventKind + * event type to clear + * @param requestID + * request ID to clear + * @return ReplyPacket for corresponding command + */ + public ReplyPacket clearEvent(byte eventKind, int requestID) { + // Create new command packet + CommandPacket commandPacket = new CommandPacket(); + + // Set command. "2" - is ID of Clear command in EventRequest Command Set + commandPacket + .setCommand(JDWPCommands.EventRequestCommandSet.ClearCommand); + + // Set command set. "15" - is ID of EventRequest Command Set + commandPacket + .setCommandSet(JDWPCommands.EventRequestCommandSet.CommandSetID); + + // Set outgoing data + // Set event type to clear + commandPacket.setNextValueAsByte(eventKind); + + // Set ID of request to clear + commandPacket.setNextValueAsInt(requestID); + + // Send packet + return checkReply(performCommand(commandPacket)); + } + + /** + * Sends CommandPacket to debuggee VM and waits for ReplyPacket using + * default timeout. All thrown exceptions are wrapped into + * TestErrorException. Consider using checkReply() for checking error code + * in reply packet. + * + * @param command + * Command packet to be sent + * @return received ReplyPacket + */ + public ReplyPacket performCommand(CommandPacket command) + throws TestErrorException { + ReplyPacket replyPacket = null; + try { + replyPacket = packetDispatcher.performCommand(command); + } catch (IOException e) { + throw new TestErrorException(e); + } catch (InterruptedException e) { + throw new TestErrorException(e); + } + + return replyPacket; + } + + /** + * Sends CommandPacket to debuggee VM and waits for ReplyPacket using + * specified timeout. + * + * @param command + * Command packet to be sent + * @param timeout + * Timeout in milliseconds for waiting reply packet + * @return received ReplyPacket + * @throws InterruptedException + * @throws IOException + * @throws TimeoutException + */ + public ReplyPacket performCommand(CommandPacket command, long timeout) + throws IOException, InterruptedException, TimeoutException { + + return packetDispatcher.performCommand(command, timeout); + } + + /** + * Sends CommandPacket to debuggee VM without waiting for the reply. This + * method is intended for special cases when there is need to divide + * command's performing into two actions: command's sending and receiving + * reply (e.g. for asynchronous JDWP commands' testing). After this method + * the 'receiveReply()' method must be used latter for receiving reply for + * sent command. It is NOT recommended to use this method for usual cases - + * 'performCommand()' method must be used. + * + * @param command + * Command packet to be sent + * @return command ID of sent command + * @throws IOException + * if any connection error occurred + */ + public int sendCommand(CommandPacket command) throws IOException { + return packetDispatcher.sendCommand(command); + } + + /** + * Waits for reply for command which was sent before by 'sendCommand()' + * method. Default timeout is used as time limit for waiting. This method + * (jointly with 'sendCommand()') is intended for special cases when there + * is need to divide command's performing into two actions: command's + * sending and receiving reply (e.g. for asynchronous JDWP commands' + * testing). It is NOT recommended to use 'sendCommand()- receiveReply()' + * pair for usual cases - 'performCommand()' method must be used. + * + * @param commandId + * Command ID of sent before command, reply from which is + * expected to be received + * @return received ReplyPacket + * @throws IOException + * if any connection error occurred + * @throws InterruptedException + * if reply packet's waiting was interrupted + * @throws TimeoutException + * if timeout exceeded + */ + public ReplyPacket receiveReply(int commandId) throws InterruptedException, + IOException, TimeoutException { + return packetDispatcher.receiveReply(commandId, config.getTimeout()); + } + + /** + * Waits for reply for command which was sent before by 'sendCommand()' + * method. Specified timeout is used as time limit for waiting. This method + * (jointly with 'sendCommand()') is intended for special cases when there + * is need to divide command's performing into two actions: command's + * sending and receiving reply (e.g. for asynchronous JDWP commands' + * testing). It is NOT recommended to use 'sendCommand()- receiveReply()' + * pair for usual cases - 'performCommand()' method must be used. + * + * @param commandId + * Command ID of sent before command, reply from which is + * expected to be received + * @param timeout + * Specified timeout in milliseconds to wait for reply + * @return received ReplyPacket + * @throws IOException + * if any connection error occurred + * @throws InterruptedException + * if reply packet's waiting was interrupted + * @throws TimeoutException + * if timeout exceeded + */ + public ReplyPacket receiveReply(int commandId, long timeout) + throws InterruptedException, IOException, TimeoutException { + return packetDispatcher.receiveReply(commandId, timeout); + } + + /** + * Waits for EventPacket using default timeout. All thrown exceptions are + * wrapped into TestErrorException. + * + * @return received EventPacket + */ + public EventPacket receiveEvent() throws TestErrorException { + try { + return receiveEvent(config.getTimeout()); + } catch (IOException e) { + throw new TestErrorException(e); + } catch (InterruptedException e) { + throw new TestErrorException(e); + } + } + + /** + * Waits for EventPacket using specified timeout. + * + * @param timeout + * Timeout in milliseconds to wait for event + * @return received EventPacket + * @throws IOException + * @throws InterruptedException + * @throws TimeoutException + */ + public EventPacket receiveEvent(long timeout) throws IOException, + InterruptedException, TimeoutException { + + return packetDispatcher.receiveEvent(timeout); + } + + /** + * Waits for expected event kind using default timeout. Throws + * TestErrorException if received event is not of expected kind or not a + * single event in the received event set. + * + * @param eventKind + * Type of expected event - + * @see JDWPConstants.EventKind + * @return received EventPacket + */ + public EventPacket receiveCertainEvent(byte eventKind) + throws TestErrorException { + + EventPacket eventPacket = receiveEvent(); + ParsedEvent[] parsedEvents = ParsedEvent.parseEventPacket(eventPacket); + + if (parsedEvents.length == 1 + && parsedEvents[0].getEventKind() == eventKind) + return eventPacket; + + switch (parsedEvents.length) { + case (0): + throw new TestErrorException( + "Unexpected event received: zero length"); + case (1): + throw new TestErrorException("Unexpected event received: " + + parsedEvents[0].getEventKind()); + default: + throw new TestErrorException( + "Unexpected event received: Event was grouped in a composite event"); + } + } + + /** + * Returns JDWP connection channel used by this VmMirror. + * + * @return connection channel + */ + public TransportWrapper getConnection() { + return connection; + } + + /** + * Sets established connection channel to be used with this VmMirror and + * starts reading packets. + * + * @param connection + * connection channel to be set + */ + public void setConnection(TransportWrapper connection) { + this.connection = connection; + packetDispatcher = new PacketDispatcher(connection, config, logWriter); + } + + /** + * Closes connection channel used with this VmMirror and stops reading + * packets. + * + */ + public void closeConnection() throws IOException { + if (connection != null && connection.isOpen()) + connection.close(); + + // wait for packetDispatcher is closed + if (packetDispatcher != null) { + try { + packetDispatcher.join(); + } catch (InterruptedException e) { + // do nothing but print a stack trace + e.printStackTrace(); + } + } + } + + /** + * Returns the count of frames on this thread's stack + * + * @param threadID + * The thread object ID. + * @return The count of frames on this thread's stack + */ + public final int getFrameCount(long threadID) { + CommandPacket command = new CommandPacket( + JDWPCommands.ThreadReferenceCommandSet.CommandSetID, + JDWPCommands.ThreadReferenceCommandSet.FrameCountCommand); + command.setNextValueAsThreadID(threadID); + ReplyPacket reply = checkReply(performCommand(command)); + return reply.getNextValueAsInt(); + } + + /** + * Returns a list containing all frames of a certain thread + * + * @param threadID + * ID of the thread + * @return A list of frames + */ + public final List getAllThreadFrames(long threadID) { + if (!isThreadSuspended(threadID)) { + return new ArrayList(0); + } + + ReplyPacket reply = getThreadFrames(threadID, 0, -1); + int framesCount = reply.getNextValueAsInt(); + if (framesCount == 0) { + return new ArrayList(0); + } + + ArrayList frames = new ArrayList(framesCount); + for (int i = 0; i < framesCount; i++) { + Frame frame = new Frame(); + frame.setThreadID(threadID); + frame.setID(reply.getNextValueAsFrameID()); + frame.setLocation(reply.getNextValueAsLocation()); + frames.add(frame); + } + + return frames; + } + + /** + * Returns a set of frames of a certain suspended thread + * + * @param threadID + * ID of the thread whose frames to obtain + * @param startIndex + * The index of the first frame to retrieve. + * @param length + * The count of frames to retrieve (-1 means all remaining). + * @return ReplyPacket for corresponding command + */ + public final ReplyPacket getThreadFrames(long threadID, int startIndex, + int length) { + CommandPacket command = new CommandPacket( + JDWPCommands.ThreadReferenceCommandSet.CommandSetID, + JDWPCommands.ThreadReferenceCommandSet.FramesCommand); + command.setNextValueAsThreadID(threadID); + command.setNextValueAsInt(startIndex); // start frame's index + command.setNextValueAsInt(length); // get all remaining frames; + return checkReply(performCommand(command)); + } + + /** + * Returns variable information for the method + * + * @param classID + * The class ID + * @param methodID + * The method ID + * @return A list containing all variables (arguments and locals) declared + * within the method. + */ + public final List getVariableTable(long classID, long methodID) { + CommandPacket command = new CommandPacket( + JDWPCommands.MethodCommandSet.CommandSetID, + JDWPCommands.MethodCommandSet.VariableTableCommand); + command.setNextValueAsReferenceTypeID(classID); + command.setNextValueAsMethodID(methodID); + // ReplyPacket reply = + // debuggeeWrapper.vmMirror.checkReply(debuggeeWrapper.vmMirror.performCommand(command)); + ReplyPacket reply = performCommand(command); + if (reply.getErrorCode() == JDWPConstants.Error.ABSENT_INFORMATION + || reply.getErrorCode() == JDWPConstants.Error.NATIVE_METHOD) { + return null; + } + + checkReply(reply); + + reply.getNextValueAsInt(); // argCnt, is not used + int slots = reply.getNextValueAsInt(); + if (slots == 0) { + return null; + } + + ArrayList vars = new ArrayList(slots); + for (int i = 0; i < slots; i++) { + Variable var = new Frame().new Variable(); + var.setCodeIndex(reply.getNextValueAsLong()); + var.setName(reply.getNextValueAsString()); + var.setSignature(reply.getNextValueAsString()); + var.setLength(reply.getNextValueAsInt()); + var.setSlot(reply.getNextValueAsInt()); + vars.add(var); + } + + return vars; + } + + /** + * Returns values of local variables in a given frame + * + * @param frame + * Frame whose variables to get + * @return An array of Value objects + */ + public final Value[] getFrameValues(Frame frame) { + CommandPacket command = new CommandPacket( + JDWPCommands.StackFrameCommandSet.CommandSetID, + JDWPCommands.StackFrameCommandSet.GetValuesCommand); + command.setNextValueAsThreadID(frame.getThreadID()); + command.setNextValueAsFrameID(frame.getID()); + int slots = frame.getVars().size(); + command.setNextValueAsInt(slots); + Iterator it = frame.getVars().iterator(); + while (it.hasNext()) { + Frame.Variable var = (Frame.Variable) it.next(); + command.setNextValueAsInt(var.getSlot()); + command.setNextValueAsByte(var.getTag()); + } + + ReplyPacket reply = checkReply(performCommand(command)); + reply.getNextValueAsInt(); // number of values , is not used + Value[] values = new Value[slots]; + for (int i = 0; i < slots; i++) { + values[i] = reply.getNextValueAsValue(); + } + + return values; + } + + /** + * Returns the immediate superclass of a class + * + * @param classID + * The class ID whose superclass ID is to get + * @return The superclass ID (null if the class ID for java.lang.Object is + * specified). + */ + public final long getSuperclassId(long classID) { + CommandPacket command = new CommandPacket( + JDWPCommands.ClassTypeCommandSet.CommandSetID, + JDWPCommands.ClassTypeCommandSet.SuperclassCommand); + command.setNextValueAsClassID(classID); + ReplyPacket reply = checkReply(performCommand(command)); + return reply.getNextValueAsClassID(); + } + + /** + * Returns the runtime type of the object + * + * @param objectID + * The object ID + * @return The runtime reference type. + */ + public final long getReferenceType(long objectID) { + CommandPacket command = new CommandPacket( + JDWPCommands.ObjectReferenceCommandSet.CommandSetID, + JDWPCommands.ObjectReferenceCommandSet.ReferenceTypeCommand); + command.setNextValueAsObjectID(objectID); + ReplyPacket reply = checkReply(performCommand(command)); + reply.getNextValueAsByte(); + return reply.getNextValueAsLong(); + } + + /** + * Returns the class object corresponding to this type + * + * @param refType + * The reference type ID. + * @return The class object. + */ + public final long getClassObjectId(long refType) { + CommandPacket command = new CommandPacket( + JDWPCommands.ReferenceTypeCommandSet.CommandSetID, + JDWPCommands.ReferenceTypeCommandSet.ClassObjectCommand); + command.setNextValueAsReferenceTypeID(refType); + ReplyPacket reply = checkReply(performCommand(command)); + return reply.getNextValueAsObjectID(); + } + + /** + * Returns line number information for the method, if present. + * + * @param refType + * The class ID + * @param methodID + * The method ID + * @return ReplyPacket for corresponding command. + */ + public final ReplyPacket getLineTable(long refType, long methodID) { + CommandPacket command = new CommandPacket( + JDWPCommands.MethodCommandSet.CommandSetID, + JDWPCommands.MethodCommandSet.LineTableCommand); + command.setNextValueAsReferenceTypeID(refType); + command.setNextValueAsMethodID(methodID); + // ReplyPacket reply = + // debuggeeWrapper.vmMirror.checkReply(debuggeeWrapper.vmMirror.performCommand(command)); + // it is impossible to obtain line table information from native + // methods, so reply checking is not performed + ReplyPacket reply = performCommand(command); + if (reply.getErrorCode() != JDWPConstants.Error.NONE) { + if (reply.getErrorCode() == JDWPConstants.Error.NATIVE_METHOD) { + return reply; + } + } + + return checkReply(reply); + } + + /** + * Returns the value of one or more instance fields. + * + * @param objectID + * The object ID + * @param fieldIDs + * IDs of fields to get + * @return An array of Value objects representing each field's value + */ + public final Value[] getObjectReferenceValues(long objectID, long[] fieldIDs) { + int fieldsCount = fieldIDs.length; + if (fieldsCount == 0) { + return null; + } + + CommandPacket command = new CommandPacket( + JDWPCommands.ObjectReferenceCommandSet.CommandSetID, + JDWPCommands.ObjectReferenceCommandSet.GetValuesCommand); + command.setNextValueAsReferenceTypeID(objectID); + command.setNextValueAsInt(fieldsCount); + for (int i = 0; i < fieldsCount; i++) { + command.setNextValueAsFieldID(fieldIDs[i]); + } + + ReplyPacket reply = checkReply(performCommand(command)); + reply.getNextValueAsInt(); // fields returned, is not used + Value[] values = new Value[fieldsCount]; + for (int i = 0; i < fieldsCount; i++) { + values[i] = reply.getNextValueAsValue(); + } + + return values; + } + + /** + * Returns the value of one or more static fields of the reference type + * + * @param refTypeID + * The reference type ID. + * @param fieldIDs + * IDs of fields to get + * @return An array of Value objects representing each field's value + */ + public final Value[] getReferenceTypeValues(long refTypeID, long[] fieldIDs) { + int fieldsCount = fieldIDs.length; + if (fieldsCount == 0) { + return null; + } + + CommandPacket command = new CommandPacket( + JDWPCommands.ReferenceTypeCommandSet.CommandSetID, + JDWPCommands.ReferenceTypeCommandSet.GetValuesCommand); + command.setNextValueAsReferenceTypeID(refTypeID); + command.setNextValueAsInt(fieldsCount); + for (int i = 0; i < fieldsCount; i++) { + command.setNextValueAsFieldID(fieldIDs[i]); + } + + ReplyPacket reply = checkReply(performCommand(command)); + reply.getNextValueAsInt(); // fields returned, is not used + Value[] values = new Value[fieldsCount]; + for (int i = 0; i < fieldsCount; i++) { + values[i] = reply.getNextValueAsValue(); + } + + return values; + } + + /** + * Returns the value of the 'this' reference for this frame + * + * @param threadID + * The frame's thread ID + * @param frameID + * The frame ID. + * @return The 'this' object ID for this frame. + */ + public final long getThisObject(long threadID, long frameID) { + CommandPacket command = new CommandPacket( + JDWPCommands.StackFrameCommandSet.CommandSetID, + JDWPCommands.StackFrameCommandSet.ThisObjectCommand); + command.setNextValueAsThreadID(threadID); + command.setNextValueAsFrameID(frameID); + ReplyPacket reply = checkReply(performCommand(command)); + TaggedObject taggedObject = reply.getNextValueAsTaggedObject(); + return taggedObject.objectID; + } + + /** + * Returns information for each field in a reference type including + * inherited fields + * + * @param classID + * The reference type ID + * @return A list of Field objects representing each field of the class + */ + public final List getAllFields(long classID) { + ArrayList fields = new ArrayList(0); + + long superID = getSuperclassId(classID); + if (superID != 0) { + List superClassFields = getAllFields(superID); + for (int i = 0; i < superClassFields.size(); i++) { + fields.add(superClassFields.toArray()[i]); + } + } + + ReplyPacket reply = getFieldsInClass(classID); + int fieldsCount = reply.getNextValueAsInt(); + for (int i = 0; i < fieldsCount; i++) { + Field field = new Field(reply.getNextValueAsFieldID(), classID, + reply.getNextValueAsString(), reply.getNextValueAsString(), + reply.getNextValueAsInt()); + fields.add(field); + } + + return fields; + } + + /** + * Returns the reference type reflected by this class object + * + * @param classObjectID + * The class object ID. + * @return ReplyPacket for corresponding command + */ + public final ReplyPacket getReflectedType(long classObjectID) { + CommandPacket command = new CommandPacket( + JDWPCommands.ClassObjectReferenceCommandSet.CommandSetID, + JDWPCommands.ClassObjectReferenceCommandSet.ReflectedTypeCommand); + command.setNextValueAsClassObjectID(classObjectID); + return checkReply(performCommand(command)); + } + + /** + * Returns the JNI signature of a reference type. JNI signature formats are + * described in the Java Native Interface Specification + * + * @param refTypeID + * The reference type ID. + * @return The JNI signature for the reference type. + */ + public final String getReferenceTypeSignature(long refTypeID) { + CommandPacket command = new CommandPacket( + JDWPCommands.ReferenceTypeCommandSet.CommandSetID, + JDWPCommands.ReferenceTypeCommandSet.SignatureCommand); + command.setNextValueAsReferenceTypeID(refTypeID); + ReplyPacket reply = checkReply(performCommand(command)); + return reply.getNextValueAsString(); + } + + /** + * Returns the thread group that contains a given thread + * + * @param threadID + * The thread object ID. + * @return The thread group ID of this thread. + */ + public final long getThreadGroupID(long threadID) { + CommandPacket command = new CommandPacket( + JDWPCommands.ThreadReferenceCommandSet.CommandSetID, + JDWPCommands.ThreadReferenceCommandSet.ThreadGroupCommand); + command.setNextValueAsThreadID(threadID); + ReplyPacket reply = checkReply(performCommand(command)); + return reply.getNextValueAsThreadGroupID(); + } + + /** + * Checks whether a given thread is suspended or not + * + * @param threadID + * The thread object ID. + * @return True if a given thread is suspended, false otherwise. + */ + public final boolean isThreadSuspended(long threadID) { + CommandPacket command = new CommandPacket( + JDWPCommands.ThreadReferenceCommandSet.CommandSetID, + JDWPCommands.ThreadReferenceCommandSet.StatusCommand); + command.setNextValueAsThreadID(threadID); + ReplyPacket reply = checkReply(performCommand(command)); + reply.getNextValueAsInt(); // the thread's status; is not used + return reply.getNextValueAsInt() == JDWPConstants.SuspendStatus.SUSPEND_STATUS_SUSPENDED; + } + + /** + * Returns JNI signature of method. + * + * @param classID + * The reference type ID. + * @param methodID + * The method ID. + * @return JNI signature of method. + */ + public final String getMethodSignature(long classID, long methodID) { + CommandPacket command = new CommandPacket( + JDWPCommands.ReferenceTypeCommandSet.CommandSetID, + JDWPCommands.ReferenceTypeCommandSet.MethodsCommand); + command.setNextValueAsReferenceTypeID(classID); + ReplyPacket reply = checkReply(performCommand(command)); + int methods = reply.getNextValueAsInt(); + String value = null; + for (int i = 0; i < methods; i++) { + long mID = reply.getNextValueAsMethodID(); + reply.getNextValueAsString(); // name of the method; is not used + String methodSign = reply.getNextValueAsString(); + reply.getNextValueAsInt(); + if (mID == methodID) { + value = methodSign; + value = value.replaceAll("/", "."); + int lastRoundBracketIndex = value.lastIndexOf(")"); + value = value.substring(0, lastRoundBracketIndex + 1); + break; + } + } + + return value; + } + + /** + * Returns the characters contained in the string + * + * @param objectID + * The String object ID. + * @return A string value. + */ + public final String getStringValue(long objectID) { + CommandPacket command = new CommandPacket( + JDWPCommands.StringReferenceCommandSet.CommandSetID, + JDWPCommands.StringReferenceCommandSet.ValueCommand); + command.setNextValueAsObjectID(objectID); + ReplyPacket reply = checkReply(performCommand(command)); + return reply.getNextValueAsString(); + } + + /** + * Returns a range of array components + * + * @param objectID + * The array object ID. + * @return The retrieved values. + */ + public Value[] getArrayValues(long objectID) { + CommandPacket command = new CommandPacket( + JDWPCommands.ArrayReferenceCommandSet.CommandSetID, + JDWPCommands.ArrayReferenceCommandSet.LengthCommand); + command.setNextValueAsArrayID(objectID); + ReplyPacket reply = checkReply(performCommand(command)); + int length = reply.getNextValueAsInt(); + + if (length == 0) { + return null; + } + + command = new CommandPacket( + JDWPCommands.ArrayReferenceCommandSet.CommandSetID, + JDWPCommands.ArrayReferenceCommandSet.GetValuesCommand); + command.setNextValueAsArrayID(objectID); + command.setNextValueAsInt(0); + command.setNextValueAsInt(length); + reply = checkReply(performCommand(command)); + ArrayRegion arrayRegion = reply.getNextValueAsArrayRegion(); + + Value[] values = new Value[length]; + for (int i = 0; i < length; i++) { + values[i] = arrayRegion.getValue(i); + } + + return values; + } + + /** + * Returns a source line number according to a corresponding line code index + * in a method's line table. + * + * @param classID + * The class object ID. + * @param methodID + * The method ID. + * @param codeIndex + * The line code index. + * @return An integer line number. + */ + public final int getLineNumber(long classID, long methodID, long codeIndex) { + int lineNumber = -1; + ReplyPacket reply = getLineTable(classID, methodID); + if (reply.getErrorCode() != JDWPConstants.Error.NONE) { + return lineNumber; + } + + reply.getNextValueAsLong(); // start line index, is not used + reply.getNextValueAsLong(); // end line index, is not used + int lines = reply.getNextValueAsInt(); + for (int i = 0; i < lines; i++) { + long lineCodeIndex = reply.getNextValueAsLong(); + lineNumber = reply.getNextValueAsInt(); + if (lineCodeIndex == codeIndex) { + break; + } + + if (lineCodeIndex > codeIndex) { + --lineNumber; + break; + } + } + + return lineNumber; + } + + /** + * Returns a line code index according to a corresponding line number in a + * method's line table. + * + * @param classID + * The class object ID. + * @param methodID + * The method ID. + * @param lineNumber + * A source line number. + * @return An integer representing the line code index. + */ + public final long getLineCodeIndex(long classID, long methodID, + int lineNumber) { + ReplyPacket reply = getLineTable(classID, methodID); + if (reply.getErrorCode() != JDWPConstants.Error.NONE) { + return -1L; + } + + reply.getNextValueAsLong(); // start line index, is not used + reply.getNextValueAsLong(); // end line index, is not used + int lines = reply.getNextValueAsInt(); + for (int i = 0; i < lines; i++) { + long lineCodeIndex = reply.getNextValueAsLong(); + if (lineNumber == reply.getNextValueAsInt()) { + return lineCodeIndex; + } + } + + return -1L; + } + + /** + * Returns all variables which are visible within the given frame. + * + * @param frame + * The frame whose visible local variables to retrieve. + * @return A list of Variable objects representing each visible local + * variable within the given frame. + */ + public final List getLocalVars(Frame frame) { + List vars = getVariableTable(frame.getLocation().classID, frame + .getLocation().methodID); + if (vars == null) { + return null; + } + + // All variables that are not visible from within current frame must be + // removed from the list + long frameCodeIndex = frame.getLocation().index; + for (int i = 0; i < vars.size(); i++) { + Variable var = (Variable) vars.toArray()[i]; + long varCodeIndex = var.getCodeIndex(); + if (varCodeIndex > frameCodeIndex + || (frameCodeIndex >= varCodeIndex + var.getLength())) { + vars.remove(i); + --i; + continue; + } + } + + return vars; + } + + /** + * Sets the value of one or more local variables + * + * @param frame + * The frame ID. + * @param vars + * An array of Variable objects whose values to set + * @param values + * An array of Value objects to set + */ + public final void setLocalVars(Frame frame, Variable[] vars, Value[] values) { + if (vars.length != values.length) { + throw new TestErrorException( + "Number of variables doesn't correspond to number of their values"); + } + + CommandPacket command = new CommandPacket( + JDWPCommands.StackFrameCommandSet.CommandSetID, + JDWPCommands.StackFrameCommandSet.SetValuesCommand); + command.setNextValueAsThreadID(frame.getThreadID()); + command.setNextValueAsFrameID(frame.getID()); + command.setNextValueAsInt(vars.length); + for (int i = 0; i < vars.length; i++) { + command.setNextValueAsInt(vars[i].getSlot()); + command.setNextValueAsValue(values[i]); + } + + checkReply(performCommand(command)); + } + + /** + * Sets the value of one or more instance fields + * + * @param objectID + * The object ID. + * @param fieldIDs + * An array of fields IDs + * @param values + * An array of Value objects representing each value to set + */ [... 176 lines stripped ...]