From 9642e79c6284ece9ad9f1bba2c88d13127b4d992 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Tue, 24 Sep 2019 15:54:17 -0400 Subject: [PATCH] Revamped expression resolver to have context This is needed to resolve ambiguous expressions such as those that could be a type -or- a variable name. Partial fix for https://devdiv.visualstudio.com/DevDiv/_workitems/edit/939728/ --- Mono.Debugging.Soft/SoftDebuggerBacktrace.cs | 2 +- Mono.Debugging.Soft/SoftDebuggerSession.cs | 2 +- Mono.Debugging.Win32/CorDebuggerBacktrace.cs | 2 +- .../Mono.Debugging.Backend/IBacktrace.cs | 2 ++ .../Mono.Debugging.Client/DebuggerSession.cs | 12 +++++----- .../Mono.Debugging.Client/StackFrame.cs | 23 +++++++++++++------ .../BaseBacktrace.cs | 2 +- .../ExpressionEvaluator.cs | 12 +++++++++- .../NRefactoryExpressionEvaluator.cs | 14 +++++------ .../NRefactoryExpressionResolverVisitor.cs | 18 +++++++++++++-- .../ObjectValueAdaptor.cs | 2 +- 11 files changed, 63 insertions(+), 28 deletions(-) diff --git a/Mono.Debugging.Soft/SoftDebuggerBacktrace.cs b/Mono.Debugging.Soft/SoftDebuggerBacktrace.cs index 4cdda111e..1f5932488 100644 --- a/Mono.Debugging.Soft/SoftDebuggerBacktrace.cs +++ b/Mono.Debugging.Soft/SoftDebuggerBacktrace.cs @@ -210,7 +210,7 @@ DC.StackFrame CreateStackFrame (MDB.StackFrame frame, int frameIndex) return new SoftDebuggerStackFrame (frame, addressSpace, location, language, external, hasDebugInfo, hidden, typeFQN, typeFullName); } - protected override EvaluationContext GetEvaluationContext (int frameIndex, EvaluationOptions options) + public override EvaluationContext GetEvaluationContext (int frameIndex, EvaluationOptions options) { ValidateStack (); diff --git a/Mono.Debugging.Soft/SoftDebuggerSession.cs b/Mono.Debugging.Soft/SoftDebuggerSession.cs index 46da83f52..97f028cb2 100644 --- a/Mono.Debugging.Soft/SoftDebuggerSession.cs +++ b/Mono.Debugging.Soft/SoftDebuggerSession.cs @@ -2372,7 +2372,7 @@ string EvaluateExpression (ThreadMirror thread, string expression, BreakEvent bp // resolve types... if (ctx.SourceCodeAvailable) - expression = ctx.Evaluator.Resolve (this, GetSourceLocation (frames[0]), expression); + expression = ctx.Evaluator.Resolve (this, ctx, GetSourceLocation (frames[0]), expression); } ValueReference val = ctx.Evaluator.Evaluate (ctx, expression); diff --git a/Mono.Debugging.Win32/CorDebuggerBacktrace.cs b/Mono.Debugging.Win32/CorDebuggerBacktrace.cs index 1dac76122..a53f5d16b 100644 --- a/Mono.Debugging.Win32/CorDebuggerBacktrace.cs +++ b/Mono.Debugging.Win32/CorDebuggerBacktrace.cs @@ -64,7 +64,7 @@ internal List FrameList { } } - protected override EvaluationContext GetEvaluationContext (int frameIndex, EvaluationOptions options) + public override EvaluationContext GetEvaluationContext (int frameIndex, EvaluationOptions options) { var ctx = new CorEvaluationContext (session, this, frameIndex, options); ctx.Thread = thread; diff --git a/Mono.Debugging/Mono.Debugging.Backend/IBacktrace.cs b/Mono.Debugging/Mono.Debugging.Backend/IBacktrace.cs index 7ace99947..3707f0be7 100644 --- a/Mono.Debugging/Mono.Debugging.Backend/IBacktrace.cs +++ b/Mono.Debugging/Mono.Debugging.Backend/IBacktrace.cs @@ -1,5 +1,6 @@ using Mono.Debugging.Client; +using Mono.Debugging.Evaluation; namespace Mono.Debugging.Backend { @@ -12,6 +13,7 @@ public interface IBacktrace: IDebuggerBackendObject ObjectValue GetThisReference (int frameIndex, EvaluationOptions options); ExceptionInfo GetException (int frameIndex, EvaluationOptions options); ObjectValue[] GetAllLocals (int frameIndex, EvaluationOptions options); + EvaluationContext GetEvaluationContext (int frameIndex, EvaluationOptions options); ObjectValue[] GetExpressionValues (int frameIndex, string[] expressions, EvaluationOptions options); CompletionData GetExpressionCompletionData (int frameIndex, string exp); AssemblyLine[] Disassemble (int frameIndex, int firstLine, int count); diff --git a/Mono.Debugging/Mono.Debugging.Client/DebuggerSession.cs b/Mono.Debugging/Mono.Debugging.Client/DebuggerSession.cs index 52e165fd4..9b67d62f5 100644 --- a/Mono.Debugging/Mono.Debugging.Client/DebuggerSession.cs +++ b/Mono.Debugging/Mono.Debugging.Client/DebuggerSession.cs @@ -890,18 +890,18 @@ public AssemblyLine[] DisassembleFile (string file) } } - public string ResolveExpression (string expression, string file, int line, int column, int endLine, int endColumn) + public string ResolveExpression (EvaluationContext ctx, string expression, string file, int line, int column, int endLine, int endColumn) { - return ResolveExpression (expression, new SourceLocation (null, file, line, column, endLine, endColumn, null, null)); + return ResolveExpression (ctx, expression, new SourceLocation (null, file, line, column, endLine, endColumn, null, null)); } - public virtual string ResolveExpression (string expression, SourceLocation location) + public virtual string ResolveExpression (EvaluationContext ctx, string expression, SourceLocation location) { var key = expression + " " + location; if (!resolvedExpressionCache.TryGetValue (key, out var resolved)) { try { - resolved = OnResolveExpression (expression, location); + resolved = OnResolveExpression (ctx, expression, location); } catch (Exception ex) { OnDebuggerOutput (true, "Error while resolving expression: " + ex.Message); } @@ -992,13 +992,13 @@ protected void RaiseStopEvent () /// /// The resolved expression /// - protected virtual string OnResolveExpression (string expression, SourceLocation location) + protected virtual string OnResolveExpression (EvaluationContext ctx, string expression, SourceLocation location) { var resolver = defaultResolver; if (GetExpressionEvaluator != null) resolver = GetExpressionEvaluator(System.IO.Path.GetExtension(location.FileName))?.Evaluator ?? defaultResolver; - return resolver.Resolve(this, location, expression); + return resolver.Resolve (this, ctx, location, expression); } internal protected string ResolveIdentifierAsType (string identifier, SourceLocation location) diff --git a/Mono.Debugging/Mono.Debugging.Client/StackFrame.cs b/Mono.Debugging/Mono.Debugging.Client/StackFrame.cs index c5c1869c9..c1565936b 100644 --- a/Mono.Debugging/Mono.Debugging.Client/StackFrame.cs +++ b/Mono.Debugging/Mono.Debugging.Client/StackFrame.cs @@ -3,6 +3,7 @@ using System.Threading; using Mono.Debugging.Backend; +using Mono.Debugging.Evaluation; namespace Mono.Debugging.Client { @@ -272,9 +273,9 @@ public ExceptionInfo GetException (EvaluationOptions options) return value; } - public string ResolveExpression (string exp) + public string ResolveExpression (EvaluationContext ctx, string expression) { - return session.ResolveExpression (exp, location); + return session.ResolveExpression (ctx, expression, location); } public ObjectValue[] GetExpressionValues (string[] expressions, bool evaluateMethods) @@ -297,9 +298,11 @@ public ObjectValue[] GetExpressionValues (string[] expressions, EvaluationOption } if (options.UseExternalTypeResolver) { + var ctx = sourceBacktrace.GetEvaluationContext (index, options); + var resolved = new string [expressions.Length]; for (int n = 0; n < expressions.Length; n++) - resolved[n] = ResolveExpression (expressions[n]); + resolved[n] = ResolveExpression (ctx, expressions[n]); expressions = resolved; } @@ -323,8 +326,11 @@ public ObjectValue GetExpressionValue (string expression, EvaluationOptions opti return ObjectValue.CreateUnknown (expression); } - if (options.UseExternalTypeResolver) - expression = ResolveExpression (expression); + if (options.UseExternalTypeResolver) { + var ctx = sourceBacktrace.GetEvaluationContext (index, options); + + expression = ResolveExpression (ctx, expression); + } var values = sourceBacktrace.GetExpressionValues (index, new [] { expression }, options); ObjectValue.ConnectCallbacks (this, values); @@ -344,8 +350,11 @@ public bool ValidateExpression (string expression) /// public ValidationResult ValidateExpression (string expression, EvaluationOptions options) { - if (options.UseExternalTypeResolver) - expression = ResolveExpression (expression); + if (options.UseExternalTypeResolver) { + var ctx = sourceBacktrace.GetEvaluationContext (index, options); + + expression = ResolveExpression (ctx, expression); + } return sourceBacktrace.ValidateExpression (index, expression, options); } diff --git a/Mono.Debugging/Mono.Debugging.Evaluation/BaseBacktrace.cs b/Mono.Debugging/Mono.Debugging.Evaluation/BaseBacktrace.cs index 67828cb32..68832036d 100644 --- a/Mono.Debugging/Mono.Debugging.Evaluation/BaseBacktrace.cs +++ b/Mono.Debugging/Mono.Debugging.Evaluation/BaseBacktrace.cs @@ -45,7 +45,7 @@ protected BaseBacktrace (ObjectValueAdaptor adaptor) public ObjectValueAdaptor Adaptor { get; set; } - protected abstract EvaluationContext GetEvaluationContext (int frameIndex, EvaluationOptions options); + public abstract EvaluationContext GetEvaluationContext (int frameIndex, EvaluationOptions options); public abstract int FrameCount { get; } diff --git a/Mono.Debugging/Mono.Debugging.Evaluation/ExpressionEvaluator.cs b/Mono.Debugging/Mono.Debugging.Evaluation/ExpressionEvaluator.cs index 47d951353..3bee6f96b 100644 --- a/Mono.Debugging/Mono.Debugging.Evaluation/ExpressionEvaluator.cs +++ b/Mono.Debugging/Mono.Debugging.Evaluation/ExpressionEvaluator.cs @@ -183,13 +183,18 @@ public virtual bool CaseSensitive { get { return true; } } - public abstract string Resolve (DebuggerSession session, SourceLocation location, string expression); + public abstract string Resolve (DebuggerSession session, EvaluationContext ctx, SourceLocation location, string expression); public virtual IEnumerable GetLocalVariables (EvaluationContext ctx) { return ctx.Adapter.GetLocalVariables (ctx); } + public virtual ValueReference GetLocalVariable (EvaluationContext ctx, string name) + { + return ctx.Adapter.GetLocalVariable (ctx, name); + } + public virtual ValueReference GetThisReference (EvaluationContext ctx) { return ctx.Adapter.GetThisReference (ctx); @@ -200,6 +205,11 @@ public virtual IEnumerable GetParameters (EvaluationContext ctx) return ctx.Adapter.GetParameters (ctx); } + public virtual ValueReference GetParameter (EvaluationContext ctx, string name) + { + return ctx.Adapter.GetParameter (ctx, name); + } + public virtual ValueReference GetCurrentException (EvaluationContext ctx) { return ctx.Adapter.GetCurrentException (ctx); diff --git a/Mono.Debugging/Mono.Debugging.Evaluation/NRefactoryExpressionEvaluator.cs b/Mono.Debugging/Mono.Debugging.Evaluation/NRefactoryExpressionEvaluator.cs index 26d3b2892..0025c5350 100644 --- a/Mono.Debugging/Mono.Debugging.Evaluation/NRefactoryExpressionEvaluator.cs +++ b/Mono.Debugging/Mono.Debugging.Evaluation/NRefactoryExpressionEvaluator.cs @@ -83,20 +83,20 @@ public override ValueReference Evaluate (EvaluationContext ctx, string expressio return expr.AcceptVisitor (evaluator); } - public override string Resolve (DebuggerSession session, SourceLocation location, string exp) + public override string Resolve (DebuggerSession session, EvaluationContext ctx, SourceLocation location, string expression) { - return Resolve (session, location, exp, false); + return Resolve (session, ctx, location, expression, false); } - string Resolve (DebuggerSession session, SourceLocation location, string expression, bool tryTypeOf) + string Resolve (DebuggerSession session, EvaluationContext ctx, SourceLocation location, string expression, bool tryTypeOf) { expression = expression.Trim (); if (expression.Length > 0 && expression[0] == '?') - return "?" + Resolve (session, location, expression.Substring (1).Trim ()); + return "?" + Resolve (session, ctx, location, expression.Substring (1).TrimStart ()); if (expression.Length > 3 && expression.StartsWith ("var", StringComparison.Ordinal) && char.IsWhiteSpace (expression[3])) - return "var " + Resolve (session, location, expression.Substring (4).Trim (' ', '\t')); + return "var " + Resolve (session, ctx, location, expression.Substring (4).TrimStart ()); expression = ReplaceExceptionTag (expression, session.Options.EvaluationOptions.CurrentExceptionTag); @@ -104,7 +104,7 @@ string Resolve (DebuggerSession session, SourceLocation location, string express if (expr == null) return expression; - var resolver = new NRefactoryExpressionResolverVisitor (session, location, expression); + var resolver = new NRefactoryExpressionResolverVisitor (this, session, ctx, location, expression); expr.AcceptVisitor (resolver); string resolved = resolver.GetResolvedExpression (); @@ -112,7 +112,7 @@ string Resolve (DebuggerSession session, SourceLocation location, string express // This is a hack to be able to parse expressions such as "List". The NRefactory parser // can parse a single type name, so a solution is to wrap it around a typeof(). We do it if // the evaluation fails. - string res = Resolve (session, location, "typeof(" + expression + ")", true); + string res = Resolve (session, ctx, location, "typeof(" + expression + ")", true); return res.Substring (7, res.Length - 8); } diff --git a/Mono.Debugging/Mono.Debugging.Evaluation/NRefactoryExpressionResolverVisitor.cs b/Mono.Debugging/Mono.Debugging.Evaluation/NRefactoryExpressionResolverVisitor.cs index 7d9d61eac..242761705 100644 --- a/Mono.Debugging/Mono.Debugging.Evaluation/NRefactoryExpressionResolverVisitor.cs +++ b/Mono.Debugging/Mono.Debugging.Evaluation/NRefactoryExpressionResolverVisitor.cs @@ -24,6 +24,7 @@ // THE SOFTWARE. using System; +using System.Linq; using System.Text; using System.Collections.Generic; @@ -38,8 +39,10 @@ namespace Mono.Debugging.Evaluation public class NRefactoryExpressionResolverVisitor : DepthFirstAstVisitor { readonly List replacements = new List (); + readonly ExpressionEvaluator evaluator; readonly SourceLocation location; readonly DebuggerSession session; + readonly EvaluationContext ctx; readonly string expression; string parentType; @@ -50,11 +53,13 @@ class Replacement public int Length; } - public NRefactoryExpressionResolverVisitor (DebuggerSession session, SourceLocation location, string expression) + public NRefactoryExpressionResolverVisitor (ExpressionEvaluator evaluator, DebuggerSession session, EvaluationContext ctx, SourceLocation location, string expression) { this.expression = expression.Replace ("\n", "").Replace ("\r", ""); + this.evaluator = evaluator; this.session = session; this.location = location; + this.ctx = ctx; } internal string GetResolvedExpression () @@ -128,8 +133,17 @@ public override void VisitIdentifierExpression (IdentifierExpression identifierE int length = identifierExpression.IdentifierToken.EndLocation.Column - identifierExpression.IdentifierToken.StartLocation.Column; int offset = identifierExpression.IdentifierToken.StartLocation.Column - 1; + var name = identifierExpression.Identifier; - ReplaceType (identifierExpression.Identifier, identifierExpression.TypeArguments.Count, offset, length); + // check if the identifier is a local variable or a member variable + if (evaluator.GetLocalVariable (ctx, name) != null) + return; + + var thisReference = evaluator.GetThisReference (ctx); + if (thisReference != null && thisReference.GetChild (name, ctx.Options) != null) + return; + + ReplaceType (name, identifierExpression.TypeArguments.Count, offset, length); } public override void VisitTypeReferenceExpression (TypeReferenceExpression typeReferenceExpression) diff --git a/Mono.Debugging/Mono.Debugging.Evaluation/ObjectValueAdaptor.cs b/Mono.Debugging/Mono.Debugging.Evaluation/ObjectValueAdaptor.cs index dbd60c2ae..40c2decb9 100644 --- a/Mono.Debugging/Mono.Debugging.Evaluation/ObjectValueAdaptor.cs +++ b/Mono.Debugging/Mono.Debugging.Evaluation/ObjectValueAdaptor.cs @@ -730,7 +730,7 @@ protected virtual ValueReference OnGetLocalVariable (EvaluationContext ctx, stri return best; } - public virtual ValueReference GetParameter (EvaluationContext ctx, string name) + public ValueReference GetParameter (EvaluationContext ctx, string name) { return OnGetParameter (ctx, name); }