[mono][wasm][debugger] Implement conditional breakpoint (#48785)
authorThays Grazia <thaystg@gmail.com>
Wed, 10 Mar 2021 23:02:23 +0000 (20:02 -0300)
committerGitHub <noreply@github.com>
Wed, 10 Mar 2021 23:02:23 +0000 (17:02 -0600)
* Implementing conditional breakpoint

* Adding tests

* Changing what @radical suggested

* Changing what @radical suggested.

* Changed what @radical suggested.

* Implement support to conditional breakpoints returning string.
Adding more tests.

* Changing tests to use TheoryData as suggested by @radical.

* Apply suggestions from code review

Co-authored-by: Larry Ewing <lewing@microsoft.com>
Co-authored-by: Larry Ewing <lewing@microsoft.com>
src/mono/wasm/debugger/BrowserDebugProxy/DebugStore.cs
src/mono/wasm/debugger/BrowserDebugProxy/DevToolsHelper.cs
src/mono/wasm/debugger/BrowserDebugProxy/MemberReferenceResolver.cs
src/mono/wasm/debugger/BrowserDebugProxy/MonoProxy.cs
src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs [new file with mode: 0644]
src/mono/wasm/debugger/DebuggerTestSuite/DebuggerTestBase.cs
src/mono/wasm/debugger/DebuggerTestSuite/Tests.cs
src/mono/wasm/debugger/tests/debugger-test/debugger-driver.html
src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs

index 685046a..5953d2a 100644 (file)
@@ -27,6 +27,7 @@ namespace Microsoft.WebAssembly.Diagnostics
         public string File { get; private set; }
         public int Line { get; private set; }
         public int Column { get; private set; }
+        public string Condition { get; private set; }
         public MethodInfo Method { get; private set; }
 
         private JObject request;
@@ -51,6 +52,7 @@ namespace Microsoft.WebAssembly.Diagnostics
         {
             Id = id;
             this.request = request;
+            Condition = request?["condition"]?.Value<string>();
         }
 
         public static BreakpointRequest Parse(string id, JObject args)
@@ -98,6 +100,7 @@ namespace Microsoft.WebAssembly.Diagnostics
 
             return store.AllSources().FirstOrDefault(source => TryResolve(source)) != null;
         }
+
     }
 
     internal class VarInfo
index 1ba773e..2f9a9cf 100644 (file)
@@ -244,7 +244,8 @@ namespace Microsoft.WebAssembly.Diagnostics
         public int RemoteId { get; set; }
         public BreakpointState State { get; set; }
         public string StackId { get; private set; }
-
+        public string Condition { get; private set; }
+        public bool ConditionAlreadyEvaluatedWithError { get; set; }
         public static bool TryParseId(string stackId, out int id)
         {
             id = -1;
@@ -254,11 +255,13 @@ namespace Microsoft.WebAssembly.Diagnostics
             return int.TryParse(stackId.Substring("dotnet:".Length), out id);
         }
 
-        public Breakpoint(string stackId, SourceLocation loc, BreakpointState state)
+        public Breakpoint(string stackId, SourceLocation loc, string condition, BreakpointState state)
         {
             this.StackId = stackId;
             this.Location = loc;
             this.State = state;
+            this.Condition = condition;
+            this.ConditionAlreadyEvaluatedWithError = false;
         }
     }
 
index bde6974..21f8dab 100644 (file)
@@ -13,7 +13,7 @@ namespace Microsoft.WebAssembly.Diagnostics
 {
     internal class MemberReferenceResolver
     {
-        private MessageId messageId;
+        private SessionId sessionId;
         private int scopeId;
         private MonoProxy proxy;
         private ExecutionContext ctx;
@@ -22,9 +22,9 @@ namespace Microsoft.WebAssembly.Diagnostics
         private ILogger logger;
         private bool locals_fetched;
 
-        public MemberReferenceResolver(MonoProxy proxy, ExecutionContext ctx, MessageId msg_id, int scope_id, ILogger logger)
+        public MemberReferenceResolver(MonoProxy proxy, ExecutionContext ctx, SessionId session_id, int scope_id, ILogger logger)
         {
-            messageId = msg_id;
+            sessionId = session_id;
             scopeId = scope_id;
             this.proxy = proxy;
             this.ctx = ctx;
@@ -37,7 +37,7 @@ namespace Microsoft.WebAssembly.Diagnostics
         {
             if (scopeCache.Locals.Count == 0 && !locals_fetched)
             {
-                Result scope_res = await proxy.GetScopeProperties(messageId, scopeId, token);
+                Result scope_res = await proxy.GetScopeProperties(sessionId, scopeId, token);
                 if (scope_res.IsErr)
                     throw new Exception($"BUG: Unable to get properties for scope: {scopeId}. {scope_res}");
                 locals_fetched = true;
@@ -57,7 +57,7 @@ namespace Microsoft.WebAssembly.Diagnostics
                 varIds = scope.Method.GetLiveVarsAt(scope.Location.CliLocation.Offset);
             }
 
-            Result res = await proxy.SendMonoCommand(messageId, MonoCommands.EvaluateMemberAccess(scopeId, var_name, varIds), token);
+            Result res = await proxy.SendMonoCommand(sessionId, MonoCommands.EvaluateMemberAccess(scopeId, var_name, varIds), token);
             if (res.IsOk)
             {
                 ret = res.Value?["result"]?["value"]?["value"]?.Value<JObject>();
index 180fb8c..672fa66 100644 (file)
@@ -508,6 +508,39 @@ namespace Microsoft.WebAssembly.Diagnostics
             return res;
         }
 
+        private async Task<bool> EvaluateCondition(SessionId sessionId, ExecutionContext context, JObject mono_frame, Breakpoint bp, CancellationToken token)
+        {
+            if (string.IsNullOrEmpty(bp?.Condition) || mono_frame == null)
+                return true;
+
+            string condition = bp.Condition;
+
+            if (bp.ConditionAlreadyEvaluatedWithError)
+                return false;
+            try {
+                var resolver = new MemberReferenceResolver(this, context, sessionId, mono_frame["frame_id"].Value<int>(), logger);
+
+                JObject retValue = await resolver.Resolve(condition, token);
+                if (retValue == null)
+                    retValue = await EvaluateExpression.CompileAndRunTheExpression(condition, resolver, token);
+                if (retValue?["value"]?.Type == JTokenType.Boolean ||
+                    retValue?["value"]?.Type == JTokenType.Integer ||
+                    retValue?["value"]?.Type == JTokenType.Float) {
+                    if (retValue?["value"]?.Value<bool>() == true)
+                        return true;
+                }
+                else if (retValue?["value"]?.Type != JTokenType.Null)
+                    return true;
+            }
+            catch (Exception e)
+            {
+                Log("info", $"Unable evaluate conditional breakpoint: {e} condition:{condition}");
+                bp.ConditionAlreadyEvaluatedWithError = true;
+                return false;
+            }
+            return false;
+        }
+
         private async Task<bool> OnPause(SessionId sessionId, JObject args, CancellationToken token)
         {
             //FIXME we should send release objects every now and then? Or intercept those we inject and deal in the runtime
@@ -656,6 +689,11 @@ namespace Microsoft.WebAssembly.Diagnostics
                         context.CallStack = frames;
 
                     }
+                    if (!await EvaluateCondition(sessionId, context, the_mono_frames?.First(), bp, token))
+                    {
+                        await SendCommand(sessionId, "Debugger.resume", new JObject(), token);
+                        return true;
+                    }
                 }
                 else if (!(function_name.StartsWith("wasm-function", StringComparison.Ordinal) ||
                         url.StartsWith("wasm://wasm/", StringComparison.Ordinal)))
@@ -921,7 +959,7 @@ namespace Microsoft.WebAssembly.Diagnostics
             return true;
         }
 
-        internal async Task<Result> GetScopeProperties(MessageId msg_id, int scope_id, CancellationToken token)
+        internal async Task<Result> GetScopeProperties(SessionId msg_id, int scope_id, CancellationToken token)
         {
             try
             {
@@ -957,9 +995,9 @@ namespace Microsoft.WebAssembly.Diagnostics
             }
         }
 
-        private async Task<Breakpoint> SetMonoBreakpoint(SessionId sessionId, string reqId, SourceLocation location, CancellationToken token)
+        private async Task<Breakpoint> SetMonoBreakpoint(SessionId sessionId, string reqId, SourceLocation location, string condition, CancellationToken token)
         {
-            var bp = new Breakpoint(reqId, location, BreakpointState.Pending);
+            var bp = new Breakpoint(reqId, location, condition, BreakpointState.Pending);
             string asm_name = bp.Location.CliLocation.Method.Assembly.Name;
             uint method_token = bp.Location.CliLocation.Method.Token;
             int il_offset = bp.Location.CliLocation.Offset;
@@ -1092,7 +1130,7 @@ namespace Microsoft.WebAssembly.Diagnostics
             foreach (IGrouping<SourceId, SourceLocation> sourceId in locations)
             {
                 SourceLocation loc = sourceId.First();
-                Breakpoint bp = await SetMonoBreakpoint(sessionId, req.Id, loc, token);
+                Breakpoint bp = await SetMonoBreakpoint(sessionId, req.Id, loc, req.Condition, token);
 
                 // If we didn't successfully enable the breakpoint
                 // don't add it to the list of locations for this id
diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs
new file mode 100644 (file)
index 0000000..e7d710d
--- /dev/null
@@ -0,0 +1,257 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.WebAssembly.Diagnostics;
+using Newtonsoft.Json.Linq;
+using Xunit;
+
+namespace DebuggerTests
+{
+
+    public class BreakpointTests : DebuggerTestBase
+    {
+        [Fact]
+        public async Task CreateGoodBreakpoint()
+        {
+            var bp1_res = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8);
+
+            Assert.EndsWith("debugger-test.cs", bp1_res.Value["breakpointId"].ToString());
+            Assert.Equal(1, bp1_res.Value["locations"]?.Value<JArray>()?.Count);
+
+            var loc = bp1_res.Value["locations"]?.Value<JArray>()[0];
+
+            Assert.NotNull(loc["scriptId"]);
+            Assert.Equal("dotnet://debugger-test.dll/debugger-test.cs", scripts[loc["scriptId"]?.Value<string>()]);
+            Assert.Equal(10, loc["lineNumber"]);
+            Assert.Equal(8, loc["columnNumber"]);
+        }
+
+        [Fact]
+        public async Task CreateJSBreakpoint()
+        {
+            // Test that js breakpoints get set correctly
+            // 13 24
+            // 13 31
+            var bp1_res = await SetBreakpoint("/debugger-driver.html", 13, 24);
+
+            Assert.EndsWith("debugger-driver.html", bp1_res.Value["breakpointId"].ToString());
+            Assert.Equal(1, bp1_res.Value["locations"]?.Value<JArray>()?.Count);
+
+            var loc = bp1_res.Value["locations"]?.Value<JArray>()[0];
+
+            Assert.NotNull(loc["scriptId"]);
+            Assert.Equal(13, loc["lineNumber"]);
+            Assert.Equal(24, loc["columnNumber"]);
+
+            var bp2_res = await SetBreakpoint("/debugger-driver.html", 13, 31);
+
+            Assert.EndsWith("debugger-driver.html", bp2_res.Value["breakpointId"].ToString());
+            Assert.Equal(1, bp2_res.Value["locations"]?.Value<JArray>()?.Count);
+
+            var loc2 = bp2_res.Value["locations"]?.Value<JArray>()[0];
+
+            Assert.NotNull(loc2["scriptId"]);
+            Assert.Equal(13, loc2["lineNumber"]);
+            Assert.Equal(31, loc2["columnNumber"]);
+        }
+
+        [Fact]
+        public async Task CreateJS0Breakpoint()
+        {
+            // 13 24
+            // 13 31
+            var bp1_res = await SetBreakpoint("/debugger-driver.html", 13, 0);
+
+            Assert.EndsWith("debugger-driver.html", bp1_res.Value["breakpointId"].ToString());
+            Assert.Equal(1, bp1_res.Value["locations"]?.Value<JArray>()?.Count);
+
+            var loc = bp1_res.Value["locations"]?.Value<JArray>()[0];
+
+            Assert.NotNull(loc["scriptId"]);
+            Assert.Equal(13, loc["lineNumber"]);
+            Assert.Equal(24, loc["columnNumber"]);
+
+            var bp2_res = await SetBreakpoint("/debugger-driver.html", 13, 31);
+
+            Assert.EndsWith("debugger-driver.html", bp2_res.Value["breakpointId"].ToString());
+            Assert.Equal(1, bp2_res.Value["locations"]?.Value<JArray>()?.Count);
+
+            var loc2 = bp2_res.Value["locations"]?.Value<JArray>()[0];
+
+            Assert.NotNull(loc2["scriptId"]);
+            Assert.Equal(13, loc2["lineNumber"]);
+            Assert.Equal(31, loc2["columnNumber"]);
+        }
+
+        [Theory]
+        [InlineData(0)]
+        [InlineData(50)]
+        public async Task CheckMultipleBreakpointsOnSameLine(int col)
+        {
+            var bp1_res = await SetBreakpoint("dotnet://debugger-test.dll/debugger-array-test.cs", 219, col);
+            Assert.EndsWith("debugger-array-test.cs", bp1_res.Value["breakpointId"].ToString());
+            Assert.Equal(1, bp1_res.Value["locations"]?.Value<JArray>()?.Count);
+
+            var loc = bp1_res.Value["locations"]?.Value<JArray>()[0];
+
+            CheckLocation("dotnet://debugger-test.dll/debugger-array-test.cs", 219, 50, scripts, loc);
+
+            var bp2_res = await SetBreakpoint("dotnet://debugger-test.dll/debugger-array-test.cs", 219, 55);
+            Assert.EndsWith("debugger-array-test.cs", bp2_res.Value["breakpointId"].ToString());
+            Assert.Equal(1, bp2_res.Value["locations"]?.Value<JArray>()?.Count);
+
+            var loc2 = bp2_res.Value["locations"]?.Value<JArray>()[0];
+
+            CheckLocation("dotnet://debugger-test.dll/debugger-array-test.cs", 219, 55, scripts, loc2);
+        }
+
+        [Fact]
+        public async Task CreateBadBreakpoint()
+        {
+            var bp1_req = JObject.FromObject(new
+            {
+                lineNumber = 8,
+                columnNumber = 2,
+                url = "dotnet://debugger-test.dll/this-file-doesnt-exist.cs",
+            });
+
+            var bp1_res = await cli.SendCommand("Debugger.setBreakpointByUrl", bp1_req, token);
+
+            Assert.True(bp1_res.IsOk);
+            Assert.Empty(bp1_res.Value["locations"].Values<object>());
+            //Assert.Equal ((int)MonoErrorCodes.BpNotFound, bp1_res.Error ["code"]?.Value<int> ());
+        }
+
+        [Fact]
+        public async Task CreateGoodBreakpointAndHit()
+        {
+            var bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8);
+
+            var eval_req = JObject.FromObject(new
+            {
+                expression = "window.setTimeout(function() { invoke_add(); }, 1);",
+            });
+
+            await EvaluateAndCheck(
+                "window.setTimeout(function() { invoke_add(); }, 1);",
+                "dotnet://debugger-test.dll/debugger-test.cs", 10, 8,
+                "IntAdd",
+                wait_for_event_fn: (pause_location) =>
+                {
+                    Assert.Equal("other", pause_location["reason"]?.Value<string>());
+                    Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value<string>());
+
+                    var top_frame = pause_location["callFrames"][0];
+                    Assert.Equal("IntAdd", top_frame["functionName"].Value<string>());
+                    Assert.Contains("debugger-test.cs", top_frame["url"].Value<string>());
+
+                    CheckLocation("dotnet://debugger-test.dll/debugger-test.cs", 8, 4, scripts, top_frame["functionLocation"]);
+
+                    //now check the scope
+                    var scope = top_frame["scopeChain"][0];
+                    Assert.Equal("local", scope["type"]);
+                    Assert.Equal("IntAdd", scope["name"]);
+
+                    Assert.Equal("object", scope["object"]["type"]);
+                    CheckLocation("dotnet://debugger-test.dll/debugger-test.cs", 8, 4, scripts, scope["startLocation"]);
+                    CheckLocation("dotnet://debugger-test.dll/debugger-test.cs", 14, 4, scripts, scope["endLocation"]);
+                    return Task.CompletedTask;
+                }
+            );
+        }
+
+        public static TheoryData<string, string, string, bool> FalseConditions = new TheoryData<string, string, string, bool>
+        {
+            { "invoke_add()", "IntAdd", "0.0", false },
+            { "invoke_add()", "IntAdd", "c == 40", false },
+            { "invoke_add()", "IntAdd", "c < 0", false },
+        };
+
+        public static TheoryData<string, string, string, bool> TrueConditions = new TheoryData<string, string, string, bool>
+        {
+            { "invoke_add()", "IntAdd", "c == 30", true },
+            { "invoke_add()", "IntAdd", "true", true },
+            { "invoke_add()", "IntAdd", "5", true },
+            { "invoke_add()", "IntAdd", "c < 40", true },
+            { "invoke_use_complex()", "UseComplex", "complex.A == 10", true },
+            { "invoke_add()", "IntAdd", "1.0", true },
+            { "invoke_add()", "IntAdd", "\"foo\"", true },
+            { "invoke_add()", "IntAdd", "\"true\"", true },
+            { "invoke_add()", "IntAdd", "\"false\"", true },
+        };
+
+        public static TheoryData<string, string, string, bool> InvalidConditions = new TheoryData<string, string, string, bool>
+        {
+            { "invoke_add()", "IntAdd", "foo.bar", false },
+            { "invoke_add()", "IntAdd", "Math.IntAdd()", false },
+            { "invoke_add()", "IntAdd", "c == \"xyz\"", false },
+            { "invoke_add()", "IntAdd", "Math.NonExistantProperty", false },
+            { "invoke_add()", "IntAdd", "g == 40", false },
+            { "invoke_add()", "IntAdd", "null", false },
+        };
+
+        [Theory]
+        [MemberData(nameof(FalseConditions))]
+        [MemberData(nameof(TrueConditions))]
+        [MemberData(nameof(InvalidConditions))]
+        public async Task ConditionalBreakpoint(string function_to_call, string method_to_stop, string condition, bool bp_stop_expected)
+        {
+            Result [] bps = new Result[2];
+            bps[0] = await SetBreakpointInMethod("debugger-test.dll", "Math", method_to_stop, 3, condition:condition);
+            bps[1] = await SetBreakpointInMethod("debugger-test.dll", "Math", method_to_stop, 4);
+            await EvaluateAndCheck(
+                "window.setTimeout(function() { " + function_to_call + "; }, 1);",
+                "dotnet://debugger-test.dll/debugger-test.cs",
+                bps[bp_stop_expected ? 0 : 1].Value["locations"][0]["lineNumber"].Value<int>(),
+                bps[bp_stop_expected ? 0 : 1].Value["locations"][0]["columnNumber"].Value<int>(),
+                method_to_stop);
+        }
+
+        [Theory]
+        [InlineData("c == 15", 78, 3, 78, 11)]
+        [InlineData("c == 17", 78, 3, 79, 3)]
+        [InlineData("g == 17", 78, 3, 79, 3)]
+        [InlineData("true", 78, 3, 78, 11)]
+        [InlineData("\"false\"", 78, 3, 78, 11)]
+        [InlineData("\"true\"", 78, 3, 78, 11)]
+        [InlineData("5", 78, 3, 78, 11)]
+        [InlineData("p", 78, 3, 79, 3)]
+        [InlineData("0.0", 78, 3, 79, 3)]
+        public async Task JSConditionalBreakpoint(string condition, int line_bp, int column_bp, int line_expected, int column_expected)
+        {
+            await SetBreakpoint("/debugger-driver.html", line_bp, column_bp, condition: condition);
+            await SetBreakpoint("/debugger-driver.html", 79, 3);
+
+            await EvaluateAndCheck(
+                "window.setTimeout(function() { conditional_breakpoint_test(5, 10, null); }, 1);",
+                "debugger-driver.html", line_expected, column_expected, "conditional_breakpoint_test");
+        }
+
+        [Theory]
+        [InlineData("invoke_add_with_parms(10, 20)", "invoke_add_with_parms(10, 20)",  "IntAdd", "c == 30", true, true)]
+        [InlineData("invoke_add_with_parms(5, 10)", "invoke_add_with_parms(10, 20)",  "IntAdd", "c == 30", false, true)]
+        [InlineData("invoke_add_with_parms(10, 20)", "invoke_add_with_parms(5, 10)",  "IntAdd", "c == 30", true, false)]
+        public async Task ConditionalBreakpointHitTwice(string function_to_call, string function_to_call2, string method_to_stop, string condition, bool bp_stop_expected, bool bp_stop_expected2)
+        {
+            Result [] bps = new Result[2];
+            bps[0] = await SetBreakpointInMethod("debugger-test.dll", "Math", method_to_stop, 3, condition:condition);
+            bps[1] = await SetBreakpointInMethod("debugger-test.dll", "Math", method_to_stop, 4);
+            await EvaluateAndCheck(
+                "window.setTimeout(function() { " + function_to_call + "; " +  function_to_call2 + ";}, 1);",
+                "dotnet://debugger-test.dll/debugger-test.cs",
+                bps[bp_stop_expected ? 0 : 1].Value["locations"][0]["lineNumber"].Value<int>(),
+                bps[bp_stop_expected ? 0 : 1].Value["locations"][0]["columnNumber"].Value<int>(),
+                method_to_stop);
+
+            await SendCommandAndCheck(null, "Debugger.resume",
+                null,
+                bps[bp_stop_expected2 ? 0 : 1].Value["locations"][0]["lineNumber"].Value<int>(),
+                bps[bp_stop_expected2 ? 0 : 1].Value["locations"][0]["columnNumber"].Value<int>(),
+                method_to_stop);
+        }
+
+    }
+}
index fdd23b0..f4e538f 100644 (file)
@@ -127,6 +127,9 @@ namespace DebuggerTests
                 }
                 else if (!String.IsNullOrEmpty(url))
                 {
+                    var dbgUrl = args["url"]?.Value<string>();
+                    var arrStr = dbgUrl.Split("/");
+                    dicScriptsIdToUrl[script_id] = arrStr[arrStr.Length - 1];
                     dicFileToUrl[new Uri(url).AbsolutePath] = url;
                 }
                 await Task.FromResult(0);
@@ -799,11 +802,11 @@ namespace DebuggerTests
             return res;
         }
 
-        internal async Task<Result> SetBreakpoint(string url_key, int line, int column, bool expect_ok = true, bool use_regex = false)
+        internal async Task<Result> SetBreakpoint(string url_key, int line, int column, bool expect_ok = true, bool use_regex = false, string condition = "")
         {
             var bp1_req = !use_regex ?
-                JObject.FromObject(new { lineNumber = line, columnNumber = column, url = dicFileToUrl[url_key], }) :
-                JObject.FromObject(new { lineNumber = line, columnNumber = column, urlRegex = url_key, });
+                JObject.FromObject(new { lineNumber = line, columnNumber = column, url = dicFileToUrl[url_key], condition}) :
+                JObject.FromObject(new { lineNumber = line, columnNumber = column, urlRegex = url_key, condition});
 
             var bp1_res = await cli.SendCommand("Debugger.setBreakpointByUrl", bp1_req, token);
             Assert.True(expect_ok ? bp1_res.IsOk : bp1_res.IsErr);
@@ -817,7 +820,7 @@ namespace DebuggerTests
             return exc_res;
         }
 
-        internal async Task<Result> SetBreakpointInMethod(string assembly, string type, string method, int lineOffset = 0, int col = 0)
+        internal async Task<Result> SetBreakpointInMethod(string assembly, string type, string method, int lineOffset = 0, int col = 0, string condition = "")
         {
             var req = JObject.FromObject(new { assemblyName = assembly, typeName = type, methodName = method, lineOffset = lineOffset });
 
@@ -832,7 +835,8 @@ namespace DebuggerTests
             {
                 lineNumber = m_line + lineOffset,
                 columnNumber = col,
-                url = m_url
+                url = m_url,
+                condition
             });
 
             res = await cli.SendCommand("Debugger.setBreakpointByUrl", bp1_req, token);
index d168447..6109660 100644 (file)
@@ -26,156 +26,6 @@ namespace DebuggerTests
         }
 
         [Fact]
-        public async Task CreateGoodBreakpoint()
-        {
-            var bp1_res = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8);
-
-            Assert.EndsWith("debugger-test.cs", bp1_res.Value["breakpointId"].ToString());
-            Assert.Equal(1, bp1_res.Value["locations"]?.Value<JArray>()?.Count);
-
-            var loc = bp1_res.Value["locations"]?.Value<JArray>()[0];
-
-            Assert.NotNull(loc["scriptId"]);
-            Assert.Equal("dotnet://debugger-test.dll/debugger-test.cs", scripts[loc["scriptId"]?.Value<string>()]);
-            Assert.Equal(10, loc["lineNumber"]);
-            Assert.Equal(8, loc["columnNumber"]);
-        }
-
-        [Fact]
-        public async Task CreateJSBreakpoint()
-        {
-            // Test that js breakpoints get set correctly
-            // 13 24
-            // 13 31
-            var bp1_res = await SetBreakpoint("/debugger-driver.html", 13, 24);
-
-            Assert.EndsWith("debugger-driver.html", bp1_res.Value["breakpointId"].ToString());
-            Assert.Equal(1, bp1_res.Value["locations"]?.Value<JArray>()?.Count);
-
-            var loc = bp1_res.Value["locations"]?.Value<JArray>()[0];
-
-            Assert.NotNull(loc["scriptId"]);
-            Assert.Equal(13, loc["lineNumber"]);
-            Assert.Equal(24, loc["columnNumber"]);
-
-            var bp2_res = await SetBreakpoint("/debugger-driver.html", 13, 31);
-
-            Assert.EndsWith("debugger-driver.html", bp2_res.Value["breakpointId"].ToString());
-            Assert.Equal(1, bp2_res.Value["locations"]?.Value<JArray>()?.Count);
-
-            var loc2 = bp2_res.Value["locations"]?.Value<JArray>()[0];
-
-            Assert.NotNull(loc2["scriptId"]);
-            Assert.Equal(13, loc2["lineNumber"]);
-            Assert.Equal(31, loc2["columnNumber"]);
-        }
-
-        [Fact]
-        public async Task CreateJS0Breakpoint()
-        {
-            // 13 24
-            // 13 31
-            var bp1_res = await SetBreakpoint("/debugger-driver.html", 13, 0);
-
-            Assert.EndsWith("debugger-driver.html", bp1_res.Value["breakpointId"].ToString());
-            Assert.Equal(1, bp1_res.Value["locations"]?.Value<JArray>()?.Count);
-
-            var loc = bp1_res.Value["locations"]?.Value<JArray>()[0];
-
-            Assert.NotNull(loc["scriptId"]);
-            Assert.Equal(13, loc["lineNumber"]);
-            Assert.Equal(24, loc["columnNumber"]);
-
-            var bp2_res = await SetBreakpoint("/debugger-driver.html", 13, 31);
-
-            Assert.EndsWith("debugger-driver.html", bp2_res.Value["breakpointId"].ToString());
-            Assert.Equal(1, bp2_res.Value["locations"]?.Value<JArray>()?.Count);
-
-            var loc2 = bp2_res.Value["locations"]?.Value<JArray>()[0];
-
-            Assert.NotNull(loc2["scriptId"]);
-            Assert.Equal(13, loc2["lineNumber"]);
-            Assert.Equal(31, loc2["columnNumber"]);
-        }
-
-        [Theory]
-        [InlineData(0)]
-        [InlineData(50)]
-        public async Task CheckMultipleBreakpointsOnSameLine(int col)
-        {
-            var bp1_res = await SetBreakpoint("dotnet://debugger-test.dll/debugger-array-test.cs", 219, col);
-            Assert.EndsWith("debugger-array-test.cs", bp1_res.Value["breakpointId"].ToString());
-            Assert.Equal(1, bp1_res.Value["locations"]?.Value<JArray>()?.Count);
-
-            var loc = bp1_res.Value["locations"]?.Value<JArray>()[0];
-
-            CheckLocation("dotnet://debugger-test.dll/debugger-array-test.cs", 219, 50, scripts, loc);
-
-            var bp2_res = await SetBreakpoint("dotnet://debugger-test.dll/debugger-array-test.cs", 219, 55);
-            Assert.EndsWith("debugger-array-test.cs", bp2_res.Value["breakpointId"].ToString());
-            Assert.Equal(1, bp2_res.Value["locations"]?.Value<JArray>()?.Count);
-
-            var loc2 = bp2_res.Value["locations"]?.Value<JArray>()[0];
-
-            CheckLocation("dotnet://debugger-test.dll/debugger-array-test.cs", 219, 55, scripts, loc2);
-        }
-
-        [Fact]
-        public async Task CreateBadBreakpoint()
-        {
-            var bp1_req = JObject.FromObject(new
-            {
-                lineNumber = 8,
-                columnNumber = 2,
-                url = "dotnet://debugger-test.dll/this-file-doesnt-exist.cs",
-            });
-
-            var bp1_res = await cli.SendCommand("Debugger.setBreakpointByUrl", bp1_req, token);
-
-            Assert.True(bp1_res.IsOk);
-            Assert.Empty(bp1_res.Value["locations"].Values<object>());
-            //Assert.Equal ((int)MonoErrorCodes.BpNotFound, bp1_res.Error ["code"]?.Value<int> ());
-        }
-
-        [Fact]
-        public async Task CreateGoodBreakpointAndHit()
-        {
-            var bp = await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 10, 8);
-
-            var eval_req = JObject.FromObject(new
-            {
-                expression = "window.setTimeout(function() { invoke_add(); }, 1);",
-            });
-
-            await EvaluateAndCheck(
-                "window.setTimeout(function() { invoke_add(); }, 1);",
-                "dotnet://debugger-test.dll/debugger-test.cs", 10, 8,
-                "IntAdd",
-                wait_for_event_fn: (pause_location) =>
-                {
-                    Assert.Equal("other", pause_location["reason"]?.Value<string>());
-                    Assert.Equal(bp.Value["breakpointId"]?.ToString(), pause_location["hitBreakpoints"]?[0]?.Value<string>());
-
-                    var top_frame = pause_location["callFrames"][0];
-                    Assert.Equal("IntAdd", top_frame["functionName"].Value<string>());
-                    Assert.Contains("debugger-test.cs", top_frame["url"].Value<string>());
-
-                    CheckLocation("dotnet://debugger-test.dll/debugger-test.cs", 8, 4, scripts, top_frame["functionLocation"]);
-
-                    //now check the scope
-                    var scope = top_frame["scopeChain"][0];
-                    Assert.Equal("local", scope["type"]);
-                    Assert.Equal("IntAdd", scope["name"]);
-
-                    Assert.Equal("object", scope["object"]["type"]);
-                    CheckLocation("dotnet://debugger-test.dll/debugger-test.cs", 8, 4, scripts, scope["startLocation"]);
-                    CheckLocation("dotnet://debugger-test.dll/debugger-test.cs", 14, 4, scripts, scope["endLocation"]);
-                    return Task.CompletedTask;
-                }
-            );
-        }
-
-        [Fact]
         public async Task ExceptionThrownInJS()
         {
             var eval_req = JObject.FromObject(new
index 0e41730..87ba804 100644 (file)
                function invoke_run_all () {
                        return App.run_all ();
                }
+               function conditional_breakpoint_test (a, b, p) {
+                       var c = a + b;
+                       var d = a + c;
+                       var e = c + d;
+                       console.log(c + d + e);
+               }
+               function invoke_add_with_parms (a, b) {
+                       return App.int_add (a, b);
+               }
       </script>
       <script type="text/javascript" src="mono-config.js"></script>
       <script type="text/javascript" src="runtime-debugger.js"></script>
index 498c028..443bf45 100644 (file)
@@ -34,7 +34,6 @@ namespace DebuggerTests
         {
             TestEvaluate f = new TestEvaluate();
             f.run(100, 200, "9000", "test", 45);
-
             var f_s = new EvaluateTestsStructWithProperties();
             f_s.InstanceMethod(100, 200, "test", f_s);
             f_s.GenericInstanceMethod<int>(100, 200, "test", f_s);