From 72ad10820e980472ad14fe9b9ea26730a2674e8c Mon Sep 17 00:00:00 2001 From: "DESKTOP-GEPIA6N\\Thays" Date: Thu, 11 Nov 2021 13:18:18 -0300 Subject: [PATCH 01/14] Fix memory consumption. --- .../BrowserDebugProxy.csproj | 1 + .../BrowserDebugProxy/EvaluateExpression.cs | 89 ++++--------------- 2 files changed, 17 insertions(+), 73 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/BrowserDebugProxy.csproj b/src/mono/wasm/debugger/BrowserDebugProxy/BrowserDebugProxy.csproj index 169856bc09123..0c5d37b7afe9a 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/BrowserDebugProxy.csproj +++ b/src/mono/wasm/debugger/BrowserDebugProxy/BrowserDebugProxy.csproj @@ -7,6 +7,7 @@ + diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs index 6d1367fd209a1..12a3cd61cb930 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs @@ -12,8 +12,10 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Emit; +using Microsoft.CodeAnalysis.Scripting; using Newtonsoft.Json; using Newtonsoft.Json.Linq; @@ -32,6 +34,7 @@ private class FindVariableNMethodCall : CSharpSyntaxWalker private int visitCount; public bool hasMethodCalls; public bool hasElementAccesses; + internal string variableDefinition; public void VisitInternal(SyntaxNode node) { @@ -193,9 +196,6 @@ public SyntaxTree ReplaceVars(SyntaxTree syntaxTree, IEnumerable ma_val CompilationUnitSyntax UpdateWithNewMethodParam(CompilationUnitSyntax root, string id_name, JObject value) { - var classDeclaration = root.Members.ElementAt(0) as ClassDeclarationSyntax; - var method = classDeclaration.Members.ElementAt(0) as MethodDeclarationSyntax; - if (paramsSet.Contains(id_name)) { // repeated member access expression @@ -203,16 +203,7 @@ CompilationUnitSyntax UpdateWithNewMethodParam(CompilationUnitSyntax root, strin return root; } - argValues.Add(ConvertJSToCSharpType(value)); - - MethodDeclarationSyntax updatedMethod = method.AddParameterListParameters( - SyntaxFactory.Parameter( - SyntaxFactory.Identifier(id_name)) - .WithType(SyntaxFactory.ParseTypeName(GetTypeFullName(value)))); - - paramsSet.Add(id_name); - root = root.ReplaceNode(method, updatedMethod); - + variableDefinition += $"{GetTypeFullName(value)} {id_name} = {ConvertJSToCSharpType(value)};\n"; return root; } } @@ -226,7 +217,7 @@ private object ConvertJSToCSharpType(JToken variable) switch (type) { case "string": - return value?.Value(); + return $"\"{value?.Value()}\""; case "number": return value?.Value(); case "boolean": @@ -266,13 +257,8 @@ private string GetTypeFullName(JToken variable) private static SyntaxNode GetExpressionFromSyntaxTree(SyntaxTree syntaxTree) { CompilationUnitSyntax root = syntaxTree.GetCompilationUnitRoot(); - ClassDeclarationSyntax classDeclaration = root.Members.ElementAt(0) as ClassDeclarationSyntax; - MethodDeclarationSyntax methodDeclaration = classDeclaration.Members.ElementAt(0) as MethodDeclarationSyntax; - BlockSyntax blockValue = methodDeclaration.Body; - ReturnStatementSyntax returnValue = blockValue.Statements.ElementAt(0) as ReturnStatementSyntax; - ParenthesizedExpressionSyntax expressionParenthesized = returnValue.Expression as ParenthesizedExpressionSyntax; - return expressionParenthesized?.Expression; + return root; } private static async Task> ResolveMemberAccessExpressions(IEnumerable member_accesses, @@ -335,8 +321,6 @@ private static async Task> ResolveElementAccess(IEnumerable CompileAndRunTheExpression(string expression, MemberReferenceResolver resolver, CancellationToken token) { expression = expression.Trim(); @@ -344,23 +328,13 @@ internal static async Task CompileAndRunTheExpression(string expression { expression = "(" + expression + ")"; } - SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(@" - using System; - public class CompileAndRunTheExpression - { - public static object Evaluate() - { - return " + expression + @"; - } - }", cancellationToken: token); + SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(expression + @";", cancellationToken: token); SyntaxNode expressionTree = GetExpressionFromSyntaxTree(syntaxTree); if (expressionTree == null) throw new Exception($"BUG: Unable to evaluate {expression}, could not get expression from the syntax tree"); - FindVariableNMethodCall findVarNMethodCall = new FindVariableNMethodCall(); findVarNMethodCall.VisitInternal(expressionTree); - // this fails with `"a)"` // because the code becomes: return (a)); // and the returned expression from GetExpressionFromSyntaxTree is `a`! @@ -413,45 +387,14 @@ public static object Evaluate() if (expressionTree == null) throw new Exception($"BUG: Unable to evaluate {expression}, could not get expression from the syntax tree"); - MetadataReference[] references = new MetadataReference[] - { - MetadataReference.CreateFromFile(typeof(object).Assembly.Location), - MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location) - }; - - CSharpCompilation compilation = CSharpCompilation.Create( - "compileAndRunTheExpression", - syntaxTrees: new[] { syntaxTree }, - references: references, - options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); - - SemanticModel semanticModel = compilation.GetSemanticModel(syntaxTree); - CodeAnalysis.TypeInfo TypeInfo = semanticModel.GetTypeInfo(expressionTree, cancellationToken: token); - - using (var ms = new MemoryStream()) - { - EmitResult result = compilation.Emit(ms, cancellationToken: token); - if (!result.Success) - { - var sb = new StringBuilder(); - foreach (Diagnostic d in result.Diagnostics) - sb.Append(d.ToString()); - - throw new ReturnAsErrorException(sb.ToString(), "CompilationError"); - } - - ms.Seek(0, SeekOrigin.Begin); - Assembly assembly = Assembly.Load(ms.ToArray()); - Type type = assembly.GetType("CompileAndRunTheExpression"); - - object ret = type.InvokeMember("Evaluate", - BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public, - null, - null, - findVarNMethodCall.argValues.ToArray()); - - return JObject.FromObject(ConvertCSharpToJSType(ret, TypeInfo.Type)); - } + var task = CSharpScript.EvaluateAsync( + findVarNMethodCall.variableDefinition + "return " + syntaxTree.ToString(), + ScriptOptions.Default.WithReferences( + typeof(object).Assembly, + typeof(Enumerable).Assembly + ), + cancellationToken: token); + return JObject.FromObject(ConvertCSharpToJSType(task.Result, task.Result.GetType())); } private static readonly HashSet NumericTypes = new HashSet @@ -462,7 +405,7 @@ public static object Evaluate() typeof(float), typeof(double) }; - private static object ConvertCSharpToJSType(object v, ITypeSymbol type) + private static object ConvertCSharpToJSType(object v, Type type) { if (v == null) return new { type = "object", subtype = "null", className = type.ToString(), description = type.ToString() }; From 9755954f5b05109133fc47d6b73ffda939883cfa Mon Sep 17 00:00:00 2001 From: "DESKTOP-GEPIA6N\\Thays" Date: Thu, 11 Nov 2021 15:17:23 -0300 Subject: [PATCH 02/14] Fix debugger-tests --- .../BrowserDebugProxy/EvaluateExpression.cs | 62 +++++++++++++------ .../EvaluateOnCallFrameTests.cs | 2 +- 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs index 12a3cd61cb930..1384b8d3e71b1 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs @@ -117,7 +117,7 @@ public SyntaxTree ReplaceVars(SyntaxTree syntaxTree, IEnumerable ma_val { // Generate a random suffix string suffix = Guid.NewGuid().ToString().Substring(0, 5); - string prefix = iesStr.Trim().Replace(".", "_").Replace("(", "_").Replace(")", "_"); + string prefix = iesStr.Trim().Replace(".", "_").Replace("(", "_").Replace(")", "_").Replace(", ", "_").Replace("\"", "_").Replace("'", "_"); id_name = $"{prefix}_{suffix}"; methodCallToParamName[iesStr] = id_name; } @@ -202,7 +202,7 @@ CompilationUnitSyntax UpdateWithNewMethodParam(CompilationUnitSyntax root, strin // eg. this.a + this.a return root; } - + paramsSet.Add(id_name); variableDefinition += $"{GetTypeFullName(value)} {id_name} = {ConvertJSToCSharpType(value)};\n"; return root; } @@ -213,7 +213,6 @@ private object ConvertJSToCSharpType(JToken variable) JToken value = variable["value"]; string type = variable["type"].Value(); string subType = variable["subtype"]?.Value(); - switch (type) { case "string": @@ -221,11 +220,30 @@ private object ConvertJSToCSharpType(JToken variable) case "number": return value?.Value(); case "boolean": - return value?.Value(); + return value?.Value().ToLower(); case "object": - return variable; + { + if (subType == "null") + return "Newtonsoft.Json.Linq.JObject.FromObject(new {" + + $"type = \"{type}\"," + + $"description = \"{variable["description"].Value()}\"," + + $"className = \"{variable["className"].Value()}\"," + + $"subtype = \"{subType}\"" + + "})"; + return "Newtonsoft.Json.Linq.JObject.FromObject(new {" + + $"type = \"{type}\"," + + $"description = \"{variable["description"].Value()}\"," + + $"className = \"{variable["className"].Value()}\"," + + $"objectId = \"{variable["objectId"].Value()}\"" + + "})"; + } case "void": - return null; + return "Newtonsoft.Json.Linq.JObject.FromObject(new {" + + $"type = \"object\"," + + $"description = \"object\"," + + $"className = \"object\"," + + $"subtype = \"null\"" + + "})"; } throw new Exception($"Evaluate of this datatype {type} not implemented yet");//, "Unsupported"); } @@ -239,14 +257,11 @@ private string GetTypeFullName(JToken variable) switch (type) { case "object": - { - if (subType == "null") - return variable["className"].Value(); - else - return "object"; - } + return "object"; case "void": return "object"; + case "boolean": + return "System.Boolean"; default: return value.GetType().FullName; } @@ -387,14 +402,21 @@ internal static async Task CompileAndRunTheExpression(string expression if (expressionTree == null) throw new Exception($"BUG: Unable to evaluate {expression}, could not get expression from the syntax tree"); - var task = CSharpScript.EvaluateAsync( - findVarNMethodCall.variableDefinition + "return " + syntaxTree.ToString(), - ScriptOptions.Default.WithReferences( - typeof(object).Assembly, - typeof(Enumerable).Assembly - ), - cancellationToken: token); - return JObject.FromObject(ConvertCSharpToJSType(task.Result, task.Result.GetType())); + try { + var task = CSharpScript.EvaluateAsync( + findVarNMethodCall.variableDefinition + "return " + syntaxTree.ToString(), + ScriptOptions.Default.WithReferences( + typeof(object).Assembly, + typeof(Enumerable).Assembly, + typeof(JObject).Assembly + ), + cancellationToken: token); + return JObject.FromObject(ConvertCSharpToJSType(task.Result, task.Result.GetType())); + } + catch (Exception e) + { + throw new ReturnAsErrorException($"Cannot evaluate '{expression}'.", "CompilationError"); + } } private static readonly HashSet NumericTypes = new HashSet diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index 524ff68fb9e2d..6e9e4de6b381f 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -461,7 +461,7 @@ public async Task NegativeTestsInStaticMethod() => await CheckInspectLocalsAtBre await EvaluateOnCallFrameFail(id, ("me.foo", "ReferenceError"), - ("this", "ReferenceError"), + ("this", "CompilationError"), ("this.NullIfAIsNotZero.foo", "ReferenceError")); }); From 3e832bf76bed8c0d272d3cfeb9284acc84353b1f Mon Sep 17 00:00:00 2001 From: "DESKTOP-GEPIA6N\\Thays" Date: Thu, 11 Nov 2021 15:21:09 -0300 Subject: [PATCH 03/14] Fix compilation. --- src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs index 1384b8d3e71b1..1dbd2c6aa3278 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs @@ -413,7 +413,7 @@ internal static async Task CompileAndRunTheExpression(string expression cancellationToken: token); return JObject.FromObject(ConvertCSharpToJSType(task.Result, task.Result.GetType())); } - catch (Exception e) + catch (Exception) { throw new ReturnAsErrorException($"Cannot evaluate '{expression}'.", "CompilationError"); } From 498e2c113630948dad6b4f434a231aa531d82c13 Mon Sep 17 00:00:00 2001 From: "DESKTOP-GEPIA6N\\Thays" Date: Thu, 11 Nov 2021 16:14:56 -0300 Subject: [PATCH 04/14] Addressing @lewing PR. --- .../BrowserDebugProxy/EvaluateExpression.cs | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs index 1dbd2c6aa3278..30072c7dbdb45 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs @@ -18,6 +18,7 @@ using Microsoft.CodeAnalysis.Scripting; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using System.Text.RegularExpressions; namespace Microsoft.WebAssembly.Diagnostics { @@ -100,7 +101,7 @@ public SyntaxTree ReplaceVars(SyntaxTree syntaxTree, IEnumerable ma_val { // Generate a random suffix string suffix = Guid.NewGuid().ToString().Substring(0, 5); - string prefix = ma_str.Trim().Replace(".", "_"); + string prefix = Regex.Replace(ma_str, @"[^A-Za-z0-9_]", "_"); id_name = $"{prefix}_{suffix}"; memberAccessToParamName[ma_str] = id_name; @@ -117,7 +118,7 @@ public SyntaxTree ReplaceVars(SyntaxTree syntaxTree, IEnumerable ma_val { // Generate a random suffix string suffix = Guid.NewGuid().ToString().Substring(0, 5); - string prefix = iesStr.Trim().Replace(".", "_").Replace("(", "_").Replace(")", "_").Replace(", ", "_").Replace("\"", "_").Replace("'", "_"); + string prefix = Regex.Replace(iesStr, @"[^A-Za-z0-9_]", "_"); id_name = $"{prefix}_{suffix}"; methodCallToParamName[iesStr] = id_name; } @@ -251,7 +252,6 @@ private object ConvertJSToCSharpType(JToken variable) private string GetTypeFullName(JToken variable) { string type = variable["type"].ToString(); - string subType = variable["subtype"]?.Value(); object value = ConvertJSToCSharpType(variable); switch (type) @@ -269,13 +269,6 @@ private string GetTypeFullName(JToken variable) } } - private static SyntaxNode GetExpressionFromSyntaxTree(SyntaxTree syntaxTree) - { - CompilationUnitSyntax root = syntaxTree.GetCompilationUnitRoot(); - - return root; - } - private static async Task> ResolveMemberAccessExpressions(IEnumerable member_accesses, MemberReferenceResolver resolver, CancellationToken token) { @@ -345,7 +338,7 @@ internal static async Task CompileAndRunTheExpression(string expression } SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(expression + @";", cancellationToken: token); - SyntaxNode expressionTree = GetExpressionFromSyntaxTree(syntaxTree); + SyntaxNode expressionTree = syntaxTree.GetCompilationUnitRoot(token); if (expressionTree == null) throw new Exception($"BUG: Unable to evaluate {expression}, could not get expression from the syntax tree"); FindVariableNMethodCall findVarNMethodCall = new FindVariableNMethodCall(); @@ -377,7 +370,7 @@ internal static async Task CompileAndRunTheExpression(string expression if (findVarNMethodCall.hasMethodCalls) { - expressionTree = GetExpressionFromSyntaxTree(syntaxTree); + expressionTree = syntaxTree.GetCompilationUnitRoot(token); findVarNMethodCall.VisitInternal(expressionTree); @@ -389,7 +382,7 @@ internal static async Task CompileAndRunTheExpression(string expression // eg. "elements[0]" if (findVarNMethodCall.hasElementAccesses) { - expressionTree = GetExpressionFromSyntaxTree(syntaxTree); + expressionTree = syntaxTree.GetCompilationUnitRoot(token); findVarNMethodCall.VisitInternal(expressionTree); @@ -398,7 +391,7 @@ internal static async Task CompileAndRunTheExpression(string expression syntaxTree = findVarNMethodCall.ReplaceVars(syntaxTree, null, null, null, elementAccessValues); } - expressionTree = GetExpressionFromSyntaxTree(syntaxTree); + expressionTree = syntaxTree.GetCompilationUnitRoot(token); if (expressionTree == null) throw new Exception($"BUG: Unable to evaluate {expression}, could not get expression from the syntax tree"); From d505d40b4c507937f0b72e1d3ea2bfec1878200f Mon Sep 17 00:00:00 2001 From: "DESKTOP-GEPIA6N\\Thays" Date: Thu, 11 Nov 2021 16:35:53 -0300 Subject: [PATCH 05/14] Address @lewing comment --- .../BrowserDebugProxy/EvaluateExpression.cs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs index 30072c7dbdb45..b9886bb3f803a 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs @@ -214,6 +214,7 @@ private object ConvertJSToCSharpType(JToken variable) JToken value = variable["value"]; string type = variable["type"].Value(); string subType = variable["subtype"]?.Value(); + string objectId = variable["objectId"]?.Value(); switch (type) { case "string": @@ -224,19 +225,13 @@ private object ConvertJSToCSharpType(JToken variable) return value?.Value().ToLower(); case "object": { - if (subType == "null") - return "Newtonsoft.Json.Linq.JObject.FromObject(new {" - + $"type = \"{type}\"," - + $"description = \"{variable["description"].Value()}\"," - + $"className = \"{variable["className"].Value()}\"," - + $"subtype = \"{subType}\"" - + "})"; return "Newtonsoft.Json.Linq.JObject.FromObject(new {" - + $"type = \"{type}\"," - + $"description = \"{variable["description"].Value()}\"," - + $"className = \"{variable["className"].Value()}\"," - + $"objectId = \"{variable["objectId"].Value()}\"" - + "})"; + + $"type = \"{type}\"" + + $", description = \"{variable["description"].Value()}\"" + + $", className = \"{variable["className"].Value()}\"" + + (subType != null ? $", subtype = \"{subType}\"" : "") + + (objectId != null ? $", objectId = \"{objectId}\"" : "") + + "})"; } case "void": return "Newtonsoft.Json.Linq.JObject.FromObject(new {" From 69ad64765197d795926b42032059064d4bd05eb5 Mon Sep 17 00:00:00 2001 From: "DESKTOP-GEPIA6N\\Thays" Date: Thu, 11 Nov 2021 17:27:12 -0300 Subject: [PATCH 06/14] Addressing @radical comment. --- .../wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs index b9886bb3f803a..0d6e36dd19e27 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs @@ -26,6 +26,7 @@ internal static class EvaluateExpression { private class FindVariableNMethodCall : CSharpSyntaxWalker { + private static Regex regexForReplaceVarName = new Regex(@"[^A-Za-z0-9_]", RegexOptions.Singleline); public List identifiers = new List(); public List methodCall = new List(); public List memberAccesses = new List(); @@ -101,7 +102,7 @@ public SyntaxTree ReplaceVars(SyntaxTree syntaxTree, IEnumerable ma_val { // Generate a random suffix string suffix = Guid.NewGuid().ToString().Substring(0, 5); - string prefix = Regex.Replace(ma_str, @"[^A-Za-z0-9_]", "_"); + string prefix = regexForReplaceVarName.Replace(ma_str, "_"); id_name = $"{prefix}_{suffix}"; memberAccessToParamName[ma_str] = id_name; @@ -118,7 +119,7 @@ public SyntaxTree ReplaceVars(SyntaxTree syntaxTree, IEnumerable ma_val { // Generate a random suffix string suffix = Guid.NewGuid().ToString().Substring(0, 5); - string prefix = Regex.Replace(iesStr, @"[^A-Za-z0-9_]", "_"); + string prefix = regexForReplaceVarName.Replace(iesStr, "_"); id_name = $"{prefix}_{suffix}"; methodCallToParamName[iesStr] = id_name; } From d8e3e57925b3776208cc52044591defd47ce5f79 Mon Sep 17 00:00:00 2001 From: "DESKTOP-GEPIA6N\\Thays" Date: Thu, 11 Nov 2021 18:23:23 -0300 Subject: [PATCH 07/14] Addressing comments. --- .../BrowserDebugProxy/EvaluateExpression.cs | 25 +++++++++++-------- .../EvaluateOnCallFrameTests.cs | 1 + 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs index 0d6e36dd19e27..feaa8b03f6638 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs @@ -36,7 +36,7 @@ private class FindVariableNMethodCall : CSharpSyntaxWalker private int visitCount; public bool hasMethodCalls; public bool hasElementAccesses; - internal string variableDefinition; + internal StringBuilder variableDefinition = new StringBuilder(); public void VisitInternal(SyntaxNode node) { @@ -156,7 +156,7 @@ public SyntaxTree ReplaceVars(SyntaxTree syntaxTree, IEnumerable ma_val throw new Exception($"BUG: Expected to find an id name for the member access string: {node_str}"); } memberAccessValues[id_name] = value; - root = UpdateWithNewMethodParam(root, id_name, value); + root = CreateNAssingVariableForExpression(id_name, value); } } @@ -164,7 +164,7 @@ public SyntaxTree ReplaceVars(SyntaxTree syntaxTree, IEnumerable ma_val { foreach ((IdentifierNameSyntax idns, JObject value) in identifiers.Zip(id_values)) { - root = UpdateWithNewMethodParam(root, idns.Identifier.Text, value); + root = CreateNAssingVariableForExpression(idns.Identifier.Text, value); } } @@ -177,7 +177,7 @@ public SyntaxTree ReplaceVars(SyntaxTree syntaxTree, IEnumerable ma_val { throw new Exception($"BUG: Expected to find an id name for the member access string: {node_str}"); } - root = UpdateWithNewMethodParam(root, id_name, value); + root = CreateNAssingVariableForExpression(id_name, value); } } @@ -190,13 +190,13 @@ public SyntaxTree ReplaceVars(SyntaxTree syntaxTree, IEnumerable ma_val { throw new Exception($"BUG: Expected to find an id name for the element access string: {node_str}"); } - root = UpdateWithNewMethodParam(root, id_name, value); + root = CreateNAssingVariableForExpression(id_name, value); } } return syntaxTree.WithRootAndOptions(root, syntaxTree.Options); - CompilationUnitSyntax UpdateWithNewMethodParam(CompilationUnitSyntax root, string id_name, JObject value) + CompilationUnitSyntax CreateNAssingVariableForExpression(string id_name, JObject value) { if (paramsSet.Contains(id_name)) { @@ -205,7 +205,7 @@ CompilationUnitSyntax UpdateWithNewMethodParam(CompilationUnitSyntax root, strin return root; } paramsSet.Add(id_name); - variableDefinition += $"{GetTypeFullName(value)} {id_name} = {ConvertJSToCSharpType(value)};\n"; + variableDefinition.Append($"{GetTypeFullName(value)} {id_name} = {ConvertJSToCSharpType(value)};\n"); return root; } } @@ -219,7 +219,11 @@ private object ConvertJSToCSharpType(JToken variable) switch (type) { case "string": - return $"\"{value?.Value()}\""; + { + var str = value?.Value(); + str = str.Replace("\"", "\\\""); + return $"\"{str}\""; + } case "number": return value?.Value(); case "boolean": @@ -392,7 +396,7 @@ internal static async Task CompileAndRunTheExpression(string expression throw new Exception($"BUG: Unable to evaluate {expression}, could not get expression from the syntax tree"); try { - var task = CSharpScript.EvaluateAsync( + var result = await CSharpScript.EvaluateAsync( findVarNMethodCall.variableDefinition + "return " + syntaxTree.ToString(), ScriptOptions.Default.WithReferences( typeof(object).Assembly, @@ -400,10 +404,11 @@ internal static async Task CompileAndRunTheExpression(string expression typeof(JObject).Assembly ), cancellationToken: token); - return JObject.FromObject(ConvertCSharpToJSType(task.Result, task.Result.GetType())); + return JObject.FromObject(ConvertCSharpToJSType(result, result.GetType())); } catch (Exception) { + Console.WriteLine(findVarNMethodCall.variableDefinition + "return " + syntaxTree.ToString()); throw new ReturnAsErrorException($"Cannot evaluate '{expression}'.", "CompilationError"); } } diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index 6e9e4de6b381f..4ab6b3489f0b7 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -542,6 +542,7 @@ await EvaluateOnCallFrameAndCheck(id, ("this.CallMethodWithParmBool(true)", TString("TRUE")), ("this.CallMethodWithParmBool(false)", TString("FALSE")), ("this.CallMethodWithParmString(\"concat\")", TString("str_const_concat")), + ("this.CallMethodWithParmString(\"\\\"\\\"\")", TString("str_const_\"\"")), ("this.CallMethodWithParm(10) + this.a", TNumber(12)), ("this.CallMethodWithObj(null)", TNumber(-1)), ("this.CallMethodWithChar('a')", TString("str_const_a"))); From 01289385bcdcaa0263cb38558ec30f4ef1cff98c Mon Sep 17 00:00:00 2001 From: "DESKTOP-GEPIA6N\\Thays" Date: Thu, 11 Nov 2021 19:09:39 -0300 Subject: [PATCH 08/14] Addressing @radical comments. --- .../BrowserDebugProxy/EvaluateExpression.cs | 71 ++++++++++--------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs index feaa8b03f6638..7fada0e6ed33d 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs @@ -36,7 +36,7 @@ private class FindVariableNMethodCall : CSharpSyntaxWalker private int visitCount; public bool hasMethodCalls; public bool hasElementAccesses; - internal StringBuilder variableDefinition = new StringBuilder(); + internal List variableDefinitions = new List(); public void VisitInternal(SyntaxNode node) { @@ -143,7 +143,7 @@ public SyntaxTree ReplaceVars(SyntaxTree syntaxTree, IEnumerable ma_val return SyntaxFactory.IdentifierName(id_name); }); - var paramsSet = new HashSet(); + var localsSet = new HashSet(); // 2. For every unique member ref, add a corresponding method param if (ma_values != null) @@ -156,7 +156,7 @@ public SyntaxTree ReplaceVars(SyntaxTree syntaxTree, IEnumerable ma_val throw new Exception($"BUG: Expected to find an id name for the member access string: {node_str}"); } memberAccessValues[id_name] = value; - root = CreateNAssingVariableForExpression(id_name, value); + AddLocalVariableWithValue(id_name, value); } } @@ -164,7 +164,7 @@ public SyntaxTree ReplaceVars(SyntaxTree syntaxTree, IEnumerable ma_val { foreach ((IdentifierNameSyntax idns, JObject value) in identifiers.Zip(id_values)) { - root = CreateNAssingVariableForExpression(idns.Identifier.Text, value); + AddLocalVariableWithValue(idns.Identifier.Text, value); } } @@ -177,7 +177,7 @@ public SyntaxTree ReplaceVars(SyntaxTree syntaxTree, IEnumerable ma_val { throw new Exception($"BUG: Expected to find an id name for the member access string: {node_str}"); } - root = CreateNAssingVariableForExpression(id_name, value); + AddLocalVariableWithValue(id_name, value); } } @@ -190,28 +190,28 @@ public SyntaxTree ReplaceVars(SyntaxTree syntaxTree, IEnumerable ma_val { throw new Exception($"BUG: Expected to find an id name for the element access string: {node_str}"); } - root = CreateNAssingVariableForExpression(id_name, value); + AddLocalVariableWithValue(id_name, value); } } return syntaxTree.WithRootAndOptions(root, syntaxTree.Options); - CompilationUnitSyntax CreateNAssingVariableForExpression(string id_name, JObject value) + void AddLocalVariableWithValue(string idName, JObject value) { - if (paramsSet.Contains(id_name)) + if (localsSet.Contains(idName)) { // repeated member access expression // eg. this.a + this.a - return root; } - paramsSet.Add(id_name); - variableDefinition.Append($"{GetTypeFullName(value)} {id_name} = {ConvertJSToCSharpType(value)};\n"); - return root; + localsSet.Add(idName); + variableDefinitions.Add(ConvertJSToCSharpVariable(idName, value)); } } - private object ConvertJSToCSharpType(JToken variable) + private string ConvertJSToCSharpVariable(string idName, JToken variable) { + string typeRet; + object valueRet; JToken value = variable["value"]; string type = variable["type"].Value(); string subType = variable["subtype"]?.Value(); @@ -222,50 +222,53 @@ private object ConvertJSToCSharpType(JToken variable) { var str = value?.Value(); str = str.Replace("\"", "\\\""); - return $"\"{str}\""; + valueRet = $"\"{str}\""; + break; } case "number": - return value?.Value(); + valueRet = value?.Value(); + break; case "boolean": - return value?.Value().ToLower(); + valueRet = value?.Value().ToLower(); + break; case "object": - { - return "Newtonsoft.Json.Linq.JObject.FromObject(new {" + valueRet = "Newtonsoft.Json.Linq.JObject.FromObject(new {" + $"type = \"{type}\"" + $", description = \"{variable["description"].Value()}\"" + $", className = \"{variable["className"].Value()}\"" + (subType != null ? $", subtype = \"{subType}\"" : "") + (objectId != null ? $", objectId = \"{objectId}\"" : "") + "})"; - } + break; case "void": - return "Newtonsoft.Json.Linq.JObject.FromObject(new {" + valueRet = "Newtonsoft.Json.Linq.JObject.FromObject(new {" + $"type = \"object\"," + $"description = \"object\"," + $"className = \"object\"," + $"subtype = \"null\"" + "})"; + break; + default: + throw new Exception($"Evaluate of this datatype {type} not implemented yet");//, "Unsupported"); } - throw new Exception($"Evaluate of this datatype {type} not implemented yet");//, "Unsupported"); - } - - private string GetTypeFullName(JToken variable) - { - string type = variable["type"].ToString(); - object value = ConvertJSToCSharpType(variable); switch (type) { case "object": - return "object"; + typeRet = "object"; + break; case "void": - return "object"; + typeRet = "object"; + break; case "boolean": - return "System.Boolean"; + typeRet = "System.Boolean"; + break; default: - return value.GetType().FullName; + typeRet = valueRet.GetType().FullName; + break; } - throw new ReturnAsErrorException($"GetTypefullName: Evaluate of this datatype {type} not implemented yet", "Unsupported"); + + return $"{typeRet} {idName} = {valueRet};"; } } @@ -397,7 +400,7 @@ internal static async Task CompileAndRunTheExpression(string expression try { var result = await CSharpScript.EvaluateAsync( - findVarNMethodCall.variableDefinition + "return " + syntaxTree.ToString(), + string.Join("\n", findVarNMethodCall.variableDefinitions) + "\nreturn " + syntaxTree.ToString(), ScriptOptions.Default.WithReferences( typeof(object).Assembly, typeof(Enumerable).Assembly, @@ -408,7 +411,7 @@ internal static async Task CompileAndRunTheExpression(string expression } catch (Exception) { - Console.WriteLine(findVarNMethodCall.variableDefinition + "return " + syntaxTree.ToString()); + Console.WriteLine(string.Join("\n", findVarNMethodCall.variableDefinitions) + "\nreturn " + syntaxTree.ToString()); throw new ReturnAsErrorException($"Cannot evaluate '{expression}'.", "CompilationError"); } } From 194ee4f2eb025135577b0dca96aced0dcc4ac21a Mon Sep 17 00:00:00 2001 From: "DESKTOP-GEPIA6N\\Thays" Date: Thu, 11 Nov 2021 19:13:04 -0300 Subject: [PATCH 09/14] missing return. --- .../wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs index 7fada0e6ed33d..05743d92f89fb 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs @@ -199,10 +199,7 @@ public SyntaxTree ReplaceVars(SyntaxTree syntaxTree, IEnumerable ma_val void AddLocalVariableWithValue(string idName, JObject value) { if (localsSet.Contains(idName)) - { - // repeated member access expression - // eg. this.a + this.a - } + return; localsSet.Add(idName); variableDefinitions.Add(ConvertJSToCSharpVariable(idName, value)); } From 36e7e56ff3e333fd15776706ea8876e38cd77a05 Mon Sep 17 00:00:00 2001 From: "DESKTOP-GEPIA6N\\Thays" Date: Thu, 11 Nov 2021 19:21:24 -0300 Subject: [PATCH 10/14] Addressing @radical comments --- .../BrowserDebugProxy/EvaluateExpression.cs | 26 +++++-------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs index 05743d92f89fb..38f3ad9bdaeab 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs @@ -201,11 +201,11 @@ void AddLocalVariableWithValue(string idName, JObject value) if (localsSet.Contains(idName)) return; localsSet.Add(idName); - variableDefinitions.Add(ConvertJSToCSharpVariable(idName, value)); + variableDefinitions.Add(ConvertJSToCSharpLocalVariableAssignment(idName, value)); } } - private string ConvertJSToCSharpVariable(string idName, JToken variable) + private string ConvertJSToCSharpLocalVariableAssignment(string idName, JToken variable) { string typeRet; object valueRet; @@ -220,13 +220,16 @@ private string ConvertJSToCSharpVariable(string idName, JToken variable) var str = value?.Value(); str = str.Replace("\"", "\\\""); valueRet = $"\"{str}\""; + typeRet = valueRet.GetType().FullName; break; } case "number": valueRet = value?.Value(); + typeRet = valueRet.GetType().FullName; break; case "boolean": valueRet = value?.Value().ToLower(); + typeRet = "System.Boolean"; break; case "object": valueRet = "Newtonsoft.Json.Linq.JObject.FromObject(new {" @@ -236,6 +239,7 @@ private string ConvertJSToCSharpVariable(string idName, JToken variable) + (subType != null ? $", subtype = \"{subType}\"" : "") + (objectId != null ? $", objectId = \"{objectId}\"" : "") + "})"; + typeRet = "object"; break; case "void": valueRet = "Newtonsoft.Json.Linq.JObject.FromObject(new {" @@ -244,27 +248,11 @@ private string ConvertJSToCSharpVariable(string idName, JToken variable) + $"className = \"object\"," + $"subtype = \"null\"" + "})"; - break; - default: - throw new Exception($"Evaluate of this datatype {type} not implemented yet");//, "Unsupported"); - } - - switch (type) - { - case "object": - typeRet = "object"; - break; - case "void": typeRet = "object"; break; - case "boolean": - typeRet = "System.Boolean"; - break; default: - typeRet = valueRet.GetType().FullName; - break; + throw new Exception($"Evaluate of this datatype {type} not implemented yet");//, "Unsupported"); } - return $"{typeRet} {idName} = {valueRet};"; } } From 0a686ec873e14a5e8ce935c96c7debc478e6bb80 Mon Sep 17 00:00:00 2001 From: Thays Grazia Date: Thu, 11 Nov 2021 19:22:28 -0300 Subject: [PATCH 11/14] Adding test case Co-authored-by: Larry Ewing --- .../wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index 4ab6b3489f0b7..f958e8fe4e404 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -543,6 +543,8 @@ await EvaluateOnCallFrameAndCheck(id, ("this.CallMethodWithParmBool(false)", TString("FALSE")), ("this.CallMethodWithParmString(\"concat\")", TString("str_const_concat")), ("this.CallMethodWithParmString(\"\\\"\\\"\")", TString("str_const_\"\"")), + ("this.CallMethodWithParmString(\"🛶\")", TString("str_const_🛶")), + ("this.CallMethodWithParmString(\"\\uD83D\\uDE80\")", TString("str_const_🛶")), ("this.CallMethodWithParm(10) + this.a", TNumber(12)), ("this.CallMethodWithObj(null)", TNumber(-1)), ("this.CallMethodWithChar('a')", TString("str_const_a"))); From 97b98ceffe2874179914aec15e79669d89f7d973 Mon Sep 17 00:00:00 2001 From: "DESKTOP-GEPIA6N\\Thays" Date: Thu, 11 Nov 2021 20:02:11 -0300 Subject: [PATCH 12/14] Fixing tests. --- .../debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index f958e8fe4e404..1cd09e7ab4c4a 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -544,7 +544,8 @@ await EvaluateOnCallFrameAndCheck(id, ("this.CallMethodWithParmString(\"concat\")", TString("str_const_concat")), ("this.CallMethodWithParmString(\"\\\"\\\"\")", TString("str_const_\"\"")), ("this.CallMethodWithParmString(\"🛶\")", TString("str_const_🛶")), - ("this.CallMethodWithParmString(\"\\uD83D\\uDE80\")", TString("str_const_🛶")), + ("this.CallMethodWithParmString(\"\\uD83D\\uDEF6\")", TString("str_const_🛶")), + ("this.CallMethodWithParmString(\"🚀\")", TString("str_const_🚀")), ("this.CallMethodWithParm(10) + this.a", TNumber(12)), ("this.CallMethodWithObj(null)", TNumber(-1)), ("this.CallMethodWithChar('a')", TString("str_const_a"))); From bd552fdca6e46042880bde03c5c51616951c7449 Mon Sep 17 00:00:00 2001 From: "DESKTOP-GEPIA6N\\Thays" Date: Thu, 11 Nov 2021 20:27:35 -0300 Subject: [PATCH 13/14] Adding another test case. Thanks @lewing. --- .../debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs | 1 + .../debugger/tests/debugger-test/debugger-evaluate-test.cs | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs index 1cd09e7ab4c4a..ae471cf0f94f9 100644 --- a/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs +++ b/src/mono/wasm/debugger/DebuggerTestSuite/EvaluateOnCallFrameTests.cs @@ -546,6 +546,7 @@ await EvaluateOnCallFrameAndCheck(id, ("this.CallMethodWithParmString(\"🛶\")", TString("str_const_🛶")), ("this.CallMethodWithParmString(\"\\uD83D\\uDEF6\")", TString("str_const_🛶")), ("this.CallMethodWithParmString(\"🚀\")", TString("str_const_🚀")), + ("this.CallMethodWithParmString_λ(\"🚀\")", TString("λ_🚀")), ("this.CallMethodWithParm(10) + this.a", TNumber(12)), ("this.CallMethodWithObj(null)", TNumber(-1)), ("this.CallMethodWithChar('a')", TString("str_const_a"))); diff --git a/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs b/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs index 74153fa379376..b8124c6496aea 100644 --- a/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs +++ b/src/mono/wasm/debugger/tests/debugger-test/debugger-evaluate-test.cs @@ -378,6 +378,11 @@ public string CallMethodWithParmString(string parm) return str + parm; } + public string CallMethodWithParmString_λ(string parm) + { + return "λ_" + parm; + } + public string CallMethodWithParmBool(bool parm) { if (parm) From 351f897f815335106f52bfed34110a72765f9553 Mon Sep 17 00:00:00 2001 From: "DESKTOP-GEPIA6N\\Thays" Date: Mon, 15 Nov 2021 12:08:08 -0300 Subject: [PATCH 14/14] Reuse the script. --- .../BrowserDebugProxy/EvaluateExpression.cs | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs index 38f3ad9bdaeab..5545a26203697 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs @@ -24,6 +24,13 @@ namespace Microsoft.WebAssembly.Diagnostics { internal static class EvaluateExpression { + internal static Script script = CSharpScript.Create( + "", + ScriptOptions.Default.WithReferences( + typeof(object).Assembly, + typeof(Enumerable).Assembly, + typeof(JObject).Assembly + )); private class FindVariableNMethodCall : CSharpSyntaxWalker { private static Regex regexForReplaceVarName = new Regex(@"[^A-Za-z0-9_]", RegexOptions.Singleline); @@ -220,16 +227,16 @@ private string ConvertJSToCSharpLocalVariableAssignment(string idName, JToken va var str = value?.Value(); str = str.Replace("\"", "\\\""); valueRet = $"\"{str}\""; - typeRet = valueRet.GetType().FullName; + typeRet = "string"; break; } case "number": valueRet = value?.Value(); - typeRet = valueRet.GetType().FullName; + typeRet = "double"; break; case "boolean": valueRet = value?.Value().ToLower(); - typeRet = "System.Boolean"; + typeRet = "bool"; break; case "object": valueRet = "Newtonsoft.Json.Linq.JObject.FromObject(new {" @@ -384,19 +391,15 @@ internal static async Task CompileAndRunTheExpression(string expression throw new Exception($"BUG: Unable to evaluate {expression}, could not get expression from the syntax tree"); try { - var result = await CSharpScript.EvaluateAsync( - string.Join("\n", findVarNMethodCall.variableDefinitions) + "\nreturn " + syntaxTree.ToString(), - ScriptOptions.Default.WithReferences( - typeof(object).Assembly, - typeof(Enumerable).Assembly, - typeof(JObject).Assembly - ), - cancellationToken: token); - return JObject.FromObject(ConvertCSharpToJSType(result, result.GetType())); + var newScript = script.ContinueWith( + string.Join("\n", findVarNMethodCall.variableDefinitions) + "\nreturn " + syntaxTree.ToString()); + + var state = await newScript.RunAsync(cancellationToken: token); + + return JObject.FromObject(ConvertCSharpToJSType(state.ReturnValue, state.ReturnValue.GetType())); } catch (Exception) { - Console.WriteLine(string.Join("\n", findVarNMethodCall.variableDefinitions) + "\nreturn " + syntaxTree.ToString()); throw new ReturnAsErrorException($"Cannot evaluate '{expression}'.", "CompilationError"); } }