async Task <bool> OnEvaluateOnCallFrame(MessageId msg_id, int scope_id, string expression, CancellationToken token) { try { var context = GetContext(msg_id); if (context.CallStack == null) { return(false); } var resolver = new MemberReferenceResolver(this, context, msg_id, scope_id, logger); JObject retValue = await resolver.Resolve(expression, token); if (retValue == null) { retValue = await EvaluateExpression.CompileAndRunTheExpression(expression, resolver, token); } if (retValue != null) { SendResponse(msg_id, Result.OkFromObject(new { result = retValue }), token); } else { SendResponse(msg_id, Result.Err($"Unable to evaluate '{expression}'"), token); } } catch (ReturnAsErrorException ree) { SendResponse(msg_id, ree.Error, token); } catch (Exception e) { logger.LogDebug($"Error in EvaluateOnCallFrame for expression '{expression}' with '{e}."); SendResponse(msg_id, Result.Exception(e), token); } return(true); }
private static async Task <IList <JObject> > ResolveMemberAccessExpressions(IEnumerable <MemberAccessExpressionSyntax> member_accesses, MemberReferenceResolver resolver, CancellationToken token) { var memberAccessValues = new List <JObject>(); foreach (MemberAccessExpressionSyntax maes in member_accesses) { string memberAccessString = maes.ToString(); JObject value = await resolver.Resolve(memberAccessString, token); if (value == null) { throw new ReturnAsErrorException($"Failed to resolve member access for {memberAccessString}", "ReferenceError"); } memberAccessValues.Add(value); } return(memberAccessValues); }
internal static async Task <JObject> CompileAndRunTheExpression(string expression, MemberReferenceResolver resolver, CancellationToken token) { expression = expression.Trim(); if (!expression.StartsWith('(')) { expression = "(" + expression + ")"; } SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(@" using System; public class CompileAndRunTheExpression { public static object Evaluate() { return " + 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`! if (expressionTree.Kind() == SyntaxKind.IdentifierName || expressionTree.Kind() == SyntaxKind.ThisExpression) { string varName = expressionTree.ToString(); JObject value = await resolver.Resolve(varName, token); if (value == null) { throw new ReturnAsErrorException($"Cannot find member named '{varName}'.", "ReferenceError"); } return(value); } IList <JObject> memberAccessValues = await ResolveMemberAccessExpressions(findVarNMethodCall.memberAccesses, resolver, token); // eg. "this.dateTime", " dateTime.TimeOfDay" if (expressionTree.Kind() == SyntaxKind.SimpleMemberAccessExpression && findVarNMethodCall.memberAccesses.Count == 1) { return(memberAccessValues[0]); } IList <JObject> identifierValues = await ResolveIdentifiers(findVarNMethodCall.identifiers, resolver, token); syntaxTree = findVarNMethodCall.ReplaceVars(syntaxTree, memberAccessValues, identifierValues, null); if (findVarNMethodCall.hasMethodCalls) { expressionTree = GetExpressionFromSyntaxTree(syntaxTree); findVarNMethodCall.VisitInternal(expressionTree); IList <JObject> methodValues = await ResolveMethodCalls(findVarNMethodCall.methodCall, findVarNMethodCall.memberAccessValues, resolver, token); syntaxTree = findVarNMethodCall.ReplaceVars(syntaxTree, null, null, methodValues); } expressionTree = GetExpressionFromSyntaxTree(syntaxTree); 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))); } }
private static async Task <IList <JObject> > ResolveMethodCalls(IEnumerable <InvocationExpressionSyntax> methodCalls, Dictionary <string, JObject> memberAccessValues, MemberReferenceResolver resolver, CancellationToken token) { var values = new List <JObject>(); foreach (InvocationExpressionSyntax methodCall in methodCalls) { JObject value = await resolver.Resolve(methodCall, memberAccessValues, token); if (value == null) { throw new ReturnAsErrorException($"Failed to resolve member access for {methodCall}", "ReferenceError"); } values.Add(value); } return(values); }
private static async Task <IList <JObject> > ResolveIdentifiers(IEnumerable <IdentifierNameSyntax> identifiers, MemberReferenceResolver resolver, CancellationToken token) { var values = new List <JObject>(); foreach (IdentifierNameSyntax var in identifiers) { JObject value = await resolver.Resolve(var.Identifier.Text, token); if (value == null) { throw new ReturnAsErrorException($"The name {var.Identifier.Text} does not exist in the current context", "ReferenceError"); } values.Add(value); } return(values); }
internal static async Task <JObject> CompileAndRunTheExpression(string expression, MemberReferenceResolver resolver, CancellationToken token) { expression = expression.Trim(); if (!expression.StartsWith('(')) { expression = "(" + expression + "\n)"; } SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(expression + @";", cancellationToken: token); 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(); findVarNMethodCall.VisitInternal(expressionTree); // this fails with `"a)"` // because the code becomes: return (a)); // and the returned expression from GetExpressionFromSyntaxTree is `a`! if (expressionTree.Kind() == SyntaxKind.IdentifierName || expressionTree.Kind() == SyntaxKind.ThisExpression) { string varName = expressionTree.ToString(); JObject value = await resolver.Resolve(varName, token); if (value == null) { throw new ReturnAsErrorException($"Cannot find member named '{varName}'.", "ReferenceError"); } return(value); } IList <JObject> memberAccessValues = await ResolveMemberAccessExpressions(findVarNMethodCall.memberAccesses, resolver, token); // eg. "this.dateTime", " dateTime.TimeOfDay" if (expressionTree.Kind() == SyntaxKind.SimpleMemberAccessExpression && findVarNMethodCall.memberAccesses.Count == 1) { return(memberAccessValues[0]); } IList <JObject> identifierValues = await ResolveIdentifiers(findVarNMethodCall.identifiers, resolver, token); syntaxTree = findVarNMethodCall.ReplaceVars(syntaxTree, memberAccessValues, identifierValues, null, null); if (findVarNMethodCall.hasMethodCalls) { expressionTree = syntaxTree.GetCompilationUnitRoot(token); findVarNMethodCall.VisitInternal(expressionTree); IList <JObject> methodValues = await ResolveMethodCalls(findVarNMethodCall.methodCall, findVarNMethodCall.memberAccessValues, resolver, token); syntaxTree = findVarNMethodCall.ReplaceVars(syntaxTree, null, null, methodValues, null); } // eg. "elements[0]" if (findVarNMethodCall.hasElementAccesses) { expressionTree = syntaxTree.GetCompilationUnitRoot(token); findVarNMethodCall.VisitInternal(expressionTree); IList <JObject> elementAccessValues = await ResolveElementAccess(findVarNMethodCall.elementAccess, findVarNMethodCall.memberAccessValues, resolver, token); syntaxTree = findVarNMethodCall.ReplaceVars(syntaxTree, null, null, null, elementAccessValues); } expressionTree = syntaxTree.GetCompilationUnitRoot(token); if (expressionTree == null) { throw new Exception($"BUG: Unable to evaluate {expression}, could not get expression from the syntax tree"); } try { 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 (CompilationErrorException cee) { throw new ReturnAsErrorException($"Cannot evaluate '{expression}': {cee.Message}", "CompilationError"); } catch (Exception ex) { throw new Exception($"Internal Error: Unable to run {expression}, error: {ex.Message}.", ex); } }
private static async Task <IList <JObject> > ResolveElementAccess(IEnumerable <ElementAccessExpressionSyntax> elementAccesses, Dictionary <string, JObject> memberAccessValues, MemberReferenceResolver resolver, CancellationToken token) { var values = new List <JObject>(); JObject index = null; foreach (ElementAccessExpressionSyntax elementAccess in elementAccesses.Reverse()) { index = await resolver.Resolve(elementAccess, memberAccessValues, index, token); if (index == null) { throw new ReturnAsErrorException($"Failed to resolve element access for {elementAccess}", "ReferenceError"); } } values.Add(index); return(values); }