This is the mail archive of the
java-patches@gcc.gnu.org
mailing list for the Java project.
[RFA] gnu.gcj.jdwp.transport: packet classes
- From: Keith Seitz <keiths at redhat dot com>
- To: GCJ Patches <java-patches at gcc dot gnu dot org>
- Date: Fri, 27 May 2005 14:22:29 -0700
- Subject: [RFA] gnu.gcj.jdwp.transport: packet classes
Hi,
So, to start simple, here are three classes to deal with constructing
packets.
JDWP has two packet types: command packets and reply packets. The
debugger sends (only) command packets to the vm. Each packet has its own
packet id.
When a reply is sent from the VM, the reply packet must use the same id
as the corresponding command packet.
Other than that, I don't think there is anything too confusing about
this. A nice and easy one to start. :-)
[Yes, I do have unit tests for this. Someone will send testsuite patches
soon.]
Questions/comments/concerns?
Keith
ChangeLog
2005-05-27 Keith Seitz <keiths@redhat.com>
* gnu/gcj/jdwp/transport/JdwpPacket.java: New file.
* gnu/gcj/jdwp/transport/JdwpCommandPacket.java: New file.
* gnu/gcj/jdwp/transport/JdwpReplyPacket.java: New file.
Index: gnu/gcj/jdwp/transport/JdwpCommandPacket.java
--- /dev/null
+++ gnu/gcj/jdwp/transport/JdwpCommandPacket.java
@@ -0,0 +1,94 @@
+/* Copyright (C) 2005 Free Software Foundation
+
+ This file is part of libgcj.
+
+This software is copyrighted work licensed under the terms of the
+Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
+details. */
+
+package gnu.gcj.jdwp.transport;
+
+/**
+ * A class representing a JDWP command packet
+ *
+ * @author Keith Seitz <keiths@redhat.com>
+ */
+public class JdwpCommandPacket extends JdwpPacket
+{
+ // header stuff
+ protected byte _commandSet;
+ protected byte _command;
+
+ // Minimum packet size [excluding super class] ( commandSet (1) + command (1) )
+ private static final int MINIMUM_LENGTH = 2;
+
+ /**
+ * Constructs a new <code>JdwpCommandPacket</code>
+ */
+ public JdwpCommandPacket ()
+ {
+ // Don't assign an id. This constructor is called by
+ // JdwpPacket.fromBytes, and that will assign a packet id.
+ }
+
+ /**
+ * Constructs a new <code>JdwpCommandPacket</code>
+ * with the given command set and command
+ *
+ * @param set the command set
+ * @param command the command
+ */
+ public JdwpCommandPacket (byte set, byte command)
+ {
+ _id = ++_last_id;
+ _commandSet = set;
+ _command = command;
+ }
+
+ /**
+ * Retuns the length of this packet
+ */
+ public int getLength () { return MINIMUM_LENGTH + super.getLength (); }
+
+ /**
+ * Returns the command set
+ */
+ public byte getCommandSet () { return _commandSet; }
+
+ /**
+ * Sets the command set
+ */
+ public void setCommandSet (byte cs) { _commandSet = cs; }
+
+ /**
+ * Returns the command
+ */
+ public byte getCommand () { return _command; }
+
+ /**
+ * Sets the command
+ */
+ public void setCommand (byte cmd) { _command = cmd; }
+
+ // Reads command packet data from the given buffer, starting
+ // at the given offset
+ protected int myFromBytes (byte[] bytes, int index)
+ {
+ int i = 0;
+ setCommandSet (bytes[index + i++]);
+ setCommand (bytes[index + i++]);
+ return i;
+ }
+
+ // Writes the command packet data into the given buffer
+ protected int myToBytes (byte[] bytes, int index)
+ {
+ // Need to add command set & command
+ int i = 0;
+ bytes[index + i++] = getCommandSet ();
+ bytes[index + i++] = getCommand ();
+
+ return i;
+ }
+}
+
Index: gnu/gcj/jdwp/transport/JdwpPacket.java
--- /dev/null
+++ gnu/gcj/jdwp/transport/JdwpPacket.java
@@ -0,0 +1,222 @@
+/* Copyright (C) 2005 Free Software Foundation
+
+ This file is part of libgcj.
+
+This software is copyrighted work licensed under the terms of the
+Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
+details. */
+
+package gnu.gcj.jdwp.transport;
+
+/**
+ * A baseclass representing common data in JDWP packets
+ *
+ * @author Keith Seitz <keiths@redhat.com>
+ */
+public abstract class JdwpPacket
+{
+ // Last id of packet constructed
+ protected static int _last_id = 0;
+
+ protected static final int JDWP_FLAG_REPLY = 0x80;
+
+ /* Packet layout:
+ * header:
+ * length (4 bytes) : size of entire packet, including length
+ * id (4 bytes) : unique packet id
+ * flags (1 byte) : flag byte
+ * [command packet stuff | reply packet stuff]
+ * data (variable) : unique command-/reply-specific data
+ *
+ * This class deal with everything except the command- and reply-specific
+ * stuff, which get handled in JdwpCommandPacket and JdwpReplyPacket.
+ */
+
+ // Minimum packet size excluding sub-class stuff ( length (4) + id (4) + flags (1) )
+ protected static final int MINIMUM_SIZE = 9;
+
+ // Id of command/reply
+ protected int _id;
+
+ // Flags
+ protected byte _flags;
+
+ // Variable data
+ protected byte[] _data;
+
+ /**
+ * Constructor
+ */
+ public JdwpPacket ()
+ {
+ // By default, DON'T assign an id. This way when a packet
+ // is constructed from fromBytes, _last_id won't increment (i.e.,
+ // it won't leave holes int the outgoing packet ids).
+ }
+
+ /**
+ * Constructs a <code>JdwpPacket</code> with the id
+ * from the given packet.
+ *
+ * @param pkt a packet whose id will be used in this new packet
+ */
+ public JdwpPacket (JdwpPacket pkt)
+ {
+ _id = pkt.getId ();
+ }
+
+ /**
+ * Returns the packet id
+ */
+ public int getId () { return _id; }
+
+ /**
+ * Sets the packet id
+ */
+ public void setId (int id) { _id = id; }
+
+ /**
+ * Returns the packet flags
+ */
+ public byte getFlags () { return _flags; }
+
+ /**
+ * Sets the packet flags
+ */
+ public void setFlags (byte flags) { _flags = flags; }
+
+ /**
+ * Gets the command/reply-specific data in this packet
+ */
+ public byte[] getData () { return _data; }
+
+ /**
+ * Sets the command/reply-specific data in this packet
+ */
+ public void setData (byte[] data) { _data = data; }
+
+ /**
+ * Returns the length of this entire packet
+ */
+ public int getLength () { return MINIMUM_SIZE + (_data == null ? 0 : _data.length); }
+
+ /**
+ * Allow subclasses to initialize from data
+ *
+ * @param bytes packet data from the wire
+ * @param index index into <code>bytes</code> to start processing
+ * @return number of bytes in <code>bytes</code> processed
+ */
+ protected abstract int myFromBytes (byte[] bytes, int index);
+
+ /**
+ * Convert the given bytes into a <code>JdwpPacket</code>. Uses the
+ * abstract method <code>myFromBytes</code> to allow subclasses to
+ * process data.
+ *
+ * If the given data does not represent a valid JDWP packet, it returns
+ * <code>null</code>.
+ *
+ * @param bytes packet data from the wire
+ * @param index index into <code>bytes</code> to start processing
+ * @return number of bytes in <code>bytes</code> processed
+ */
+ public static JdwpPacket fromBytes (byte[] bytes)
+ {
+ int i = 0;
+ int length = (bytes[i++] << 24) | (bytes[i++] << 16) | (bytes[i++] << 8) | bytes[i++];
+ int id = 0;
+ byte flags = 0;
+
+ if (bytes.length == length)
+ {
+ id = (bytes[i++] << 24) | (bytes[i++] << 16) | (bytes[i++] << 8) | bytes[i++];
+ flags = bytes[i++];
+
+ Class clazz = null;
+ if (flags == 0)
+ clazz = JdwpCommandPacket.class;
+ else if ((flags & JDWP_FLAG_REPLY) != 0)
+ clazz = JdwpReplyPacket.class;
+ else
+ {
+ // Malformed packet. Discard it.
+ return null;
+ }
+
+ JdwpPacket pkt = null;
+ try
+ {
+ pkt = (JdwpPacket) clazz.newInstance ();
+ }
+ catch (InstantiationException ie)
+ {
+ // Discard packet
+ return null;
+ }
+ catch (IllegalAccessException iae)
+ {
+ // Discard packet
+ return null;
+ }
+
+ pkt.setId (id);
+ pkt.setFlags (flags);
+
+ i += pkt.myFromBytes (bytes, i);
+ byte[] data = new byte[length - i];
+ System.arraycopy (bytes, i, data, 0, data.length);
+ pkt.setData (data);
+
+ return pkt;
+ }
+
+ return null;
+ }
+
+ // Put subclass information into bytes
+ protected abstract int myToBytes (byte[] bytes, int index);
+
+ // Convert this packet to it byte representation (ready to send on the wire)
+ // NOTE: All integers should be big-endian.
+ public byte[] toBytes ()
+ {
+ // Allocate a new array to hold contents of packet
+ int length = getLength ();
+ byte[] bytes = new byte[length];
+
+ int i = 0;
+
+ //
+ // Packet layout: length, id, flags, packet-specific, data (optional)
+ //
+
+ // length
+ bytes[i++] = (byte) ((length >>> 24) & 0xff);
+ bytes[i++] = (byte) ((length >>> 16) & 0xff);
+ bytes[i++] = (byte) ((length >>> 8) & 0xff);
+ bytes[i++] = (byte) (length & 0xff);
+
+ // id
+ bytes[i++] = (byte) ((getId () >>> 24) & 0xff);
+ bytes[i++] = (byte) ((getId () >>> 16) & 0xff);
+ bytes[i++] = (byte) ((getId () >>> 8) & 0xff);
+ bytes[i++] = (byte) (getId () & 0xff);
+
+ // flag
+ bytes[i++] = getFlags ();
+
+ // packet-specific stuff
+ i += myToBytes (bytes, i);
+
+ // data (if any)
+ byte[] data = getData ();
+ if (data.length > 0 && i < length)
+ {
+ // Would it pay to be over cautious?
+ System.arraycopy (data, 0, bytes, i, data.length);
+ }
+
+ return bytes;
+ }
+}
Index: gnu/gcj/jdwp/transport/JdwpReplyPacket.java
--- /dev/null
+++ gnu/gcj/jdwp/transport/JdwpReplyPacket.java
@@ -0,0 +1,81 @@
+/* Copyright (C) 2005 Free Software Foundation
+
+ This file is part of libgcj.
+
+This software is copyrighted work licensed under the terms of the
+Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
+details. */
+package gnu.gcj.jdwp.transport;
+
+/**
+ * This class represents a JDWP reply packet
+ *
+ * @author Keith Seitz <keiths@redhat.com>
+ */
+public class JdwpReplyPacket extends JdwpPacket
+{
+ // header stuff
+ protected short _errorCode;
+
+ // Minimum packet size [excluding super class] ( errorCode (2) )
+ private static final int MINIMUM_LENGTH = 2;
+
+ /**
+ * Constructs a <code>JdwpReplyPacket</code>.
+ *
+ */
+ public JdwpReplyPacket ()
+ {
+ // Don't assign a packet id. This is called by JdwpPacket.fromBytes
+ // which assigns a packet id. (Not that a VM would do that...)
+ }
+
+ /**
+ * Constructs a <code>JdwpReplyPacket</code> with the
+ * id from the given packet and error code
+ *
+ * @param pkt the packet whose id this packet will use
+ * @param errorCode the error code
+ */
+ public JdwpReplyPacket (JdwpPacket pkt, short errorCode)
+ {
+ super (pkt);
+ _flags = (byte) JDWP_FLAG_REPLY;
+ _errorCode = errorCode;
+ }
+
+ /**
+ * Returns the length of this packet
+ */
+ public int getLength () { return MINIMUM_LENGTH + super.getLength (); }
+
+ /**
+ * Returns the error code
+ */
+ public short getErrorCode () { return _errorCode; }
+
+ /**
+ * Sets the error code
+ */
+ public void setErrorCode (short ec) { _errorCode = ec; }
+
+ // Reads command packet data from the given buffer, starting
+ // at the given offset
+ protected int myFromBytes (byte[] bytes, int index)
+ {
+ int i = 0;
+ setErrorCode ((short) ((bytes[index + i++] << 8) | bytes[index + i++]));
+ return i;
+ }
+
+ // Writes the command packet data into the given buffer
+ protected int myToBytes (byte[] bytes, int index)
+ {
+ // Need to add error code
+ int i = 0;
+ bytes[index + i++] = (byte) ((getErrorCode () >>> 8) & 0xff);
+ bytes[index + i++] = (byte) getErrorCode ();
+
+ return i;
+ }
+}