public void Run(AstNode node) { Run(node, null); // Declare all the variables at the end, after all the logic has run. // This is done so that definite assignment analysis can work on a single representation and doesn't have to be updated // when we change the AST. foreach (var v in variablesToDeclare) { if (v.ReplacedAssignment == null) { BlockStatement block = (BlockStatement)v.InsertionPoint.Parent; var decl = new VariableDeclarationStatement(v.ILVariable != null && v.ILVariable.IsParameter ? TextTokenKind.Parameter : TextTokenKind.Local, (AstType)v.Type.Clone(), v.Name); if (v.ILVariable != null) { decl.Variables.Single().AddAnnotation(v.ILVariable); } block.Statements.InsertBefore( v.InsertionPoint, decl); } } // First do all the insertions, then do all the replacements. This is necessary because a replacement might remove our reference point from the AST. foreach (var v in variablesToDeclare) { if (v.ReplacedAssignment != null) { // We clone the right expression so that it doesn't get removed from the old ExpressionStatement, // which might be still in use by the definite assignment graph. VariableInitializer initializer = new VariableInitializer(v.ILVariable != null && v.ILVariable.IsParameter ? TextTokenKind.Parameter : TextTokenKind.Local, v.Name, v.ReplacedAssignment.Right.Detach()).CopyAnnotationsFrom(v.ReplacedAssignment).WithAnnotation(v.ILVariable); VariableDeclarationStatement varDecl = new VariableDeclarationStatement { Type = (AstType)v.Type.Clone(), Variables = { initializer } }; ExpressionStatement es = v.ReplacedAssignment.Parent as ExpressionStatement; if (es != null) { // Note: if this crashes with 'Cannot replace the root node', check whether two variables were assigned the same name es.ReplaceWith(varDecl.CopyAnnotationsFrom(es)); varDecl.AddAnnotation(es.GetAllRecursiveILRanges()); } else { varDecl.AddAnnotation(v.ReplacedAssignment.GetAllRecursiveILRanges()); v.ReplacedAssignment.ReplaceWith(varDecl); } } } variablesToDeclare.Clear(); }
public void Run(AstNode node) { Run(node, null); // Declare all the variables at the end, after all the logic has run. // This is done so that definite assignment analysis can work on a single representation and doesn't have to be updated // when we change the AST. foreach (var v in variablesToDeclare) { if (v.ReplacedAssignment == null) { BlockStatement block = (BlockStatement)v.InsertionPoint.Parent; block.Statements.InsertBefore( v.InsertionPoint, new VariableDeclarationStatement((AstType)v.Type.Clone(), v.Name)); } } // First do all the insertions, then do all the replacements. This is necessary because a replacement might remove our reference point from the AST. foreach (var v in variablesToDeclare) { if (v.ReplacedAssignment != null) { // We clone the right expression so that it doesn't get removed from the old ExpressionStatement, // which might be still in use by the definite assignment graph. VariableDeclarationStatement varDecl = new VariableDeclarationStatement { Type = (AstType)v.Type.Clone(), Variables = { new VariableInitializer(v.Name, v.ReplacedAssignment.Right.Detach()).CopyAnnotationsFrom(v.ReplacedAssignment) } }; ExpressionStatement es = v.ReplacedAssignment.Parent as ExpressionStatement; if (es != null) { es.ReplaceWith(varDecl.CopyAnnotationsFrom(es)); } else { v.ReplacedAssignment.ReplaceWith(varDecl); } } } variablesToDeclare = null; }
Statement TransformForeachOnMultiDimArray(ExpressionStatement expressionStatement) { if (!context.Settings.ForEachStatement) { return(null); } Match m; Statement stmt = expressionStatement; IL.ILVariable collection = null; IL.ILVariable[] upperBounds = null; List <Statement> statementsToDelete = new List <Statement>(); int i = 0; // first we look for all the upper bound initializations do { m = variableAssignUpperBoundPattern.Match(stmt); if (!m.Success) { break; } if (upperBounds == null) { collection = m.Get <IdentifierExpression>("collection").Single().GetILVariable(); if (!(collection?.Type is Decompiler.TypeSystem.ArrayType arrayType)) { break; } upperBounds = new IL.ILVariable[arrayType.Dimensions]; } else { statementsToDelete.Add(stmt); } var nextCollection = m.Get <IdentifierExpression>("collection").Single().GetILVariable(); if (nextCollection != collection) { break; } if (!int.TryParse(m.Get <PrimitiveExpression>("index").Single().Value?.ToString() ?? "", out int index) || index != i) { break; } upperBounds[i] = m.Get <IdentifierExpression>("variable").Single().GetILVariable(); stmt = stmt.GetNextStatement(); i++; } while (stmt != null && i < upperBounds.Length); if (upperBounds?.LastOrDefault() == null || collection == null) { return(null); } if (!MatchForeachOnMultiDimArray(upperBounds, collection, stmt, out var foreachVariable, out var statements, out var lowerBounds)) { return(null); } statementsToDelete.Add(stmt); statementsToDelete.Add(stmt.GetNextStatement()); var itemVariable = foreachVariable.GetILVariable(); if (itemVariable == null || !itemVariable.IsSingleDefinition || !upperBounds.All(ub => ub.IsSingleDefinition && ub.LoadCount == 1) || !lowerBounds.All(lb => lb.StoreCount == 2 && lb.LoadCount == 3 && lb.AddressCount == 0)) { return(null); } var body = new BlockStatement(); foreach (var statement in statements) { body.Statements.Add(statement.Detach()); } var foreachStmt = new ForeachStatement { VariableType = context.Settings.AnonymousTypes && itemVariable.Type.ContainsAnonymousType() ? new SimpleType("var") : context.TypeSystemAstBuilder.ConvertType(itemVariable.Type), VariableName = itemVariable.Name, InExpression = m.Get <IdentifierExpression>("collection").Single().Detach(), EmbeddedStatement = body }; foreach (var statement in statementsToDelete) { statement.Detach(); } //foreachStmt.CopyAnnotationsFrom(forStatement); itemVariable.Kind = IL.VariableKind.ForeachLocal; // Add the variable annotation for highlighting (TokenTextWriter expects it directly on the ForeachStatement). foreachStmt.AddAnnotation(new ILVariableResolveResult(itemVariable, itemVariable.Type)); // TODO : add ForeachAnnotation expressionStatement.ReplaceWith(foreachStmt); return(foreachStmt); }