private Task SetBreakpointInDNA(PossibleBreakpointLocation breakpointLocation) { var jsExpression = $@"(function() {{ if (window._dnaRuntimeHasStarted) {{ setBreakpointInDna('{breakpointLocation.DnaMethodIdentifier}', {breakpointLocation.SequencePointIndex}); }} else {{ // Can't rely on any script files being loaded yet, so just write to global state window._pendingBreakpoints = window._pendingBreakpoints || []; window._pendingBreakpoints.push({{ dnaMethodId: '{breakpointLocation.DnaMethodIdentifier}', ilOffset: {breakpointLocation.SequencePointIndex} }}); }} }})()"; return(EvaluateJsInBrowser(jsExpression)); }
private async Task HandleMessageFromBrowser(MessageBase message) { switch (message.Method) { case "Debugger.paused": { var callFrames = message.Params.GetValue("callFrames").Value <JArray>(); var topCallFrame = callFrames.FirstOrDefault()?.Value <JObject>(); var functionName = topCallFrame?.GetValue("functionName").Value <string>(); if (functionName == "SendDebuggerMessage") { await NotifyIdeAboutDotnetBreakpointHit(message, topCallFrame); return; } break; } case "Debugger.resumed": { _currentlyPausedInDotNetBreakpoint = null; break; } case "Runtime.executionContextCreated": { var context = message.Params.GetValue("context").Value <JObject>(); var auxData = context.GetValue("auxData").Value <JObject>(); var isDefaultExecutionContext = auxData.GetValue("isDefault").Value <bool>(); if (isDefaultExecutionContext) { var contextId = context.GetValue("id").Value <int>(); await OnDefaultExecutionContextCreated(contextId, auxData); } break; } } // Temporarily, just proxy everything to the IDE await _ideConnection.SendMessageAsync(message); }
private async Task NotifyIdeAboutDotnetBreakpointHit(MessageBase message, JObject nativeTopCallFrame) { var nativeCallFrameId = nativeTopCallFrame.GetValue("callFrameId").Value <string>(); var debuggerMessage = await EvaluateJsInBrowser("message", nativeCallFrameId); var debuggerMessageJson = debuggerMessage.GetValue("value").Value <string>(); var debuggerMessageParsed = JsonConvert.DeserializeObject <JObject>(debuggerMessageJson); var dnaLocationId = debuggerMessageParsed.GetValue("ID").Value <string>(); var ilOffset = debuggerMessageParsed.GetValue("ilOffset").Value <int>(); var breakpointId = CreateBreakpointId(dnaLocationId, ilOffset); // Modify the message to indicate we hit the .NET breakpoint, then pass it through to // the IDE. TODO: Also fix up the call stack, etc. var breakpoint = _debugInfoStore.FindBreakpointUsingDnaData(dnaLocationId, ilOffset, out var sourceFile); if (breakpoint == null) { Console.WriteLine($"Could not find .NET breakpoint corresponding to IL offset {ilOffset} in ID {dnaLocationId}"); await _ideConnection.SendMessageAsync(message); return; } _currentlyPausedInDotNetBreakpoint = breakpoint; var lineNumber = breakpoint.SequencePointInfo.StartLine; var colNumber = breakpoint.SequencePointInfo.StartColumn; Console.WriteLine($"Hit .NET breakpoint at {sourceFile.FileName} line {lineNumber} col {colNumber}\n"); var nativeCallFrames = message.Params.GetValue("callFrames").Values <JObject>(); var dotNetCallFrames = new[] { // TODO: Include all .NET call frames, not just the top one DebuggerConnection.ToJObject(new { CallFrameId = "dotnetcallframe:0", FunctionName = breakpoint.MethodName, FunctionLocation = new { ScriptId = sourceFile.Id, LineNumber = lineNumber - 1, // TODO: Get actual line/col where the function starts, not the breakpoint hit ColumnNumber = colNumber - 1 }, Location = new { ScriptId = sourceFile.Id, LineNumber = lineNumber - 1, ColumnNumber = colNumber - 1 }, ScopeChain = EmptyJObject, // TODO: Populate, so it can show locals This = EmptyJObject }) }; await _ideConnection.SendMessageAsync(new MessageBase { Method = "Debugger.paused", Params = DebuggerConnection.ToJObject(new { CallFrames = dotNetCallFrames.Concat(nativeCallFrames), Reason = "other", HitBreakpoints = new[] { breakpointId } }) }); }