void EnsureExpressionStatementsAreValid(AstNode rootNode) { foreach (var stmt in rootNode.DescendantsAndSelf.OfType <ExpressionStatement>()) { if (!IsValidInStatementExpression(stmt.Expression)) { // fetch ILFunction var function = stmt.Ancestors.SelectMany(a => a.Annotations.OfType <ILFunction>()).First(f => f.Parent == null); // if possible use C# 7.0 discard-assignment if (context.Settings.Discards && !ExpressionBuilder.HidesVariableWithName(function, "_")) { stmt.Expression = new AssignmentExpression( new IdentifierExpression("_"), // no ResolveResult stmt.Expression.Detach()); } else { // assign result to dummy variable var type = stmt.Expression.GetResolveResult().Type; var v = function.RegisterVariable( VariableKind.StackSlot, type, AssignVariableNames.GenerateVariableName(function, type, stmt.Expression.Annotations.OfType <ILInstruction>() .Where(AssignVariableNames.IsSupportedInstruction).FirstOrDefault(), mustResolveConflicts: true) ); stmt.Expression = new AssignmentExpression( new IdentifierExpression(v.Name).WithRR(new ILVariableResolveResult(v, v.Type)), stmt.Expression.Detach()); } } } }
protected internal override Statement VisitUsingInstruction(UsingInstruction inst) { var transformed = TransformToForeach(inst, out var resource); if (transformed != null) { return(transformed); } AstNode usingInit = resource; var var = inst.Variable; if (!inst.ResourceExpression.MatchLdNull() && !NullableType.GetUnderlyingType(var.Type).GetAllBaseTypes().Any(b => b.IsKnownType(KnownTypeCode.IDisposable))) { var.Kind = VariableKind.Local; var disposeType = exprBuilder.compilation.FindType(KnownTypeCode.IDisposable); var disposeVariable = currentFunction.RegisterVariable( VariableKind.Local, disposeType, AssignVariableNames.GenerateVariableName(currentFunction, disposeType) ); return(new BlockStatement { new ExpressionStatement(new AssignmentExpression(exprBuilder.ConvertVariable(var).Expression, resource.Detach())), new TryCatchStatement { TryBlock = ConvertAsBlock(inst.Body), FinallyBlock = new BlockStatement() { new ExpressionStatement(new AssignmentExpression(exprBuilder.ConvertVariable(disposeVariable).Expression, new AsExpression(exprBuilder.ConvertVariable(var).Expression, exprBuilder.ConvertType(disposeType)))), new IfElseStatement { Condition = new BinaryOperatorExpression(exprBuilder.ConvertVariable(disposeVariable), BinaryOperatorType.InEquality, new NullReferenceExpression()), TrueStatement = new ExpressionStatement(new InvocationExpression(new MemberReferenceExpression(exprBuilder.ConvertVariable(disposeVariable).Expression, "Dispose"))) } } }, }); } else { if (var.LoadCount > 0 || var.AddressCount > 0) { var type = settings.AnonymousTypes && var.Type.ContainsAnonymousType() ? new SimpleType("var") : exprBuilder.ConvertType(var.Type); var vds = new VariableDeclarationStatement(type, var.Name, resource); vds.Variables.Single().AddAnnotation(new ILVariableResolveResult(var, var.Type)); usingInit = vds; } return(new UsingStatement { ResourceAcquisition = usingInit, EmbeddedStatement = ConvertAsBlock(inst.Body) }); } }
void EnsureExpressionStatementsAreValid(AstNode rootNode) { foreach (var stmt in rootNode.DescendantsAndSelf.OfType <ExpressionStatement>()) { if (!IsValidInStatementExpression(stmt.Expression)) { // fetch ILFunction var function = stmt.Ancestors.SelectMany(a => a.Annotations.OfType <ILFunction>()).First(f => f.Parent == null); // assign result to dummy variable var type = stmt.Expression.GetResolveResult().Type; var v = function.RegisterVariable( VariableKind.StackSlot, type, AssignVariableNames.GenerateVariableName(function, type, stmt.Expression.Annotations.OfType <ILInstruction>().Where(AssignVariableNames.IsSupportedInstruction).FirstOrDefault()) ); stmt.Expression = new AssignmentExpression( new IdentifierExpression(v.Name).WithRR(new ILVariableResolveResult(v, v.Type)), stmt.Expression.Detach()); } } }
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); }
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); }