public InferenceDisassembler(VMConstants constants, KoiStream koiStream) { Constants = constants ?? throw new ArgumentNullException(nameof(constants)); KoiStream = koiStream ?? throw new ArgumentNullException(nameof(koiStream)); _decoder = new InstructionDecoder(constants, KoiStream.Contents.CreateReader()); _processor = new InstructionProcessor(this); _cfgBuilder = new ControlFlowGraphBuilder(); }
public static ControlFlowGraph CreateControlFlowGraph(MethodDefinition method) { if (null == method) { throw new ArgumentNullException("method"); } ControlFlowGraphBuilder builder = new ControlFlowGraphBuilder(method); return(builder.BuildGraph()); }
private BlockStatement DecompileMethodPartially(MethodBody body, out DecompilationContext context, bool needDecompiledMember = false) { context = null; if (this.IsCachingEnabled && this.cacheService.IsDecompiledMemberInCache(body.Method, this.language, this.renameInvalidMembers)) { CachedDecompiledMember cachedDecompiledMember = this.cacheService.GetDecompiledMemberFromCache(body.Method, this.language, this.renameInvalidMembers); return(cachedDecompiledMember.Member.Statement as BlockStatement); } //Performance improvement ControlFlowGraph cfg = new ControlFlowGraphBuilder(body.Method).CreateGraph(); if (cfg.Blocks.Length > 2) { return(null); } BlockStatement block; try { DecompilationPipeline pipeline; DecompilationContext decompilationContext = new DecompilationContext(new MethodSpecificContext(body), this.typeContext ?? new TypeSpecificContext(body.Method.DeclaringType)); if (!needDecompiledMember) { decompilationContext.MethodContext.EnableEventAnalysis = false; } pipeline = new DecompilationPipeline(BaseLanguage.IntermediateRepresenationPipeline.Steps, decompilationContext); context = pipeline.Run(body); block = pipeline.Body; } catch (Exception ex) { this.ExceptionsWhileDecompiling.Add(body.Method); block = new BlockStatement(); block.AddStatement(new ExceptionStatement(ex, body.Method)); OnExceptionThrown(ex); } return(block); }
private BlockStatement GetStatements(MethodBody body) { //Performance improvement ControlFlowGraph cfg = new ControlFlowGraphBuilder(body.Method).CreateGraph(); if (cfg.Blocks.Length > 2) { return(null); } DecompilationPipeline pipeline = new DecompilationPipeline(BaseLanguage.IntermediateRepresenationPipeline.Steps, new DecompilationContext(new MethodSpecificContext(body) { EnableEventAnalysis = false }, new TypeSpecificContext(body.Method.DeclaringType))); pipeline.Run(body); return(pipeline.Body); }
public static void RunExpressionTest(InstructionSpan instructions, Expression expectedExpression) { // Add a `pop` to the end to make it a full statement instructions = BuildSpan( instructions.First.Offset, Enumerable.Concat(instructions, new[] { Instruction.Create(OpCodes.Pop) })); var controlFlowGraph = ControlFlowGraphBuilder.Build(instructions.ToList(), Array.Empty <ExceptionHandler>()); var actualGraph = SyntaxGraphBuilder.Create(controlFlowGraph, new MethodVariables()); // Extract the expression Assert.Equal(1, actualGraph.Nodes.Count); var node = actualGraph.Nodes.First(); Assert.Equal(1, node.Statements.Count); var discardStatement = Assert.IsType <DiscardStatement>(node.Statements.Single()); Assert.Equal(expectedExpression, discardStatement.Value); }
private void Transform(FunctionDeclarationSyntax function, LiveVariables liveness) { var graph = function.ControlFlow; var builder = new ControlFlowGraphBuilder(graph.VariableDeclarations); foreach (var block in graph.BasicBlocks) { var blockBuilder = builder.NewBlock(); if (block.Number != blockBuilder.BlockNumber) { throw new Exception("New block number does not match old block number"); } foreach (var statement in block.ExpressionStatements) { // Copy the existing statement blockBuilder.Add(statement); var before = liveness.Before(statement); var after = liveness.After(statement); // dead = live before and not live after var dead = ((BitArray)after.Clone()).Not().And(before); foreach (var variableNumber in dead.TrueIndexes()) { var variable = graph.VariableDeclarations[variableNumber]; if (variable.Type is ObjectType type && type.IsOwned) { // The delete happens after the last statement var span = new TextSpan(statement.Span.End, 0); blockBuilder.AddDelete(new VariableReference(variableNumber, span), type, span); } } } // TODO what about if there is a variable delete after the terminator? blockBuilder.Add(block.Terminator); } function.ControlFlow = builder.Build(); }
private static ConstructorDeclaration BuildDefaultConstructor( ClassDeclarationSyntax classDeclaration) { var className = classDeclaration.FullName; var constructorName = className.Qualify(SpecialName.New); var selfType = ((Metatype)classDeclaration.Type.Resolved()).Instance; var selfName = className.Qualify(SpecialName.Self); var selfParameter = new Parameter(true, selfName, selfType); var parameters = selfParameter.Yield().ToFixedList(); var constructorType = new FunctionType(selfType.Yield(), selfType); var graph = new ControlFlowGraphBuilder(); graph.AddParameter(true, selfType, SpecialName.Self); var block = graph.NewBlock(); block.AddReturn(); var defaultConstructor = new ConstructorDeclaration(constructorName, constructorType, parameters, selfType, graph.Build()); return(defaultConstructor); }
internal static ReachabilityAnalysis Create(Statement statement, Func<AstNode, CancellationToken, ResolveResult> resolver, CSharpTypeResolveContext typeResolveContext, CancellationToken cancellationToken) { var cfgBuilder = new ControlFlowGraphBuilder(); var cfg = cfgBuilder.BuildControlFlowGraph(statement, resolver, typeResolveContext, cancellationToken); return Create(cfg, cancellationToken); }
public static ReachabilityAnalysis Create(Statement statement, CSharpAstResolver resolver = null, CancellationToken cancellationToken = default(CancellationToken)) { var cfgBuilder = new ControlFlowGraphBuilder(); var cfg = cfgBuilder.BuildControlFlowGraph(statement, resolver, cancellationToken); return Create(cfg, cancellationToken); }
public static void ValidateIOperations(Func <Compilation> createCompilation) { if (!EnableVerifyIOperation) { return; } var compilation = createCompilation(); var roots = ArrayBuilder <IOperation> .GetInstance(); var stopWatch = new Stopwatch(); if (!System.Diagnostics.Debugger.IsAttached) { stopWatch.Start(); } void checkTimeout() { const int timeout = 15000; Assert.False(stopWatch.ElapsedMilliseconds > timeout, $"ValidateIOperations took too long: {stopWatch.ElapsedMilliseconds} ms"); } foreach (var tree in compilation.SyntaxTrees) { var semanticModel = compilation.GetSemanticModel(tree); var root = tree.GetRoot(); foreach (var node in root.DescendantNodesAndSelf()) { checkTimeout(); var operation = semanticModel.GetOperation(node); if (operation != null) { // Make sure IOperation returned by GetOperation(syntaxnode) will have same syntaxnode as the given syntaxnode(IOperation.Syntax == syntaxnode). Assert.True(node == operation.Syntax, $"Expected : {node} - Actual : {operation.Syntax}"); Assert.True(operation.Type == null || !operation.MustHaveNullType(), $"Unexpected non-null type: {operation.Type}"); Assert.Same(semanticModel, operation.SemanticModel); Assert.NotSame(semanticModel, ((Operation)operation).OwningSemanticModel); Assert.NotNull(((Operation)operation).OwningSemanticModel); Assert.Same(semanticModel, ((Operation)operation).OwningSemanticModel.ContainingModelOrSelf); Assert.Same(semanticModel, semanticModel.ContainingModelOrSelf); if (operation.Parent == null) { roots.Add(operation); } } } } var explicitNodeMap = new Dictionary <SyntaxNode, IOperation>(); var visitor = TestOperationVisitor.Singleton; foreach (var root in roots) { foreach (var operation in root.DescendantsAndSelf()) { checkTimeout(); if (!operation.IsImplicit) { try { explicitNodeMap.Add(operation.Syntax, operation); } catch (ArgumentException) { Assert.False(true, $"Duplicate explicit node for syntax ({operation.Syntax.RawKind}): {operation.Syntax.ToString()}"); } } visitor.Visit(operation); } stopWatch.Stop(); checkControlFlowGraph(root); stopWatch.Start(); } roots.Free(); stopWatch.Stop(); return; void checkControlFlowGraph(IOperation root) { switch (root) { case IBlockOperation blockOperation: // https://github.com/dotnet/roslyn/issues/27593 tracks adding ControlFlowGraph support in script code. if (blockOperation.Syntax.SyntaxTree.Options.Kind != SourceCodeKind.Script) { ControlFlowGraphVerifier.GetFlowGraph(compilation, ControlFlowGraphBuilder.Create(blockOperation)); } break; case IMethodBodyOperation methodBody: case IConstructorBodyOperation constructorBody: case IFieldInitializerOperation fieldInitializerOperation: case IPropertyInitializerOperation propertyInitializerOperation: ControlFlowGraphVerifier.GetFlowGraph(compilation, ControlFlowGraphBuilder.Create(root)); break; case IParameterInitializerOperation parameterInitializerOperation: // https://github.com/dotnet/roslyn/issues/27594 tracks adding support for getting ControlFlowGraph for parameter initializers for local functions. if ((parameterInitializerOperation.Parameter.ContainingSymbol as IMethodSymbol)?.MethodKind != MethodKind.LocalFunction) { ControlFlowGraphVerifier.GetFlowGraph(compilation, ControlFlowGraphBuilder.Create(root)); } break; } } }
private static async Task <int> Execute(string assemblyPath, string typeName, string memberName, string outFile, string format) { var disassembler = new DisassemblerSession(); var disassembly = await disassembler.LoadAsync(assemblyPath); var type = disassembly.FindType(typeName); if (type == null) { return(Error($"could not find type: {typeName}")); } var member = type.Members.FirstOrDefault(t => t.Name.Equals(memberName)); if (member == null) { return(Error($"could not find member: {memberName} in type {typeName}")); } ControlFlowGraph graph = null; if (member.MemberType == MemberType.Method) { graph = ControlFlowGraphBuilder.Build(((MethodDefinition)member.Definition).Body); } else { return(Error($"Member type not supported: {member.MemberType}")); } if (!string.IsNullOrEmpty(outFile)) { format = string.IsNullOrEmpty(format) ? "dot" : format; if (!Formats.TryGetValue(format, out var handler)) { return(Error($"Unknown format: {format}")); } await handler(graph, outFile); } else { Console.WriteLine($"Control flow graph for {typeName}.{memberName}"); foreach (var node in graph.Nodes) { Console.WriteLine(); Console.WriteLine(node.ToString()); } // Write Exception handlers foreach (var handler in graph.ExceptionHandlers) { Console.WriteLine(); Console.WriteLine(" .try {"); Console.WriteLine($" IL_{handler.TryStart.Offset:X4} -> IL_{handler.TryEnd.Offset:X4}"); Console.WriteLine(" }"); Console.WriteLine($" {FormatHandlerType(handler)} {{"); if (handler.HandlerType == ExceptionHandlerType.Filter) { Console.WriteLine($" IL_{handler.FilterStart.Offset:X4} -> IL_{handler.HandlerStart.Offset:X4}"); Console.WriteLine(" }"); Console.WriteLine(" .catch {"); } Console.WriteLine($" IL_{handler.HandlerStart.Offset:X4} -> IL_{handler.HandlerEnd.Offset:X4}"); Console.WriteLine(" }"); } } Console.WriteLine(); return(0); }
void AddIssueFor(AstNode currentFunction) { if (IsAsync(currentFunction)) { return; } //Only suggest modifying functions that return void, Task or Task<T>. IType returnType = GetReturnType(ctx, currentFunction); if (returnType == null) { return; } bool isVoid = false; IType resultType = null; switch (returnType.FullName) { case "System.Void": isVoid = true; break; case "System.Threading.Tasks.Task": resultType = returnType.IsParameterized ? returnType.TypeArguments.FirstOrDefault() : null; break; default: return; } var functionBody = currentFunction.GetChildByRole(Roles.Body); var statements = GetStatements(functionBody).ToList(); var returnStatements = statements.OfType <ReturnStatement>().ToList(); var invocations = new List <InvocationExpression>(); var nextInChain = new Dictionary <InvocationExpression, InvocationExpression>(); foreach (var invocation in currentFunction.Descendants.OfType <InvocationExpression>()) { if (invocation.Arguments.Count != 1) { continue; } var lambdaOrDelegate = invocation.Arguments.Single(); Statement lambdaBody; if (lambdaOrDelegate is LambdaExpression) { lambdaBody = lambdaOrDelegate.GetChildByRole(LambdaExpression.BodyRole) as BlockStatement; if (lambdaBody == null) { continue; } } else if (lambdaOrDelegate is AnonymousMethodExpression) { lambdaBody = lambdaOrDelegate.GetChildByRole(Roles.Body); } else { continue; } var resolveResult = ctx.Resolve(invocation) as MemberResolveResult; if (resolveResult == null) { continue; } if (resolveResult.Member.FullName != "System.Threading.Tasks.Task.ContinueWith") { continue; } var parentExpression = invocation.Parent as Expression; if (parentExpression != null) { var mreParent = parentExpression as MemberReferenceExpression; if (mreParent == null || mreParent.MemberName != "ContinueWith") { continue; } var parentInvocation = mreParent.Parent as InvocationExpression; if (parentInvocation == null || parentInvocation.Arguments.Count != 1) { continue; } nextInChain[invocation] = parentInvocation; } invocations.Add(invocation); } if (isVoid && invocations.Count == 0) { //Prevent functions like void Foo() {} from being accepted return; } string taskCompletionSourceIdentifier = null; InvocationExpression returnedContinuation = null; if (isVoid) { if (returnStatements.Any()) { return; } } else if (!isVoid) { if (returnStatements.Count() != 1) { return; } var returnStatement = returnStatements.Single(); if (functionBody.Statements.Last() != returnStatement) { return; } var match = ReturnTaskCompletionSourcePattern.Match(returnStatement); if (match.Success) { var taskCompletionSource = match.Get <IdentifierExpression>("target").Single(); var taskCompletionSourceResolveResult = ctx.Resolve(taskCompletionSource); //Make sure the TaskCompletionSource is a local variable if (!(taskCompletionSourceResolveResult is LocalResolveResult) || taskCompletionSourceResolveResult.Type.FullName != "System.Threading.Tasks.TaskCompletionSource") { return; } taskCompletionSourceIdentifier = taskCompletionSource.Identifier; var cfgBuilder = new ControlFlowGraphBuilder(); var cachedControlFlowGraphs = new Dictionary <BlockStatement, IList <ControlFlowNode> >(); //Make sure there are no unsupported uses of the task completion source foreach (var identifier in functionBody.Descendants.OfType <Identifier>()) { if (identifier.Name != taskCompletionSourceIdentifier) { continue; } var statement = identifier.GetParent <Statement>(); var variableStatement = statement as VariableDeclarationStatement; if (variableStatement != null) { if (functionBody.Statements.First() != variableStatement || variableStatement.Variables.Count != 1) { //This may actually be valid, but it would add even more complexity to this action return; } var initializer = variableStatement.Variables.First().Initializer as ObjectCreateExpression; if (initializer == null || initializer.Arguments.Count != 0 || !initializer.Initializer.IsNull) { return; } var constructedType = ctx.ResolveType(initializer.Type); if (constructedType.FullName != "System.Threading.Tasks.TaskCompletionSource") { return; } continue; } if (statement == returnStatement) { continue; } if (identifier.Parent is MemberReferenceExpression) { //Right side of the member. //We don't care about this case since it's not a reference to the variable. continue; } //The method's taskCompletionSource can only be used on the left side of a member //reference expression (specifically tcs.SetResult). var identifierExpressionParent = identifier.Parent as IdentifierExpression; if (identifierExpressionParent == null) { return; } var memberReferenceExpression = identifierExpressionParent.Parent as MemberReferenceExpression; if (memberReferenceExpression == null) { return; } if (memberReferenceExpression.MemberName != "SetResult") { //Aside from the final return statement, the only member of task completion source //that can be used is SetResult. //Perhaps future versions could also include SetException and SetCancelled. return; } //We found a SetResult -- we will now find out if it is in a proper context AstNode node = memberReferenceExpression; for (;;) { node = node.Parent; if (node == null) { //Abort since this is unexpected (it should never happen) return; } if (node is MethodDeclaration) { //Ok -- tcs.SetResult is in method declaration break; } if (node is LambdaExpression || node is AnonymousMethodExpression) { //It's time to verify if the lambda is supported var lambdaParent = node.Parent as InvocationExpression; if (lambdaParent == null || !invocations.Contains(lambdaParent)) { return; } break; } } var containingContinueWith = node.Parent as InvocationExpression; if (containingContinueWith != null) { if (nextInChain.ContainsKey(containingContinueWith)) { //Unsupported: ContinueWith has a SetResult //but it's not the last in the chain return; } } var containingFunctionBlock = node is LambdaExpression ? (BlockStatement)node.GetChildByRole(LambdaExpression.BodyRole) : node.GetChildByRole(Roles.Body); //Finally, tcs.SetResult must be at the end of its method IList <ControlFlowNode> nodes; if (!cachedControlFlowGraphs.TryGetValue(containingFunctionBlock, out nodes)) { nodes = cfgBuilder.BuildControlFlowGraph(containingFunctionBlock, ctx.CancellationToken); cachedControlFlowGraphs[containingFunctionBlock] = nodes; } var setResultNode = nodes.FirstOrDefault(candidateNode => candidateNode.PreviousStatement == statement); if (setResultNode != null && HasReachableNonReturnNodes(setResultNode)) { //The only allowed outgoing nodes are return statements return; } } } else { //Not TaskCompletionSource-based //Perhaps it is return Task.ContinueWith(foo); if (!invocations.Any()) { return; } var outerMostInvocations = new List <InvocationExpression>(); InvocationExpression currentInvocation = invocations.First(); do { outerMostInvocations.Add(currentInvocation); } while (nextInChain.TryGetValue(currentInvocation, out currentInvocation)); var lastInvocation = outerMostInvocations.Last(); if (returnStatement.Expression != lastInvocation) { return; } //Found return <1>.ContinueWith(<2>); returnedContinuation = lastInvocation; } } //We do not support "return expr" in continuations //The only exception is when the outer method returns that continuation. invocations.RemoveAll(invocation => invocation != returnedContinuation && invocation.Arguments.First().Children.OfType <Statement>().First().DescendantNodesAndSelf(node => node is Statement).OfType <ReturnStatement>().Any(returnStatement => !returnStatement.Expression.IsNull)); AddIssue(new CodeIssue(GetFunctionToken(currentFunction), ctx.TranslateString("Function can be converted to C# 5-style async function"), ctx.TranslateString("Convert to C# 5-style async function"), script => { AddOriginalNodeAnnotations(currentFunction); var newFunction = currentFunction.Clone(); RemoveOriginalNodeAnnotations(currentFunction); //Set async var lambda = newFunction as LambdaExpression; if (lambda != null) { lambda.IsAsync = true; } var anonymousMethod = newFunction as AnonymousMethodExpression; if (anonymousMethod != null) { anonymousMethod.IsAsync = true; } var methodDeclaration = newFunction as MethodDeclaration; if (methodDeclaration != null) { methodDeclaration.Modifiers |= Modifiers.Async; } TransformBody(invocations, isVoid, resultType != null, returnedContinuation, taskCompletionSourceIdentifier, newFunction.GetChildByRole(Roles.Body)); script.Replace(currentFunction, newFunction); })); }
private static async Task <int> Execute(string assemblyPath, string typeName, string memberName) { var disassembler = new DisassemblerSession(); var disassembly = await disassembler.LoadAsync(assemblyPath); var type = disassembly.FindType(typeName); if (type == null) { return(Error($"could not find type: {typeName}")); } var member = type.Members.FirstOrDefault(t => t.Name.Equals(memberName)); if (member == null) { return(Error($"could not find member: {memberName} in type {typeName}")); } ControlFlowGraph graph = null; MethodDefinition method = null; if (member.MemberType == MemberType.Method) { method = (MethodDefinition)member.Definition; graph = ControlFlowGraphBuilder.Build(method.Body); } else { return(Error($"Member type not supported: {member.MemberType}")); } var syntax = SyntaxGraphBuilder.Create(graph, method); var arguments = string.Join(", ", method.Parameters.Select(p => $"{p.ParameterType.FullName} {p.Name}")); Console.WriteLine($"Syntax analysis for {typeName}.{memberName}({arguments})"); if (method.Body.Variables.Any()) { Console.WriteLine(); foreach (var local in method.Body.Variables) { Console.WriteLine($" .local {local.VariableType.FullName} _{local.Index}"); } } foreach (var node in syntax.Nodes) { Console.WriteLine(); Console.WriteLine($" {node.DisplayName} : {{"); foreach (var statement in node.Statements) { Console.WriteLine($" {statement}"); } Console.WriteLine($" }}{FormatLinks(node)}"); } Console.WriteLine(); return(0); }
public static void RunControlFlowTest(InstructionSpan instructions, ControlFlowGraph expectedGraph) { var actualGraph = ControlFlowGraphBuilder.Build(instructions.ToList(), Array.Empty <ExceptionHandler>()); Assert.Equal(expectedGraph, actualGraph, TestControlFlowGraphComparer.Instance); }