diff --git a/Tvl.Java.DebugHost/Interop/jvmtiCapabilities.cs b/Tvl.Java.DebugHost/Interop/jvmtiCapabilities.cs index 790a7ad..dd60e9c 100644 --- a/Tvl.Java.DebugHost/Interop/jvmtiCapabilities.cs +++ b/Tvl.Java.DebugHost/Interop/jvmtiCapabilities.cs @@ -22,6 +22,38 @@ public jvmtiCapabilities(CapabilityFlags1 flags1 = 0, CapabilityFlags2 flags2 = _flags4 = flags4; } + public CapabilityFlags1 Capabilities1 + { + get + { + return _flags1; + } + } + + public CapabilityFlags2 Capabilities2 + { + get + { + return _flags2; + } + } + + public CapabilityFlags3 Capabilities3 + { + get + { + return _flags3; + } + } + + public CapabilityFlags4 Capabilities4 + { + get + { + return _flags4; + } + } + public bool CanTagObjects { get diff --git a/Tvl.Java.DebugHost/Services/DebugProtocolService.cs b/Tvl.Java.DebugHost/Services/DebugProtocolService.cs index d983dda..ebfe50d 100644 --- a/Tvl.Java.DebugHost/Services/DebugProtocolService.cs +++ b/Tvl.Java.DebugHost/Services/DebugProtocolService.cs @@ -267,7 +267,24 @@ public Error CreateString(string value, out StringId stringObject) public Error GetCapabilities(out Capabilities capabilities) { - throw new NotImplementedException(); + capabilities = default(Capabilities); + + JniEnvironment nativeEnvironment; + JvmtiEnvironment environment; + jvmtiError error = GetEnvironment(out environment, out nativeEnvironment); + if (error != jvmtiError.None) + return GetStandardError(error); + + jvmtiCapabilities jvmCapabilities; + error = Environment.GetCapabilities(out jvmCapabilities); + if (error != jvmtiError.None) + return GetStandardError(error); + + ulong capabilityValue = (ulong)jvmCapabilities.Capabilities1 | ((ulong)jvmCapabilities.Capabilities2 << 32); + capabilities = (Capabilities)capabilityValue + | Capabilities.CanStepByStatement + | Capabilities.CanInvokeWithoutThread; + return GetStandardError(error); } public Error GetClassPaths(out string baseDirectory, out string[] classPaths, out string[] bootClassPaths) diff --git a/Tvl.Java.DebugInterface.Client/Jdwp/ArrayReferenceCommand.cs b/Tvl.Java.DebugInterface.Client/Jdwp/ArrayReferenceCommand.cs new file mode 100644 index 0000000..92308b6 --- /dev/null +++ b/Tvl.Java.DebugInterface.Client/Jdwp/ArrayReferenceCommand.cs @@ -0,0 +1,10 @@ +namespace Tvl.Java.DebugInterface.Client.Jdwp +{ + public enum ArrayReferenceCommand : byte + { + Invalid = 0, + Length = 1, + GetValues = 2, + SetValues = 3, + } +} diff --git a/Tvl.Java.DebugInterface.Client/Jdwp/ClassObjectReferenceCommand.cs b/Tvl.Java.DebugInterface.Client/Jdwp/ClassObjectReferenceCommand.cs new file mode 100644 index 0000000..5ffd8be --- /dev/null +++ b/Tvl.Java.DebugInterface.Client/Jdwp/ClassObjectReferenceCommand.cs @@ -0,0 +1,8 @@ +namespace Tvl.Java.DebugInterface.Client.Jdwp +{ + public enum ClassObjectReferenceCommand : byte + { + Invalid = 0, + ReflectedType = 1, + } +} diff --git a/Tvl.Java.DebugInterface.Client/Jdwp/ClassTypeCommand.cs b/Tvl.Java.DebugInterface.Client/Jdwp/ClassTypeCommand.cs new file mode 100644 index 0000000..1256601 --- /dev/null +++ b/Tvl.Java.DebugInterface.Client/Jdwp/ClassTypeCommand.cs @@ -0,0 +1,11 @@ +namespace Tvl.Java.DebugInterface.Client.Jdwp +{ + public enum ClassTypeCommand : byte + { + Invalid = 0, + Superclass = 1, + SetValues = 2, + InvokeMethod = 3, + NewInstance = 4 + } +} diff --git a/Tvl.Java.DebugInterface.Client/Jdwp/CommandSet.cs b/Tvl.Java.DebugInterface.Client/Jdwp/CommandSet.cs new file mode 100644 index 0000000..72b6707 --- /dev/null +++ b/Tvl.Java.DebugInterface.Client/Jdwp/CommandSet.cs @@ -0,0 +1,23 @@ +namespace Tvl.Java.DebugInterface.Client.Jdwp +{ + public enum CommandSet : byte + { + VirtualMachine = 1, + ReferenceType = 2, + ClassType = 3, + ArrayType = 4, + InterfaceType = 5, + Method = 6, + Field = 8, + ObjectReference = 9, + StringReference = 10, + ThreadReference = 11, + ThreadGroupReference = 12, + ArrayReference = 13, + ClassLoaderReference = 14, + EventRequest = 15, + StackFrame = 16, + ClassObjectReference = 17, + Event = 64, + } +} diff --git a/Tvl.Java.DebugInterface.Client/Jdwp/EventRequestCommand.cs b/Tvl.Java.DebugInterface.Client/Jdwp/EventRequestCommand.cs new file mode 100644 index 0000000..86466d2 --- /dev/null +++ b/Tvl.Java.DebugInterface.Client/Jdwp/EventRequestCommand.cs @@ -0,0 +1,10 @@ +namespace Tvl.Java.DebugInterface.Client.Jdwp +{ + public enum EventRequestCommand : byte + { + Invalid = 0, + Set = 1, + Clear = 2, + ClearAllBreakpoints = 3, + } +} diff --git a/Tvl.Java.DebugInterface.Client/Jdwp/JdwpDebugProtocolService.cs b/Tvl.Java.DebugInterface.Client/Jdwp/JdwpDebugProtocolService.cs new file mode 100644 index 0000000..3851e48 --- /dev/null +++ b/Tvl.Java.DebugInterface.Client/Jdwp/JdwpDebugProtocolService.cs @@ -0,0 +1,2594 @@ +namespace Tvl.Java.DebugInterface.Client.Jdwp +{ + using System; + using System.Collections.Concurrent; + using System.Collections.Generic; + using System.Net; + using System.Net.Sockets; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using Tvl.Java.DebugInterface.Client.DebugProtocol; + using Tvl.Java.DebugInterface.Types; + using Tvl.Java.DebugInterface.Types.Loader; + + public class JdwpDebugProtocolService : IDebugProtocolService + { + private const int HeaderSize = 11; + + private readonly IDebugProtocolServiceCallback _callback; + + private readonly CancellationTokenSource _cancellationTokenSource = + new CancellationTokenSource(); + private readonly ConcurrentDictionary> _tasks = + new ConcurrentDictionary>(); + + private int _nextMessageId = 1; + + private bool _handshakeComplete; + private int? _fieldIdSize; + private int? _methodIdSize; + private int? _objectIdSize; + private int? _referenceTypeIdSize; + private int? _frameIdSize; + + private Socket _socket; + private readonly LinkedList _buffers = new LinkedList(); + + public JdwpDebugProtocolService(IDebugProtocolServiceCallback callback) + { + if (callback == null) + throw new ArgumentNullException("callback"); + + _callback = callback; + _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + _socket.Connect(IPAddress.Loopback, 6777); + } + + private int ObjectIdSize + { + get + { + return _objectIdSize.Value; + } + } + + private int ThreadIdSize + { + get + { + return ObjectIdSize; + } + } + + private int ReferenceTypeIdSize + { + get + { + return _referenceTypeIdSize.Value; + } + } + + private int ClassIdSize + { + get + { + return ReferenceTypeIdSize; + } + } + + private int FieldIdSize + { + get + { + return _fieldIdSize.Value; + } + } + + private int MethodIdSize + { + get + { + return _methodIdSize.Value; + } + } + + private int FrameIdSize + { + get + { + return _frameIdSize.Value; + } + } + + private int LocationSize + { + get + { + return sizeof(byte) + ReferenceTypeIdSize + MethodIdSize + sizeof(long); + } + } + + private void ReceiveAsync() + { + if (!_handshakeComplete) + throw new InvalidOperationException(); + + try + { + SocketAsyncEventArgs e = new SocketAsyncEventArgs(); + e.SetBuffer(new byte[1024], 0, 1024); + e.RemoteEndPoint = _socket.RemoteEndPoint; + e.Completed += HandleSocketReceive; + if (!_socket.ReceiveAsync(e)) + HandleSocketReceive(this, e); + } + catch + { + _cancellationTokenSource.Cancel(); + throw; + } + } + + private void HandleSocketReceive(object sender, SocketAsyncEventArgs e) + { + byte[] data = new byte[e.BytesTransferred]; + Buffer.BlockCopy(e.Buffer, e.Offset, data, 0, e.BytesTransferred); + _buffers.AddLast(data); + + // process messages in _buffers + while (TryProcessNextMessage()) + { + // intentionally empty + } + + if (!_socket.Connected) + { + _cancellationTokenSource.Cancel(); + return; + } + + // start the next receive operation + Task.Factory.StartNew(ReceiveAsync).HandleNonCriticalExceptions(); + } + + private bool TryProcessNextMessage() + { + if (_buffers.Count == 0) + return false; + + byte[] segment = _buffers.First.Value; + _buffers.RemoveFirst(); + while (segment.Length < HeaderSize && _buffers.Count > 0) + { + // combine the segments + byte[] nextSegment = _buffers.First.Value; + _buffers.RemoveFirst(); + byte[] combined = new byte[segment.Length + nextSegment.Length]; + Buffer.BlockCopy(segment, 0, combined, 0, segment.Length); + Buffer.BlockCopy(nextSegment, 0, combined, segment.Length, nextSegment.Length); + segment = combined; + } + + if (segment.Length < HeaderSize) + { + _buffers.AddFirst(segment); + return false; + } + + // read the length of the packet to make sure we actually have the full packet + int offset = 0; + int packetSize = ReadInt32(segment, ref offset); + while (segment.Length < packetSize && _buffers.Count > 0) + { + // combine the segments + byte[] nextSegment = _buffers.First.Value; + _buffers.RemoveFirst(); + byte[] combined = new byte[segment.Length + nextSegment.Length]; + Buffer.BlockCopy(segment, 0, combined, 0, segment.Length); + Buffer.BlockCopy(nextSegment, 0, combined, segment.Length, nextSegment.Length); + segment = combined; + } + + if (segment.Length < packetSize) + { + _buffers.AddFirst(segment); + return false; + } + + byte[] packet = new byte[packetSize]; + Buffer.BlockCopy(segment, 0, packet, 0, packetSize); + if (segment.Length > packetSize) + { + byte[] remaining = new byte[segment.Length - packetSize]; + Buffer.BlockCopy(segment, packetSize, remaining, 0, segment.Length - packetSize); + _buffers.AddFirst(remaining); + } + + Task.Factory.StartNew(() => ProcessPacket(packet)).HandleNonCriticalExceptions(); + return true; + } + + private void ProcessPacket(byte[] packet) + { + int offset = sizeof(int); + int id = ReadInt32(packet, ref offset); + TaskCompletionSource completionSource; + if (_tasks.TryRemove(id, out completionSource)) + { + completionSource.SetResult(packet); + return; + } + + // this is probably an event if we get here + CommandSet commandSet = (CommandSet)packet[sizeof(int) + sizeof(int) + sizeof(byte)]; + if (commandSet != CommandSet.Event) + throw new NotImplementedException(); + + ProcessEventPacket(packet); + } + + private void ProcessEventPacket(byte[] packet) + { + if (!_objectIdSize.HasValue) + { + RequestIdSizes(); + } + + int offset = HeaderSize; + SuspendPolicy suspendPolicy = (SuspendPolicy)ReadByte(packet, ref offset); + int eventCount = ReadInt32(packet, ref offset); + for (int i = 0; i < eventCount; i++) + { + EventKind eventKind = (EventKind)ReadByte(packet, ref offset); + switch (eventKind) + { + case EventKind.SingleStep: + { + RequestId requestId = new RequestId(ReadInt32(packet, ref offset)); + RequestId mappedRequestId; + if (_requestRemap.TryGetValue(requestId, out mappedRequestId)) + requestId = mappedRequestId; + + ThreadId threadId = (ThreadId)ReadObjectId(packet, ref offset); + Location location = ReadLocation(packet, ref offset); + _callback.SingleStep(suspendPolicy, requestId, threadId, location); + } + continue; + + case EventKind.Breakpoint: + { + RequestId requestId = new RequestId(ReadInt32(packet, ref offset)); + ThreadId threadId = (ThreadId)ReadObjectId(packet, ref offset); + Location location = ReadLocation(packet, ref offset); + _callback.Breakpoint(suspendPolicy, requestId, threadId, location); + } + continue; + + case EventKind.FramePop: + throw new NotImplementedException(); + + case EventKind.Exception: + { + RequestId requestId = new RequestId(ReadInt32(packet, ref offset)); + ThreadId threadId = (ThreadId)ReadObjectId(packet, ref offset); + Location location = ReadLocation(packet, ref offset); + TaggedObjectId exception = ReadTaggedObjectId(packet, ref offset); + Location catchLocation = ReadLocation(packet, ref offset); + _callback.Exception(suspendPolicy, requestId, threadId, location, exception, catchLocation); + } + continue; + + case EventKind.UserDefined: + throw new NotImplementedException(); + + case EventKind.ThreadStart: + { + RequestId requestId = new RequestId(ReadInt32(packet, ref offset)); + ThreadId threadId = (ThreadId)ReadObjectId(packet, ref offset); + _callback.ThreadStart(suspendPolicy, requestId, threadId); + } + continue; + + case EventKind.ThreadDeath: + //case EventKind.ThreadEnd: + { + RequestId requestId = new RequestId(ReadInt32(packet, ref offset)); + ThreadId threadId = (ThreadId)ReadObjectId(packet, ref offset); + _callback.ThreadDeath(suspendPolicy, requestId, threadId); + } + continue; + + case EventKind.ClassPrepare: + { + RequestId requestId = new RequestId(ReadInt32(packet, ref offset)); + ThreadId threadId = (ThreadId)ReadObjectId(packet, ref offset); + TypeTag typeTag = (TypeTag)ReadByte(packet, ref offset); + ReferenceTypeId typeId = ReadReferenceTypeId(packet, ref offset); + string signature = ReadString(packet, ref offset); + ClassStatus status = (ClassStatus)ReadInt32(packet, ref offset); + _callback.ClassPrepare(suspendPolicy, requestId, threadId, typeTag, typeId, signature, status); + } + continue; + + case EventKind.ClassUnload: + { + RequestId requestId = new RequestId(ReadInt32(packet, ref offset)); + string signature = ReadString(packet, ref offset); + _callback.ClassUnload(suspendPolicy, requestId, signature); + } + continue; + + case EventKind.ClassLoad: + throw new NotImplementedException(); + + case EventKind.FieldAccess: + { + RequestId requestId = new RequestId(ReadInt32(packet, ref offset)); + ThreadId threadId = (ThreadId)ReadObjectId(packet, ref offset); + Location location = ReadLocation(packet, ref offset); + TypeTag typeTag = (TypeTag)ReadByte(packet, ref offset); + ReferenceTypeId typeId = ReadReferenceTypeId(packet, ref offset); + FieldId fieldId = ReadFieldId(packet, ref offset); + TaggedObjectId objectId = ReadTaggedObjectId(packet, ref offset); + _callback.FieldAccess(suspendPolicy, requestId, threadId, location, typeTag, typeId, fieldId, objectId); + } + continue; + + case EventKind.FieldModification: + { + RequestId requestId = new RequestId(ReadInt32(packet, ref offset)); + ThreadId threadId = (ThreadId)ReadObjectId(packet, ref offset); + Location location = ReadLocation(packet, ref offset); + TypeTag typeTag = (TypeTag)ReadByte(packet, ref offset); + ReferenceTypeId typeId = ReadReferenceTypeId(packet, ref offset); + FieldId fieldId = ReadFieldId(packet, ref offset); + TaggedObjectId objectId = ReadTaggedObjectId(packet, ref offset); + Value newValue = ReadValue(packet, ref offset); + _callback.FieldModification(suspendPolicy, requestId, threadId, location, typeTag, typeId, fieldId, objectId, newValue); + } + continue; + + case EventKind.ExceptionCatch: + throw new NotImplementedException(); + + case EventKind.MethodEntry: + { + RequestId requestId = new RequestId(ReadInt32(packet, ref offset)); + ThreadId threadId = (ThreadId)ReadObjectId(packet, ref offset); + Location location = ReadLocation(packet, ref offset); + _callback.MethodEntry(suspendPolicy, requestId, threadId, location); + } + continue; + + case EventKind.MethodExit: + { + RequestId requestId = new RequestId(ReadInt32(packet, ref offset)); + ThreadId threadId = (ThreadId)ReadObjectId(packet, ref offset); + Location location = ReadLocation(packet, ref offset); + _callback.MethodExit(suspendPolicy, requestId, threadId, location, default(Value)); + } + continue; + + case EventKind.MethodExitWithReturnValue: + { + RequestId requestId = new RequestId(ReadInt32(packet, ref offset)); + ThreadId threadId = (ThreadId)ReadObjectId(packet, ref offset); + Location location = ReadLocation(packet, ref offset); + Value returnValue = ReadValue(packet, ref offset); + _callback.MethodExit(suspendPolicy, requestId, threadId, location, returnValue); + } + continue; + + case EventKind.VirtualMachineStart: + //case EventKind.VirtualMachineInit: + { + RequestId requestId = new RequestId(ReadInt32(packet, ref offset)); + ThreadId threadId = (ThreadId)ReadObjectId(packet, ref offset); + _callback.VirtualMachineStart(suspendPolicy, requestId, threadId); + } + continue; + + case EventKind.VirtualMachineDeath: + { + RequestId requestId = new RequestId(ReadInt32(packet, ref offset)); + _callback.VirtualMachineDeath(suspendPolicy, requestId); + } + continue; + + case EventKind.VirtualMachineDisconnected: + throw new NotImplementedException(); + + case EventKind.Invalid: + default: + throw new NotImplementedException(); + } + } + } + + private void RequestIdSizes() + { + // the ID sizes will be needed by many calls + int fieldIdSize; + int methodIdSize; + int objectIdSize; + int referenceTypeIdSize; + int frameIdSize; + Error result = GetIdSizes(out fieldIdSize, out methodIdSize, out objectIdSize, out referenceTypeIdSize, out frameIdSize); + if (result != Error.None) + return; + + _fieldIdSize = fieldIdSize; + _methodIdSize = methodIdSize; + _objectIdSize = objectIdSize; + _referenceTypeIdSize = referenceTypeIdSize; + _frameIdSize = frameIdSize; + } + + public Error Attach() + { + byte[] packet = Encoding.ASCII.GetBytes("JDWP-Handshake"); + if (_socket.Send(packet) != packet.Length) + return Error.TransportInit; + + byte[] response = new byte[14]; + if (_socket.Receive(response) != response.Length) + return Error.TransportInit; + + // start processing messages + _handshakeComplete = true; + ReceiveAsync(); + return Error.None; + } + + public Error GetVersion(out string description, out int majorVersion, out int minorVersion, out string vmVersion, out string vmName) + { + byte[] packet = new byte[HeaderSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, VirtualMachineCommand.Version); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + description = null; + majorVersion = 0; + minorVersion = 0; + vmVersion = null; + vmName = null; + return errorCode; + } + + int offset = HeaderSize; + description = ReadString(response, ref offset); + majorVersion = ReadInt32(response, ref offset); + minorVersion = ReadInt32(response, ref offset); + vmVersion = ReadString(response, ref offset); + vmName = ReadString(response, ref offset); + return Error.None; + } + + public Error GetClassesBySignature(out ReferenceTypeData[] classes, string signature) + { + byte[] encoded = Encoding.UTF8.GetBytes(signature); + byte[] packet = new byte[HeaderSize + sizeof(int) + encoded.Length]; + + int id = GetMessageId(); + SerializeHeader(packet, id, VirtualMachineCommand.ClassesBySignature); + WriteInt32(packet, HeaderSize, encoded.Length); + Buffer.BlockCopy(encoded, 0, packet, HeaderSize + sizeof(int), encoded.Length); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + classes = null; + return errorCode; + } + + int offset = HeaderSize; + int count = ReadInt32(response, ref offset); + classes = new ReferenceTypeData[count]; + for (int i = 0; i < count; i++) + { + TypeTag typeTag = (TypeTag)ReadByte(response, ref offset); + ReferenceTypeId typeId = ReadReferenceTypeId(response, ref offset); + ClassStatus status = (ClassStatus)ReadInt32(response, ref offset); + + string actualSignature; + string genericSignature; + errorCode = GetSignature(out actualSignature, out genericSignature, typeId); + if (errorCode != Error.None) + { + classes = null; + return errorCode; + } + + classes[i] = new ReferenceTypeData(new TaggedReferenceTypeId(typeTag, typeId), actualSignature, genericSignature, status); + } + + return errorCode; + } + + public Error GetAllClasses(out ReferenceTypeData[] classes) + { + byte[] packet = new byte[HeaderSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, VirtualMachineCommand.AllClassesWithGeneric); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + classes = null; + return errorCode; + } + + int offset = HeaderSize; + int classCount = ReadInt32(response, ref offset); + classes = new ReferenceTypeData[classCount]; + for (int i = 0; i < classCount; i++) + { + TypeTag typeTag = (TypeTag)ReadByte(response, ref offset); + ReferenceTypeId typeId = ReadReferenceTypeId(response, ref offset); + string signature = ReadString(response, ref offset); + string genericSignature = ReadString(response, ref offset); + ClassStatus status = (ClassStatus)ReadInt32(response, ref offset); + classes[i] = new ReferenceTypeData(new TaggedReferenceTypeId(typeTag, typeId), signature, genericSignature, status); + } + + return Error.None; + } + + public Error GetAllThreads(out ThreadId[] threads) + { + byte[] packet = new byte[HeaderSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, VirtualMachineCommand.AllThreads); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + threads = null; + return errorCode; + } + + int offset = HeaderSize; + int threadCount = ReadInt32(response, ref offset); + threads = new ThreadId[threadCount]; + for (int i = 0; i < threadCount; i++) + threads[i] = (ThreadId)ReadObjectId(response, ref offset); + + return Error.None; + } + + public Error GetTopLevelThreadGroups(out ThreadGroupId[] groups) + { + throw new NotImplementedException(); + } + + public Error Dispose() + { + byte[] packet = new byte[HeaderSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, VirtualMachineCommand.Dispose); + + byte[] response = SendPacket(id, packet); + return ReadErrorCode(response); + } + + public Error GetIdSizes(out int fieldIdSize, out int methodIdSize, out int objectIdSize, out int referenceTypeIdSize, out int frameIdSize) + { + byte[] packet = new byte[HeaderSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, VirtualMachineCommand.IDSizes); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + fieldIdSize = 0; + methodIdSize = 0; + objectIdSize = 0; + referenceTypeIdSize = 0; + frameIdSize = 0; + return errorCode; + } + + int offset = HeaderSize; + fieldIdSize = ReadInt32(response, ref offset); + methodIdSize = ReadInt32(response, ref offset); + objectIdSize = ReadInt32(response, ref offset); + referenceTypeIdSize = ReadInt32(response, ref offset); + frameIdSize = ReadInt32(response, ref offset); + return Error.None; + } + + public Error Suspend() + { + byte[] packet = new byte[HeaderSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, VirtualMachineCommand.Suspend); + + byte[] response = SendPacket(id, packet); + return ReadErrorCode(response); + } + + public Error Resume() + { + byte[] packet = new byte[HeaderSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, VirtualMachineCommand.Resume); + + byte[] response = SendPacket(id, packet); + return ReadErrorCode(response); + } + + public Error Exit(int exitCode) + { + byte[] packet = new byte[HeaderSize + sizeof(int)]; + int id = GetMessageId(); + SerializeHeader(packet, id, VirtualMachineCommand.Exit); + WriteInt32(packet, HeaderSize, exitCode); + + byte[] response = SendPacket(id, packet); + return ReadErrorCode(response); + } + + public Error CreateString(out StringId stringObject, string value) + { + throw new NotImplementedException(); + } + + public Error GetCapabilities(out Capabilities capabilities) + { + byte[] packet = new byte[HeaderSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, VirtualMachineCommand.CapabilitiesNew); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + capabilities = default(Capabilities); + return errorCode; + } + + Capabilities[] returnedFields = + { + Capabilities.CanGenerateFieldModificationEvents, + Capabilities.CanGenerateFieldAccessEvents, + Capabilities.CanGetBytecodes, + Capabilities.CanGetSyntheticAttribute, + Capabilities.CanGetOwnedMonitorInfo, + Capabilities.CanGetCurrentContendedMonitor, + Capabilities.CanGetMonitorInfo, + Capabilities.CanRedefineClasses, + Capabilities.None, // add method? + Capabilities.CanRedefineAnyClass, + Capabilities.CanPopFrame, + Capabilities.None, // use instance filters? + Capabilities.CanGetSourceDebugExtension, + Capabilities.None, // request VM death event? + Capabilities.None, // set default stratum? + Capabilities.None, // get instance info? + Capabilities.CanGenerateMonitorEvents, + Capabilities.CanGetOwnedMonitorStackDepthInfo, + Capabilities.None, // use source name filters? + Capabilities.CanGetConstantPool, + Capabilities.CanForceEarlyReturn, + Capabilities.None, // reserved22 + Capabilities.None, // reserved23 + Capabilities.None, // reserved24 + Capabilities.None, // reserved25 + Capabilities.None, // reserved26 + Capabilities.None, // reserved27 + Capabilities.None, // reserved28 + Capabilities.None, // reserved29 + Capabilities.None, // reserved30 + Capabilities.None, // reserved31 + Capabilities.None, // reserved32 + }; + + capabilities = Capabilities.None; + int offset = HeaderSize; + foreach (Capabilities capability in returnedFields) + { + if (ReadBoolean(response, ref offset)) + capabilities |= capability; + } + + return Error.None; + } + + public Error GetClassPaths(out string baseDirectory, out string[] classPaths, out string[] bootClassPaths) + { + throw new NotImplementedException(); + } + + public Error DisposeObjects(ObjectReferenceCountData[] requests) + { + throw new NotImplementedException(); + } + + public Error HoldEvents() + { + byte[] packet = new byte[HeaderSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, VirtualMachineCommand.HoldEvents); + + byte[] response = SendPacket(id, packet); + return ReadErrorCode(response); + } + + public Error ReleaseEvents() + { + byte[] packet = new byte[HeaderSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, VirtualMachineCommand.ReleaseEvents); + + byte[] response = SendPacket(id, packet); + return ReadErrorCode(response); + } + + public Error RedefineClasses(ClassDefinitionData[] definitions) + { + throw new NotImplementedException(); + } + + public Error SetDefaultStratum(string stratumId) + { + throw new NotImplementedException(); + } + + public Error GetSignature(out string signature, out string genericSignature, ReferenceTypeId referenceType) + { + byte[] packet = new byte[HeaderSize + ReferenceTypeIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ReferenceTypeCommand.SignatureWithGeneric); + WriteReferenceTypeId(packet, HeaderSize, referenceType); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + signature = null; + genericSignature = null; + return errorCode; + } + + int offset = HeaderSize; + signature = ReadString(response, ref offset); + genericSignature = ReadString(response, ref offset); + return Error.None; + } + + public Error GetClassLoader(out ClassLoaderId classLoader, ReferenceTypeId referenceType) + { + byte[] packet = new byte[HeaderSize + ReferenceTypeIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ReferenceTypeCommand.ClassLoader); + WriteReferenceTypeId(packet, HeaderSize, referenceType); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + classLoader = default(ClassLoaderId); + return errorCode; + } + + int offset = HeaderSize; + classLoader = (ClassLoaderId)ReadObjectId(response, ref offset); + return Error.None; + } + + public Error GetModifiers(out AccessModifiers modifiers, ReferenceTypeId referenceType) + { + byte[] packet = new byte[HeaderSize + ReferenceTypeIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ReferenceTypeCommand.Modifiers); + WriteReferenceTypeId(packet, HeaderSize, referenceType); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + modifiers = default(AccessModifiers); + return errorCode; + } + + int offset = HeaderSize; + modifiers = (AccessModifiers)ReadInt32(response, ref offset); + return Error.None; + } + + public Error GetFields(out DeclaredFieldData[] fields, ReferenceTypeId referenceType) + { + byte[] packet = new byte[HeaderSize + ReferenceTypeIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ReferenceTypeCommand.FieldsWithGeneric); + WriteReferenceTypeId(packet, HeaderSize, referenceType); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + fields = null; + return errorCode; + } + + int offset = HeaderSize; + int fieldCount = ReadInt32(response, ref offset); + fields = new DeclaredFieldData[fieldCount]; + for (int i = 0; i < fieldCount; i++) + { + FieldId fieldId = ReadFieldId(response, ref offset); + string name = ReadString(response, ref offset); + string signature = ReadString(response, ref offset); + string genericSignature = ReadString(response, ref offset); + AccessModifiers modifiers = (AccessModifiers)ReadInt32(response, ref offset); + fields[i] = new DeclaredFieldData(fieldId, name, signature, genericSignature, modifiers); + } + + return Error.None; + } + + public Error GetMethods(out DeclaredMethodData[] methods, ReferenceTypeId referenceType) + { + byte[] packet = new byte[HeaderSize + ReferenceTypeIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ReferenceTypeCommand.MethodsWithGeneric); + WriteReferenceTypeId(packet, HeaderSize, referenceType); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + methods = null; + return errorCode; + } + + int offset = HeaderSize; + int methodCount = ReadInt32(response, ref offset); + methods = new DeclaredMethodData[methodCount]; + for (int i = 0; i < methodCount; i++) + { + MethodId methodId = ReadMethodId(response, ref offset); + string name = ReadString(response, ref offset); + string signature = ReadString(response, ref offset); + string genericSignature = ReadString(response, ref offset); + AccessModifiers modifiers = (AccessModifiers)ReadInt32(response, ref offset); + methods[i] = new DeclaredMethodData(methodId, name, signature, genericSignature, modifiers); + } + + return Error.None; + } + + public Error GetReferenceTypeValues(out Value[] values, ReferenceTypeId referenceType, FieldId[] fields) + { + byte[] packet = new byte[HeaderSize + ReferenceTypeIdSize + sizeof(int) + (fields.Length * FieldIdSize)]; + int id = GetMessageId(); + SerializeHeader(packet, id, ReferenceTypeCommand.GetValues); + WriteReferenceTypeId(packet, HeaderSize, referenceType); + WriteInt32(packet, HeaderSize + ReferenceTypeIdSize, fields.Length); + for (int i = 0; i < fields.Length; i++) + { + WriteFieldId(packet, HeaderSize + ReferenceTypeIdSize + sizeof(int) + (i * FieldIdSize), fields[i]); + } + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + values = null; + return errorCode; + } + + int offset = HeaderSize; + int valueCount = ReadInt32(response, ref offset); + values = new Value[valueCount]; + for (int i = 0; i < valueCount; i++) + { + values[i] = ReadValue(response, ref offset); + } + + return Error.None; + } + + public Error GetSourceFile(out string sourceFile, ReferenceTypeId referenceType) + { + byte[] packet = new byte[HeaderSize + _referenceTypeIdSize.Value]; + int id = GetMessageId(); + SerializeHeader(packet, id, ReferenceTypeCommand.SourceFile); + WriteReferenceTypeId(packet, HeaderSize, referenceType); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + sourceFile = null; + return errorCode; + } + + int offset = HeaderSize; + sourceFile = ReadString(response, ref offset); + return Error.None; + } + + public Error GetNestedTypes(out TaggedReferenceTypeId[] classes, ReferenceTypeId referenceType) + { + throw new NotImplementedException(); + } + + public Error GetReferenceTypeStatus(out ClassStatus status, ReferenceTypeId referenceType) + { + byte[] packet = new byte[HeaderSize + _referenceTypeIdSize.Value]; + int id = GetMessageId(); + SerializeHeader(packet, id, ReferenceTypeCommand.Status); + WriteReferenceTypeId(packet, HeaderSize, referenceType); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + status = default(ClassStatus); + return errorCode; + } + + int offset = HeaderSize; + status = (ClassStatus)ReadInt32(response, ref offset); + return Error.None; + } + + public Error GetInterfaces(out InterfaceId[] interfaces, ReferenceTypeId referenceType) + { + byte[] packet = new byte[HeaderSize + ReferenceTypeIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ReferenceTypeCommand.Interfaces); + WriteReferenceTypeId(packet, HeaderSize, referenceType); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + interfaces = null; + return errorCode; + } + + int offset = HeaderSize; + int interfaceCount = ReadInt32(response, ref offset); + interfaces = new InterfaceId[interfaceCount]; + for (int i = 0; i < interfaceCount; i++) + { + interfaces[i] = (InterfaceId)ReadReferenceTypeId(response, ref offset); + } + + return Error.None; + } + + public Error GetClassObject(out ClassObjectId classObject, ReferenceTypeId referenceType) + { + byte[] packet = new byte[HeaderSize + ReferenceTypeIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ReferenceTypeCommand.ClassObject); + WriteReferenceTypeId(packet, HeaderSize, referenceType); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + classObject = default(ClassObjectId); + return errorCode; + } + + int offset = HeaderSize; + classObject = (ClassObjectId)ReadObjectId(response, ref offset); + return Error.None; + } + + public Error GetSourceDebugExtension(out string extension, ReferenceTypeId referenceType) + { + byte[] packet = new byte[HeaderSize + ReferenceTypeIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ReferenceTypeCommand.SourceDebugExtension); + WriteReferenceTypeId(packet, HeaderSize, referenceType); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + extension = null; + return errorCode; + } + + int offset = HeaderSize; + extension = ReadString(response, ref offset); + return Error.None; + } + + public Error GetInstances(out TaggedObjectId[] instances, ReferenceTypeId referenceType, int maxInstances) + { + throw new NotImplementedException(); + } + + public Error GetClassFileVersion(out int majorVersion, out int minorVersion, ReferenceTypeId referenceType) + { + byte[] packet = new byte[HeaderSize + ReferenceTypeIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ReferenceTypeCommand.ClassFileVersion); + WriteReferenceTypeId(packet, HeaderSize, referenceType); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + majorVersion = 0; + minorVersion = 0; + return errorCode; + } + + int offset = HeaderSize; + majorVersion = ReadInt32(response, ref offset); + minorVersion = ReadInt32(response, ref offset); + return Error.None; + } + + public Error GetConstantPool(out int constantPoolCount, out byte[] data, ReferenceTypeId referenceType) + { + byte[] packet = new byte[HeaderSize + ReferenceTypeIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ReferenceTypeCommand.ConstantPool); + WriteReferenceTypeId(packet, HeaderSize, referenceType); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + constantPoolCount = 0; + data = null; + return errorCode; + } + + int offset = HeaderSize; + constantPoolCount = ReadInt32(response, ref offset); + data = new byte[ReadInt32(response, ref offset)]; + Buffer.BlockCopy(response, offset, data, 0, data.Length); + return Error.None; + } + + public Error GetSuperclass(out ClassId superclass, ClassId @class) + { + byte[] packet = new byte[HeaderSize + ObjectIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ClassTypeCommand.Superclass); + WriteReferenceTypeId(packet, HeaderSize, @class); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + superclass = default(ClassId); + return errorCode; + } + + int offset = HeaderSize; + superclass = (ClassId)ReadReferenceTypeId(response, ref offset); + return Error.None; + } + + public Error SetClassValues(ClassId @class, FieldId[] fields, Value[] values) + { + throw new NotImplementedException(); + } + + public Error InvokeClassMethod(out Value returnValue, out TaggedObjectId thrownException, ClassId @class, ThreadId thread, MethodId method, InvokeOptions options, Value[] arguments) + { + throw new NotImplementedException(); + } + + public Error CreateClassInstance(out TaggedObjectId newObject, out TaggedObjectId thrownException, ClassId @class, ThreadId thread, MethodId method, InvokeOptions options, Value[] arguments) + { + throw new NotImplementedException(); + } + + public Error CreateArrayInstance(out TaggedObjectId newArray, ArrayTypeId arrayType, int length) + { + throw new NotImplementedException(); + } + + public Error GetMethodExceptionTable(out ExceptionTableEntry[] entries, ReferenceTypeId referenceType, MethodId method) + { + entries = null; + return Error.NotImplemented; + } + + public Error GetMethodLineTable(out long start, out long end, out LineNumberData[] lines, ReferenceTypeId referenceType, MethodId method) + { + byte[] packet = new byte[HeaderSize + ReferenceTypeIdSize + MethodIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, MethodCommand.LineTable); + WriteReferenceTypeId(packet, HeaderSize, referenceType); + WriteMethodId(packet, HeaderSize + ReferenceTypeIdSize, method); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + start = 0; + end = 0; + lines = null; + return errorCode; + } + + int offset = HeaderSize; + start = ReadInt64(response, ref offset); + end = ReadInt64(response, ref offset); + int lineCount = ReadInt32(response, ref offset); + lines = new LineNumberData[lineCount]; + for (int i = 0; i < lineCount; i++) + { + long lineCodeIndex = ReadInt64(response, ref offset); + int lineNumber = ReadInt32(response, ref offset); + lines[i] = new LineNumberData(lineCodeIndex, lineNumber); + } + + return Error.None; + } + + public Error GetMethodVariableTable(out VariableData[] slots, ReferenceTypeId referenceType, MethodId method) + { + byte[] packet = new byte[HeaderSize + ReferenceTypeIdSize + MethodIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, MethodCommand.VariableTableWithGeneric); + WriteReferenceTypeId(packet, HeaderSize, referenceType); + WriteMethodId(packet, HeaderSize + ReferenceTypeIdSize, method); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + slots = null; + return errorCode; + } + + int offset = HeaderSize; + int argumentCount = ReadInt32(response, ref offset); + int slotCount = ReadInt32(response, ref offset); + slots = new VariableData[slotCount]; + for (int i = 0; i < slotCount; i++) + { + ulong codeIndex = ReadUInt64(response, ref offset); + string name = ReadString(response, ref offset); + string signature = ReadString(response, ref offset); + string genericSignature = ReadString(response, ref offset); + uint length = ReadUInt32(response, ref offset); + int slot = ReadInt32(response, ref offset); + slots[i] = new VariableData(slot, codeIndex, length, name, signature, genericSignature); + } + + return Error.None; + } + + public Error GetMethodBytecodes(out byte[] bytecode, ReferenceTypeId referenceType, MethodId method) + { + byte[] packet = new byte[HeaderSize + ReferenceTypeIdSize + MethodIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, MethodCommand.Bytecodes); + WriteReferenceTypeId(packet, HeaderSize, referenceType); + WriteMethodId(packet, HeaderSize + ReferenceTypeIdSize, method); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + bytecode = null; + return errorCode; + } + + int offset = HeaderSize; + int bytes = ReadInt32(response, ref offset); + bytecode = new byte[bytes]; + Buffer.BlockCopy(response, offset, bytecode, 0, bytes); + return Error.None; + } + + public Error GetMethodIsObsolete(out bool result, ReferenceTypeId referenceType, MethodId method) + { + byte[] packet = new byte[HeaderSize + ReferenceTypeIdSize + MethodIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, MethodCommand.Bytecodes); + WriteReferenceTypeId(packet, HeaderSize, referenceType); + WriteMethodId(packet, HeaderSize + ReferenceTypeIdSize, method); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + result = false; + return errorCode; + } + + int offset = HeaderSize; + result = ReadByte(response, ref offset) != 0; + return Error.None; + } + + public Error GetObjectReferenceType(out TypeTag typeTag, out ReferenceTypeId typeId, ObjectId @object) + { + byte[] packet = new byte[HeaderSize + ObjectIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ObjectReferenceCommand.ReferenceType); + WriteObjectId(packet, HeaderSize, @object); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + typeTag = default(TypeTag); + typeId = default(ReferenceTypeId); + return errorCode; + } + + int offset = HeaderSize; + typeTag = (TypeTag)ReadByte(response, ref offset); + typeId = ReadReferenceTypeId(response, ref offset); + return Error.None; + } + + public Error GetObjectValues(out Value[] values, ObjectId @object, FieldId[] fields) + { + byte[] packet = new byte[HeaderSize + ObjectIdSize + sizeof(int) + (fields.Length * FieldIdSize)]; + int id = GetMessageId(); + SerializeHeader(packet, id, ObjectReferenceCommand.GetValues); + WriteObjectId(packet, HeaderSize, @object); + WriteInt32(packet, HeaderSize + ObjectIdSize, fields.Length); + for (int i = 0; i < fields.Length; i++) + { + WriteFieldId(packet, HeaderSize + ObjectIdSize + sizeof(int) + (i * FieldIdSize), fields[i]); + } + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + values = null; + return errorCode; + } + + int offset = HeaderSize; + int valueCount = ReadInt32(response, ref offset); + values = new Value[valueCount]; + for (int i = 0; i < valueCount; i++) + { + values[i] = ReadValue(response, ref offset); + } + + return Error.None; + } + + public Error SetObjectValues(ObjectId @object, FieldId[] fields, Value[] values) + { + throw new NotImplementedException(); + } + + public Error GetObjectMonitorInfo(out ThreadId owner, out int entryCount, out ThreadId[] waiters, ObjectId @object) + { + throw new NotImplementedException(); + } + + public Error InvokeObjectMethod(out Value returnValue, out TaggedObjectId thrownException, ObjectId @object, ThreadId thread, ClassId @class, MethodId method, InvokeOptions options, Value[] arguments) + { + if (thread == default(ThreadId)) + throw new ArgumentException(); + + byte[] packet = new byte[HeaderSize + ObjectIdSize + ThreadIdSize + ClassIdSize + MethodIdSize + sizeof(int)]; + WriteObjectId(packet, HeaderSize, @object); + WriteObjectId(packet, HeaderSize + ObjectIdSize, thread); + WriteReferenceTypeId(packet, HeaderSize + ObjectIdSize + ThreadIdSize, @class); + WriteMethodId(packet, HeaderSize + ObjectIdSize + ThreadIdSize + ClassIdSize, method); + WriteInt32(packet, HeaderSize + ObjectIdSize + ThreadIdSize + ClassIdSize + MethodIdSize, arguments.Length); + + List packetData = new List(packet); + foreach (Value argument in arguments) + { + switch (argument.Tag) + { + case Tag.Byte: + throw new NotImplementedException(); + + case Tag.Char: + throw new NotImplementedException(); + + case Tag.Float: + throw new NotImplementedException(); + + case Tag.Double: + throw new NotImplementedException(); + + case Tag.Int: + throw new NotImplementedException(); + + case Tag.Long: + throw new NotImplementedException(); + + case Tag.Short: + throw new NotImplementedException(); + + case Tag.Boolean: + throw new NotImplementedException(); + + case Tag.Array: + case Tag.Object: + case Tag.String: + case Tag.Thread: + case Tag.ThreadGroup: + case Tag.ClassLoader: + case Tag.ClassObject: + throw new NotImplementedException(); + + case Tag.Void: + throw new NotImplementedException(); + + case Tag.Invalid: + default: + throw new InvalidOperationException(); + } + } + + byte[] optionsData = new byte[sizeof(int)]; + WriteInt32(optionsData, 0, (int)options); + packetData.AddRange(optionsData); + + packet = packetData.ToArray(); + int id = GetMessageId(); + SerializeHeader(packet, id, ObjectReferenceCommand.InvokeMethod); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + returnValue = default(Value); + thrownException = default(TaggedObjectId); + return errorCode; + } + + int offset = HeaderSize; + returnValue = ReadValue(response, ref offset); + thrownException = ReadTaggedObjectId(response, ref offset); + return Error.None; + } + + public Error DisableObjectCollection(ObjectId @object) + { + byte[] packet = new byte[HeaderSize + ObjectIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ObjectReferenceCommand.DisableCollection); + WriteObjectId(packet, HeaderSize, @object); + + byte[] response = SendPacket(id, packet); + return ReadErrorCode(response); + } + + public Error EnableObjectCollection(ObjectId @object) + { + byte[] packet = new byte[HeaderSize + ObjectIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ObjectReferenceCommand.EnableCollection); + WriteObjectId(packet, HeaderSize, @object); + + byte[] response = SendPacket(id, packet); + return ReadErrorCode(response); + } + + public Error GetIsObjectCollected(out bool result, ObjectId @object) + { + byte[] packet = new byte[HeaderSize + ObjectIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ObjectReferenceCommand.IsCollected); + WriteObjectId(packet, HeaderSize, @object); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + result = false; + return errorCode; + } + + int offset = HeaderSize; + result = ReadByte(response, ref offset) != 0; + return Error.None; + } + + public Error GetStringValue(out string stringValue, ObjectId stringObject) + { + byte[] packet = new byte[HeaderSize + ObjectIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, StringReferenceCommand.Value); + WriteObjectId(packet, HeaderSize, stringObject); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + stringValue = null; + return errorCode; + } + + int offset = HeaderSize; + stringValue = ReadString(response, ref offset); + return Error.None; + } + + public Error GetThreadName(out string name, ThreadId thread) + { + byte[] packet = new byte[HeaderSize + ReferenceTypeIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ThreadReferenceCommand.Name); + WriteObjectId(packet, HeaderSize, thread); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + name = null; + return errorCode; + } + + int offset = HeaderSize; + name = ReadString(response, ref offset); + return Error.None; + } + + public Error SuspendThread(ThreadId thread) + { + byte[] packet = new byte[HeaderSize + ThreadIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ThreadReferenceCommand.Suspend); + WriteObjectId(packet, HeaderSize, thread); + + byte[] response = SendPacket(id, packet); + return ReadErrorCode(response); + } + + public Error ResumeThread(ThreadId thread) + { + byte[] packet = new byte[HeaderSize + ThreadIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ThreadReferenceCommand.Resume); + WriteObjectId(packet, HeaderSize, thread); + + byte[] response = SendPacket(id, packet); + return ReadErrorCode(response); + } + + public Error GetThreadStatus(out ThreadStatus threadStatus, out SuspendStatus suspendStatus, ThreadId thread) + { + byte[] packet = new byte[HeaderSize + ThreadIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ThreadReferenceCommand.Status); + WriteObjectId(packet, HeaderSize, thread); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + threadStatus = default(ThreadStatus); + suspendStatus = default(SuspendStatus); + return errorCode; + } + + int offset = HeaderSize; + threadStatus = (ThreadStatus)ReadInt32(response, ref offset); + suspendStatus = (SuspendStatus)ReadInt32(response, ref offset); + return Error.None; + } + + public Error GetThreadGroup(out ThreadGroupId threadGroup, ThreadId thread) + { + throw new NotImplementedException(); + } + + public Error GetThreadFrames(out FrameLocationData[] frames, ThreadId thread, int startFrame, int length) + { + Error errorCode = GetRawThreadFrames(out frames, thread, startFrame, length); + if (errorCode != Error.None) + return errorCode; + + for (int i = 0; i < frames.Length; i++) + { + frames[i] = new FrameLocationData(new FrameId(startFrame + i), frames[i].Location); + } + + return Error.None; + } + + public Error GetRawThreadFrames(out FrameLocationData[] frames, ThreadId thread, int startFrame, int length) + { + byte[] packet = new byte[HeaderSize + ThreadIdSize + sizeof(int) + sizeof(int)]; + int id = GetMessageId(); + SerializeHeader(packet, id, ThreadReferenceCommand.Frames); + WriteObjectId(packet, HeaderSize, thread); + WriteInt32(packet, HeaderSize + ThreadIdSize, startFrame); + WriteInt32(packet, HeaderSize + ThreadIdSize + sizeof(int), length); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + frames = null; + return errorCode; + } + + int offset = HeaderSize; + int frameCount = ReadInt32(response, ref offset); + frames = new FrameLocationData[frameCount]; + for (int i = 0; i < frameCount; i++) + { + FrameId frameId = ReadFrameId(response, ref offset); + Location location = ReadLocation(response, ref offset); + frames[i] = new FrameLocationData(frameId, location); + } + + return Error.None; + } + + public Error GetThreadFrameCount(out int frameCount, ThreadId thread) + { + byte[] packet = new byte[HeaderSize + ThreadIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ThreadReferenceCommand.FrameCount); + WriteObjectId(packet, HeaderSize, thread); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + frameCount = 0; + return errorCode; + } + + int offset = HeaderSize; + frameCount = ReadInt32(response, ref offset); + return Error.None; + } + + public Error GetThreadOwnedMonitors(out TaggedObjectId[] monitors, ThreadId thread) + { + throw new NotImplementedException(); + } + + public Error GetThreadCurrentContendedMonitor(out TaggedObjectId monitor, ThreadId thread) + { + throw new NotImplementedException(); + } + + public Error StopThread(ThreadId thread, ObjectId throwable) + { + throw new NotImplementedException(); + } + + public Error InterruptThread(ThreadId thread) + { + throw new NotImplementedException(); + } + + public Error GetThreadSuspendCount(out int suspendCount, ThreadId thread) + { + byte[] packet = new byte[HeaderSize + ThreadIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ThreadReferenceCommand.SuspendCount); + WriteObjectId(packet, HeaderSize, thread); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + suspendCount = 0; + return errorCode; + } + + int offset = HeaderSize; + suspendCount = ReadInt32(response, ref offset); + return Error.None; + } + + public Error GetThreadGroupName(out string groupName, ThreadGroupId group) + { + throw new NotImplementedException(); + } + + public Error GetThreadGroupParent(out ThreadGroupId parentGroup, ThreadGroupId group) + { + throw new NotImplementedException(); + } + + public Error GetThreadGroupChildren(out ThreadId[] childThreads, out ThreadGroupId[] childGroups, ThreadGroupId group) + { + throw new NotImplementedException(); + } + + public Error GetArrayLength(out int arrayLength, ArrayId arrayObject) + { + byte[] packet = new byte[HeaderSize + ObjectIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ArrayReferenceCommand.Length); + WriteObjectId(packet, HeaderSize, arrayObject); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + arrayLength = 0; + return errorCode; + } + + int offset = HeaderSize; + arrayLength = ReadInt32(response, ref offset); + return Error.None; + } + + public Error GetArrayValues(out Value[] values, ArrayId arrayObject, int firstIndex, int length) + { + if (length == 0) + { + // The JDWP implementation doesn't handle zero-length requests at the end of an array + int arrayLength; + Error lengthErrorCode = GetArrayLength(out arrayLength, arrayObject); + if (lengthErrorCode == Error.None && arrayLength == firstIndex) + { + values = new Value[0]; + return Error.None; + } + } + + byte[] packet = new byte[HeaderSize + ObjectIdSize + (2 * sizeof(int))]; + int id = GetMessageId(); + SerializeHeader(packet, id, ArrayReferenceCommand.GetValues); + WriteObjectId(packet, HeaderSize, arrayObject); + WriteInt32(packet, HeaderSize + ObjectIdSize, firstIndex); + WriteInt32(packet, HeaderSize + ObjectIdSize + sizeof(int), length); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + values = null; + return errorCode; + } + + int offset = HeaderSize; + + // "arrayregion" + Tag tag = (Tag)ReadByte(response, ref offset); + int count = ReadInt32(response, ref offset); + values = new Value[count]; + for (int i = 0; i < count; i++) + { + switch (tag) + { + case Tag.Byte: + case Tag.Char: + case Tag.Float: + case Tag.Double: + case Tag.Int: + case Tag.Long: + case Tag.Short: + case Tag.Boolean: + values[i] = ReadUntaggedValue(response, ref offset, tag); + break; + + case Tag.Array: + case Tag.Object: + case Tag.String: + case Tag.Thread: + case Tag.ThreadGroup: + case Tag.ClassLoader: + case Tag.ClassObject: + values[i] = ReadValue(response, ref offset); + break; + + case Tag.Invalid: + case Tag.Void: + default: + throw new InvalidOperationException(); + } + } + + return Error.None; + } + + public Error SetArrayValues(ArrayId arrayObject, int firstIndex, Value[] values) + { + throw new NotImplementedException(); + } + + public Error GetClassLoaderVisibleClasses(out TaggedReferenceTypeId[] classes, ClassLoaderId classLoaderObject) + { + throw new NotImplementedException(); + } + + private readonly ConcurrentDictionary> _linkedRequests = + new ConcurrentDictionary>(); + private readonly ConcurrentDictionary _requestRemap = + new ConcurrentDictionary(); + + public Error SetEvent(out RequestId requestId, EventKind eventKind, SuspendPolicy suspendPolicy, EventRequestModifier[] modifiers) + { + if (eventKind == EventKind.SingleStep && modifiers.Length == 1 && modifiers[0].Thread == default(ThreadId)) + { + ThreadId[] threads; + Error threadsErrorCode = GetAllThreads(out threads); + if (threadsErrorCode != Error.None) + { + requestId = default(RequestId); + return threadsErrorCode; + } + + requestId = default(RequestId); + + threadsErrorCode = Suspend(); + if (threadsErrorCode != Error.None) + return threadsErrorCode; + + List requests = new List(); + foreach (var thread in threads) + { + EventRequestModifier modifier = modifiers[0]; + modifier.Thread = thread; + threadsErrorCode = SetEvent(out requestId, eventKind, suspendPolicy, new[] { modifier }); + if (threadsErrorCode != Error.None) + return threadsErrorCode; + + requests.Add(requestId); + } + + _linkedRequests[requestId] = requests; + foreach (var request in requests) + _requestRemap[request] = requestId; + + threadsErrorCode = Resume(); + if (threadsErrorCode != Error.None) + return threadsErrorCode; + + return Error.None; + } + + byte[] packet = new byte[HeaderSize + 6]; + packet[HeaderSize] = (byte)eventKind; + packet[HeaderSize + 1] = (byte)suspendPolicy; + WriteInt32(packet, HeaderSize + 2, modifiers.Length); + + List packetData = new List(packet); + foreach (EventRequestModifier modifier in modifiers) + { + packetData.Add((byte)modifier.Kind); + + switch (modifier.Kind) + { + case ModifierKind.Count: + { + byte[] additionalData = new byte[sizeof(int)]; + WriteInt32(additionalData, 0, modifier.Count); + packetData.AddRange(additionalData); + } + continue; + + case ModifierKind.Conditional: + { + byte[] additionalData = new byte[sizeof(int)]; + WriteInt32(additionalData, 0, modifier.ExpressionId); + packetData.AddRange(additionalData); + } + continue; + + case ModifierKind.ThreadFilter: + { + byte[] additionalData = new byte[ThreadIdSize]; + WriteObjectId(additionalData, 0, modifier.Thread); + packetData.AddRange(additionalData); + } + continue; + + case ModifierKind.ClassTypeFilter: + { + byte[] additionalData = new byte[ReferenceTypeIdSize]; + WriteReferenceTypeId(additionalData, 0, modifier.Class); + packetData.AddRange(additionalData); + } + continue; + + case ModifierKind.ClassMatchFilter: + case ModifierKind.ClassExcludeFilter: + { + byte[] stringData = Encoding.UTF8.GetBytes(modifier.ClassPattern); + byte[] sizeData = new byte[sizeof(int)]; + WriteInt32(sizeData, 0, stringData.Length); + packetData.AddRange(sizeData); + packetData.AddRange(stringData); + } + continue; + + case ModifierKind.LocationFilter: + { + byte[] additionalData = new byte[LocationSize]; + WriteLocation(additionalData, 0, modifier.Location); + packetData.AddRange(additionalData); + } + continue; + + case ModifierKind.ExceptionFilter: + { + byte[] additionalData = new byte[_referenceTypeIdSize.Value + 2]; + WriteReferenceTypeId(additionalData, 0, modifier.ExceptionOrNull); + additionalData[_referenceTypeIdSize.Value] = (byte)(modifier.Caught ? 1 : 0); + additionalData[_referenceTypeIdSize.Value + 1] = (byte)(modifier.Uncaught ? 1 : 0); + packetData.AddRange(additionalData); + } + continue; + + case ModifierKind.FieldFilter: + { + byte[] additionalData = new byte[ReferenceTypeIdSize + FieldIdSize]; + WriteReferenceTypeId(additionalData, 0, modifier.Class); + WriteFieldId(additionalData, ReferenceTypeIdSize, modifier.Field); + packetData.AddRange(additionalData); + } + continue; + + case ModifierKind.Step: + { + if (modifier.StepSize == StepSize.Statement) + throw new NotSupportedException(); + + byte[] additionalData = new byte[ThreadIdSize + (2 * sizeof(int))]; + WriteObjectId(additionalData, 0, modifier.Thread); + WriteInt32(additionalData, ThreadIdSize, (int)modifier.StepSize); + WriteInt32(additionalData, ThreadIdSize + sizeof(int), (int)modifier.StepDepth); + packetData.AddRange(additionalData); + } + continue; + + case ModifierKind.InstanceFilter: + { + byte[] additionalData = new byte[ObjectIdSize]; + WriteObjectId(additionalData, 0, modifier.Instance); + packetData.AddRange(additionalData); + } + continue; + + case ModifierKind.SourceNameMatchFilter: + { + byte[] stringData = Encoding.UTF8.GetBytes(modifier.SourceNamePattern); + byte[] sizeData = new byte[sizeof(int)]; + WriteInt32(sizeData, 0, stringData.Length); + packetData.AddRange(sizeData); + packetData.AddRange(stringData); + } + continue; + + case ModifierKind.Invalid: + default: + throw new InvalidOperationException(); + } + } + + packet = packetData.ToArray(); + int id = GetMessageId(); + SerializeHeader(packet, id, EventRequestCommand.Set); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + requestId = default(RequestId); + return errorCode; + } + + int offset = HeaderSize; + requestId = new RequestId(ReadInt32(response, ref offset)); + return Error.None; + } + + public Error ClearEvent(EventKind eventKind, RequestId requestId) + { + List linkedRequests; + if (_linkedRequests.TryRemove(requestId, out linkedRequests)) + { + Suspend(); + + foreach (RequestId request in linkedRequests) + { + RequestId ignored; + _requestRemap.TryRemove(request, out ignored); + + ClearEvent(eventKind, request); + } + + return Resume(); + } + + byte[] packet = new byte[HeaderSize + sizeof(byte) + sizeof(int)]; + int id = GetMessageId(); + SerializeHeader(packet, id, EventRequestCommand.Clear); + packet[HeaderSize] = (byte)eventKind; + WriteInt32(packet, HeaderSize + sizeof(byte), requestId.Id); + + byte[] response = SendPacket(id, packet); + return ReadErrorCode(response); + } + + public Error ClearAllBreakpoints() + { + byte[] packet = new byte[HeaderSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, EventRequestCommand.ClearAllBreakpoints); + + byte[] response = SendPacket(id, packet); + return ReadErrorCode(response); + } + + public Error GetValues(out Value[] values, ThreadId thread, FrameId frame, int[] slots) + { + int frameIndex = (int)frame.Handle; + FrameLocationData[] frames = new FrameLocationData[1]; + Error errorCode = GetRawThreadFrames(out frames, thread, frameIndex, 1); + if (errorCode != Error.None) + { + values = null; + return errorCode; + } + + frame = frames[0].FrameId; + + VariableData[] variableData; + errorCode = GetMethodVariableTable(out variableData, frames[0].Location.Class, frames[0].Location.Method); + if (errorCode != Error.None) + { + values = null; + return errorCode; + } + + Tag[] tags = new Tag[slots.Length]; + for (int i = 0; i < slots.Length; i++) + { + tags[i] = Tag.Object; + foreach (VariableData variable in variableData) + { + if (variable.Slot != slots[i]) + continue; + + if (variable.CodeIndex > frames[0].Location.Index) + continue; + + if (variable.CodeIndex + variable.Length <= frames[0].Location.Index) + continue; + + if (string.IsNullOrEmpty(variable.Signature)) + continue; + + tags[i] = (Tag)variable.Signature[0]; + break; + } + } + + byte[] packet = new byte[HeaderSize + ThreadIdSize + FrameIdSize + sizeof(int) + (slots.Length * (sizeof(int) + sizeof(byte)))]; + int id = GetMessageId(); + SerializeHeader(packet, id, StackFrameCommand.GetValues); + WriteObjectId(packet, HeaderSize, thread); + WriteFrameId(packet, HeaderSize + ThreadIdSize, frame); + WriteInt32(packet, HeaderSize + ThreadIdSize + FrameIdSize, slots.Length); + + int baseOffset = HeaderSize + ThreadIdSize + FrameIdSize + sizeof(int); + for (int i = 0; i < slots.Length; i++) + { + int slotOffset = baseOffset + i * (sizeof(int) + sizeof(byte)); + WriteInt32(packet, slotOffset, slots[i]); + packet[slotOffset + sizeof(int)] = (byte)tags[i]; + } + + byte[] response = SendPacket(id, packet); + errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + values = null; + return errorCode; + } + + int offset = HeaderSize; + int valueCount = ReadInt32(response, ref offset); + values = new Value[valueCount]; + for (int i = 0; i < valueCount; i++) + { + values[i] = ReadValue(response, ref offset); + } + + return Error.None; + } + + public Error SetValues(ThreadId thread, FrameId frame, int[] slots, Value[] values) + { + int frameIndex = (int)frame.Handle; + FrameLocationData[] frames = new FrameLocationData[1]; + Error errorCode = GetRawThreadFrames(out frames, thread, frameIndex, 1); + if (errorCode != Error.None) + return errorCode; + + frame = frames[0].FrameId; + + throw new NotImplementedException(); + } + + public Error GetThisObject(out TaggedObjectId thisObject, ThreadId thread, FrameId frame) + { + int frameIndex = (int)frame.Handle; + FrameLocationData[] frames = new FrameLocationData[1]; + Error errorCode = GetRawThreadFrames(out frames, thread, frameIndex, 1); + if (errorCode != Error.None) + { + thisObject = default(TaggedObjectId); + return errorCode; + } + + frame = frames[0].FrameId; + + byte[] packet = new byte[HeaderSize + ThreadIdSize + FrameIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, StackFrameCommand.ThisObject); + WriteObjectId(packet, HeaderSize, thread); + WriteFrameId(packet, HeaderSize + ThreadIdSize, frame); + + byte[] response = SendPacket(id, packet); + errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + thisObject = default(TaggedObjectId); + return errorCode; + } + + int offset = HeaderSize; + thisObject = ReadTaggedObjectId(response, ref offset); + return Error.None; + } + + public Error PopFrames(ThreadId thread, FrameId frame) + { + int frameIndex = (int)frame.Handle; + FrameLocationData[] frames = new FrameLocationData[1]; + Error errorCode = GetRawThreadFrames(out frames, thread, frameIndex, 1); + if (errorCode != Error.None) + return errorCode; + + frame = frames[0].FrameId; + + byte[] packet = new byte[HeaderSize + ThreadIdSize + FrameIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, StackFrameCommand.PopFrames); + WriteObjectId(packet, HeaderSize, thread); + WriteFrameId(packet, HeaderSize + ThreadIdSize, frame); + + byte[] response = SendPacket(id, packet); + return ReadErrorCode(response); + } + + public Error GetReflectedType(out TypeTag typeTag, out ReferenceTypeId typeId, ClassObjectId classObject) + { + byte[] packet = new byte[HeaderSize + ObjectIdSize]; + int id = GetMessageId(); + SerializeHeader(packet, id, ClassObjectReferenceCommand.ReflectedType); + WriteObjectId(packet, HeaderSize, classObject); + + byte[] response = SendPacket(id, packet); + Error errorCode = ReadErrorCode(response); + if (errorCode != Error.None) + { + typeTag = default(TypeTag); + typeId = default(ReferenceTypeId); + return errorCode; + } + + int offset = HeaderSize; + typeTag = (TypeTag)ReadByte(response, ref offset); + typeId = ReadReferenceTypeId(response, ref offset); + return Error.None; + } + + private static void SerializeHeader(byte[] packet, int id, VirtualMachineCommand command) + { + SerializeHeader(packet, packet.Length, id, 0, (byte)CommandSet.VirtualMachine, (byte)command); + } + + private static void SerializeHeader(byte[] packet, int id, ReferenceTypeCommand command) + { + SerializeHeader(packet, packet.Length, id, 0, (byte)CommandSet.ReferenceType, (byte)command); + } + + private static void SerializeHeader(byte[] packet, int id, ClassTypeCommand command) + { + SerializeHeader(packet, packet.Length, id, 0, (byte)CommandSet.ClassType, (byte)command); + } + + private static void SerializeHeader(byte[] packet, int id, MethodCommand command) + { + SerializeHeader(packet, packet.Length, id, 0, (byte)CommandSet.Method, (byte)command); + } + + private static void SerializeHeader(byte[] packet, int id, ObjectReferenceCommand command) + { + SerializeHeader(packet, packet.Length, id, 0, (byte)CommandSet.ObjectReference, (byte)command); + } + + private static void SerializeHeader(byte[] packet, int id, StringReferenceCommand command) + { + SerializeHeader(packet, packet.Length, id, 0, (byte)CommandSet.StringReference, (byte)command); + } + + private static void SerializeHeader(byte[] packet, int id, ThreadReferenceCommand command) + { + SerializeHeader(packet, packet.Length, id, 0, (byte)CommandSet.ThreadReference, (byte)command); + } + + private static void SerializeHeader(byte[] packet, int id, ArrayReferenceCommand command) + { + SerializeHeader(packet, packet.Length, id, 0, (byte)CommandSet.ArrayReference, (byte)command); + } + + private static void SerializeHeader(byte[] packet, int id, EventRequestCommand command) + { + SerializeHeader(packet, packet.Length, id, 0, (byte)CommandSet.EventRequest, (byte)command); + } + + private static void SerializeHeader(byte[] packet, int id, StackFrameCommand command) + { + SerializeHeader(packet, packet.Length, id, 0, (byte)CommandSet.StackFrame, (byte)command); + } + + private static void SerializeHeader(byte[] packet, int id, ClassObjectReferenceCommand command) + { + SerializeHeader(packet, packet.Length, id, 0, (byte)CommandSet.ClassObjectReference, (byte)command); + } + + private static void SerializeHeader(byte[] packet, int length, int id, byte flags, byte commandSet, byte command) + { + WriteInt32(packet, 0, length); + WriteInt32(packet, 4, id); + packet[8] = flags; + packet[9] = commandSet; + packet[10] = command; + } + + private static void WriteInt16(byte[] data, int offset, short value) + { + data[offset + 0] = (byte)(value >> 8); + data[offset + 1] = (byte)value; + } + + private static void WriteInt32(byte[] data, int offset, int value) + { + data[offset + 0] = (byte)(value >> 24); + data[offset + 1] = (byte)(value >> 16); + data[offset + 2] = (byte)(value >> 8); + data[offset + 3] = (byte)value; + } + + private static void WriteInt64(byte[] data, int offset, long value) + { + WriteInt32(data, offset, (int)(value >> 32)); + WriteInt32(data, offset + sizeof(int), (int)value); + } + + private byte[] SendPacket(int id, byte[] packet) + { + TaskCompletionSource completionSource = new TaskCompletionSource(); + if (!_tasks.TryAdd(id, completionSource)) + throw new InvalidOperationException("Attempted to send a packet using the same ID as another in-progress operation."); + + SocketAsyncEventArgs e = new SocketAsyncEventArgs(); + e.SetBuffer(packet, 0, packet.Length); + _socket.SendAsync(e); + + completionSource.Task.Wait(_cancellationTokenSource.Token); + return completionSource.Task.Result; + } + + private int GetMessageId() + { + return Interlocked.Increment(ref _nextMessageId); + } + + private static string ReadString(byte[] response, ref int offset) + { + int length = ReadInt32(response, ref offset); + string result = Encoding.UTF8.GetString(response, offset, length); + offset += length; + return result; + } + + private static byte ReadByte(byte[] response, ref int offset) + { + byte result = response[offset]; + offset++; + return result; + } + + private static bool ReadBoolean(byte[] data, ref int offset) + { + return ReadByte(data, ref offset) != 0; + } + + private static short ReadInt16(byte[] response, ref int offset) + { + return (short)ReadUInt16(response, ref offset); + } + + private static ushort ReadUInt16(byte[] response, ref int offset) + { + ushort result = (ushort)(response[offset + 1] | (response[offset] << 8)); + offset += 2; + return result; + } + + private static char ReadChar(byte[] response, ref int offset) + { + return (char)ReadUInt16(response, ref offset); + } + + private static int ReadInt32(byte[] response, ref int offset) + { + return (int)ReadUInt32(response, ref offset); + } + + private static uint ReadUInt32(byte[] response, ref int offset) + { + ushort highOrder = ReadUInt16(response, ref offset); + ushort lowOrder = ReadUInt16(response, ref offset); + return (uint)(lowOrder | (highOrder << 16)); + } + + private static long ReadInt64(byte[] response, ref int offset) + { + return (long)ReadUInt64(response, ref offset); + } + + private static ulong ReadUInt64(byte[] response, ref int offset) + { + uint highOrder = ReadUInt32(response, ref offset); + uint lowOrder = ReadUInt32(response, ref offset); + return (ulong)(lowOrder | ((long)highOrder << 32)); + } + + private static Error ReadErrorCode(byte[] response) + { + int errorCodeOffset = HeaderSize - 2; + short value = ReadInt16(response, ref errorCodeOffset); + return (Error)value; + } + + private ReferenceTypeId ReadReferenceTypeId(byte[] packet, ref int offset) + { + if (!_referenceTypeIdSize.HasValue) + throw new InvalidOperationException(); + + switch (_referenceTypeIdSize.Value) + { + case 2: + return new ReferenceTypeId(ReadInt16(packet, ref offset)); + + case 4: + return new ReferenceTypeId(ReadInt32(packet, ref offset)); + + case 8: + return new ReferenceTypeId(ReadInt64(packet, ref offset)); + + default: + throw new NotImplementedException(); + } + } + + private void WriteReferenceTypeId(byte[] packet, int offset, ReferenceTypeId referenceTypeId) + { + if (!_referenceTypeIdSize.HasValue) + throw new InvalidOperationException(); + + switch (_referenceTypeIdSize.Value) + { + case 2: + WriteInt16(packet, offset, (short)referenceTypeId.Handle); + break; + + case 4: + WriteInt32(packet, offset, (int)referenceTypeId.Handle); + break; + + case 8: + WriteInt64(packet, offset, referenceTypeId.Handle); + break; + + default: + throw new NotImplementedException(); + } + } + + private FieldId ReadFieldId(byte[] packet, ref int offset) + { + if (!_fieldIdSize.HasValue) + throw new InvalidOperationException(); + + switch (_fieldIdSize.Value) + { + case 2: + return new FieldId(ReadInt16(packet, ref offset)); + + case 4: + return new FieldId(ReadInt32(packet, ref offset)); + + case 8: + return new FieldId(ReadInt64(packet, ref offset)); + + default: + throw new NotImplementedException(); + } + } + + private void WriteFieldId(byte[] packet, int offset, FieldId fieldId) + { + if (!_fieldIdSize.HasValue) + throw new InvalidOperationException(); + + switch (_fieldIdSize.Value) + { + case 2: + WriteInt16(packet, offset, (short)fieldId.Handle); + break; + + case 4: + WriteInt32(packet, offset, (int)fieldId.Handle); + break; + + case 8: + WriteInt64(packet, offset, fieldId.Handle); + break; + + default: + throw new NotImplementedException(); + } + } + + private MethodId ReadMethodId(byte[] packet, ref int offset) + { + if (!_methodIdSize.HasValue) + throw new InvalidOperationException(); + + switch (_methodIdSize.Value) + { + case 2: + return new MethodId(ReadInt16(packet, ref offset)); + + case 4: + return new MethodId(ReadInt32(packet, ref offset)); + + case 8: + return new MethodId(ReadInt64(packet, ref offset)); + + default: + throw new NotImplementedException(); + } + } + + private void WriteMethodId(byte[] packet, int offset, MethodId methodId) + { + if (!_methodIdSize.HasValue) + throw new InvalidOperationException(); + + switch (_methodIdSize.Value) + { + case 2: + WriteInt16(packet, offset, (short)methodId.Handle); + break; + + case 4: + WriteInt32(packet, offset, (int)methodId.Handle); + break; + + case 8: + WriteInt64(packet, offset, methodId.Handle); + break; + + default: + throw new NotImplementedException(); + } + } + + private FrameId ReadFrameId(byte[] packet, ref int offset) + { + switch (FrameIdSize) + { + case 2: + return new FrameId(ReadInt16(packet, ref offset)); + + case 4: + return new FrameId(ReadInt32(packet, ref offset)); + + case 8: + return new FrameId(ReadInt64(packet, ref offset)); + + default: + throw new NotImplementedException(); + } + } + + private void WriteFrameId(byte[] packet, int offset, FrameId frameId) + { + switch (FrameIdSize) + { + case 2: + WriteInt16(packet, offset, (short)frameId.Handle); + break; + + case 4: + WriteInt32(packet, offset, (int)frameId.Handle); + break; + + case 8: + WriteInt64(packet, offset, frameId.Handle); + break; + + default: + throw new NotImplementedException(); + } + } + + private ObjectId ReadObjectId(byte[] packet, ref int offset) + { + if (!_objectIdSize.HasValue) + throw new InvalidOperationException(); + + switch (_referenceTypeIdSize.Value) + { + case 2: + return new ObjectId(ReadInt16(packet, ref offset)); + + case 4: + return new ObjectId(ReadInt32(packet, ref offset)); + + case 8: + return new ObjectId(ReadInt64(packet, ref offset)); + + default: + throw new NotImplementedException(); + } + } + + private void WriteObjectId(byte[] packet, int offset, ObjectId objectId) + { + if (!_objectIdSize.HasValue) + throw new InvalidOperationException(); + + switch (_objectIdSize.Value) + { + case 2: + WriteInt16(packet, offset, (short)objectId.Handle); + break; + + case 4: + WriteInt32(packet, offset, (int)objectId.Handle); + break; + + case 8: + WriteInt64(packet, offset, objectId.Handle); + break; + + default: + throw new NotImplementedException(); + } + } + + private TaggedObjectId ReadTaggedObjectId(byte[] packet, ref int offset) + { + if (!_objectIdSize.HasValue) + throw new InvalidOperationException(); + + Tag tag = (Tag)ReadByte(packet, ref offset); + ObjectId objectId = ReadObjectId(packet, ref offset); + return new TaggedObjectId(tag, objectId, default(TaggedReferenceTypeId)); + } + + private Value ReadValue(byte[] response, ref int offset) + { + Tag tag = (Tag)ReadByte(response, ref offset); + return ReadUntaggedValue(response, ref offset, tag); + } + + private Value ReadUntaggedValue(byte[] response, ref int offset, Tag tag) + { + switch (tag) + { + case Tag.Byte: + { + byte value = ReadByte(response, ref offset); + return new Value(tag, value); + } + + case Tag.Char: + { + char value = ReadChar(response, ref offset); + return new Value(tag, value); + } + + case Tag.Float: + { + uint value = ReadUInt32(response, ref offset); + return new Value(tag, value); + } + + case Tag.Double: + { + ulong value = ReadUInt64(response, ref offset); + return new Value(tag, (long)value); + } + + case Tag.Int: + { + int value = ReadInt32(response, ref offset); + return new Value(tag, value); + } + + case Tag.Long: + { + long value = ReadInt64(response, ref offset); + return new Value(tag, value); + } + + case Tag.Short: + { + short value = ReadInt16(response, ref offset); + return new Value(tag, value); + } + + case Tag.Void: + return new Value(tag, 0); + + case Tag.Boolean: + { + byte value = ReadByte(response, ref offset); + return new Value(tag, value); + } + + case Tag.Array: + case Tag.Object: + case Tag.String: + case Tag.Thread: + case Tag.ThreadGroup: + case Tag.ClassLoader: + case Tag.ClassObject: + { + ObjectId objectId = ReadObjectId(response, ref offset); + return new Value(tag, objectId.Handle); + } + + case Tag.Invalid: + default: + throw new NotImplementedException(); + } + } + + private Location ReadLocation(byte[] packet, ref int offset) + { + TypeTag typeTag = (TypeTag)ReadByte(packet, ref offset); + ClassId classId = (ClassId)ReadReferenceTypeId(packet, ref offset); + MethodId methodId = ReadMethodId(packet, ref offset); + ulong index = ReadUInt64(packet, ref offset); + return new Location(typeTag, classId, methodId, index); + } + + private void WriteLocation(byte[] data, int offset, Location location) + { + data[offset] = (byte)location.TypeTag; + WriteReferenceTypeId(data, sizeof(byte), location.Class); + WriteMethodId(data, sizeof(byte) + ReferenceTypeIdSize, location.Method); + WriteInt64(data, sizeof(byte) + ReferenceTypeIdSize + MethodIdSize, (long)location.Index); + } + } +} diff --git a/Tvl.Java.DebugInterface.Client/Jdwp/MethodCommand.cs b/Tvl.Java.DebugInterface.Client/Jdwp/MethodCommand.cs new file mode 100644 index 0000000..c687291 --- /dev/null +++ b/Tvl.Java.DebugInterface.Client/Jdwp/MethodCommand.cs @@ -0,0 +1,12 @@ +namespace Tvl.Java.DebugInterface.Client.Jdwp +{ + public enum MethodCommand : byte + { + Invalid = 0, + LineTable = 1, + VariableTable = 2, + Bytecodes = 3, + IsObsolete = 4, + VariableTableWithGeneric = 5, + } +} diff --git a/Tvl.Java.DebugInterface.Client/Jdwp/ObjectReferenceCommand.cs b/Tvl.Java.DebugInterface.Client/Jdwp/ObjectReferenceCommand.cs new file mode 100644 index 0000000..9ff456c --- /dev/null +++ b/Tvl.Java.DebugInterface.Client/Jdwp/ObjectReferenceCommand.cs @@ -0,0 +1,16 @@ +namespace Tvl.Java.DebugInterface.Client.Jdwp +{ + public enum ObjectReferenceCommand : byte + { + Invalid = 0, + ReferenceType = 1, + GetValues = 2, + SetValues = 3, + MonitorInfo = 5, + InvokeMethod = 6, + DisableCollection = 7, + EnableCollection = 8, + IsCollected = 9, + ReferringObjects = 10, + } +} diff --git a/Tvl.Java.DebugInterface.Client/Jdwp/ReferenceTypeCommand.cs b/Tvl.Java.DebugInterface.Client/Jdwp/ReferenceTypeCommand.cs new file mode 100644 index 0000000..3071410 --- /dev/null +++ b/Tvl.Java.DebugInterface.Client/Jdwp/ReferenceTypeCommand.cs @@ -0,0 +1,25 @@ +namespace Tvl.Java.DebugInterface.Client.Jdwp +{ + public enum ReferenceTypeCommand : byte + { + Invalid = 0, + Signature = 1, + ClassLoader = 2, + Modifiers = 3, + Fields = 4, + Methods = 5, + GetValues = 6, + SourceFile = 7, + NestedTypes = 8, + Status = 9, + Interfaces = 10, + ClassObject = 11, + SourceDebugExtension = 12, + SignatureWithGeneric = 13, + FieldsWithGeneric = 14, + MethodsWithGeneric = 15, + Instances = 16, + ClassFileVersion = 17, + ConstantPool = 18, + } +} diff --git a/Tvl.Java.DebugInterface.Client/Jdwp/StackFrameCommand.cs b/Tvl.Java.DebugInterface.Client/Jdwp/StackFrameCommand.cs new file mode 100644 index 0000000..c5141e6 --- /dev/null +++ b/Tvl.Java.DebugInterface.Client/Jdwp/StackFrameCommand.cs @@ -0,0 +1,11 @@ +namespace Tvl.Java.DebugInterface.Client.Jdwp +{ + public enum StackFrameCommand : byte + { + Invalid = 0, + GetValues = 1, + SetValues = 2, + ThisObject = 3, + PopFrames = 4, + } +} diff --git a/Tvl.Java.DebugInterface.Client/Jdwp/StringReferenceCommand.cs b/Tvl.Java.DebugInterface.Client/Jdwp/StringReferenceCommand.cs new file mode 100644 index 0000000..c3df227 --- /dev/null +++ b/Tvl.Java.DebugInterface.Client/Jdwp/StringReferenceCommand.cs @@ -0,0 +1,8 @@ +namespace Tvl.Java.DebugInterface.Client.Jdwp +{ + public enum StringReferenceCommand : byte + { + Invalid = 0, + Value = 1, + } +} diff --git a/Tvl.Java.DebugInterface.Client/Jdwp/ThreadReferenceCommand.cs b/Tvl.Java.DebugInterface.Client/Jdwp/ThreadReferenceCommand.cs new file mode 100644 index 0000000..225649e --- /dev/null +++ b/Tvl.Java.DebugInterface.Client/Jdwp/ThreadReferenceCommand.cs @@ -0,0 +1,21 @@ +namespace Tvl.Java.DebugInterface.Client.Jdwp +{ + public enum ThreadReferenceCommand : byte + { + Invalid = 0, + Name = 1, + Suspend = 2, + Resume = 3, + Status = 4, + ThreadGroup = 5, + Frames = 6, + FrameCount = 7, + OwnedMonitors = 8, + CurrentContendedMonitor = 9, + Stop = 10, + Interrupt = 11, + SuspendCount = 12, + OwnedMonitorsStackDepthInfo = 13, + ForceEarlyReturn = 14, + } +} diff --git a/Tvl.Java.DebugInterface.Client/Jdwp/VirtualMachineCommand.cs b/Tvl.Java.DebugInterface.Client/Jdwp/VirtualMachineCommand.cs new file mode 100644 index 0000000..10061ac --- /dev/null +++ b/Tvl.Java.DebugInterface.Client/Jdwp/VirtualMachineCommand.cs @@ -0,0 +1,28 @@ +namespace Tvl.Java.DebugInterface.Client.Jdwp +{ + public enum VirtualMachineCommand : byte + { + Invalid = 0, + Version = 1, + ClassesBySignature = 2, + AllClasses = 3, + AllThreads = 4, + TopLevelThreadGroups = 5, + Dispose = 6, + IDSizes = 7, + Suspend = 8, + Resume = 9, + Exit = 10, + CreateString = 11, + Capabilities = 12, + ClassPaths = 13, + DisposeObjects = 14, + HoldEvents = 15, + ReleaseEvents = 16, + CapabilitiesNew = 17, + RedefineClasses = 18, + SetDefaultStratum = 19, + AllClassesWithGeneric = 20, + InstanceCounts = 21, + } +} diff --git a/Tvl.Java.DebugInterface.Client/ObjectReference.cs b/Tvl.Java.DebugInterface.Client/ObjectReference.cs index 214f285..a15ab02 100644 --- a/Tvl.Java.DebugInterface.Client/ObjectReference.cs +++ b/Tvl.Java.DebugInterface.Client/ObjectReference.cs @@ -111,11 +111,51 @@ public IStrongValueHandle InvokeMethod(IThreadReference thread, IMethod { Types.Value returnValue; TaggedObjectId thrownException; - ThreadId threadId = (thread != null) ? ((ThreadReference)thread).ThreadId : default(ThreadId); - DebugErrorHandler.ThrowOnFailure(VirtualMachine.ProtocolService.InvokeObjectMethod(out returnValue, out thrownException, ObjectId, threadId, (ClassId)((Method)method).DeclaringType.TaggedReferenceTypeId, ((Method)method).MethodId, (Types.InvokeOptions)options, arguments.Cast().Select(Value.ToNetworkValue).ToArray())); - if (thrownException != default(TaggedObjectId)) + if (thread != null || VirtualMachine.GetCanInvokeWithoutThread()) { - throw new NotImplementedException(); + ThreadId threadId = default(ThreadId); + if (thread != null) + threadId = ((ThreadReference)thread).ThreadId; + + DebugErrorHandler.ThrowOnFailure(VirtualMachine.ProtocolService.InvokeObjectMethod(out returnValue, out thrownException, ObjectId, threadId, (ClassId)((Method)method).DeclaringType.TaggedReferenceTypeId, ((Method)method).MethodId, (Types.InvokeOptions)options, arguments.Cast().Select(Value.ToNetworkValue).ToArray())); + } + else + { + returnValue = default(Types.Value); + thrownException = default(TaggedObjectId); + Error errorCode = Error.ThreadNotSuspended; + + foreach (var vmThread in VirtualMachine.GetAllThreads()) + { + ThreadReference threadReference = vmThread as ThreadReference; + if (threadReference == null) + continue; + + ThreadStatus threadStatus; + SuspendStatus suspendStatus; + DebugErrorHandler.ThrowOnFailure(VirtualMachine.ProtocolService.GetThreadStatus(out threadStatus, out suspendStatus, threadReference.ThreadId)); + if (threadStatus != ThreadStatus.Running) + continue; + + if (suspendStatus != SuspendStatus.Suspended) + continue; + + int suspendCount; + DebugErrorHandler.ThrowOnFailure(VirtualMachine.ProtocolService.GetThreadSuspendCount(out suspendCount, threadReference.ThreadId)); + + errorCode = VirtualMachine.ProtocolService.InvokeObjectMethod(out returnValue, out thrownException, ObjectId, threadReference.ThreadId, (ClassId)((Method)method).DeclaringType.TaggedReferenceTypeId, ((Method)method).MethodId, (Types.InvokeOptions)options, arguments.Cast().Select(Value.ToNetworkValue).ToArray()); + if (errorCode == Error.InvalidThread) + continue; + + break; + } + + DebugErrorHandler.ThrowOnFailure(errorCode); + } + + if (thrownException.ObjectId != default(ObjectId)) + { + throw new InternalException((int)Error.Internal, "An exception was thrown by the invoked method."); } Value returnValueMirror = VirtualMachine.GetMirrorOf(returnValue); diff --git a/Tvl.Java.DebugInterface.Client/Tvl.Java.DebugInterface.Client.csproj b/Tvl.Java.DebugInterface.Client/Tvl.Java.DebugInterface.Client.csproj index 928d272..3fa0ab6 100644 --- a/Tvl.Java.DebugInterface.Client/Tvl.Java.DebugInterface.Client.csproj +++ b/Tvl.Java.DebugInterface.Client/Tvl.Java.DebugInterface.Client.csproj @@ -90,6 +90,7 @@ + @@ -138,6 +139,19 @@ + + + + + + + + + + + + + diff --git a/Tvl.Java.DebugInterface.Client/VirtualMachine.cs b/Tvl.Java.DebugInterface.Client/VirtualMachine.cs index a85a124..2a71894 100644 --- a/Tvl.Java.DebugInterface.Client/VirtualMachine.cs +++ b/Tvl.Java.DebugInterface.Client/VirtualMachine.cs @@ -6,6 +6,7 @@ using System.Collections.ObjectModel; using System.Diagnostics.Contracts; using System.Linq; + using System.Management; using System.ServiceModel; using Tvl.Java.DebugInterface.Client.DebugProtocol; using Tvl.Java.DebugInterface.Client.Events; @@ -24,6 +25,7 @@ internal partial class VirtualMachine : IVirtualMachine private readonly EventQueue _eventQueue; private readonly EventRequestManager _eventRequestManager; private readonly string[] _sourcePaths; + private readonly bool _jdwp; private EventWaitHandle _ipcHandle; private DebugProtocol.IDebugProtocolService _protocolService; @@ -40,7 +42,7 @@ internal partial class VirtualMachine : IVirtualMachine private readonly Dictionary _types = new Dictionary(); - public VirtualMachine(string[] sourcePaths) + public VirtualMachine(string[] sourcePaths, bool jdwp) { Contract.Requires(sourcePaths != null, "sourcePaths"); @@ -48,6 +50,7 @@ public VirtualMachine(string[] sourcePaths) _eventRequestManager = new EventRequestManager(this); _eventQueue = new EventQueue(this); _sourcePaths = sourcePaths; + _jdwp = jdwp; } public event EventHandler AttachComplete; @@ -102,8 +105,25 @@ public bool IsDisposed internal static VirtualMachine BeginAttachToProcess(int processId, string[] sourcePaths) { - VirtualMachine virtualMachine = new VirtualMachine(sourcePaths); - virtualMachine._ipcHandle = new EventWaitHandle(false, EventResetMode.ManualReset, string.Format("JavaDebuggerInitHandle{0}", processId)); + string wmiQuery = string.Format("select CommandLine from Win32_Process where ProcessId={0}", processId); + ManagementObjectSearcher searcher = new ManagementObjectSearcher(wmiQuery); + ManagementObjectCollection collection = searcher.Get(); + string commandLine = null; + foreach (ManagementObject managementObject in collection) + { + commandLine = (string)managementObject["CommandLine"]; + break; + } + + bool jdwp = false; + if (!string.IsNullOrEmpty(commandLine) && commandLine.IndexOf("-Xrunjdwp:", StringComparison.Ordinal) >= 0) + jdwp = true; + + VirtualMachine virtualMachine = new VirtualMachine(sourcePaths, jdwp); + if (!jdwp) + { + virtualMachine._ipcHandle = new EventWaitHandle(false, EventResetMode.ManualReset, string.Format("JavaDebuggerInitHandle{0}", processId)); + } Task initializeTask = Task.Factory.StartNew(virtualMachine.InitializeServicesAfterProcessStarts).HandleNonCriticalExceptions(); @@ -114,9 +134,12 @@ private void InitializeServicesAfterProcessStarts() { try { - _ipcHandle.WaitOne(); - _ipcHandle.Dispose(); - _ipcHandle = null; + if (!_jdwp) + { + _ipcHandle.WaitOne(); + _ipcHandle.Dispose(); + _ipcHandle = null; + } CreateProtocolServiceClient(); _protocolService.Attach(); @@ -132,17 +155,25 @@ private void InitializeServicesAfterProcessStarts() private void CreateProtocolServiceClient() { - var binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None) - { - MaxReceivedMessageSize = 10 * 1024 * 1024, - ReceiveTimeout = TimeSpan.MaxValue, - SendTimeout = TimeSpan.MaxValue - }; - DebugProtocolCallback callback = new DebugProtocolCallback(this); - var callbackInstance = new InstanceContext(callback); - var remoteAddress = new EndpointAddress("net.pipe://localhost/Tvl.Java.DebugHost/DebugProtocolService/"); - _protocolService = new DebugProtocol.DebugProtocolServiceClient(callbackInstance, binding, remoteAddress); + + if (_jdwp) + { + _protocolService = new Jdwp.JdwpDebugProtocolService(callback); + } + else + { + var binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None) + { + MaxReceivedMessageSize = 10 * 1024 * 1024, + ReceiveTimeout = TimeSpan.MaxValue, + SendTimeout = TimeSpan.MaxValue + }; + + var callbackInstance = new InstanceContext(callback); + var remoteAddress = new EndpointAddress("net.pipe://localhost/Tvl.Java.DebugHost/DebugProtocolService/"); + _protocolService = new DebugProtocol.DebugProtocolServiceClient(callbackInstance, binding, remoteAddress); + } } #region IVirtualMachine Members @@ -405,6 +436,26 @@ public bool GetCanWatchFieldModification() return (capabilities & Capabilities.CanGenerateFieldModificationEvents) != 0; } + public bool GetCanStepByStatement() + { + if (ProtocolService == null) + throw new VirtualMachineDisconnectedException(); + + Capabilities capabilities; + DebugErrorHandler.ThrowOnFailure(ProtocolService.GetCapabilities(out capabilities)); + return (capabilities & Capabilities.CanStepByStatement) != 0; + } + + public bool GetCanInvokeWithoutThread() + { + if (ProtocolService == null) + throw new VirtualMachineDisconnectedException(); + + Capabilities capabilities; + DebugErrorHandler.ThrowOnFailure(ProtocolService.GetCapabilities(out capabilities)); + return (capabilities & Capabilities.CanInvokeWithoutThread) != 0; + } + public ReadOnlyCollection GetClassesByName(string className) { return new ReadOnlyCollection(GetAllClasses().Where(i => string.Equals(i.GetName(), className, StringComparison.Ordinal)).ToArray()); diff --git a/Tvl.Java.DebugInterface.Types/Analysis/BytecodeDisassembler.cs b/Tvl.Java.DebugInterface.Types/Analysis/BytecodeDisassembler.cs index 57915c8..e998f88 100644 --- a/Tvl.Java.DebugInterface.Types/Analysis/BytecodeDisassembler.cs +++ b/Tvl.Java.DebugInterface.Types/Analysis/BytecodeDisassembler.cs @@ -364,6 +364,19 @@ public static DisassembledMethod Disassemble(byte[] bytecode) default: throw new FormatException(); } + + if (workQueue.Count == 0) + { + for (int i = 0; i < depths.Length; i++) + { + if (depths[i].HasValue) + continue; + + depths[i] = 1; + workQueue.Enqueue(i); + break; + } + } } return new ImmutableList(depths); diff --git a/Tvl.Java.DebugInterface.Types/Capabilities.cs b/Tvl.Java.DebugInterface.Types/Capabilities.cs index 2f606a6..04c6fcf 100644 --- a/Tvl.Java.DebugInterface.Types/Capabilities.cs +++ b/Tvl.Java.DebugInterface.Types/Capabilities.cs @@ -88,5 +88,9 @@ public enum Capabilities : long CanGenerateResourceExhaustionHeapEvents = 0x0000008000000000, CanGenerateResourceExhaustionThreadsEvents = 0x0000010000000000, + + CanStepByStatement = 0x0100000000000000, + + CanInvokeWithoutThread = 0x0200000000000000, } } diff --git a/Tvl.Java.DebugInterface.Types/EventKind.cs b/Tvl.Java.DebugInterface.Types/EventKind.cs index 5a046c7..b63019b 100644 --- a/Tvl.Java.DebugInterface.Types/EventKind.cs +++ b/Tvl.Java.DebugInterface.Types/EventKind.cs @@ -18,6 +18,11 @@ public enum EventKind ExceptionCatch = 30, MethodEntry = 40, MethodExit = 41, + MethodExitWithReturnValue = 42, + MonitorContendedEnter = 43, + MonitorContendedEntered = 44, + MonitorWait = 45, + MonitorWaited = 46, VirtualMachineInit = 90, VirtualMachineDeath = 99, VirtualMachineDisconnected = 100, diff --git a/Tvl.Java.DebugInterface.Types/StepDepth.cs b/Tvl.Java.DebugInterface.Types/StepDepth.cs index f1090f0..be583d2 100644 --- a/Tvl.Java.DebugInterface.Types/StepDepth.cs +++ b/Tvl.Java.DebugInterface.Types/StepDepth.cs @@ -2,8 +2,19 @@ { public enum StepDepth { + /// + /// Step into any method calls that occur before the end of the step. + /// Into = 0, + + /// + /// Step into any method calls that occur before the end of the step. + /// Over = 1, + + /// + /// Step out of the current method. + /// Out = 2, } } diff --git a/Tvl.Java.DebugInterface.Types/ThreadId.cs b/Tvl.Java.DebugInterface.Types/ThreadId.cs index 36ec750..6fa5440 100644 --- a/Tvl.Java.DebugInterface.Types/ThreadId.cs +++ b/Tvl.Java.DebugInterface.Types/ThreadId.cs @@ -62,5 +62,10 @@ public override int GetHashCode() { return Handle.GetHashCode(); } + + public override string ToString() + { + return string.Format("Thread #{0}", Handle); + } } } diff --git a/Tvl.Java.DebugInterface/Contracts/IPathSearchingVirtualMachineContracts.cs b/Tvl.Java.DebugInterface/Contracts/IPathSearchingVirtualMachineContracts.cs index 8e2159a..4641c1e 100644 --- a/Tvl.Java.DebugInterface/Contracts/IPathSearchingVirtualMachineContracts.cs +++ b/Tvl.Java.DebugInterface/Contracts/IPathSearchingVirtualMachineContracts.cs @@ -166,6 +166,16 @@ public bool GetCanWatchFieldModification() throw new NotImplementedException(); } + public bool GetCanStepByStatement() + { + throw new NotImplementedException(); + } + + public bool GetCanInvokeWithoutThread() + { + throw new NotImplementedException(); + } + public ReadOnlyCollection GetClassesByName(string className) { throw new NotImplementedException(); diff --git a/Tvl.Java.DebugInterface/Contracts/IVirtualMachineContracts.cs b/Tvl.Java.DebugInterface/Contracts/IVirtualMachineContracts.cs index c967003..e4f9e4f 100644 --- a/Tvl.Java.DebugInterface/Contracts/IVirtualMachineContracts.cs +++ b/Tvl.Java.DebugInterface/Contracts/IVirtualMachineContracts.cs @@ -147,6 +147,16 @@ public bool GetCanWatchFieldModification() throw new NotImplementedException(); } + public bool GetCanStepByStatement() + { + throw new NotImplementedException(); + } + + public bool GetCanInvokeWithoutThread() + { + throw new NotImplementedException(); + } + public ReadOnlyCollection GetClassesByName(string className) { Contract.Requires(className != null, "className"); diff --git a/Tvl.Java.DebugInterface/IVirtualMachine.cs b/Tvl.Java.DebugInterface/IVirtualMachine.cs index c2ae549..9063c0c 100644 --- a/Tvl.Java.DebugInterface/IVirtualMachine.cs +++ b/Tvl.Java.DebugInterface/IVirtualMachine.cs @@ -138,6 +138,16 @@ public interface IVirtualMachine : IDisposable, IMirror /// bool GetCanWatchFieldModification(); + /// + /// Determines if the target VM supports the step size. + /// + bool GetCanStepByStatement(); + + /// + /// Determines if the target VM supports invoking methods without explicitly providing a thread. + /// + bool GetCanInvokeWithoutThread(); + /// /// Returns the loaded reference types that match a given name. /// diff --git a/Tvl.VisualStudio.Language.Java/Debugger/Events/DebugOutputStringEvent.cs b/Tvl.VisualStudio.Language.Java/Debugger/Events/DebugOutputStringEvent.cs index 6e05562..ace746d 100644 --- a/Tvl.VisualStudio.Language.Java/Debugger/Events/DebugOutputStringEvent.cs +++ b/Tvl.VisualStudio.Language.Java/Debugger/Events/DebugOutputStringEvent.cs @@ -11,8 +11,8 @@ public class DebugOutputStringEvent : DebugEvent, IDebugOutputStringEvent2 { private readonly string _message; - public DebugOutputStringEvent(enum_EVENTATTRIBUTES attributes, string message) - : base(attributes) + public DebugOutputStringEvent(string message) + : base(enum_EVENTATTRIBUTES.EVENT_ASYNCHRONOUS) { Contract.Requires(message != null, "message"); diff --git a/Tvl.VisualStudio.Language.Java/Debugger/Extensions/DebugEventCallbackExtensions.cs b/Tvl.VisualStudio.Language.Java/Debugger/Extensions/DebugEventCallbackExtensions.cs index a9dd970..782d2f2 100644 --- a/Tvl.VisualStudio.Language.Java/Debugger/Extensions/DebugEventCallbackExtensions.cs +++ b/Tvl.VisualStudio.Language.Java/Debugger/Extensions/DebugEventCallbackExtensions.cs @@ -13,9 +13,7 @@ public static int Event(this IDebugEventCallback2 callback, IDebugEngine2 engine Contract.Requires(debugEvent != null, "debugEvent"); Contract.Requires(engine != null, "engine"); - Guid guid = debugEvent.EventGuid; - enum_EVENTATTRIBUTES attributes = debugEvent.Attributes; - return callback.Event(engine, process, program, thread, debugEvent, ref guid, (uint)attributes); + return callback.Event(engine, process, program, thread, debugEvent, debugEvent.EventGuid, (uint)debugEvent.Attributes); } } } diff --git a/Tvl.VisualStudio.Language.Java/Debugger/JavaDebugDisassemblyStream.cs b/Tvl.VisualStudio.Language.Java/Debugger/JavaDebugDisassemblyStream.cs index d04576b..a16ae35 100644 --- a/Tvl.VisualStudio.Language.Java/Debugger/JavaDebugDisassemblyStream.cs +++ b/Tvl.VisualStudio.Language.Java/Debugger/JavaDebugDisassemblyStream.cs @@ -11,6 +11,7 @@ using Tvl.Java.DebugInterface; using Tvl.Java.DebugInterface.Types; using Tvl.Java.DebugInterface.Types.Analysis; + using Tvl.Java.DebugInterface.Types.Loader; [ComVisible(true)] public class JavaDebugDisassemblyStream : IDebugDisassemblyStream2 @@ -31,7 +32,15 @@ public JavaDebugDisassemblyStream(JavaDebugCodeContext executionContext) _disassembledMethod = BytecodeDisassembler.Disassemble(_bytecode); var constantPool = executionContext.Location.GetDeclaringType().GetConstantPool(); - var exceptionTable = executionContext.Location.GetMethod().GetExceptionTable(); + ReadOnlyCollection exceptionTable; + try + { + exceptionTable = executionContext.Location.GetMethod().GetExceptionTable(); + } + catch (DebuggerException) + { + exceptionTable = new ReadOnlyCollection(new ExceptionTableEntry[0]); + } _evaluationStackDepths = BytecodeDisassembler.GetEvaluationStackDepths(_disassembledMethod, constantPool, exceptionTable); } @@ -210,7 +219,8 @@ public int Read(uint dwInstructions, enum_DISASSEMBLY_STREAM_FIELDS dwFields, ou if (dwFields.GetOpCode()) { - prgDisassembly[i].bstrOpcode = string.Format("[{0}]{1}", _evaluationStackDepths[_currentInstructionIndex + i], instruction.OpCode.Name ?? "???"); + string depth = _evaluationStackDepths != null ? string.Format("[{0}]", _evaluationStackDepths[_currentInstructionIndex + i]) : string.Empty; + prgDisassembly[i].bstrOpcode = string.Format("{0}{1}", depth, instruction.OpCode.Name ?? "???"); prgDisassembly[i].dwFields |= enum_DISASSEMBLY_STREAM_FIELDS.DSF_OPCODE; } diff --git a/Tvl.VisualStudio.Language.Java/Debugger/JavaDebugDocumentContext.cs b/Tvl.VisualStudio.Language.Java/Debugger/JavaDebugDocumentContext.cs index 0053eee..6605d71 100644 --- a/Tvl.VisualStudio.Language.Java/Debugger/JavaDebugDocumentContext.cs +++ b/Tvl.VisualStudio.Language.Java/Debugger/JavaDebugDocumentContext.cs @@ -13,6 +13,7 @@ using Tvl.Collections; using Tvl.Java.DebugInterface; using Tvl.Java.DebugInterface.Types.Analysis; + using Tvl.Java.DebugInterface.Types.Loader; using File = System.IO.File; [ComVisible(true)] @@ -259,7 +260,15 @@ private bool TryGetAssociatedTree(out IParseTree associatedTree, out IList exceptionTable; + try + { + exceptionTable = _location.GetMethod().GetExceptionTable(); + } + catch (DebuggerException) + { + exceptionTable = new ReadOnlyCollection(new ExceptionTableEntry[0]); + } ImmutableList evaluationStackDepths = BytecodeDisassembler.GetEvaluationStackDepths(disassembledMethod, constantPool, exceptionTable); ReadOnlyCollection locations = _location.GetMethod().GetLineLocations(); diff --git a/Tvl.VisualStudio.Language.Java/Debugger/JavaDebugProgram.cs b/Tvl.VisualStudio.Language.Java/Debugger/JavaDebugProgram.cs index 24fe0df..7b002af 100644 --- a/Tvl.VisualStudio.Language.Java/Debugger/JavaDebugProgram.cs +++ b/Tvl.VisualStudio.Language.Java/Debugger/JavaDebugProgram.cs @@ -438,7 +438,7 @@ public int Step(IDebugThread2 pThread, enum_STEPKIND sk, enum_STEPUNIT Step) break; case enum_STEPUNIT.STEP_STATEMENT: - size = StepSize.Statement; + size = VirtualMachine.GetCanStepByStatement() ? StepSize.Statement : StepSize.Line; break; default: @@ -1003,7 +1003,7 @@ private void HandleException(object sender, ExceptionEventArgs e) else { string message = exceptionEvent.GetDescription() + Environment.NewLine; - DebugEvent debugEvent = new DebugOutputStringEvent(GetAttributesForEvent(e), message); + DebugEvent debugEvent = new DebugOutputStringEvent(message); SetEventProperties(debugEvent, e, true); Callback.Event(DebugEngine, Process, this, thread, debugEvent); @@ -1098,7 +1098,7 @@ private void HandleClassPrepare(object sender, ClassPrepareEventArgs e) // The format of the message created by the .NET debugger is this: // 'devenv.exe' (Managed (v4.0.30319)): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\Microsoft.VisualStudio.Windows.Forms\v4.0_10.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.Windows.Forms.dll' string message = string.Format("'{0}' ({1}): Loaded '{2}'\n", Process.GetName(enum_GETNAME_TYPE.GN_BASENAME), Java.Constants.JavaLanguageName, e.Type.GetName()); - DebugEvent outputEvent = new DebugOutputStringEvent(GetAttributesForEvent(e), message); + DebugEvent outputEvent = new DebugOutputStringEvent(message); SetEventProperties(outputEvent, e, true); Callback.Event(DebugEngine, Process, this, thread, outputEvent); diff --git a/Tvl.VisualStudio.Language.Java/Debugger/JavaDebugThread.cs b/Tvl.VisualStudio.Language.Java/Debugger/JavaDebugThread.cs index 7ac088a..83cd455 100644 --- a/Tvl.VisualStudio.Language.Java/Debugger/JavaDebugThread.cs +++ b/Tvl.VisualStudio.Language.Java/Debugger/JavaDebugThread.cs @@ -208,10 +208,18 @@ public int GetThreadId(out uint pdwThreadId) if (_getIdMethod == null) return VSConstants.E_FAIL; - using (var result = _thread.InvokeMethod(null, _getIdMethod, InvokeOptions.None)) + try { - pdwThreadId = (uint)((ILongValue)result.Value).GetValue(); - return VSConstants.S_OK; + using (var result = _thread.InvokeMethod(null, _getIdMethod, InvokeOptions.SingleThreaded)) + { + pdwThreadId = (uint)((ILongValue)result.Value).GetValue(); + return VSConstants.S_OK; + } + } + catch (DebuggerException) + { + pdwThreadId = 0; + return VSConstants.E_FAIL; } } @@ -232,10 +240,18 @@ public int GetThreadPriorityId(out int priorityId) if (_getPriorityMethod == null) return VSConstants.E_FAIL; - using (var result = _thread.InvokeMethod(null, _getPriorityMethod, InvokeOptions.None)) + try { - priorityId = ((IIntegerValue)result.Value).GetValue(); - return VSConstants.S_OK; + using (var result = _thread.InvokeMethod(null, _getPriorityMethod, InvokeOptions.SingleThreaded)) + { + priorityId = ((IIntegerValue)result.Value).GetValue(); + return VSConstants.S_OK; + } + } + catch (DebuggerException) + { + priorityId = 0; + return VSConstants.E_FAIL; } } diff --git a/Tvl.VisualStudio.Language.Java/Project/JavaConfigConstants.cs b/Tvl.VisualStudio.Language.Java/Project/JavaConfigConstants.cs index 5d108fc..5e58442 100644 --- a/Tvl.VisualStudio.Language.Java/Project/JavaConfigConstants.cs +++ b/Tvl.VisualStudio.Language.Java/Project/JavaConfigConstants.cs @@ -57,6 +57,7 @@ public static class JavaConfigConstants public const string DebugWorkingDirectory = "WorkingDirectory"; public const string DebugUseRemoteMachine = "UseRemoteMachine"; public const string DebugRemoteMachineName = "RemoteMachineName"; + public const string DebugAgent = "DebugAgent"; public const string DebugJvmArguments = "DebugJvmArguments"; public const string DebugAgentArguments = "DebugAgentArguments"; } diff --git a/Tvl.VisualStudio.Language.Java/Project/JavaProjectConfig.cs b/Tvl.VisualStudio.Language.Java/Project/JavaProjectConfig.cs index 7e2b43a..1a463f3 100644 --- a/Tvl.VisualStudio.Language.Java/Project/JavaProjectConfig.cs +++ b/Tvl.VisualStudio.Language.Java/Project/JavaProjectConfig.cs @@ -12,6 +12,7 @@ using _PersistStorageType = Microsoft.VisualStudio.Shell.Interop._PersistStorageType; using CommandLineBuilder = Microsoft.Build.Utilities.CommandLineBuilder; using DEBUG_LAUNCH_OPERATION = Microsoft.VisualStudio.Shell.Interop.DEBUG_LAUNCH_OPERATION; + using DebugAgent = Tvl.VisualStudio.Language.Java.Project.PropertyPages.DebugAgent; using Directory = System.IO.Directory; using File = System.IO.File; using IVsDebugger2 = Microsoft.VisualStudio.Shell.Interop.IVsDebugger2; @@ -154,14 +155,28 @@ public override int DebugLaunch(uint grfLaunch) if (x64) agentBaseFileName += "X64"; - string agentFolder = Path.GetDirectoryName(typeof(JavaProjectConfig).Assembly.Location); - string agentFileName = agentBaseFileName + ".dll"; - string agentPath = Path.GetFullPath(Path.Combine(agentFolder, agentFileName)); - commandLine.AppendSwitchIfNotNull("-agentpath:", agentPath); + bool useDevelopmentEnvironment = (grfLaunch & (uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_NoDebug) == 0; - string agentArguments = GetConfigurationProperty(JavaConfigConstants.DebugAgentArguments, _PersistStorageType.PST_USER_FILE, false); - if (!string.IsNullOrEmpty(agentArguments)) - commandLine.AppendTextUnquoted("=" + agentArguments); + string debugAgentName = GetConfigurationProperty(JavaConfigConstants.DebugAgent, _PersistStorageType.PST_USER_FILE, false); + bool useJdwp = string.Equals(DebugAgent.Jdwp.ToString(), debugAgentName, StringComparison.OrdinalIgnoreCase); + + if (useJdwp) + { + commandLine.AppendSwitch("-Xdebug"); + string serverValue = useDevelopmentEnvironment ? "y" : "n"; + commandLine.AppendSwitch("-Xrunjdwp:transport=dt_socket,server=" + serverValue + ",address=6777"); + } + else + { + string agentFolder = Path.GetDirectoryName(typeof(JavaProjectConfig).Assembly.Location); + string agentFileName = agentBaseFileName + ".dll"; + string agentPath = Path.GetFullPath(Path.Combine(agentFolder, agentFileName)); + commandLine.AppendSwitchIfNotNull("-agentpath:", agentPath); + + string agentArguments = GetConfigurationProperty(JavaConfigConstants.DebugAgentArguments, _PersistStorageType.PST_USER_FILE, false); + if (!string.IsNullOrEmpty(agentArguments)) + commandLine.AppendTextUnquoted("=" + agentArguments); + } switch (GetConfigurationProperty(JavaConfigConstants.DebugStartAction, _PersistStorageType.PST_USER_FILE, false)) { @@ -230,7 +245,6 @@ public override int DebugLaunch(uint grfLaunch) info.Arguments = commandLine.ToString(); - bool useDevelopmentEnvironment = (grfLaunch & (uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_NoDebug) == 0; info.Executable = FindJavaBinary("java.exe", useDevelopmentEnvironment); //info.CurrentDirectory = GetConfigurationProperty("WorkingDirectory", false, _PersistStorageType.PST_USER_FILE); @@ -242,7 +256,8 @@ public override int DebugLaunch(uint grfLaunch) //VSConstants.DebugEnginesGuids.ManagedOnly_guid, //VSConstants.DebugEnginesGuids.NativeOnly_guid, }; - info.PortSupplier = new Guid("{708C1ECA-FF48-11D2-904F-00C04FA302A1}"); + Guid localPortSupplier = new Guid("{708C1ECA-FF48-11D2-904F-00C04FA302A1}"); + info.PortSupplier = localPortSupplier; info.LaunchOperation = DEBUG_LAUNCH_OPERATION.DLO_CreateProcess; info.LaunchFlags = (__VSDBGLAUNCHFLAGS)grfLaunch | (__VSDBGLAUNCHFLAGS)__VSDBGLAUNCHFLAGS2.DBGLAUNCH_MergeEnv; diff --git a/Tvl.VisualStudio.Language.Java/Project/PropertyPages/DebugAgent.cs b/Tvl.VisualStudio.Language.Java/Project/PropertyPages/DebugAgent.cs new file mode 100644 index 0000000..10aa97b --- /dev/null +++ b/Tvl.VisualStudio.Language.Java/Project/PropertyPages/DebugAgent.cs @@ -0,0 +1,13 @@ +namespace Tvl.VisualStudio.Language.Java.Project.PropertyPages +{ + using System.ComponentModel.DataAnnotations; + + public enum DebugAgent + { + [Display(Name = "High-performance debug agent (default)")] + CustomJvmti, + + [Display(Name = "JDWP debugging (compatibility mode)")] + Jdwp, + } +} diff --git a/Tvl.VisualStudio.Language.Java/Project/PropertyPages/JavaDebugPropertyPage.cs b/Tvl.VisualStudio.Language.Java/Project/PropertyPages/JavaDebugPropertyPage.cs index 4c8ac28..1307134 100644 --- a/Tvl.VisualStudio.Language.Java/Project/PropertyPages/JavaDebugPropertyPage.cs +++ b/Tvl.VisualStudio.Language.Java/Project/PropertyPages/JavaDebugPropertyPage.cs @@ -45,6 +45,13 @@ protected override void BindProperties() PropertyPagePanel.UseRemoteMachine = GetConfigPropertyBoolean(JavaConfigConstants.DebugUseRemoteMachine, _PersistStorageType.PST_USER_FILE); PropertyPagePanel.RemoteMachineName = GetConfigProperty(JavaConfigConstants.DebugRemoteMachineName, _PersistStorageType.PST_USER_FILE); + string debugAgentName = GetConfigProperty(JavaConfigConstants.DebugAgent, _PersistStorageType.PST_USER_FILE); + DebugAgent debugAgent; + if (!Enum.TryParse(debugAgentName, out debugAgent)) + debugAgent = DebugAgent.CustomJvmti; + + PropertyPagePanel.DebugAgent = debugAgent; + PropertyPagePanel.VirtualMachineArguments = GetConfigProperty(JavaConfigConstants.DebugJvmArguments, _PersistStorageType.PST_USER_FILE); PropertyPagePanel.AgentArguments = GetConfigProperty(JavaConfigConstants.DebugAgentArguments, _PersistStorageType.PST_USER_FILE); } @@ -60,6 +67,7 @@ protected override bool ApplyChanges() SetConfigProperty(JavaConfigConstants.DebugWorkingDirectory, _PersistStorageType.PST_USER_FILE, PropertyPagePanel.WorkingDirectory); SetConfigProperty(JavaConfigConstants.DebugUseRemoteMachine, _PersistStorageType.PST_USER_FILE, PropertyPagePanel.UseRemoteMachine); SetConfigProperty(JavaConfigConstants.DebugRemoteMachineName, _PersistStorageType.PST_USER_FILE, PropertyPagePanel.RemoteMachineName); + SetConfigProperty(JavaConfigConstants.DebugAgent, _PersistStorageType.PST_USER_FILE, PropertyPagePanel.DebugAgent.ToString()); SetConfigProperty(JavaConfigConstants.DebugJvmArguments, _PersistStorageType.PST_USER_FILE, PropertyPagePanel.VirtualMachineArguments); SetConfigProperty(JavaConfigConstants.DebugAgentArguments, _PersistStorageType.PST_USER_FILE, PropertyPagePanel.AgentArguments); diff --git a/Tvl.VisualStudio.Language.Java/Project/PropertyPages/JavaDebugPropertyPagePanel.Designer.cs b/Tvl.VisualStudio.Language.Java/Project/PropertyPages/JavaDebugPropertyPagePanel.Designer.cs index c33c7ce..c97b6a0 100644 --- a/Tvl.VisualStudio.Language.Java/Project/PropertyPages/JavaDebugPropertyPagePanel.Designer.cs +++ b/Tvl.VisualStudio.Language.Java/Project/PropertyPages/JavaDebugPropertyPagePanel.Designer.cs @@ -33,6 +33,7 @@ private void InitializeComponent() System.Windows.Forms.GroupBox groupBox2; System.Windows.Forms.Label label1; System.Windows.Forms.Label label5; + this.cmdDebugAgent = new System.Windows.Forms.ComboBox(); this.txtRemoteMachine = new System.Windows.Forms.TextBox(); this.chkUseRemoteMachine = new System.Windows.Forms.CheckBox(); this.btnBrowseWorkingDirectory = new System.Windows.Forms.Button(); @@ -48,8 +49,8 @@ private void InitializeComponent() this.txtStartClass = new System.Windows.Forms.TextBox(); this.txtCommandLine = new System.Windows.Forms.TextBox(); this.groupBox3 = new System.Windows.Forms.GroupBox(); - this.txtJvmArguments = new System.Windows.Forms.TextBox(); this.txtAgentArguments = new System.Windows.Forms.TextBox(); + this.txtJvmArguments = new System.Windows.Forms.TextBox(); label3 = new System.Windows.Forms.Label(); label2 = new System.Windows.Forms.Label(); groupBox2 = new System.Windows.Forms.GroupBox(); @@ -72,16 +73,17 @@ private void InitializeComponent() // label2 // label2.AutoSize = true; - label2.Location = new System.Drawing.Point(39, 22); + label2.Location = new System.Drawing.Point(39, 160); label2.Name = "label2"; - label2.Size = new System.Drawing.Size(128, 13); + label2.Size = new System.Drawing.Size(72, 13); label2.TabIndex = 0; - label2.Text = "Command line arguments:"; + label2.Text = "Debug agent:"; // // groupBox2 // groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); + groupBox2.Controls.Add(this.cmdDebugAgent); groupBox2.Controls.Add(this.txtRemoteMachine); groupBox2.Controls.Add(this.chkUseRemoteMachine); groupBox2.Controls.Add(this.btnBrowseWorkingDirectory); @@ -91,11 +93,24 @@ private void InitializeComponent() groupBox2.Controls.Add(label2); groupBox2.Location = new System.Drawing.Point(3, 112); groupBox2.Name = "groupBox2"; - groupBox2.Size = new System.Drawing.Size(553, 159); + groupBox2.Size = new System.Drawing.Size(553, 193); groupBox2.TabIndex = 1; groupBox2.TabStop = false; groupBox2.Text = "Start Options"; // + // cmdDebugAgent + // + this.cmdDebugAgent.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cmdDebugAgent.FormattingEnabled = true; + this.cmdDebugAgent.Items.AddRange(new object[] { + "High-performance debug agent (default)", + "JDWP debugging (compatibility mode)"}); + this.cmdDebugAgent.Location = new System.Drawing.Point(190, 157); + this.cmdDebugAgent.Name = "cmdDebugAgent"; + this.cmdDebugAgent.Size = new System.Drawing.Size(318, 21); + this.cmdDebugAgent.TabIndex = 7; + this.cmdDebugAgent.SelectedValueChanged += new System.EventHandler(this.HandleStateAffectingChange); + // // txtRemoteMachine // this.txtRemoteMachine.Location = new System.Drawing.Point(190, 131); @@ -159,6 +174,15 @@ private void InitializeComponent() label1.TabIndex = 0; label1.Text = "Virtual machine arguments:"; // + // label5 + // + label5.AutoSize = true; + label5.Location = new System.Drawing.Point(39, 108); + label5.Name = "label5"; + label5.Size = new System.Drawing.Size(90, 13); + label5.TabIndex = 0; + label5.Text = "Agent arguments:"; + // // groupBox1 // this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) @@ -255,31 +279,13 @@ private void InitializeComponent() this.groupBox3.Controls.Add(label5); this.groupBox3.Controls.Add(this.txtJvmArguments); this.groupBox3.Controls.Add(label1); - this.groupBox3.Location = new System.Drawing.Point(3, 277); + this.groupBox3.Location = new System.Drawing.Point(3, 311); this.groupBox3.Name = "groupBox3"; this.groupBox3.Size = new System.Drawing.Size(553, 320); this.groupBox3.TabIndex = 2; this.groupBox3.TabStop = false; this.groupBox3.Text = "Java Virtual Machine"; // - // txtJvmArguments - // - this.txtJvmArguments.Location = new System.Drawing.Point(190, 19); - this.txtJvmArguments.Multiline = true; - this.txtJvmArguments.Name = "txtJvmArguments"; - this.txtJvmArguments.Size = new System.Drawing.Size(318, 80); - this.txtJvmArguments.TabIndex = 1; - this.txtJvmArguments.TextChanged += new System.EventHandler(this.HandleCommandLineAffectingChange); - // - // label5 - // - label5.AutoSize = true; - label5.Location = new System.Drawing.Point(39, 108); - label5.Name = "label5"; - label5.Size = new System.Drawing.Size(90, 13); - label5.TabIndex = 0; - label5.Text = "Agent arguments:"; - // // txtAgentArguments // this.txtAgentArguments.Location = new System.Drawing.Point(190, 105); @@ -289,6 +295,15 @@ private void InitializeComponent() this.txtAgentArguments.TabIndex = 1; this.txtAgentArguments.TextChanged += new System.EventHandler(this.HandleCommandLineAffectingChange); // + // txtJvmArguments + // + this.txtJvmArguments.Location = new System.Drawing.Point(190, 19); + this.txtJvmArguments.Multiline = true; + this.txtJvmArguments.Name = "txtJvmArguments"; + this.txtJvmArguments.Size = new System.Drawing.Size(318, 80); + this.txtJvmArguments.TabIndex = 1; + this.txtJvmArguments.TextChanged += new System.EventHandler(this.HandleCommandLineAffectingChange); + // // JavaDebugPropertyPagePanel // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -298,7 +313,7 @@ private void InitializeComponent() this.Controls.Add(this.groupBox1); this.MinimumSize = new System.Drawing.Size(563, 533); this.Name = "JavaDebugPropertyPagePanel"; - this.Size = new System.Drawing.Size(563, 757); + this.Size = new System.Drawing.Size(563, 645); groupBox2.ResumeLayout(false); groupBox2.PerformLayout(); this.groupBox1.ResumeLayout(false); @@ -328,5 +343,6 @@ private void InitializeComponent() private System.Windows.Forms.GroupBox groupBox3; private System.Windows.Forms.TextBox txtJvmArguments; private System.Windows.Forms.TextBox txtAgentArguments; + private System.Windows.Forms.ComboBox cmdDebugAgent; } } diff --git a/Tvl.VisualStudio.Language.Java/Project/PropertyPages/JavaDebugPropertyPagePanel.cs b/Tvl.VisualStudio.Language.Java/Project/PropertyPages/JavaDebugPropertyPagePanel.cs index 569fabd..f89b070 100644 --- a/Tvl.VisualStudio.Language.Java/Project/PropertyPages/JavaDebugPropertyPagePanel.cs +++ b/Tvl.VisualStudio.Language.Java/Project/PropertyPages/JavaDebugPropertyPagePanel.cs @@ -14,6 +14,9 @@ public JavaDebugPropertyPagePanel(JavaDebugPropertyPage parentPropertyPage) : base(parentPropertyPage) { InitializeComponent(); + cmdDebugAgent.Items.Clear(); + cmdDebugAgent.Items.Add(DebugAgent.CustomJvmti); + cmdDebugAgent.Items.Add(DebugAgent.Jdwp); UpdateStates(); RefreshCommandLine(); } @@ -157,6 +160,19 @@ public string RemoteMachineName } } + public DebugAgent DebugAgent + { + get + { + return (DebugAgent)(cmdDebugAgent.SelectedItem ?? DebugAgent.CustomJvmti); + } + + set + { + cmdDebugAgent.SelectedItem = value; + } + } + public string VirtualMachineArguments { get @@ -195,7 +211,17 @@ private void RefreshCommandLine() string javaPath = projectConfig != null ? projectConfig.FindJavaBinary("java.exe", true) : null; commandLine.AppendFileNameIfNotNull(javaPath); - commandLine.AppendSwitch("-agentpath:{AgentPath}"); + string agentSwitch; + if (DebugAgent == DebugAgent.Jdwp) + { + agentSwitch = "-Xrunjdwp:transport=dt_socket,server=y,address=6777"; + } + else + { + agentSwitch = "-agentpath:{AgentPath}"; + } + + commandLine.AppendSwitch(agentSwitch); if (!string.IsNullOrEmpty(AgentArguments)) commandLine.AppendTextUnquoted("=" + AgentArguments); diff --git a/Tvl.VisualStudio.Language.Java/Tvl.VisualStudio.Language.Java.csproj b/Tvl.VisualStudio.Language.Java/Tvl.VisualStudio.Language.Java.csproj index e9f309e..aabdcd0 100644 --- a/Tvl.VisualStudio.Language.Java/Tvl.VisualStudio.Language.Java.csproj +++ b/Tvl.VisualStudio.Language.Java/Tvl.VisualStudio.Language.Java.csproj @@ -230,6 +230,7 @@ + @@ -472,6 +473,7 @@ + UserControl