Esempio n. 1
0
        /// <summary>
        /// A function to handle breakpoints in asynchronous methods. Pass this as an argument to <see cref="Compile(Func{BreakpointInfo, bool}, Func{BreakpointInfo, Task{bool}})"/>.
        /// </summary>
        /// <param name="info">A <see cref="BreakpointInfo"/> object containing information about the location of the breakpoint and the current value of local variables.</param>
        /// <returns>A <see cref="Task"/> that completes when code execution resumes after the breakpoint.</returns>
        public async Task <bool> AsynchronousBreak(BreakpointInfo info)
        {
            using SemaphoreSlim semaphore = new SemaphoreSlim(0, 1);
            void resumeHandler(object sender, EventArgs e)
            {
                semaphore.Release();
            }

            await Dispatcher.UIThread.InvokeAsync(() =>
            {
                EditorControl.ActiveBreakpoint = info.BreakpointSpan.Start - PreSource.Length - 1;
                EditorControl.SetSelection(info.BreakpointSpan.End - PreSource.Length - 1, 0);
                BreakpointPanel.SetContent(info);
                BreakpointPanel.ResumeClicked += resumeHandler;
                this.FindAncestorOfType <Window>().Closing += resumeHandler;
                OpenSidePanel();
            });

            await semaphore.WaitAsync();

            bool tbr = false;

            await Dispatcher.UIThread.InvokeAsync(() =>
            {
                tbr = BreakpointPanel.IgnoreFurtherOccurrences;
                CloseSidePanel();
                BreakpointPanel.ResumeClicked -= resumeHandler;
                this.FindAncestorOfType <Window>().Closing -= resumeHandler;
                EditorControl.ActiveBreakpoint              = -1;
            });

            semaphore.Dispose();

            return(tbr);
        }
        public void SetContent(BreakpointInfo breakpointInfo)
        {
            this.FindControl <StackPanel>("LocalVariablesContainer").Children.Clear();
            this.FindControl <ToggleButton>("IgnoreFurtherOccurrences").IsChecked = false;

            foreach (KeyValuePair <string, object> kvp in (from el in breakpointInfo.LocalVariables orderby el.Key ascending select el))
            {
                this.FindControl <StackPanel>("LocalVariablesContainer").Children.Add(new VariableExpander(kvp.Key, kvp.Value, breakpointInfo.LocalVariableDisplayParts[kvp.Key], this.FindControl <ToggleButton>("ToggleNonPublicVisibility"))
                {
                    Margin = new Thickness(0, 0, 5, 0)
                });
            }
        }
        internal static Func <int, string[], string[], object[], Task> GetBreakpointAsyncFunction(Func <BreakpointInfo, Task <bool> > callback)
        {
            Dictionary <int, bool> suppressedBreakPoints = new Dictionary <int, bool>();

            return(async(spanStart, variableNames, variableDisplay, variableValues) =>
            {
                bool suppressed = suppressedBreakPoints.TryGetValue(spanStart, out bool suppressedInDictionary) && suppressedInDictionary;

                if (!suppressed)
                {
                    BreakpointInfo info = new BreakpointInfo(spanStart, variableNames, variableDisplay, variableValues);
                    suppressedBreakPoints[spanStart] = (await callback?.Invoke(info)) == true;
                }
            });
        }
Esempio n. 4
0
        /// <summary>
        /// A function to handle breakpoints in synchronous methods. Pass this as an argument to <see cref="Compile(Func{BreakpointInfo, bool}, Func{BreakpointInfo, Task{bool}})"/>. To prevent deadlocks, this function will have no effect if called from the UI thread.
        /// </summary>
        /// <param name="info">A <see cref="BreakpointInfo"/> object containing information about the location of the breakpoint and the current value of local variables.</param>
        /// <returns><see langword="true" /> if further occurrences of the same breakpoint should be ignored; <see langword="false"/> otherwise.</returns>
        public bool SynchronousBreak(BreakpointInfo info)
        {
            if (!CheckAccess())
            {
                EventWaitHandle waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset);

                bool tbr = false;

                async void resumeHandler(object sender, EventArgs e)
                {
                    await Dispatcher.UIThread.InvokeAsync(() => tbr = BreakpointPanel.IgnoreFurtherOccurrences);

                    waitHandle.Set();
                }

                Dispatcher.UIThread.InvokeAsync(() =>
                {
                    EditorControl.ActiveBreakpoint = info.BreakpointSpan.Start - PreSource.Length - 1;
                    EditorControl.SetSelection(info.BreakpointSpan.End - PreSource.Length - 1, 0);
                    BreakpointPanel.SetContent(info);
                    BreakpointPanel.ResumeClicked += resumeHandler;
                    this.FindAncestorOfType <Window>().Closing += resumeHandler;
                    OpenSidePanel();
                });

                waitHandle.WaitOne();

                Dispatcher.UIThread.InvokeAsync(() =>
                {
                    CloseSidePanel();
                    BreakpointPanel.ResumeClicked -= resumeHandler;
                    this.FindAncestorOfType <Window>().Closing -= resumeHandler;
                    EditorControl.ActiveBreakpoint              = -1;
                });

                return(tbr);
            }
            else
            {
                return(false);
            }
        }
Esempio n. 5
0
        /* public static Action<BreakpointInfo> SynchronousBreakRemote(BreakpointInfo info)
         * {
         *   return (info) =>
         *   {
         *       EventWaitHandle waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
         *       void resumeHandler(object sender, EventArgs e)
         *       {
         *           waitHandle.Set();
         *       }
         *
         *       Dispatcher.UIThread.InvokeAsync(() =>
         *       {
         *           EditorControl.ActiveBreakpoint = info.BreakpointSpan.Start - PreSource.Length - 1;
         *           EditorControl.SetSelection(info.BreakpointSpan.End - PreSource.Length - 1, 0);
         *           BreakpointPanel.SetContent(info);
         *           BreakpointPanel.ResumeClicked += resumeHandler;
         *           this.FindAncestorOfType<Window>().Closing += resumeHandler;
         *           OpenSidePanel();
         *       });
         *
         *       waitHandle.WaitOne();
         *
         *       Dispatcher.UIThread.InvokeAsync(() =>
         *       {
         *           CloseSidePanel();
         *           BreakpointPanel.ResumeClicked -= resumeHandler;
         *           this.FindAncestorOfType<Window>().Closing -= resumeHandler;
         *           EditorControl.ActiveBreakpoint = -1;
         *       });
         *   }
         * }*/

        /// <summary>
        /// Compile the source code to an <see cref="Assembly"/>.
        /// </summary>
        /// <param name="synchronousBreak">The function to handle synchronous breakpoints. If this is <see langword="null" />, these breakpoints will be skipped. If you want to enable the default UI for breakpoints, use <see cref="SynchronousBreak(BreakpointInfo)"/> (or a function that calls it after performing additional operations).</param>
        /// <param name="asynchronousBreak">The function to handle asynchronous breakpoints. If this is <see langword="null" />, these breakpoints will be skipped. If you want to enable the default UI for breakpoints, use <see cref="AsynchronousBreak(BreakpointInfo)"/> (or a function that calls it after performing additional operations).</param>
        /// <returns>An <see cref="Assembly"/> containing the compiled code, or <see langword="null"/> if the compilation fails, as well as a <see cref="CSharpCompilation"/> that also contains information about any compilation errors.</returns>
        public async Task <(Assembly Assembly, CSharpCompilation Compilation)> Compile(Func <BreakpointInfo, bool> synchronousBreak = null, Func <BreakpointInfo, Task <bool> > asynchronousBreak = null)
        {
            string source = null;

            await Dispatcher.UIThread.InvokeAsync(() =>
            {
                source = this.FullSource;
            });

            SourceText sourceText = SourceText.From(source);

            ImmutableList <MetadataReference> references;

            lock (ReferencesLock)
            {
                references = References;
            }

            SyntaxTree tree = await this.OriginalDocument.WithText(sourceText).GetSyntaxTreeAsync();

            CSharpCompilation comp = CSharpCompilation.Create("compilation", new[] { tree }, references, this.CompilationOptions);

            string debuggerGuid = "_" + System.Guid.NewGuid().ToString("N");

            if (synchronousBreak != null || asynchronousBreak != null)
            {
                List <(TextSpan, bool)> validBreakpoints = new List <(TextSpan, bool)>();

                foreach (int i in source.AllIndicesOf(Utils.BreakpointMarker))
                {
                    SyntaxNode node = tree.GetRoot().FindNode(new TextSpan(i, 1));

                    SyntaxNode fullNode = node;

                    while (fullNode.Parent != null && !fullNode.Kind().IsStatement())
                    {
                        fullNode = fullNode.Parent;
                    }

                    if (fullNode.Kind().IsStatement())
                    {
                        SyntaxNode methodNode = fullNode;

                        while (methodNode.Parent != null && methodNode.Kind() != SyntaxKind.MethodDeclaration && methodNode.Kind() != SyntaxKind.ParenthesizedLambdaExpression && methodNode.Kind() != SyntaxKind.AnonymousMethodExpression)
                        {
                            methodNode = methodNode.Parent;
                        }

                        if (methodNode.Kind() == SyntaxKind.MethodDeclaration || methodNode.Kind() == SyntaxKind.ParenthesizedLambdaExpression || methodNode.Kind() == SyntaxKind.AnonymousMethodExpression)
                        {
                            bool isAsync = false;

                            if (methodNode.Kind() == SyntaxKind.MethodDeclaration)
                            {
                                MethodDeclarationSyntax method = (MethodDeclarationSyntax)methodNode;

                                foreach (SyntaxToken token in method.Modifiers)
                                {
                                    if (token.Text == "async")
                                    {
                                        isAsync = true;
                                    }
                                }
                            }
                            else if (methodNode.Kind() == SyntaxKind.ParenthesizedLambdaExpression)
                            {
                                ParenthesizedLambdaExpressionSyntax method = (ParenthesizedLambdaExpressionSyntax)methodNode;

                                if (method.AsyncKeyword.Text == "async")
                                {
                                    isAsync = true;
                                }
                            }
                            else if (methodNode.Kind() == SyntaxKind.AnonymousMethodExpression)
                            {
                                AnonymousMethodExpressionSyntax method = (AnonymousMethodExpressionSyntax)methodNode;

                                if (method.AsyncKeyword.Text == "async")
                                {
                                    isAsync = true;
                                }
                            }

                            if ((!isAsync && synchronousBreak != null) || (isAsync && asynchronousBreak != null))
                            {
                                validBreakpoints.Add((new TextSpan(i, Utils.BreakpointMarker.Length), isAsync));
                            }
                        }
                    }
                }

                SyntaxTree debuggerTree = BreakpointInfo.GetDebuggerSyntaxTree(debuggerGuid);

                SemanticModel model = comp.GetSemanticModel(tree, false);

                for (int i = validBreakpoints.Count - 1; i >= 0; i--)
                {
                    ILocalSymbol[] locals = (from el in model.LookupSymbols(validBreakpoints[i].Item1.Start) where el.Kind == SymbolKind.Local && !el.IsInaccessibleLocal(model, validBreakpoints[i].Item1.Start, tree.GetRoot().FindNode(validBreakpoints[i].Item1)) && !string.IsNullOrEmpty(el.Name) select(ILocalSymbol) el).ToArray();

                    string breakpointSource = BreakpointInfo.GetBreakpointSource(validBreakpoints[i].Item1, locals, model, debuggerGuid, validBreakpoints[i].Item2);

                    sourceText = sourceText.Replace(validBreakpoints[i].Item1, breakpointSource);
                }

                string text = sourceText.ToString();

                tree = await this.OriginalDocument.WithText(sourceText).GetSyntaxTreeAsync();

                comp = CSharpCompilation.Create("compilation", new[] { debuggerTree, tree }, references, this.CompilationOptions);
            }

            using MemoryStream ms = new MemoryStream();

            EmitResult result = comp.Emit(ms);

            if (!result.Success)
            {
                return(null, comp);
            }
            else
            {
                ms.Seek(0, SeekOrigin.Begin);
                Assembly assembly = Assembly.Load(ms.ToArray());

                if (synchronousBreak != null || asynchronousBreak != null)
                {
                    assembly.GetType(debuggerGuid + ".Debugger").InvokeMember("Breakpoint", BindingFlags.Static | BindingFlags.Public | BindingFlags.SetField, null, null, new object[] { BreakpointInfo.GetBreakpointFunction(synchronousBreak) });
                    assembly.GetType(debuggerGuid + ".Debugger").InvokeMember("BreakpointAsync", BindingFlags.Static | BindingFlags.Public | BindingFlags.SetField, null, null, new object[] { BreakpointInfo.GetBreakpointAsyncFunction(asynchronousBreak) });
                }

                return(assembly, comp);
            }
        }