Statement TransformForeachOnArray(ForStatement forStatement) { if (!context.Settings.ForEachStatement) { return(null); } Match m = forOnArrayPattern.Match(forStatement); if (!m.Success) { return(null); } var itemVariable = m.Get <IdentifierExpression>("itemVariable").Single().GetILVariable(); var indexVariable = m.Get <IdentifierExpression>("indexVariable").Single().GetILVariable(); var arrayVariable = m.Get <IdentifierExpression>("arrayVariable").Single().GetILVariable(); var loopContainer = forStatement.Annotation <IL.BlockContainer>(); if (itemVariable == null || indexVariable == null || arrayVariable == null) { return(null); } if (!itemVariable.IsSingleDefinition || (itemVariable.CaptureScope != null && itemVariable.CaptureScope != loopContainer)) { return(null); } if (indexVariable.StoreCount != 2 || indexVariable.LoadCount != 3 || indexVariable.AddressCount != 0) { return(null); } var body = new BlockStatement(); foreach (var statement in m.Get <Statement>("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>("arrayVariable").Single().Detach(), EmbeddedStatement = body }; 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 forStatement.ReplaceWith(foreachStmt); return(foreachStmt); }
public static ForeachStatement WithILVariable(this ForeachStatement loop, ILVariable v) { loop.AddAnnotation(v); return(loop); }
Statement TransformToForeach(UsingInstruction inst, out Expression resource) { // Check if the using resource matches the GetEnumerator pattern. resource = exprBuilder.Translate(inst.ResourceExpression); var m = getEnumeratorPattern.Match(resource); // The using body must be a BlockContainer. if (!(inst.Body is BlockContainer container) || !m.Success) { return(null); } // The using-variable is the enumerator. var enumeratorVar = inst.Variable; // If there's another BlockContainer nested in this container and it only has one child block, unwrap it. // If there's an extra leave inside the block, extract it into optionalReturnAfterLoop. var loopContainer = UnwrapNestedContainerIfPossible(container, out var optionalReturnAfterLoop); // Detect whether we're dealing with a while loop with multiple embedded statements. var loop = DetectedLoop.DetectLoop(loopContainer); if (loop.Kind != LoopKind.While || !(loop.Body is Block body)) { return(null); } // The loop condition must be a call to enumerator.MoveNext() var condition = exprBuilder.TranslateCondition(loop.Conditions.Single()); var m2 = moveNextConditionPattern.Match(condition.Expression); if (!m2.Success) { return(null); } // Check enumerator variable references. var enumeratorVar2 = m2.Get <IdentifierExpression>("enumerator").Single().GetILVariable(); if (enumeratorVar2 != enumeratorVar) { return(null); } // Detect which foreach-variable transformation is necessary/possible. var transformation = DetectGetCurrentTransformation(container, body, enumeratorVar, condition.ILInstructions.Single(), out var singleGetter, out var foreachVariable); if (transformation == RequiredGetCurrentTransformation.NoForeach) { return(null); } // The existing foreach variable, if found, can only be used in the loop container. if (foreachVariable != null && !(foreachVariable.CaptureScope == null || foreachVariable.CaptureScope == loopContainer)) { return(null); } // Extract in-expression var collectionExpr = m.Get <Expression>("collection").Single(); // Special case: foreach (var item in this) is decompiled as foreach (var item in base) // but a base reference is not valid in this context. if (collectionExpr is BaseReferenceExpression) { collectionExpr = new ThisReferenceExpression().CopyAnnotationsFrom(collectionExpr); } // Handle explicit casts: // This is the case if an explicit type different from the collection-item-type was used. // For example: foreach (ClassA item in nonGenericEnumerable) var type = singleGetter.Method.ReturnType; ILInstruction instToReplace = singleGetter; switch (instToReplace.Parent) { case CastClass cc: type = cc.Type; instToReplace = cc; break; case UnboxAny ua: type = ua.Type; instToReplace = ua; break; } // Handle the required foreach-variable transformation: switch (transformation) { case RequiredGetCurrentTransformation.UseExistingVariable: foreachVariable.Type = type; foreachVariable.Kind = VariableKind.ForeachLocal; foreachVariable.Name = AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation <ILInstruction>(), foreachVariable); break; case RequiredGetCurrentTransformation.UninlineAndUseExistingVariable: // Unwrap stloc chain. var nestedStores = new Stack <ILVariable>(); var currentInst = instToReplace; // instToReplace is the innermost value of the stloc chain. while (currentInst.Parent is StLoc stloc) { // Exclude nested stores to foreachVariable // we'll insert one store at the beginning of the block. if (stloc.Variable != foreachVariable && stloc.Parent is StLoc) { nestedStores.Push(stloc.Variable); } currentInst = stloc; } // Rebuild the nested store instructions: ILInstruction reorderedStores = new LdLoc(foreachVariable); while (nestedStores.Count > 0) { reorderedStores = new StLoc(nestedStores.Pop(), reorderedStores); } currentInst.ReplaceWith(reorderedStores); body.Instructions.Insert(0, new StLoc(foreachVariable, instToReplace)); // Adjust variable type, kind and name. goto case RequiredGetCurrentTransformation.UseExistingVariable; case RequiredGetCurrentTransformation.IntroduceNewVariable: foreachVariable = currentFunction.RegisterVariable( VariableKind.ForeachLocal, type, AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation <ILInstruction>()) ); instToReplace.ReplaceWith(new LdLoc(foreachVariable)); body.Instructions.Insert(0, new StLoc(foreachVariable, instToReplace)); break; } // Convert the modified body to C# AST: var whileLoop = (WhileStatement)ConvertAsBlock(container).First(); BlockStatement foreachBody = (BlockStatement)whileLoop.EmbeddedStatement.Detach(); // Remove the first statement, as it is the foreachVariable = enumerator.Current; statement. foreachBody.Statements.First().Detach(); // Construct the foreach loop. var foreachStmt = new ForeachStatement { VariableType = settings.AnonymousTypes && foreachVariable.Type.ContainsAnonymousType() ? new SimpleType("var") : exprBuilder.ConvertType(foreachVariable.Type), VariableName = foreachVariable.Name, InExpression = collectionExpr.Detach(), EmbeddedStatement = foreachBody }; // Add the variable annotation for highlighting (TokenTextWriter expects it directly on the ForeachStatement). foreachStmt.AddAnnotation(new ILVariableResolveResult(foreachVariable, foreachVariable.Type)); // If there was an optional return statement, return it as well. if (optionalReturnAfterLoop != null) { return(new BlockStatement { Statements = { foreachStmt, optionalReturnAfterLoop.AcceptVisitor(this) } }); } return(foreachStmt); }
/// <summary> /// Adds an <see cref="ILVariable"/> to this foreach statement. /// </summary> public static ForeachStatement WithILVariable(this ForeachStatement loop, ILVariable v) { loop.AddAnnotation(new ILVariableResolveResult(v, v.Type)); return(loop); }
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); }
Statement TransformToForeach(UsingInstruction inst, out Expression resource) { resource = exprBuilder.Translate(inst.ResourceExpression); var m = getEnumeratorPattern.Match(resource); if (!(inst.Body is BlockContainer container) || !m.Success) { return(null); } var enumeratorVar = inst.Variable; var loopContainer = UnwrapNestedContainerIfPossible(container, out var optionalReturnAfterLoop); var loop = DetectedLoop.DetectLoop(loopContainer); if (loop.Kind != LoopKind.While || !(loop.Body is Block body)) { return(null); } var condition = exprBuilder.TranslateCondition(loop.Conditions.Single()); var m2 = moveNextConditionPattern.Match(condition.Expression); if (!m2.Success) { return(null); } var enumeratorVar2 = m2.Get <IdentifierExpression>("enumerator").Single().GetILVariable(); if (enumeratorVar2 != enumeratorVar || !BodyHasSingleGetCurrent(body, enumeratorVar, condition.ILInstructions.Single(), out var singleGetter, out var needsUninlining, out var itemVariable)) { return(null); } if (itemVariable != null && !(itemVariable.CaptureScope == null || itemVariable.CaptureScope == loopContainer)) { return(null); } var collectionExpr = m.Get <Expression>("collection").Single(); if (collectionExpr is BaseReferenceExpression) { collectionExpr = new ThisReferenceExpression().CopyAnnotationsFrom(collectionExpr); } var type = singleGetter.Method.ReturnType; ILInstruction instToReplace = singleGetter; switch (instToReplace.Parent) { case CastClass cc: type = cc.Type; instToReplace = cc; break; case UnboxAny ua: type = ua.Type; instToReplace = ua; break; } if (needsUninlining) { itemVariable = currentFunction.RegisterVariable( VariableKind.ForeachLocal, type, AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation <ILInstruction>()) ); instToReplace.ReplaceWith(new LdLoc(itemVariable)); body.Instructions.Insert(0, new StLoc(itemVariable, instToReplace)); } else { if (itemVariable.StoreCount != 1) { return(null); } itemVariable.Type = type; itemVariable.Kind = VariableKind.ForeachLocal; itemVariable.Name = AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation <ILInstruction>(), itemVariable); } var whileLoop = (WhileStatement)ConvertAsBlock(container).First(); BlockStatement foreachBody = (BlockStatement)whileLoop.EmbeddedStatement.Detach(); foreachBody.Statements.First().Detach(); var foreachStmt = new ForeachStatement { VariableType = settings.AnonymousTypes && itemVariable.Type.ContainsAnonymousType() ? new SimpleType("var") : exprBuilder.ConvertType(itemVariable.Type), VariableName = itemVariable.Name, InExpression = collectionExpr.Detach(), EmbeddedStatement = foreachBody }; foreachStmt.AddAnnotation(new ILVariableResolveResult(itemVariable, itemVariable.Type)); if (optionalReturnAfterLoop != null) { return(new BlockStatement { Statements = { foreachStmt, optionalReturnAfterLoop.AcceptVisitor(this) } }); } return(foreachStmt); }