[sdb] Add support for out arguments in invokes.
authorZoltan Varga <vargaz@gmail.com>
Fri, 4 Jul 2014 13:56:02 +0000 (15:56 +0200)
committerZoltan Varga <vargaz@gmail.com>
Fri, 4 Jul 2014 13:56:16 +0000 (15:56 +0200)
mcs/class/Mono.Debugger.Soft/Mono.Debugger.Soft/Connection.cs
mcs/class/Mono.Debugger.Soft/Mono.Debugger.Soft/InvokeOptions.cs
mcs/class/Mono.Debugger.Soft/Mono.Debugger.Soft/ObjectMirror.cs
mcs/class/Mono.Debugger.Soft/Test/dtest-app.cs
mcs/class/Mono.Debugger.Soft/Test/dtest.cs
mono/mini/debugger-agent.c

index 924dc0cc9318ba5902fa068a2acbbc022433b8f5..14bf701f46fa8cae6f7082e63c161f91e010a553 100644 (file)
@@ -158,7 +158,8 @@ namespace Mono.Debugger.Soft
                NONE = 0x0,
                DISABLE_BREAKPOINTS = 0x1,
                SINGLE_THREADED = 0x2,
-               OUT_THIS = 0x4
+               OUT_THIS = 0x4,
+               OUT_ARGS = 0x8,
        }
 
        enum ElementType {
@@ -1668,30 +1669,39 @@ namespace Mono.Debugger.Soft
                        }
                }
 
-               internal delegate void InvokeMethodCallback (ValueImpl v, ValueImpl exc, ValueImpl out_this, ErrorCode error, object state);
+               internal delegate void InvokeMethodCallback (ValueImpl v, ValueImpl exc, ValueImpl out_this, ValueImpl[] out_args, ErrorCode error, object state);
+
+               void read_invoke_res (PacketReader r, out ValueImpl v, out ValueImpl exc, out ValueImpl out_this, out ValueImpl[] out_args) {
+                       int resflags = r.ReadByte ();
+                       v = null;
+                       exc = null;
+                       out_this = null;
+                       out_args = null;
+                       if (resflags == 0) {
+                               exc = r.ReadValue ();
+                       } else {
+                               v = r.ReadValue ();
+                               if ((resflags & 2) != 0)
+                                       out_this = r.ReadValue ();
+                               if ((resflags & 4) != 0) {
+                                       int nargs = r.ReadInt ();
+                                       out_args = new ValueImpl [nargs];
+                                       for (int i = 0; i < nargs; ++i)
+                                               out_args [i] = r.ReadValue ();
+                               }
+                       }
+               }
 
                internal int VM_BeginInvokeMethod (long thread, long method, ValueImpl this_arg, ValueImpl[] arguments, InvokeFlags flags, InvokeMethodCallback callback, object state) {
                        return Send (CommandSet.VM, (int)CmdVM.INVOKE_METHOD, new PacketWriter ().WriteId (thread).WriteInt ((int)flags).WriteId (method).WriteValue (this_arg).WriteInt (arguments.Length).WriteValues (arguments), delegate (PacketReader r) {
                                        ValueImpl v, exc, out_this = null;
+                                       ValueImpl[] out_args = null;
 
                                        if (r.ErrorCode != 0) {
-                                               callback (null, null, null, (ErrorCode)r.ErrorCode, state);
+                                               callback (null, null, null, null, (ErrorCode)r.ErrorCode, state);
                                        } else {
-                                               int restype = r.ReadByte ();
-                                               if (restype == 0) {
-                                                       exc = r.ReadValue ();
-                                                       v = null;
-                                               } else if (restype == 1) {
-                                                       v = r.ReadValue ();
-                                                       exc = null;
-                                               } else if (restype == 2) {
-                                                       v = r.ReadValue ();
-                                                       out_this = r.ReadValue ();
-                                                       exc = null;
-                                               } else {
-                                                       throw new NotSupportedException ();
-                                               }
-                                               callback (v, exc, out_this, 0, state);
+                                               read_invoke_res (r, out v, out exc, out out_this, out out_args);
+                                               callback (v, exc, out_this, out_args, 0, state);
                                        }
                                }, 1);
                }
@@ -1710,25 +1720,13 @@ namespace Mono.Debugger.Soft
                        }
                        return Send (CommandSet.VM, (int)CmdVM.INVOKE_METHODS, w, delegate (PacketReader r) {
                                        ValueImpl v, exc, out_this = null;
+                                       ValueImpl[] out_args = null;
 
                                        if (r.ErrorCode != 0) {
-                                               callback (null, null, null, (ErrorCode)r.ErrorCode, state);
+                                               callback (null, null, null, null, (ErrorCode)r.ErrorCode, state);
                                        } else {
-                                               int restype = r.ReadByte ();
-                                               if (restype == 0) {
-                                                       exc = r.ReadValue ();
-                                                       v = null;
-                                               } else if (restype == 1) {
-                                                       v = r.ReadValue ();
-                                                       exc = null;
-                                               } else if (restype == 2) {
-                                                       v = r.ReadValue ();
-                                                       out_this = r.ReadValue ();
-                                                       exc = null;
-                                               } else {
-                                                       throw new NotSupportedException ();
-                                               }
-                                               callback (v, exc, out_this, 0, state);
+                                               read_invoke_res (r, out v, out exc, out out_this, out out_args);
+                                               callback (v, exc, out_this, out_args, 0, state);
                                        }
                                }, methods.Length);
                }
index b3cbaf66ccced89b29ed685838b7fe56e9f9af9c..5ca987ec69495c7d68293c8fdcae1c81ea68d84b 100644 (file)
@@ -18,6 +18,10 @@ namespace Mono.Debugger.Soft
                 * Return the changed receiver when invoking
                 * a valuetype method.
                 */
-               ReturnOutThis = 4
+               ReturnOutThis = 4,
+               /*
+                * Return the values of out arguments
+                */
+               ReturnOutArgs = 8
        }
 }
index 968ff1c7b4ce9c9560520692827d326599f4221e..b24e52f257c6d7f1eec4818b9f6aa39fa24d37d3 100644 (file)
@@ -16,6 +16,12 @@ namespace Mono.Debugger.Soft
                // Since protocol version 2.35
                //
                public Value OutThis { get; set; }
+               //
+               // The value of the arguments after the call
+               // Only set when using the InvokeOptions.ReturnOutArgs flag.
+               // Since protocol version 2.35
+               //
+               public Value[] OutArgs { get; set; }
        }
 
        public class ObjectMirror : Value {
@@ -174,6 +180,21 @@ namespace Mono.Debugger.Soft
                                        }, null);
                        return tcs.Task;
                }
+
+               public Task<InvokeResult> InvokeMethodAsyncWithResult (ThreadMirror thread, MethodMirror method, IList<Value> arguments, InvokeOptions options = InvokeOptions.None) {
+                       var tcs = new TaskCompletionSource<InvokeResult> ();
+                       BeginInvokeMethod (thread, method, arguments, options, iar =>
+                                       {
+                                               try {
+                                                       tcs.SetResult (EndInvokeMethodInternalWithResult (iar));
+                                               } catch (OperationCanceledException) {
+                                                       tcs.TrySetCanceled ();
+                                               } catch (Exception ex) {
+                                                       tcs.TrySetException (ex);
+                                               }
+                                       }, null);
+                       return tcs.Task;
+               }
 #endif
 
                //
@@ -237,6 +258,10 @@ namespace Mono.Debugger.Soft
                                get; set;
                        }
 
+                       public ValueImpl[] OutArgs {
+                               get; set;
+                       }
+
                        public ValueImpl Exception {
                                get; set;
                        }
@@ -276,6 +301,8 @@ namespace Mono.Debugger.Soft
                                f |= InvokeFlags.SINGLE_THREADED;
                        if ((options & InvokeOptions.ReturnOutThis) != 0)
                                f |= InvokeFlags.OUT_THIS;
+                       if ((options & InvokeOptions.ReturnOutArgs) != 0)
+                               f |= InvokeFlags.OUT_ARGS;
 
                        InvokeAsyncResult r = new InvokeAsyncResult { AsyncState = state, AsyncWaitHandle = new ManualResetEvent (false), VM = vm, Thread = thread, Callback = callback };
                        thread.InvalidateFrames ();
@@ -285,7 +312,7 @@ namespace Mono.Debugger.Soft
                }
 
                // This is called when the result of an invoke is received
-               static void InvokeCB (ValueImpl v, ValueImpl exc, ValueImpl out_this, ErrorCode error, object state) {
+               static void InvokeCB (ValueImpl v, ValueImpl exc, ValueImpl out_this, ValueImpl[] out_args, ErrorCode error, object state) {
                        InvokeAsyncResult r = (InvokeAsyncResult)state;
 
                        if (error != 0) {
@@ -296,6 +323,7 @@ namespace Mono.Debugger.Soft
                        }
 
                        r.OutThis = out_this;
+                       r.OutArgs = out_args;
 
                        r.IsCompleted = true;
                        ((ManualResetEvent)r.AsyncWaitHandle).Set ();
@@ -327,7 +355,14 @@ namespace Mono.Debugger.Soft
                                if (r.Exception != null)
                                        throw new InvocationException ((ObjectMirror)r.VM.DecodeValue (r.Exception));
 
-                               return new InvokeResult () { Result = r.VM.DecodeValue (r.Value), OutThis = r.OutThis != null ? r.VM.DecodeValue (r.OutThis) : null };
+                               Value out_this = null;
+                               if (r.OutThis != null)
+                                       out_this = r.VM.DecodeValue (r.OutThis);
+                               Value[] out_args = null;
+                               if (r.OutArgs != null)
+                                       out_args = r.VM.DecodeValues (r.OutArgs);
+
+                               return new InvokeResult () { Result = r.VM.DecodeValue (r.Value), OutThis = out_this, OutArgs = out_args };
                        }
                }
 
@@ -400,7 +435,7 @@ namespace Mono.Debugger.Soft
                }
 
                // This is called when the result of an invoke is received
-               static void InvokeMultipleCB (ValueImpl v, ValueImpl exc, ValueImpl out_this, ErrorCode error, object state) {
+               static void InvokeMultipleCB (ValueImpl v, ValueImpl exc, ValueImpl out_this, ValueImpl[] out_args, ErrorCode error, object state) {
                        var r = (InvokeAsyncResult)state;
 
                        Interlocked.Decrement (ref r.NumPending);
index 85482ca29167c8b8d6778a7b1b50e18763de381d..06c7830a0f558fe13f0abc6b45fa2fa41b1943b5 100644 (file)
@@ -959,6 +959,10 @@ public class Tests : TestsBase, ITest2
                return 42;
        }
 
+       public void invoke_out (out int foo) {
+               foo = 5;
+       }
+
        [MethodImplAttribute (MethodImplOptions.NoInlining)]
        public static void exceptions () {
                try {
index b5984a2e500949cb4cc47778d7a07175d93efb3a..6b8630d85f7220916ebdb35890184ce41f2b9ea6 100644 (file)
@@ -2110,6 +2110,19 @@ public class DebuggerTests
                        Assert.AreEqual ("Exception", ex.Exception.Type.Name);
                }
 
+#if NET_4_5
+               // out argument
+               m = t.GetMethod ("invoke_out");
+               var out_task = this_obj.InvokeMethodAsyncWithResult (e.Thread, m, new Value [] { vm.CreateValue (1) }, InvokeOptions.ReturnOutArgs);
+               var out_args = out_task.Result.OutArgs;
+               AssertValue (5, out_args [0]);
+
+               // without ReturnOutArgs flag
+               out_task = this_obj.InvokeMethodAsyncWithResult (e.Thread, m, new Value [] { vm.CreateValue (1) });
+               out_args = out_task.Result.OutArgs;
+               Assert.IsNull (out_args);
+#endif
+
                // newobj
                m = t.GetMethod (".ctor");
                v = t.InvokeMethod (e.Thread, m, null);
index 8a69fb0e1b2bf8dd43f58e6a44b7bfd59ad1684e..a7fa9b62af7d6a29774c2a60291368fd3830d011 100644 (file)
@@ -289,7 +289,7 @@ typedef struct {
 #define HEADER_LENGTH 11
 
 #define MAJOR_VERSION 2
-#define MINOR_VERSION 35
+#define MINOR_VERSION 36
 
 typedef enum {
        CMD_SET_VM = 1,
@@ -402,7 +402,8 @@ typedef enum {
 typedef enum {
        INVOKE_FLAG_DISABLE_BREAKPOINTS = 1,
        INVOKE_FLAG_SINGLE_THREADED = 2,
-       INVOKE_FLAG_RETURN_OUT_THIS = 4
+       INVOKE_FLAG_RETURN_OUT_THIS = 4,
+       INVOKE_FLAG_RETURN_OUT_ARGS = 8
 } InvokeFlags;
 
 typedef enum {
@@ -6611,10 +6612,14 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8
                buffer_add_byte (buf, 0);
                buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &exc, domain);
        } else {
+               gboolean out_this = FALSE;
+               gboolean out_args = FALSE;
+
                if ((invoke->flags & INVOKE_FLAG_RETURN_OUT_THIS) && CHECK_PROTOCOL_VERSION (2, 35))
-                       buffer_add_byte (buf, 2);
-               else
-                       buffer_add_byte (buf, 1);
+                       out_this = TRUE;
+               if ((invoke->flags & INVOKE_FLAG_RETURN_OUT_ARGS) && CHECK_PROTOCOL_VERSION (2, 35))
+                       out_args = TRUE;
+               buffer_add_byte (buf, 1 + (out_this ? 2 : 0) + (out_args ? 4 : 0));
                if (sig->ret->type == MONO_TYPE_VOID) {
                        if (!strcmp (m->name, ".ctor") && !m->klass->valuetype) {
                                buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &this, domain);
@@ -6638,9 +6643,21 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8
                } else {
                        NOT_IMPLEMENTED;
                }
-               if ((invoke->flags & INVOKE_FLAG_RETURN_OUT_THIS) && CHECK_PROTOCOL_VERSION (2, 35))
+               if (out_this)
                        /* Return the new value of the receiver after the call */
                        buffer_add_value (buf, &m->klass->byval_arg, this_buf, domain);
+               if (out_args) {
+                       buffer_add_int (buf, nargs);
+                       for (i = 0; i < nargs; ++i) {
+                               if (MONO_TYPE_IS_REFERENCE (sig->params [i]))
+                                       buffer_add_value (buf, sig->params [i], &args [i], domain);
+                               else if (sig->params [i]->byref)
+                                       /* add_value () does an indirection */
+                                       buffer_add_value (buf, sig->params [i], &arg_buf [i], domain);
+                               else
+                                       buffer_add_value (buf, sig->params [i], arg_buf [i], domain);
+                       }
+               }
        }
 
        tls->disable_breakpoints = FALSE;