mirror of https://github.com/icsharpcode/ILSpy.git
18 changed files with 0 additions and 10193 deletions
@ -1,474 +0,0 @@
@@ -1,474 +0,0 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.Linq; |
||||
using ICSharpCode.Decompiler; |
||||
using ICSharpCode.Decompiler.IL; |
||||
using ICSharpCode.NRefactory.CSharp; |
||||
using ICSharpCode.NRefactory.PatternMatching; |
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
using Mono.Cecil; |
||||
|
||||
namespace ICSharpCode.Decompiler.CSharp.Transforms |
||||
{ |
||||
/// <summary>
|
||||
/// Converts "new Action(obj, ldftn(func))" into "new Action(obj.func)".
|
||||
/// For anonymous methods, creates an AnonymousMethodExpression.
|
||||
/// Also gets rid of any "Display Classes" left over after inlining an anonymous method.
|
||||
/// </summary>
|
||||
public class DelegateConstructionOld : ContextTrackingVisitor<object>, IAstTransform |
||||
{ |
||||
internal sealed class Annotation |
||||
{ |
||||
/// <summary>
|
||||
/// ldftn or ldvirtftn?
|
||||
/// </summary>
|
||||
public readonly bool IsVirtual; |
||||
|
||||
public readonly Expression InvocationTarget; |
||||
public readonly string MethodName; |
||||
|
||||
public Annotation(bool isVirtual, Expression invocationTarget, string methodName) |
||||
{ |
||||
this.IsVirtual = isVirtual; |
||||
this.InvocationTarget = invocationTarget; |
||||
this.MethodName = methodName; |
||||
} |
||||
} |
||||
|
||||
TransformContext context; |
||||
List<string> currentlyUsedVariableNames = new List<string>(); |
||||
int nextLocalVariableIndex; |
||||
|
||||
public void Run(AstNode rootNode, TransformContext context) |
||||
{ |
||||
this.context = context; |
||||
base.Initialize(context); |
||||
rootNode.AcceptVisitor(this); |
||||
} |
||||
|
||||
public override object VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression) |
||||
{ |
||||
Annotation annotation = objectCreateExpression.Annotation<Annotation>(); |
||||
IMethod method = objectCreateExpression.GetSymbol() as IMethod; |
||||
if (annotation != null && method != null) { |
||||
if (HandleAnonymousMethod(objectCreateExpression, annotation.InvocationTarget, method)) |
||||
return null; |
||||
} |
||||
return base.VisitObjectCreateExpression(objectCreateExpression); |
||||
} |
||||
|
||||
internal static bool IsAnonymousMethod(ITypeDefinition decompiledTypeDefinition, IMethod method) |
||||
{ |
||||
if (method == null || !(method.HasGeneratedName() || method.Name.Contains("$"))) |
||||
return false; |
||||
if (!(method.IsCompilerGenerated() || IsPotentialClosure(decompiledTypeDefinition, method.DeclaringTypeDefinition))) |
||||
return false; |
||||
return true; |
||||
} |
||||
|
||||
bool HandleAnonymousMethod(ObjectCreateExpression objectCreateExpression, Expression target, IMethod method) |
||||
{ |
||||
if (!context.Settings.AnonymousMethods) |
||||
return false; // anonymous method decompilation is disabled
|
||||
if (target != null && !(target is TypeReferenceExpression || target is IdentifierExpression || target is ThisReferenceExpression || target is NullReferenceExpression)) |
||||
return false; // don't copy arbitrary expressions, deal with identifiers only
|
||||
if (!IsAnonymousMethod(context.DecompiledTypeDefinition, method)) |
||||
return false; |
||||
|
||||
// Create AnonymousMethodExpression and prepare parameters
|
||||
AnonymousMethodExpression ame = new AnonymousMethodExpression(); |
||||
ame.CopyAnnotationsFrom(objectCreateExpression); // copy ILRanges etc.
|
||||
ame.RemoveAnnotations<MethodReference>(); // remove reference to delegate ctor
|
||||
ame.AddAnnotation(method); // add reference to anonymous method
|
||||
ame.Parameters.AddRange(MakeParameters(method)); |
||||
ame.HasParameterList = true; |
||||
|
||||
// rename variables so that they don't conflict with the parameters:
|
||||
foreach (ParameterDeclaration pd in ame.Parameters) { |
||||
EnsureVariableNameIsAvailable(objectCreateExpression, pd.Name); |
||||
} |
||||
|
||||
// Decompile the anonymous method:
|
||||
var body = DecompileBody(method); |
||||
|
||||
bool isLambda = false; |
||||
if (ame.Parameters.All(p => p.ParameterModifier == ParameterModifier.None)) { |
||||
isLambda = (body.Statements.Count == 1 && body.Statements.Single() is ReturnStatement); |
||||
} |
||||
// Remove the parameter list from an AnonymousMethodExpression if the original method had no names,
|
||||
// and the parameters are not used in the method body
|
||||
if (!isLambda && method.Parameters.All(p => string.IsNullOrEmpty(p.Name))) { |
||||
var parameterReferencingIdentifiers = |
||||
from ident in body.Descendants.OfType<IdentifierExpression>() |
||||
let v = ident.Annotation<ILVariable>() |
||||
where v != null && v.Kind == VariableKind.Parameter |
||||
select ident; |
||||
if (!parameterReferencingIdentifiers.Any()) { |
||||
ame.Parameters.Clear(); |
||||
ame.HasParameterList = false; |
||||
} |
||||
} |
||||
|
||||
// Replace all occurrences of 'this' in the method body with the delegate's target:
|
||||
foreach (AstNode node in body.Descendants) { |
||||
if (node is ThisReferenceExpression) |
||||
node.ReplaceWith(target.Clone()); |
||||
} |
||||
Expression replacement; |
||||
if (isLambda) { |
||||
LambdaExpression lambda = new LambdaExpression(); |
||||
lambda.CopyAnnotationsFrom(ame); |
||||
ame.Parameters.MoveTo(lambda.Parameters); |
||||
Expression returnExpr = ((ReturnStatement)body.Statements.Single()).Expression; |
||||
returnExpr.Remove(); |
||||
lambda.Body = returnExpr; |
||||
replacement = lambda; |
||||
} else { |
||||
ame.Body = body; |
||||
replacement = ame; |
||||
} |
||||
var expectedType = objectCreateExpression.GetResolveResult()?.Type.GetDefinition(); |
||||
if (expectedType != null && expectedType.Kind != TypeKind.Delegate) { |
||||
var simplifiedDelegateCreation = (ObjectCreateExpression)objectCreateExpression.Clone(); |
||||
simplifiedDelegateCreation.Arguments.Clear(); |
||||
simplifiedDelegateCreation.Arguments.Add(replacement); |
||||
replacement = simplifiedDelegateCreation; |
||||
} |
||||
objectCreateExpression.ReplaceWith(replacement); |
||||
return true; |
||||
} |
||||
|
||||
IEnumerable<ParameterDeclaration> MakeParameters(IMethod method) |
||||
{ |
||||
foreach (var parameter in method.Parameters) { |
||||
var pd = context.TypeSystemAstBuilder.ConvertParameter(parameter); |
||||
if (parameter.Type.ContainsAnonymousType()) |
||||
pd.Type = null; |
||||
yield return pd; |
||||
} |
||||
} |
||||
|
||||
BlockStatement DecompileBody(IMethod method) |
||||
{ |
||||
// subContext.ReservedVariableNames.AddRange(currentlyUsedVariableNames);
|
||||
return new CSharpDecompiler(context.TypeSystem, context.Settings).DecompileLambdaBody(method); |
||||
} |
||||
|
||||
internal static bool IsPotentialClosure(ITypeDefinition decompiledTypeDefinition, ITypeDefinition potentialDisplayClass) |
||||
{ |
||||
if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) |
||||
return false; |
||||
// check that methodContainingType is within containingType
|
||||
while (potentialDisplayClass != decompiledTypeDefinition) { |
||||
potentialDisplayClass = potentialDisplayClass.DeclaringTypeDefinition; |
||||
if (potentialDisplayClass == null) |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
/* |
||||
public override object VisitInvocationExpression(InvocationExpression invocationExpression) |
||||
{ |
||||
if (context.Settings.ExpressionTrees && ExpressionTreeConverter.CouldBeExpressionTree(invocationExpression)) { |
||||
Expression converted = ExpressionTreeConverter.TryConvert(context, invocationExpression); |
||||
if (converted != null) { |
||||
invocationExpression.ReplaceWith(converted); |
||||
return converted.AcceptVisitor(this); |
||||
} |
||||
} |
||||
return base.VisitInvocationExpression(invocationExpression); |
||||
} |
||||
*/ |
||||
#region Track current variables
|
||||
public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration) |
||||
{ |
||||
Debug.Assert(currentlyUsedVariableNames.Count == 0); |
||||
try { |
||||
nextLocalVariableIndex = methodDeclaration.Body.Annotation<ICollection<ILVariable>>()?.Where(v => v.Kind == VariableKind.Local).MaxOrDefault(v => v.Index) + 1 ?? 0; |
||||
currentlyUsedVariableNames.AddRange(methodDeclaration.Parameters.Select(p => p.Name)); |
||||
return base.VisitMethodDeclaration(methodDeclaration); |
||||
} finally { |
||||
currentlyUsedVariableNames.Clear(); |
||||
} |
||||
} |
||||
|
||||
public override object VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration) |
||||
{ |
||||
Debug.Assert(currentlyUsedVariableNames.Count == 0); |
||||
try { |
||||
nextLocalVariableIndex = operatorDeclaration.Body.Annotation<ICollection<ILVariable>>()?.Where(v => v.Kind == VariableKind.Local).MaxOrDefault(v => v.Index) + 1 ?? 0; |
||||
currentlyUsedVariableNames.AddRange(operatorDeclaration.Parameters.Select(p => p.Name)); |
||||
return base.VisitOperatorDeclaration(operatorDeclaration); |
||||
} finally { |
||||
currentlyUsedVariableNames.Clear(); |
||||
} |
||||
} |
||||
|
||||
public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration) |
||||
{ |
||||
Debug.Assert(currentlyUsedVariableNames.Count == 0); |
||||
try { |
||||
nextLocalVariableIndex = constructorDeclaration.Body.Annotation<ICollection<ILVariable>>()?.Where(v => v.Kind == VariableKind.Local).MaxOrDefault(v => v.Index) + 1 ?? 0; |
||||
currentlyUsedVariableNames.AddRange(constructorDeclaration.Parameters.Select(p => p.Name)); |
||||
return base.VisitConstructorDeclaration(constructorDeclaration); |
||||
} finally { |
||||
currentlyUsedVariableNames.Clear(); |
||||
} |
||||
} |
||||
|
||||
public override object VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) |
||||
{ |
||||
Debug.Assert(currentlyUsedVariableNames.Count == 0); |
||||
try { |
||||
currentlyUsedVariableNames.AddRange(indexerDeclaration.Parameters.Select(p => p.Name)); |
||||
return base.VisitIndexerDeclaration(indexerDeclaration); |
||||
} finally { |
||||
currentlyUsedVariableNames.Clear(); |
||||
} |
||||
} |
||||
|
||||
public override object VisitAccessor(Accessor accessor) |
||||
{ |
||||
try { |
||||
nextLocalVariableIndex = accessor.Body.Annotation<ICollection<ILVariable>>()?.Where(v => v.Kind == VariableKind.Local).MaxOrDefault(v => v.Index) + 1 ?? 0; |
||||
currentlyUsedVariableNames.Add("value"); |
||||
return base.VisitAccessor(accessor); |
||||
} finally { |
||||
currentlyUsedVariableNames.RemoveAt(currentlyUsedVariableNames.Count - 1); |
||||
} |
||||
} |
||||
|
||||
public override object VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement) |
||||
{ |
||||
foreach (VariableInitializer v in variableDeclarationStatement.Variables) |
||||
currentlyUsedVariableNames.Add(v.Name); |
||||
return base.VisitVariableDeclarationStatement(variableDeclarationStatement); |
||||
} |
||||
|
||||
public override object VisitFixedStatement(FixedStatement fixedStatement) |
||||
{ |
||||
foreach (VariableInitializer v in fixedStatement.Variables) |
||||
currentlyUsedVariableNames.Add(v.Name); |
||||
return base.VisitFixedStatement(fixedStatement); |
||||
} |
||||
#endregion
|
||||
|
||||
static readonly ExpressionStatement displayClassAssignmentPattern = |
||||
new ExpressionStatement(new AssignmentExpression( |
||||
new NamedNode("variable", new IdentifierExpression(Pattern.AnyString)), |
||||
new ObjectCreateExpression { Type = new AnyNode("type") } |
||||
)); |
||||
|
||||
public override object VisitBlockStatement(BlockStatement blockStatement) |
||||
{ |
||||
int numberOfVariablesOutsideBlock = currentlyUsedVariableNames.Count; |
||||
base.VisitBlockStatement(blockStatement); |
||||
foreach (ExpressionStatement stmt in blockStatement.Statements.OfType<ExpressionStatement>().ToArray()) { |
||||
Match displayClassAssignmentMatch = displayClassAssignmentPattern.Match(stmt); |
||||
if (!displayClassAssignmentMatch.Success) |
||||
continue; |
||||
|
||||
ILVariable variable = displayClassAssignmentMatch.Get<AstNode>("variable").Single().Annotation<ILVariableResolveResult>()?.Variable; |
||||
if (variable == null) |
||||
continue; |
||||
var type = variable.Type.GetDefinition(); |
||||
if (!IsPotentialClosure(context.DecompiledTypeDefinition, type)) |
||||
continue; |
||||
if (!(displayClassAssignmentMatch.Get<AstType>("type").Single().GetSymbol() as IType).GetDefinition().Equals(type)) |
||||
continue; |
||||
|
||||
// Looks like we found a display class creation. Now let's verify that the variable is used only for field accesses:
|
||||
bool ok = true; |
||||
foreach (var identExpr in blockStatement.Descendants.OfType<IdentifierExpression>()) { |
||||
if (identExpr.Identifier == variable.Name && identExpr != displayClassAssignmentMatch.Get("variable").Single()) { |
||||
if (!(identExpr.Parent is MemberReferenceExpression && identExpr.Parent.GetSymbol() is IField)) |
||||
ok = false; |
||||
} |
||||
} |
||||
if (!ok) |
||||
continue; |
||||
Dictionary<IField, AstNode> dict = new Dictionary<IField, AstNode>(); |
||||
|
||||
// Delete the variable declaration statement:
|
||||
VariableDeclarationStatement displayClassVarDecl = PatternStatementTransform.FindVariableDeclaration(stmt, variable.Name); |
||||
if (displayClassVarDecl != null) |
||||
displayClassVarDecl.Remove(); |
||||
|
||||
// Delete the assignment statement:
|
||||
AstNode cur = stmt.NextSibling; |
||||
stmt.Remove(); |
||||
|
||||
// Delete any following statements as long as they assign parameters to the display class
|
||||
BlockStatement rootBlock = blockStatement.Ancestors.OfType<BlockStatement>().LastOrDefault() ?? blockStatement; |
||||
List<ILVariable> parameterOccurrances = rootBlock.Descendants.OfType<IdentifierExpression>() |
||||
.Select(n => n.Annotation<ILVariable>()).Where(p => p != null && p.Kind == VariableKind.Parameter).ToList(); |
||||
AstNode next; |
||||
for (; cur != null; cur = next) { |
||||
next = cur.NextSibling; |
||||
|
||||
// Test for the pattern:
|
||||
// "variableName.MemberName = right;"
|
||||
ExpressionStatement closureFieldAssignmentPattern = new ExpressionStatement( |
||||
new AssignmentExpression( |
||||
new NamedNode("left", new MemberReferenceExpression { |
||||
Target = new IdentifierExpression(variable.Name), |
||||
MemberName = Pattern.AnyString |
||||
}), |
||||
new AnyNode("right") |
||||
) |
||||
); |
||||
Match m = closureFieldAssignmentPattern.Match(cur); |
||||
if (m.Success) { |
||||
AstNode right = m.Get<AstNode>("right").Single(); |
||||
bool isParameter = false; |
||||
bool isDisplayClassParentPointerAssignment = false; |
||||
if (right is ThisReferenceExpression) { |
||||
isParameter = true; |
||||
} else if (right is IdentifierExpression) { |
||||
// handle parameters only if the whole method contains no other occurrence except for 'right'
|
||||
ILVariable v = right.Annotation<ILVariable>(); |
||||
isParameter = v.Kind == VariableKind.Parameter && parameterOccurrances.Count(c => c == v) == 1; |
||||
if (!isParameter && IsPotentialClosure(context.DecompiledTypeDefinition, v.Type.GetDefinition())) { |
||||
// parent display class within the same method
|
||||
// (closure2.localsX = closure1;)
|
||||
isDisplayClassParentPointerAssignment = true; |
||||
} |
||||
} else if (right is MemberReferenceExpression) { |
||||
// copy of parent display class reference from an outer lambda
|
||||
// closure2.localsX = this.localsY
|
||||
MemberReferenceExpression mre = m.Get<MemberReferenceExpression>("right").Single(); |
||||
do { |
||||
// descend into the targets of the mre as long as the field types are closures
|
||||
var fieldDef2 = mre.GetSymbol() as IField; |
||||
if (fieldDef2 == null || !IsPotentialClosure(context.DecompiledTypeDefinition, fieldDef2.Type.GetDefinition())) { |
||||
break; |
||||
} |
||||
// if we finally get to a this reference, it's copying a display class parent pointer
|
||||
if (mre.Target is ThisReferenceExpression) { |
||||
isDisplayClassParentPointerAssignment = true; |
||||
} |
||||
mre = mre.Target as MemberReferenceExpression; |
||||
} while (mre != null); |
||||
} |
||||
var field = m.Get<MemberReferenceExpression>("left").Single().GetSymbol() as IField; |
||||
if (field != null && (isParameter || isDisplayClassParentPointerAssignment)) { |
||||
dict[field] = right; |
||||
cur.Remove(); |
||||
} else { |
||||
break; |
||||
} |
||||
} else { |
||||
break; |
||||
} |
||||
} |
||||
|
||||
// Now create variables for all fields of the display class (except for those that we already handled as parameters)
|
||||
List<Tuple<AstType, ILVariable>> variablesToDeclare = new List<Tuple<AstType, ILVariable>>(); |
||||
foreach (var field in type.Fields) { |
||||
if (field.IsStatic) |
||||
continue; // skip static fields
|
||||
if (dict.ContainsKey(field)) // skip field if it already was handled as parameter
|
||||
continue; |
||||
string capturedVariableName = field.Name; |
||||
if (capturedVariableName.StartsWith("$VB$Local_", StringComparison.Ordinal) && capturedVariableName.Length > 10) |
||||
capturedVariableName = capturedVariableName.Substring(10); |
||||
EnsureVariableNameIsAvailable(blockStatement, capturedVariableName); |
||||
currentlyUsedVariableNames.Add(capturedVariableName); |
||||
ILVariable ilVar = new ILVariable(VariableKind.Local, field.Type, nextLocalVariableIndex++) |
||||
{ |
||||
Name = capturedVariableName |
||||
}; |
||||
variablesToDeclare.Add(Tuple.Create(context.TypeSystemAstBuilder.ConvertType(field.Type), ilVar)); |
||||
dict[field] = new IdentifierExpression(capturedVariableName).WithAnnotation(ilVar); |
||||
} |
||||
|
||||
// Now figure out where the closure was accessed and use the simpler replacement expression there:
|
||||
foreach (var identExpr in blockStatement.Descendants.OfType<IdentifierExpression>()) { |
||||
if (identExpr.Identifier == variable.Name) { |
||||
MemberReferenceExpression mre = (MemberReferenceExpression)identExpr.Parent; |
||||
AstNode replacement; |
||||
if (dict.TryGetValue((IField)mre.GetSymbol(), out replacement)) { |
||||
mre.ReplaceWith(replacement.Clone()); |
||||
} |
||||
} |
||||
} |
||||
// Now insert the variable declarations (we can do this after the replacements only so that the scope detection works):
|
||||
Statement insertionPoint = blockStatement.Statements.FirstOrDefault(); |
||||
foreach (var tuple in variablesToDeclare) { |
||||
var newVarDecl = new VariableDeclarationStatement(tuple.Item1, tuple.Item2.Name); |
||||
newVarDecl.Variables.Single().AddAnnotation(new CapturedVariableAnnotation()); |
||||
newVarDecl.Variables.Single().AddAnnotation(tuple.Item2); |
||||
blockStatement.Statements.InsertBefore(insertionPoint, newVarDecl); |
||||
} |
||||
} |
||||
currentlyUsedVariableNames.RemoveRange(numberOfVariablesOutsideBlock, currentlyUsedVariableNames.Count - numberOfVariablesOutsideBlock); |
||||
return null; |
||||
} |
||||
|
||||
void EnsureVariableNameIsAvailable(AstNode currentNode, string name) |
||||
{ |
||||
int pos = currentlyUsedVariableNames.IndexOf(name); |
||||
if (pos < 0) { |
||||
// name is still available
|
||||
return; |
||||
} |
||||
throw new NotImplementedException("naming conflict: " + name); |
||||
}/* |
||||
// Naming conflict. Let's rename the existing variable so that the field keeps the name from metadata.
|
||||
NameVariables nv = new NameVariables(); |
||||
// Add currently used variable and parameter names
|
||||
foreach (string nameInUse in currentlyUsedVariableNames) |
||||
nv.AddExistingName(nameInUse); |
||||
// variables declared in child nodes of this block
|
||||
foreach (VariableInitializer vi in currentNode.Descendants.OfType<VariableInitializer>()) |
||||
nv.AddExistingName(vi.Name); |
||||
// parameters in child lambdas
|
||||
foreach (ParameterDeclaration pd in currentNode.Descendants.OfType<ParameterDeclaration>()) |
||||
nv.AddExistingName(pd.Name); |
||||
|
||||
string newName = nv.GetAlternativeName(name); |
||||
currentlyUsedVariableNames[pos] = newName; |
||||
|
||||
// find top-most block
|
||||
AstNode topMostBlock = currentNode.Ancestors.OfType<BlockStatement>().LastOrDefault() ?? currentNode; |
||||
|
||||
// rename identifiers
|
||||
foreach (IdentifierExpression ident in topMostBlock.Descendants.OfType<IdentifierExpression>()) { |
||||
if (ident.Identifier == name) { |
||||
ident.Identifier = newName; |
||||
ILVariable v = ident.Annotation<ILVariable>(); |
||||
if (v != null) |
||||
v.Name = newName; |
||||
} |
||||
} |
||||
// rename variable declarations
|
||||
foreach (VariableInitializer vi in topMostBlock.Descendants.OfType<VariableInitializer>()) { |
||||
if (vi.Name == name) { |
||||
vi.Name = newName; |
||||
ILVariable v = vi.Annotation<ILVariable>(); |
||||
if (v != null) |
||||
v.Name = newName; |
||||
} |
||||
} |
||||
}*/ |
||||
} |
||||
} |
@ -1,704 +0,0 @@
@@ -1,704 +0,0 @@
|
||||
// Copyright (c) 2012 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.Linq; |
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
|
||||
namespace ICSharpCode.Decompiler.ILAst |
||||
{ |
||||
/// <summary>
|
||||
/// Decompiler step for C# 5 async/await.
|
||||
/// </summary>
|
||||
class AsyncDecompiler |
||||
{ |
||||
public static bool IsCompilerGeneratedStateMachine(TypeDefinition type) |
||||
{ |
||||
if (!(type.DeclaringType != null && type.IsCompilerGenerated())) |
||||
return false; |
||||
foreach (TypeReference i in type.Interfaces) { |
||||
if (i.Namespace == "System.Runtime.CompilerServices" && i.Name == "IAsyncStateMachine") |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
enum AsyncMethodType |
||||
{ |
||||
Void, |
||||
Task, |
||||
TaskOfT |
||||
} |
||||
|
||||
DecompilerContext context; |
||||
|
||||
// These fields are set by MatchTaskCreationPattern()
|
||||
AsyncMethodType methodType; |
||||
int initialState; |
||||
TypeDefinition stateMachineStruct; |
||||
MethodDefinition moveNextMethod; |
||||
FieldDefinition builderField; |
||||
FieldDefinition stateField; |
||||
Dictionary<FieldDefinition, ILVariable> fieldToParameterMap = new Dictionary<FieldDefinition, ILVariable>(); |
||||
ILVariable cachedStateVar; |
||||
|
||||
// These fields are set by AnalyzeMoveNext()
|
||||
int finalState = -2; |
||||
ILTryCatchBlock mainTryCatch; |
||||
ILLabel setResultAndExitLabel; |
||||
ILLabel exitLabel; |
||||
ILExpression resultExpr; |
||||
|
||||
#region RunStep1() method
|
||||
public static void RunStep1(DecompilerContext context, ILBlock method) |
||||
{ |
||||
if (!context.Settings.AsyncAwait) |
||||
return; // abort if async decompilation is disabled
|
||||
var yrd = new AsyncDecompiler(); |
||||
yrd.context = context; |
||||
if (!yrd.MatchTaskCreationPattern(method)) |
||||
return; |
||||
#if DEBUG
|
||||
if (Debugger.IsAttached) { |
||||
yrd.Run(); |
||||
} else { |
||||
#endif
|
||||
try { |
||||
yrd.Run(); |
||||
} catch (SymbolicAnalysisFailedException) { |
||||
return; |
||||
} |
||||
#if DEBUG
|
||||
} |
||||
#endif
|
||||
context.CurrentMethodIsAsync = true; |
||||
|
||||
method.Body.Clear(); |
||||
method.EntryGoto = null; |
||||
method.Body.AddRange(yrd.newTopLevelBody); |
||||
ILAstOptimizer.RemoveRedundantCode(method); |
||||
} |
||||
|
||||
void Run() |
||||
{ |
||||
AnalyzeMoveNext(); |
||||
ValidateCatchBlock(mainTryCatch.CatchBlocks[0]); |
||||
AnalyzeStateMachine(mainTryCatch.TryBlock); |
||||
// AnalyzeStateMachine invokes ConvertBody
|
||||
MarkGeneratedVariables(); |
||||
YieldReturnDecompiler.TranslateFieldsToLocalAccess(newTopLevelBody, fieldToParameterMap); |
||||
} |
||||
#endregion
|
||||
|
||||
#region MatchTaskCreationPattern
|
||||
bool MatchTaskCreationPattern(ILBlock method) |
||||
{ |
||||
if (method.Body.Count < 5) |
||||
return false; |
||||
// Check the second-to-last instruction (the start call) first, as we can get the most information from that
|
||||
MethodReference startMethod; |
||||
ILExpression loadStartTarget, loadStartArgument; |
||||
// call(AsyncTaskMethodBuilder::Start, ldloca(builder), ldloca(stateMachine))
|
||||
if (!method.Body[method.Body.Count - 2].Match(ILCode.Call, out startMethod, out loadStartTarget, out loadStartArgument)) |
||||
return false; |
||||
if (startMethod.Name != "Start" || startMethod.DeclaringType == null || startMethod.DeclaringType.Namespace != "System.Runtime.CompilerServices") |
||||
return false; |
||||
switch (startMethod.DeclaringType.Name) { |
||||
case "AsyncTaskMethodBuilder`1": |
||||
methodType = AsyncMethodType.TaskOfT; |
||||
break; |
||||
case "AsyncTaskMethodBuilder": |
||||
methodType = AsyncMethodType.Task; |
||||
break; |
||||
case "AsyncVoidMethodBuilder": |
||||
methodType = AsyncMethodType.Void; |
||||
break; |
||||
default: |
||||
return false; |
||||
} |
||||
ILVariable stateMachineVar, builderVar; |
||||
if (!loadStartTarget.Match(ILCode.Ldloca, out builderVar)) |
||||
return false; |
||||
if (!loadStartArgument.Match(ILCode.Ldloca, out stateMachineVar)) |
||||
return false; |
||||
|
||||
stateMachineStruct = stateMachineVar.Type.ResolveWithinSameModule(); |
||||
if (stateMachineStruct == null || !stateMachineStruct.IsValueType) |
||||
return false; |
||||
moveNextMethod = stateMachineStruct.Methods.FirstOrDefault(f => f.Name == "MoveNext"); |
||||
if (moveNextMethod == null) |
||||
return false; |
||||
|
||||
// Check third-to-last instruction (copy of builder):
|
||||
// stloc(builder, ldfld(StateMachine::<>t__builder, ldloca(stateMachine)))
|
||||
ILExpression loadBuilderExpr; |
||||
if (!method.Body[method.Body.Count - 3].MatchStloc(builderVar, out loadBuilderExpr)) |
||||
return false; |
||||
FieldReference builderFieldRef; |
||||
ILExpression loadStateMachineForBuilderExpr; |
||||
if (!loadBuilderExpr.Match(ILCode.Ldfld, out builderFieldRef, out loadStateMachineForBuilderExpr)) |
||||
return false; |
||||
if (!(loadStateMachineForBuilderExpr.MatchLdloca(stateMachineVar) || loadStateMachineForBuilderExpr.MatchLdloc(stateMachineVar))) |
||||
return false; |
||||
builderField = builderFieldRef.ResolveWithinSameModule(); |
||||
if (builderField == null) |
||||
return false; |
||||
|
||||
// Check the last instruction (ret)
|
||||
if (methodType == AsyncMethodType.Void) { |
||||
if (!method.Body[method.Body.Count - 1].Match(ILCode.Ret)) |
||||
return false; |
||||
} else { |
||||
// ret(call(AsyncTaskMethodBuilder::get_Task, ldflda(StateMachine::<>t__builder, ldloca(stateMachine))))
|
||||
ILExpression returnValue; |
||||
if (!method.Body[method.Body.Count - 1].Match(ILCode.Ret, out returnValue)) |
||||
return false; |
||||
MethodReference getTaskMethod; |
||||
ILExpression builderExpr; |
||||
if (!returnValue.Match(ILCode.Call, out getTaskMethod, out builderExpr)) |
||||
return false; |
||||
ILExpression loadStateMachineForBuilderExpr2; |
||||
FieldReference builderField2; |
||||
if (!builderExpr.Match(ILCode.Ldflda, out builderField2, out loadStateMachineForBuilderExpr2)) |
||||
return false; |
||||
if (builderField2.ResolveWithinSameModule() != builderField || !loadStateMachineForBuilderExpr2.MatchLdloca(stateMachineVar)) |
||||
return false; |
||||
} |
||||
|
||||
// Check the last field assignment - this should be the state field
|
||||
ILExpression initialStateExpr; |
||||
if (!MatchStFld(method.Body[method.Body.Count - 4], stateMachineVar, out stateField, out initialStateExpr)) |
||||
return false; |
||||
if (!initialStateExpr.Match(ILCode.Ldc_I4, out initialState)) |
||||
return false; |
||||
if (initialState != -1) |
||||
return false; |
||||
|
||||
// Check the second-to-last field assignment - this should be the builder field
|
||||
FieldDefinition builderField3; |
||||
ILExpression builderInitialization; |
||||
if (!MatchStFld(method.Body[method.Body.Count - 5], stateMachineVar, out builderField3, out builderInitialization)) |
||||
return false; |
||||
MethodReference createMethodRef; |
||||
if (builderField3 != builderField || !builderInitialization.Match(ILCode.Call, out createMethodRef)) |
||||
return false; |
||||
if (createMethodRef.Name != "Create") |
||||
return false; |
||||
|
||||
for (int i = 0; i < method.Body.Count - 5; i++) { |
||||
FieldDefinition field; |
||||
ILExpression fieldInit; |
||||
if (!MatchStFld(method.Body[i], stateMachineVar, out field, out fieldInit)) |
||||
return false; |
||||
ILVariable v; |
||||
if (!fieldInit.Match(ILCode.Ldloc, out v)) |
||||
return false; |
||||
if (!v.IsParameter) |
||||
return false; |
||||
fieldToParameterMap[field] = v; |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
static bool MatchStFld(ILNode stfld, ILVariable stateMachineVar, out FieldDefinition field, out ILExpression expr) |
||||
{ |
||||
field = null; |
||||
FieldReference fieldRef; |
||||
ILExpression ldloca; |
||||
if (!stfld.Match(ILCode.Stfld, out fieldRef, out ldloca, out expr)) |
||||
return false; |
||||
field = fieldRef.ResolveWithinSameModule(); |
||||
return field != null && ldloca.MatchLdloca(stateMachineVar); |
||||
} |
||||
#endregion
|
||||
|
||||
#region Analyze MoveNext
|
||||
void AnalyzeMoveNext() |
||||
{ |
||||
ILBlock ilMethod = CreateILAst(moveNextMethod); |
||||
|
||||
int startIndex; |
||||
if (ilMethod.Body.Count == 6) { |
||||
startIndex = 0; |
||||
} else if (ilMethod.Body.Count == 7) { |
||||
// stloc(cachedState, ldfld(valuetype StateMachineStruct::<>1__state, ldloc(this)))
|
||||
ILExpression cachedStateInit; |
||||
if (!ilMethod.Body[0].Match(ILCode.Stloc, out cachedStateVar, out cachedStateInit)) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
ILExpression instanceExpr; |
||||
FieldReference loadedField; |
||||
if (!cachedStateInit.Match(ILCode.Ldfld, out loadedField, out instanceExpr) || loadedField.ResolveWithinSameModule() != stateField || !instanceExpr.MatchThis()) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
startIndex = 1; |
||||
} else { |
||||
throw new SymbolicAnalysisFailedException(); |
||||
} |
||||
|
||||
mainTryCatch = ilMethod.Body[startIndex + 0] as ILTryCatchBlock; |
||||
if (mainTryCatch == null || mainTryCatch.CatchBlocks.Count != 1) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
if (mainTryCatch.FaultBlock != null || mainTryCatch.FinallyBlock != null) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
|
||||
setResultAndExitLabel = ilMethod.Body[startIndex + 1] as ILLabel; |
||||
if (setResultAndExitLabel == null) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
|
||||
if (!MatchStateAssignment(ilMethod.Body[startIndex + 2], out finalState)) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
|
||||
// call(AsyncTaskMethodBuilder`1::SetResult, ldflda(StateMachine::<>t__builder, ldloc(this)), ldloc(<>t__result))
|
||||
MethodReference setResultMethod; |
||||
ILExpression builderExpr; |
||||
if (methodType == AsyncMethodType.TaskOfT) { |
||||
if (!ilMethod.Body[startIndex + 3].Match(ILCode.Call, out setResultMethod, out builderExpr, out resultExpr)) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
} else { |
||||
if (!ilMethod.Body[startIndex + 3].Match(ILCode.Call, out setResultMethod, out builderExpr)) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
} |
||||
if (!(setResultMethod.Name == "SetResult" && IsBuilderFieldOnThis(builderExpr))) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
|
||||
exitLabel = ilMethod.Body[startIndex + 4] as ILLabel; |
||||
if (exitLabel == null) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Creates ILAst for the specified method, optimized up to before the 'YieldReturn' step.
|
||||
/// </summary>
|
||||
ILBlock CreateILAst(MethodDefinition method) |
||||
{ |
||||
if (method == null || !method.HasBody) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
|
||||
ILBlock ilMethod = new ILBlock(); |
||||
ILAstBuilder astBuilder = new ILAstBuilder(); |
||||
ilMethod.Body = astBuilder.Build(method, true, context); |
||||
ILAstOptimizer optimizer = new ILAstOptimizer(); |
||||
optimizer.Optimize(context, ilMethod, ILAstOptimizationStep.YieldReturn); |
||||
return ilMethod; |
||||
} |
||||
|
||||
void ValidateCatchBlock(ILTryCatchBlock.CatchBlock catchBlock) |
||||
{ |
||||
if (catchBlock.ExceptionType == null || catchBlock.ExceptionType.Name != "Exception") |
||||
throw new SymbolicAnalysisFailedException(); |
||||
if (catchBlock.Body.Count != 3) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
int stateID; |
||||
if (!(MatchStateAssignment(catchBlock.Body[0], out stateID) && stateID == finalState)) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
MethodReference setExceptionMethod; |
||||
ILExpression builderExpr, exceptionExpr; |
||||
if (!catchBlock.Body[1].Match(ILCode.Call, out setExceptionMethod, out builderExpr, out exceptionExpr)) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
if (!(setExceptionMethod.Name == "SetException" && IsBuilderFieldOnThis(builderExpr) && exceptionExpr.MatchLdloc(catchBlock.ExceptionVariable))) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
|
||||
ILLabel label; |
||||
if (!(catchBlock.Body[2].Match(ILCode.Leave, out label) && label == exitLabel)) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
} |
||||
|
||||
bool IsBuilderFieldOnThis(ILExpression builderExpr) |
||||
{ |
||||
// ldflda(StateMachine::<>t__builder, ldloc(this))
|
||||
FieldReference fieldRef; |
||||
ILExpression target; |
||||
return builderExpr.Match(ILCode.Ldflda, out fieldRef, out target) |
||||
&& fieldRef.ResolveWithinSameModule() == builderField |
||||
&& target.MatchThis(); |
||||
} |
||||
|
||||
bool MatchStateAssignment(ILNode stfld, out int stateID) |
||||
{ |
||||
// stfld(StateMachine::<>1__state, ldloc(this), ldc.i4(stateId))
|
||||
stateID = 0; |
||||
FieldReference fieldRef; |
||||
ILExpression target, val; |
||||
if (stfld.Match(ILCode.Stfld, out fieldRef, out target, out val)) { |
||||
return fieldRef.ResolveWithinSameModule() == stateField |
||||
&& target.MatchThis() |
||||
&& val.Match(ILCode.Ldc_I4, out stateID); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
bool MatchRoslynStateAssignment(List<ILNode> block, int index, out int stateID) |
||||
{ |
||||
// v = ldc.i4(stateId)
|
||||
// stloc(cachedState, v)
|
||||
// stfld(StateMachine::<>1__state, ldloc(this), v)
|
||||
stateID = 0; |
||||
if (index < 0) |
||||
return false; |
||||
ILVariable v; |
||||
ILExpression val; |
||||
if (!block[index].Match(ILCode.Stloc, out v, out val) || !val.Match(ILCode.Ldc_I4, out stateID)) |
||||
return false; |
||||
ILExpression loadV; |
||||
if (!block[index + 1].MatchStloc(cachedStateVar, out loadV) || !loadV.MatchLdloc(v)) |
||||
return false; |
||||
ILExpression target; |
||||
FieldReference fieldRef; |
||||
if (block[index + 2].Match(ILCode.Stfld, out fieldRef, out target, out loadV)) { |
||||
return fieldRef.ResolveWithinSameModule() == stateField |
||||
&& target.MatchThis() |
||||
&& loadV.MatchLdloc(v); |
||||
} |
||||
return false; |
||||
} |
||||
#endregion
|
||||
|
||||
#region AnalyzeStateMachine
|
||||
ILVariable doFinallyBodies; |
||||
List<ILNode> newTopLevelBody; |
||||
|
||||
void AnalyzeStateMachine(ILBlock block) |
||||
{ |
||||
var body = block.Body; |
||||
if (body.Count == 0) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
if (DetectDoFinallyBodies(body)) { |
||||
body.RemoveAt(0); |
||||
if (body.Count == 0) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
} |
||||
StateRangeAnalysis rangeAnalysis = new StateRangeAnalysis(body[0], StateRangeAnalysisMode.AsyncMoveNext, stateField, cachedStateVar); |
||||
int bodyLength = block.Body.Count; |
||||
int pos = rangeAnalysis.AssignStateRanges(body, bodyLength); |
||||
rangeAnalysis.EnsureLabelAtPos(body, ref pos, ref bodyLength); |
||||
|
||||
var labelStateRangeMapping = rangeAnalysis.CreateLabelRangeMapping(body, pos, bodyLength); |
||||
newTopLevelBody = ConvertBody(body, pos, bodyLength, labelStateRangeMapping); |
||||
newTopLevelBody.Insert(0, MakeGoTo(labelStateRangeMapping, initialState)); |
||||
newTopLevelBody.Add(setResultAndExitLabel); |
||||
if (methodType == AsyncMethodType.TaskOfT) { |
||||
newTopLevelBody.Add(new ILExpression(ILCode.Ret, null, resultExpr)); |
||||
} else { |
||||
newTopLevelBody.Add(new ILExpression(ILCode.Ret, null)); |
||||
} |
||||
} |
||||
|
||||
bool DetectDoFinallyBodies(List<ILNode> body) |
||||
{ |
||||
ILVariable v; |
||||
ILExpression initExpr; |
||||
if (!body[0].Match(ILCode.Stloc, out v, out initExpr)) |
||||
return false; |
||||
int initialValue; |
||||
if (!(initExpr.Match(ILCode.Ldc_I4, out initialValue) && initialValue == 1)) |
||||
return false; |
||||
doFinallyBodies = v; |
||||
return true; |
||||
} |
||||
#endregion
|
||||
|
||||
#region ConvertBody
|
||||
ILExpression MakeGoTo(LabelRangeMapping mapping, int state) |
||||
{ |
||||
foreach (var pair in mapping) { |
||||
if (pair.Value.Contains(state)) |
||||
return new ILExpression(ILCode.Br, pair.Key); |
||||
} |
||||
throw new SymbolicAnalysisFailedException(); |
||||
} |
||||
|
||||
List<ILNode> ConvertBody(List<ILNode> body, int startPos, int bodyLength, LabelRangeMapping mapping) |
||||
{ |
||||
List<ILNode> newBody = new List<ILNode>(); |
||||
// Copy all instructions from the old body to newBody.
|
||||
for (int pos = startPos; pos < bodyLength; pos++) { |
||||
ILTryCatchBlock tryCatchBlock = body[pos] as ILTryCatchBlock; |
||||
ILExpression expr = body[pos] as ILExpression; |
||||
if (expr != null && expr.Code == ILCode.Leave && expr.Operand == exitLabel) { |
||||
ILVariable awaiterVar; |
||||
FieldDefinition awaiterField; |
||||
int targetStateID; |
||||
HandleAwait(newBody, out awaiterVar, out awaiterField, out targetStateID); |
||||
MarkAsGeneratedVariable(awaiterVar); |
||||
newBody.Add(new ILExpression(ILCode.Await, null, new ILExpression(ILCode.Ldloca, awaiterVar))); |
||||
newBody.Add(MakeGoTo(mapping, targetStateID)); |
||||
} else if (tryCatchBlock != null) { |
||||
ILTryCatchBlock newTryCatchBlock = new ILTryCatchBlock(); |
||||
var tryBody = tryCatchBlock.TryBlock.Body; |
||||
if (tryBody.Count == 0) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
StateRangeAnalysis rangeAnalysis = new StateRangeAnalysis(tryBody[0], StateRangeAnalysisMode.AsyncMoveNext, stateField, cachedStateVar); |
||||
int tryBodyLength = tryBody.Count; |
||||
int posInTryBody = rangeAnalysis.AssignStateRanges(tryBody, tryBodyLength); |
||||
rangeAnalysis.EnsureLabelAtPos(tryBody, ref posInTryBody, ref tryBodyLength); |
||||
|
||||
var mappingInTryBlock = rangeAnalysis.CreateLabelRangeMapping(tryBody, posInTryBody, tryBodyLength); |
||||
var newTryBody = ConvertBody(tryBody, posInTryBody, tryBodyLength, mappingInTryBlock); |
||||
newTryBody.Insert(0, MakeGoTo(mappingInTryBlock, initialState)); |
||||
|
||||
// If there's a label at the beginning of the state dispatcher, copy that
|
||||
if (posInTryBody > 0 && tryBody.FirstOrDefault() is ILLabel) |
||||
newTryBody.Insert(0, tryBody.First()); |
||||
|
||||
newTryCatchBlock.TryBlock = new ILBlock(newTryBody); |
||||
newTryCatchBlock.CatchBlocks = new List<ILTryCatchBlock.CatchBlock>(tryCatchBlock.CatchBlocks); |
||||
newTryCatchBlock.FaultBlock = tryCatchBlock.FaultBlock; |
||||
if (tryCatchBlock.FinallyBlock != null) |
||||
newTryCatchBlock.FinallyBlock = new ILBlock(ConvertFinally(tryCatchBlock.FinallyBlock.Body)); |
||||
|
||||
newBody.Add(newTryCatchBlock); |
||||
} else { |
||||
newBody.Add(body[pos]); |
||||
} |
||||
} |
||||
return newBody; |
||||
} |
||||
|
||||
List<ILNode> ConvertFinally(List<ILNode> body) |
||||
{ |
||||
List<ILNode> newBody = new List<ILNode>(body); |
||||
if (newBody.Count == 0) |
||||
return newBody; |
||||
ILLabel endFinallyLabel; |
||||
ILExpression ceqExpr; |
||||
if (newBody[0].Match(ILCode.Brtrue, out endFinallyLabel, out ceqExpr)) { |
||||
ILExpression condition; |
||||
if (MatchLogicNot(ceqExpr, out condition)) { |
||||
if (condition.MatchLdloc(doFinallyBodies)) { |
||||
newBody.RemoveAt(0); |
||||
} else if (condition.Code == ILCode.Clt && condition.Arguments[0].MatchLdloc(cachedStateVar) && condition.Arguments[1].MatchLdcI4(0)) { |
||||
newBody.RemoveAt(0); |
||||
} |
||||
} |
||||
} |
||||
return newBody; |
||||
} |
||||
|
||||
bool MatchLogicNot(ILExpression expr, out ILExpression arg) |
||||
{ |
||||
ILExpression loadZero; |
||||
object unused; |
||||
if (expr.Match(ILCode.Ceq, out unused, out arg, out loadZero)) { |
||||
int num; |
||||
return loadZero.Match(ILCode.Ldc_I4, out num) && num == 0; |
||||
} |
||||
return expr.Match(ILCode.LogicNot, out arg); |
||||
} |
||||
|
||||
void HandleAwait(List<ILNode> newBody, out ILVariable awaiterVar, out FieldDefinition awaiterField, out int targetStateID) |
||||
{ |
||||
// Handle the instructions prior to the exit out of the method to detect what is being awaited.
|
||||
// (analyses the last instructions in newBody and removes the analyzed instructions from newBody)
|
||||
|
||||
if (doFinallyBodies != null) { |
||||
// stloc(<>t__doFinallyBodies, ldc.i4(0))
|
||||
ILExpression dfbInitExpr; |
||||
if (!newBody.LastOrDefault().MatchStloc(doFinallyBodies, out dfbInitExpr)) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
int val; |
||||
if (!(dfbInitExpr.Match(ILCode.Ldc_I4, out val) && val == 0)) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
newBody.RemoveAt(newBody.Count - 1); // remove doFinallyBodies assignment
|
||||
} |
||||
|
||||
// call(AsyncTaskMethodBuilder::AwaitUnsafeOnCompleted, ldflda(StateMachine::<>t__builder, ldloc(this)), ldloca(CS$0$0001), ldloc(this))
|
||||
ILExpression callAwaitUnsafeOnCompleted = newBody.LastOrDefault() as ILExpression; |
||||
newBody.RemoveAt(newBody.Count - 1); // remove AwaitUnsafeOnCompleted call
|
||||
if (callAwaitUnsafeOnCompleted == null || callAwaitUnsafeOnCompleted.Code != ILCode.Call) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
string methodName = ((MethodReference)callAwaitUnsafeOnCompleted.Operand).Name; |
||||
if (methodName != "AwaitUnsafeOnCompleted" && methodName != "AwaitOnCompleted") |
||||
throw new SymbolicAnalysisFailedException(); |
||||
if (callAwaitUnsafeOnCompleted.Arguments.Count != 3) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
if (!callAwaitUnsafeOnCompleted.Arguments[1].Match(ILCode.Ldloca, out awaiterVar)) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
|
||||
// stfld(StateMachine::<>u__$awaiter6, ldloc(this), ldloc(CS$0$0001))
|
||||
FieldReference awaiterFieldRef; |
||||
ILExpression loadThis, loadAwaiterVar; |
||||
if (!newBody.LastOrDefault().Match(ILCode.Stfld, out awaiterFieldRef, out loadThis, out loadAwaiterVar)) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
newBody.RemoveAt(newBody.Count - 1); // remove awaiter field assignment
|
||||
awaiterField = awaiterFieldRef.ResolveWithinSameModule(); |
||||
if (!(awaiterField != null && loadThis.MatchThis() && loadAwaiterVar.MatchLdloc(awaiterVar))) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
|
||||
// stfld(StateMachine::<>1__state, ldloc(this), ldc.i4(0))
|
||||
if (MatchStateAssignment(newBody.LastOrDefault(), out targetStateID)) |
||||
newBody.RemoveAt(newBody.Count - 1); // remove awaiter field assignment
|
||||
else if (MatchRoslynStateAssignment(newBody, newBody.Count - 3, out targetStateID)) |
||||
newBody.RemoveRange(newBody.Count - 3, 3); // remove awaiter field assignment
|
||||
} |
||||
#endregion
|
||||
|
||||
#region MarkGeneratedVariables
|
||||
int smallestGeneratedVariableIndex = int.MaxValue; |
||||
|
||||
void MarkAsGeneratedVariable(ILVariable v) |
||||
{ |
||||
if (v.OriginalVariable != null && v.OriginalVariable.Index >= 0) { |
||||
smallestGeneratedVariableIndex = Math.Min(smallestGeneratedVariableIndex, v.OriginalVariable.Index); |
||||
} |
||||
} |
||||
|
||||
void MarkGeneratedVariables() |
||||
{ |
||||
var expressions = new ILBlock(newTopLevelBody).GetSelfAndChildrenRecursive<ILExpression>(); |
||||
foreach (var v in expressions.Select(e => e.Operand).OfType<ILVariable>()) { |
||||
if (v.OriginalVariable != null && v.OriginalVariable.Index >= smallestGeneratedVariableIndex) |
||||
v.IsGenerated = true; |
||||
} |
||||
} |
||||
#endregion
|
||||
|
||||
#region RunStep2() method
|
||||
public static void RunStep2(DecompilerContext context, ILBlock method) |
||||
{ |
||||
if (context.CurrentMethodIsAsync) { |
||||
Step2(method.Body); |
||||
ILAstOptimizer.RemoveRedundantCode(method); |
||||
// Repeat the inlining/copy propagation optimization because the conversion of field access
|
||||
// to local variables can open up additional inlining possibilities.
|
||||
ILInlining inlining = new ILInlining(method); |
||||
inlining.InlineAllVariables(); |
||||
inlining.CopyPropagation(); |
||||
} |
||||
} |
||||
|
||||
static void Step2(List<ILNode> body) |
||||
{ |
||||
for (int pos = 0; pos < body.Count; pos++) { |
||||
ILTryCatchBlock tc = body[pos] as ILTryCatchBlock; |
||||
if (tc != null) { |
||||
Step2(tc.TryBlock.Body); |
||||
} else { |
||||
Step2(body, ref pos); |
||||
} |
||||
} |
||||
} |
||||
|
||||
static bool Step2(List<ILNode> body, ref int pos) |
||||
{ |
||||
// stloc(CS$0$0001, callvirt(class System.Threading.Tasks.Task`1<bool>::GetAwaiter, awaiterExpr)
|
||||
// brtrue(IL_7C, call(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::get_IsCompleted, ldloca(CS$0$0001)))
|
||||
// await(ldloca(CS$0$0001))
|
||||
// ...
|
||||
// IL_7C:
|
||||
// arg_8B_0 = call(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::GetResult, ldloca(CS$0$0001))
|
||||
// initobj(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>, ldloca(CS$0$0001))
|
||||
|
||||
ILExpression loadAwaiter; |
||||
ILVariable awaiterVar; |
||||
if (!body[pos].Match(ILCode.Await, out loadAwaiter)) |
||||
return false; |
||||
if (!loadAwaiter.Match(ILCode.Ldloca, out awaiterVar)) |
||||
return false; |
||||
|
||||
ILVariable stackVar; |
||||
ILExpression stackExpr; |
||||
while (pos >= 1 && body[pos - 1].Match(ILCode.Stloc, out stackVar, out stackExpr)) |
||||
pos--; |
||||
|
||||
// stloc(CS$0$0001, callvirt(class System.Threading.Tasks.Task`1<bool>::GetAwaiter, awaiterExpr)
|
||||
ILExpression getAwaiterCall; |
||||
if (!(pos >= 2 && body[pos - 2].MatchStloc(awaiterVar, out getAwaiterCall))) |
||||
return false; |
||||
MethodReference getAwaiterMethod; |
||||
ILExpression awaitedExpr; |
||||
if (!(getAwaiterCall.Match(ILCode.Call, out getAwaiterMethod, out awaitedExpr) || getAwaiterCall.Match(ILCode.Callvirt, out getAwaiterMethod, out awaitedExpr))) |
||||
return false; |
||||
|
||||
if (awaitedExpr.Code == ILCode.AddressOf) { |
||||
// remove 'AddressOf()' when calling GetAwaiter() on a value type
|
||||
awaitedExpr = awaitedExpr.Arguments[0]; |
||||
} |
||||
|
||||
// brtrue(IL_7C, call(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>::get_IsCompleted, ldloca(CS$0$0001)))
|
||||
ILLabel label; |
||||
ILExpression getIsCompletedCall; |
||||
if (!(pos >= 1 && body[pos - 1].Match(ILCode.Brtrue, out label, out getIsCompletedCall))) |
||||
return false; |
||||
|
||||
int labelPos = body.IndexOf(label); |
||||
if (labelPos < pos) |
||||
return false; |
||||
for (int i = pos + 1; i < labelPos; i++) { |
||||
// validate that we aren't deleting any unexpected instructions -
|
||||
// between the await and the label, there should only be the stack, awaiter and state logic
|
||||
ILExpression expr = body[i] as ILExpression; |
||||
if (expr == null) |
||||
return false; |
||||
switch (expr.Code) { |
||||
case ILCode.Stloc: |
||||
case ILCode.Initobj: |
||||
case ILCode.Stfld: |
||||
case ILCode.Await: |
||||
// e.g.
|
||||
// stloc(CS$0$0001, ldfld(StateMachine::<>u__$awaitere, ldloc(this)))
|
||||
// initobj(valuetype [mscorlib]System.Runtime.CompilerServices.TaskAwaiter`1<bool>, ldloca(CS$0$0002_66))
|
||||
// stfld('<AwaitInLoopCondition>d__d'::<>u__$awaitere, ldloc(this), ldloc(CS$0$0002_66))
|
||||
// stfld('<AwaitInLoopCondition>d__d'::<>1__state, ldloc(this), ldc.i4(-1))
|
||||
break; |
||||
default: |
||||
return false; |
||||
} |
||||
} |
||||
if (labelPos + 1 >= body.Count) |
||||
return false; |
||||
ILExpression resultAssignment = body[labelPos + 1] as ILExpression; |
||||
ILVariable resultVar; |
||||
ILExpression getResultCall; |
||||
bool isResultAssignment = resultAssignment.Match(ILCode.Stloc, out resultVar, out getResultCall); |
||||
if (!isResultAssignment) |
||||
getResultCall = resultAssignment; |
||||
if (!(getResultCall.Operand is MethodReference && ((MethodReference)getResultCall.Operand).Name == "GetResult")) |
||||
return false; |
||||
|
||||
pos -= 2; // also delete 'stloc', 'brtrue' and 'await'
|
||||
body.RemoveRange(pos, labelPos - pos); |
||||
Debug.Assert(body[pos] == label); |
||||
|
||||
pos++; |
||||
if (isResultAssignment) { |
||||
Debug.Assert(body[pos] == resultAssignment); |
||||
resultAssignment.Arguments[0] = new ILExpression(ILCode.Await, null, awaitedExpr); |
||||
} else { |
||||
body[pos] = new ILExpression(ILCode.Await, null, awaitedExpr); |
||||
} |
||||
|
||||
// if the awaiter variable is cleared out in the next instruction, remove that instruction
|
||||
if (IsVariableReset(body.ElementAtOrDefault(pos + 1), awaiterVar)) { |
||||
body.RemoveAt(pos + 1); |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
static bool IsVariableReset(ILNode expr, ILVariable variable) |
||||
{ |
||||
object unused; |
||||
ILExpression ldloca; |
||||
return expr.Match(ILCode.Initobj, out unused, out ldloca) && ldloca.MatchLdloca(variable); |
||||
} |
||||
#endregion
|
||||
} |
||||
} |
@ -1,128 +0,0 @@
@@ -1,128 +0,0 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections; |
||||
using System.Collections.Generic; |
||||
|
||||
namespace ICSharpCode.Decompiler.ILAst |
||||
{ |
||||
/// <summary>
|
||||
/// Dictionary with default values.
|
||||
/// </summary>
|
||||
sealed class DefaultDictionary<TKey, TValue> : IDictionary<TKey, TValue> |
||||
{ |
||||
readonly IDictionary<TKey, TValue> dict; |
||||
readonly Func<TKey, TValue> defaultProvider; |
||||
|
||||
public DefaultDictionary(TValue defaultValue, IDictionary<TKey, TValue> dictionary = null) |
||||
: this(key => defaultValue, dictionary) |
||||
{ |
||||
} |
||||
|
||||
public DefaultDictionary(Func<TKey, TValue> defaultProvider = null, IDictionary<TKey, TValue> dictionary = null) |
||||
{ |
||||
this.dict = dictionary ?? new Dictionary<TKey, TValue>(); |
||||
this.defaultProvider = defaultProvider ?? (key => default(TValue)); |
||||
} |
||||
|
||||
public TValue this[TKey key] { |
||||
get { |
||||
TValue val; |
||||
if (dict.TryGetValue(key, out val)) |
||||
return val; |
||||
else |
||||
return dict[key] = defaultProvider(key); |
||||
} |
||||
set { |
||||
dict[key] = value; |
||||
} |
||||
} |
||||
|
||||
public ICollection<TKey> Keys { |
||||
get { return dict.Keys; } |
||||
} |
||||
|
||||
public ICollection<TValue> Values { |
||||
get { return dict.Values; } |
||||
} |
||||
|
||||
public int Count { |
||||
get { return dict.Count; } |
||||
} |
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly { |
||||
get { return false; } |
||||
} |
||||
|
||||
public bool ContainsKey(TKey key) |
||||
{ |
||||
return dict.ContainsKey(key); |
||||
} |
||||
|
||||
public void Add(TKey key, TValue value) |
||||
{ |
||||
dict.Add(key, value); |
||||
} |
||||
|
||||
public bool Remove(TKey key) |
||||
{ |
||||
return dict.Remove(key); |
||||
} |
||||
|
||||
public bool TryGetValue(TKey key, out TValue value) |
||||
{ |
||||
return dict.TryGetValue(key, out value); |
||||
} |
||||
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item) |
||||
{ |
||||
dict.Add(item); |
||||
} |
||||
|
||||
public void Clear() |
||||
{ |
||||
dict.Clear(); |
||||
} |
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item) |
||||
{ |
||||
return dict.Contains(item); |
||||
} |
||||
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) |
||||
{ |
||||
dict.CopyTo(array, arrayIndex); |
||||
} |
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item) |
||||
{ |
||||
return dict.Remove(item); |
||||
} |
||||
|
||||
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator() |
||||
{ |
||||
return dict.GetEnumerator(); |
||||
} |
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() |
||||
{ |
||||
return dict.GetEnumerator(); |
||||
} |
||||
} |
||||
} |
@ -1,319 +0,0 @@
@@ -1,319 +0,0 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Diagnostics; |
||||
using System.IO; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
|
||||
namespace ICSharpCode.Decompiler.ILAst |
||||
{ |
||||
public class GotoRemoval |
||||
{ |
||||
Dictionary<ILNode, ILNode> parent = new Dictionary<ILNode, ILNode>(); |
||||
Dictionary<ILNode, ILNode> nextSibling = new Dictionary<ILNode, ILNode>(); |
||||
|
||||
public void RemoveGotos(ILBlock method) |
||||
{ |
||||
// Build the navigation data
|
||||
parent[method] = null; |
||||
foreach (ILNode node in method.GetSelfAndChildrenRecursive<ILNode>()) { |
||||
ILNode previousChild = null; |
||||
foreach (ILNode child in node.GetChildren()) { |
||||
if (parent.ContainsKey(child)) |
||||
throw new Exception("The following expression is linked from several locations: " + child.ToString()); |
||||
parent[child] = node; |
||||
if (previousChild != null) |
||||
nextSibling[previousChild] = child; |
||||
previousChild = child; |
||||
} |
||||
if (previousChild != null) |
||||
nextSibling[previousChild] = null; |
||||
} |
||||
|
||||
// Simplify gotos
|
||||
bool modified; |
||||
do { |
||||
modified = false; |
||||
foreach (ILExpression gotoExpr in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.Code == ILCode.Br || e.Code == ILCode.Leave)) { |
||||
modified |= TrySimplifyGoto(gotoExpr); |
||||
} |
||||
} while(modified); |
||||
|
||||
RemoveRedundantCode(method); |
||||
} |
||||
|
||||
public static void RemoveRedundantCode(ILBlock method) |
||||
{ |
||||
// Remove dead lables and nops
|
||||
HashSet<ILLabel> liveLabels = new HashSet<ILLabel>(method.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())); |
||||
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { |
||||
block.Body = block.Body.Where(n => !n.Match(ILCode.Nop) && !(n is ILLabel && !liveLabels.Contains((ILLabel)n))).ToList(); |
||||
} |
||||
|
||||
// Remove redundant continue
|
||||
foreach(ILWhileLoop loop in method.GetSelfAndChildrenRecursive<ILWhileLoop>()) { |
||||
var body = loop.BodyBlock.Body; |
||||
if (body.Count > 0 && body.Last().Match(ILCode.LoopContinue)) { |
||||
body.RemoveAt(body.Count - 1); |
||||
} |
||||
} |
||||
|
||||
// Remove redundant break at the end of case
|
||||
// Remove redundant case blocks altogether
|
||||
foreach(ILSwitch ilSwitch in method.GetSelfAndChildrenRecursive<ILSwitch>()) { |
||||
foreach(ILBlock ilCase in ilSwitch.CaseBlocks) { |
||||
Debug.Assert(ilCase.EntryGoto == null); |
||||
|
||||
int count = ilCase.Body.Count; |
||||
if (count >= 2) { |
||||
if (ilCase.Body[count - 2].IsUnconditionalControlFlow() && |
||||
ilCase.Body[count - 1].Match(ILCode.LoopOrSwitchBreak)) |
||||
{ |
||||
ilCase.Body.RemoveAt(count - 1); |
||||
} |
||||
} |
||||
} |
||||
|
||||
var defaultCase = ilSwitch.CaseBlocks.SingleOrDefault(cb => cb.Values == null); |
||||
// If there is no default block, remove empty case blocks
|
||||
if (defaultCase == null || (defaultCase.Body.Count == 1 && defaultCase.Body.Single().Match(ILCode.LoopOrSwitchBreak))) { |
||||
ilSwitch.CaseBlocks.RemoveAll(b => b.Body.Count == 1 && b.Body.Single().Match(ILCode.LoopOrSwitchBreak)); |
||||
} |
||||
} |
||||
|
||||
// Remove redundant return at the end of method
|
||||
if (method.Body.Count > 0 && method.Body.Last().Match(ILCode.Ret) && ((ILExpression)method.Body.Last()).Arguments.Count == 0) { |
||||
method.Body.RemoveAt(method.Body.Count - 1); |
||||
} |
||||
|
||||
// Remove unreachable return statements
|
||||
bool modified = false; |
||||
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { |
||||
for (int i = 0; i < block.Body.Count - 1;) { |
||||
if (block.Body[i].IsUnconditionalControlFlow() && block.Body[i+1].Match(ILCode.Ret)) { |
||||
modified = true; |
||||
block.Body.RemoveAt(i+1); |
||||
} else { |
||||
i++; |
||||
} |
||||
} |
||||
} |
||||
if (modified) { |
||||
// More removals might be possible
|
||||
new GotoRemoval().RemoveGotos(method); |
||||
} |
||||
} |
||||
|
||||
IEnumerable<ILNode> GetParents(ILNode node) |
||||
{ |
||||
ILNode current = node; |
||||
while(true) { |
||||
current = parent[current]; |
||||
if (current == null) |
||||
yield break; |
||||
yield return current; |
||||
} |
||||
} |
||||
|
||||
bool TrySimplifyGoto(ILExpression gotoExpr) |
||||
{ |
||||
Debug.Assert(gotoExpr.Code == ILCode.Br || gotoExpr.Code == ILCode.Leave); |
||||
Debug.Assert(gotoExpr.Prefixes == null); |
||||
Debug.Assert(gotoExpr.Operand != null); |
||||
|
||||
ILNode target = Enter(gotoExpr, new HashSet<ILNode>()); |
||||
if (target == null) |
||||
return false; |
||||
|
||||
// The gotoExper is marked as visited because we do not want to
|
||||
// walk over node which we plan to modify
|
||||
|
||||
// The simulated path always has to start in the same try-block
|
||||
// in other for the same finally blocks to be executed.
|
||||
|
||||
if (target == Exit(gotoExpr, new HashSet<ILNode>() { gotoExpr })) { |
||||
gotoExpr.Code = ILCode.Nop; |
||||
gotoExpr.Operand = null; |
||||
if (target is ILExpression) |
||||
((ILExpression)target).ILRanges.AddRange(gotoExpr.ILRanges); |
||||
gotoExpr.ILRanges.Clear(); |
||||
return true; |
||||
} |
||||
|
||||
ILNode breakBlock = GetParents(gotoExpr).FirstOrDefault(n => n is ILWhileLoop || n is ILSwitch); |
||||
if (breakBlock != null && target == Exit(breakBlock, new HashSet<ILNode>() { gotoExpr })) { |
||||
gotoExpr.Code = ILCode.LoopOrSwitchBreak; |
||||
gotoExpr.Operand = null; |
||||
return true; |
||||
} |
||||
|
||||
ILNode continueBlock = GetParents(gotoExpr).FirstOrDefault(n => n is ILWhileLoop); |
||||
if (continueBlock != null && target == Enter(continueBlock, new HashSet<ILNode>() { gotoExpr })) { |
||||
gotoExpr.Code = ILCode.LoopContinue; |
||||
gotoExpr.Operand = null; |
||||
return true; |
||||
} |
||||
|
||||
return false; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Get the first expression to be excecuted if the instruction pointer is at the start of the given node.
|
||||
/// Try blocks may not be entered in any way. If possible, the try block is returned as the node to be executed.
|
||||
/// </summary>
|
||||
ILNode Enter(ILNode node, HashSet<ILNode> visitedNodes) |
||||
{ |
||||
if (node == null) |
||||
throw new ArgumentNullException(); |
||||
|
||||
if (!visitedNodes.Add(node)) |
||||
return null; // Infinite loop
|
||||
|
||||
ILLabel label = node as ILLabel; |
||||
if (label != null) { |
||||
return Exit(label, visitedNodes); |
||||
} |
||||
|
||||
ILExpression expr = node as ILExpression; |
||||
if (expr != null) { |
||||
if (expr.Code == ILCode.Br || expr.Code == ILCode.Leave) { |
||||
ILLabel target = (ILLabel)expr.Operand; |
||||
// Early exit - same try-block
|
||||
if (GetParents(expr).OfType<ILTryCatchBlock>().FirstOrDefault() == GetParents(target).OfType<ILTryCatchBlock>().FirstOrDefault()) |
||||
return Enter(target, visitedNodes); |
||||
// Make sure we are not entering any try-block
|
||||
var srcTryBlocks = GetParents(expr).OfType<ILTryCatchBlock>().Reverse().ToList(); |
||||
var dstTryBlocks = GetParents(target).OfType<ILTryCatchBlock>().Reverse().ToList(); |
||||
// Skip blocks that we are already in
|
||||
int i = 0; |
||||
while(i < srcTryBlocks.Count && i < dstTryBlocks.Count && srcTryBlocks[i] == dstTryBlocks[i]) i++; |
||||
if (i == dstTryBlocks.Count) { |
||||
return Enter(target, visitedNodes); |
||||
} else { |
||||
ILTryCatchBlock dstTryBlock = dstTryBlocks[i]; |
||||
// Check that the goto points to the start
|
||||
ILTryCatchBlock current = dstTryBlock; |
||||
while(current != null) { |
||||
foreach(ILNode n in current.TryBlock.Body) { |
||||
if (n is ILLabel) { |
||||
if (n == target) |
||||
return dstTryBlock; |
||||
} else if (!n.Match(ILCode.Nop)) { |
||||
current = n as ILTryCatchBlock; |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
} else if (expr.Code == ILCode.Nop) { |
||||
return Exit(expr, visitedNodes); |
||||
} else if (expr.Code == ILCode.LoopOrSwitchBreak) { |
||||
ILNode breakBlock = GetParents(expr).First(n => n is ILWhileLoop || n is ILSwitch); |
||||
return Exit(breakBlock, new HashSet<ILNode>() { expr }); |
||||
} else if (expr.Code == ILCode.LoopContinue) { |
||||
ILNode continueBlock = GetParents(expr).First(n => n is ILWhileLoop); |
||||
return Enter(continueBlock, new HashSet<ILNode>() { expr }); |
||||
} else { |
||||
return expr; |
||||
} |
||||
} |
||||
|
||||
ILBlock block = node as ILBlock; |
||||
if (block != null) { |
||||
if (block.EntryGoto != null) { |
||||
return Enter(block.EntryGoto, visitedNodes); |
||||
} else if (block.Body.Count > 0) { |
||||
return Enter(block.Body[0], visitedNodes); |
||||
} else { |
||||
return Exit(block, visitedNodes); |
||||
} |
||||
} |
||||
|
||||
ILCondition cond = node as ILCondition; |
||||
if (cond != null) { |
||||
return cond.Condition; |
||||
} |
||||
|
||||
ILWhileLoop loop = node as ILWhileLoop; |
||||
if (loop != null) { |
||||
if (loop.Condition != null) { |
||||
return loop.Condition; |
||||
} else { |
||||
return Enter(loop.BodyBlock, visitedNodes); |
||||
} |
||||
} |
||||
|
||||
ILTryCatchBlock tryCatch = node as ILTryCatchBlock; |
||||
if (tryCatch != null) { |
||||
return tryCatch; |
||||
} |
||||
|
||||
ILSwitch ilSwitch = node as ILSwitch; |
||||
if (ilSwitch != null) { |
||||
return ilSwitch.Condition; |
||||
} |
||||
|
||||
throw new NotSupportedException(node.GetType().ToString()); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Get the first expression to be excecuted if the instruction pointer is at the end of the given node
|
||||
/// </summary>
|
||||
ILNode Exit(ILNode node, HashSet<ILNode> visitedNodes) |
||||
{ |
||||
if (node == null) |
||||
throw new ArgumentNullException(); |
||||
|
||||
ILNode nodeParent = parent[node]; |
||||
if (nodeParent == null) |
||||
return null; // Exited main body
|
||||
|
||||
if (nodeParent is ILBlock) { |
||||
ILNode nextNode = nextSibling[node]; |
||||
if (nextNode != null) { |
||||
return Enter(nextNode, visitedNodes); |
||||
} else { |
||||
return Exit(nodeParent, visitedNodes); |
||||
} |
||||
} |
||||
|
||||
if (nodeParent is ILCondition) { |
||||
return Exit(nodeParent, visitedNodes); |
||||
} |
||||
|
||||
if (nodeParent is ILTryCatchBlock) { |
||||
// Finally blocks are completely ignored.
|
||||
// We rely on the fact that try blocks can not be entered.
|
||||
return Exit(nodeParent, visitedNodes); |
||||
} |
||||
|
||||
if (nodeParent is ILSwitch) { |
||||
return null; // Implicit exit from switch is not allowed
|
||||
} |
||||
|
||||
if (nodeParent is ILWhileLoop) { |
||||
return Enter(nodeParent, visitedNodes); |
||||
} |
||||
|
||||
throw new NotSupportedException(nodeParent.GetType().ToString()); |
||||
} |
||||
} |
||||
} |
@ -1,835 +0,0 @@
@@ -1,835 +0,0 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Concurrent; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using System.Text; |
||||
|
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
using Cecil = Mono.Cecil; |
||||
|
||||
namespace ICSharpCode.Decompiler.ILAst |
||||
{ |
||||
/// <summary>
|
||||
/// Converts stack-based bytecode to variable-based bytecode by calculating use-define chains
|
||||
/// </summary>
|
||||
public class ILAstBuilder |
||||
{ |
||||
/// <summary> Immutable </summary>
|
||||
struct StackSlot |
||||
{ |
||||
public readonly ByteCode[] Definitions; // Reaching definitions of this stack slot
|
||||
public readonly ILVariable LoadFrom; // Variable used for storage of the value
|
||||
|
||||
public StackSlot(ByteCode[] definitions, ILVariable loadFrom) |
||||
{ |
||||
this.Definitions = definitions; |
||||
this.LoadFrom = loadFrom; |
||||
} |
||||
|
||||
public static StackSlot[] ModifyStack(StackSlot[] stack, int popCount, int pushCount, ByteCode pushDefinition) |
||||
{ |
||||
StackSlot[] newStack = new StackSlot[stack.Length - popCount + pushCount]; |
||||
Array.Copy(stack, newStack, stack.Length - popCount); |
||||
for (int i = stack.Length - popCount; i < newStack.Length; i++) { |
||||
newStack[i] = new StackSlot(new [] { pushDefinition }, null); |
||||
} |
||||
return newStack; |
||||
} |
||||
} |
||||
|
||||
/// <summary> Immutable </summary>
|
||||
struct VariableSlot |
||||
{ |
||||
public readonly ByteCode[] Definitions; // Reaching deinitions of this variable
|
||||
public readonly bool UnknownDefinition; // Used for initial state and exceptional control flow
|
||||
|
||||
static readonly VariableSlot UnknownInstance = new VariableSlot(new ByteCode[0], true); |
||||
|
||||
public VariableSlot(ByteCode[] definitions, bool unknownDefinition) |
||||
{ |
||||
this.Definitions = definitions; |
||||
this.UnknownDefinition = unknownDefinition; |
||||
} |
||||
|
||||
public static VariableSlot[] CloneVariableState(VariableSlot[] state) |
||||
{ |
||||
VariableSlot[] clone = new VariableSlot[state.Length]; |
||||
Array.Copy(state, clone, state.Length); |
||||
return clone; |
||||
} |
||||
|
||||
public static VariableSlot[] MakeUknownState(int varCount) |
||||
{ |
||||
VariableSlot[] unknownVariableState = new VariableSlot[varCount]; |
||||
for (int i = 0; i < unknownVariableState.Length; i++) { |
||||
unknownVariableState[i] = UnknownInstance; |
||||
} |
||||
return unknownVariableState; |
||||
} |
||||
} |
||||
|
||||
sealed class ByteCode |
||||
{ |
||||
public ILLabel Label; // Non-null only if needed
|
||||
public int Offset; |
||||
public int EndOffset; |
||||
public ILCode Code; |
||||
public object Operand; |
||||
public int? PopCount; // Null means pop all
|
||||
public int PushCount; |
||||
public string Name { get { return "IL_" + this.Offset.ToString("X2"); } } |
||||
public ByteCode Next; |
||||
public Instruction[] Prefixes; // Non-null only if needed
|
||||
public StackSlot[] StackBefore; // Unique per bytecode; not shared
|
||||
public VariableSlot[] VariablesBefore; // Unique per bytecode; not shared
|
||||
public List<ILVariable> StoreTo; // Store result of instruction to those AST variables
|
||||
|
||||
public bool IsVariableDefinition { |
||||
get { |
||||
return (this.Code == ILCode.Stloc) || (this.Code == ILCode.Ldloca && this.Next != null && this.Next.Code == ILCode.Initobj); |
||||
} |
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
StringBuilder sb = new StringBuilder(); |
||||
|
||||
// Label
|
||||
sb.Append(this.Name); |
||||
sb.Append(':'); |
||||
if (this.Label != null) |
||||
sb.Append('*'); |
||||
|
||||
// Name
|
||||
sb.Append(' '); |
||||
if (this.Prefixes != null) { |
||||
foreach (var prefix in this.Prefixes) { |
||||
sb.Append(prefix.OpCode.Name); |
||||
sb.Append(' '); |
||||
} |
||||
} |
||||
sb.Append(this.Code.GetName()); |
||||
|
||||
if (this.Operand != null) { |
||||
sb.Append(' '); |
||||
if (this.Operand is Instruction) { |
||||
sb.Append("IL_" + ((Instruction)this.Operand).Offset.ToString("X2")); |
||||
} else if (this.Operand is Instruction[]) { |
||||
foreach(Instruction inst in (Instruction[])this.Operand) { |
||||
sb.Append("IL_" + inst.Offset.ToString("X2")); |
||||
sb.Append(" "); |
||||
} |
||||
} else if (this.Operand is ILLabel) { |
||||
sb.Append(((ILLabel)this.Operand).Name); |
||||
} else if (this.Operand is ILLabel[]) { |
||||
foreach(ILLabel label in (ILLabel[])this.Operand) { |
||||
sb.Append(label.Name); |
||||
sb.Append(" "); |
||||
} |
||||
} else { |
||||
sb.Append(this.Operand.ToString()); |
||||
} |
||||
} |
||||
|
||||
if (this.StackBefore != null) { |
||||
sb.Append(" StackBefore={"); |
||||
bool first = true; |
||||
foreach (StackSlot slot in this.StackBefore) { |
||||
if (!first) sb.Append(","); |
||||
bool first2 = true; |
||||
foreach(ByteCode defs in slot.Definitions) { |
||||
if (!first2) sb.Append("|"); |
||||
sb.AppendFormat("IL_{0:X2}", defs.Offset); |
||||
first2 = false; |
||||
} |
||||
first = false; |
||||
} |
||||
sb.Append("}"); |
||||
} |
||||
|
||||
if (this.StoreTo != null && this.StoreTo.Count > 0) { |
||||
sb.Append(" StoreTo={"); |
||||
bool first = true; |
||||
foreach (ILVariable stackVar in this.StoreTo) { |
||||
if (!first) sb.Append(","); |
||||
sb.Append(stackVar.Name); |
||||
first = false; |
||||
} |
||||
sb.Append("}"); |
||||
} |
||||
|
||||
if (this.VariablesBefore != null) { |
||||
sb.Append(" VarsBefore={"); |
||||
bool first = true; |
||||
foreach (VariableSlot varSlot in this.VariablesBefore) { |
||||
if (!first) sb.Append(","); |
||||
if (varSlot.UnknownDefinition) { |
||||
sb.Append("?"); |
||||
} else { |
||||
bool first2 = true; |
||||
foreach (ByteCode storedBy in varSlot.Definitions) { |
||||
if (!first2) sb.Append("|"); |
||||
sb.AppendFormat("IL_{0:X2}", storedBy.Offset); |
||||
first2 = false; |
||||
} |
||||
} |
||||
first = false; |
||||
} |
||||
sb.Append("}"); |
||||
} |
||||
|
||||
return sb.ToString(); |
||||
} |
||||
} |
||||
|
||||
MethodDefinition methodDef; |
||||
bool optimize; |
||||
|
||||
// Virtual instructions to load exception on stack
|
||||
Dictionary<ExceptionHandler, ByteCode> ldexceptions = new Dictionary<ExceptionHandler, ILAstBuilder.ByteCode>(); |
||||
|
||||
DecompilerContext context; |
||||
|
||||
public List<ILNode> Build(MethodDefinition methodDef, bool optimize, DecompilerContext context) |
||||
{ |
||||
this.methodDef = methodDef; |
||||
this.optimize = optimize; |
||||
this.context = context; |
||||
|
||||
if (methodDef.Body.Instructions.Count == 0) return new List<ILNode>(); |
||||
|
||||
List<ByteCode> body = StackAnalysis(methodDef); |
||||
|
||||
List<ILNode> ast = ConvertToAst(body, new HashSet<ExceptionHandler>(methodDef.Body.ExceptionHandlers)); |
||||
|
||||
return ast; |
||||
} |
||||
|
||||
List<ByteCode> StackAnalysis(MethodDefinition methodDef) |
||||
{ |
||||
Dictionary<Instruction, ByteCode> instrToByteCode = new Dictionary<Instruction, ByteCode>(); |
||||
|
||||
// Create temporary structure for the stack analysis
|
||||
List<ByteCode> body = new List<ByteCode>(methodDef.Body.Instructions.Count); |
||||
List<Instruction> prefixes = null; |
||||
foreach(Instruction inst in methodDef.Body.Instructions) { |
||||
if (inst.OpCode.OpCodeType == OpCodeType.Prefix) { |
||||
if (prefixes == null) |
||||
prefixes = new List<Instruction>(1); |
||||
prefixes.Add(inst); |
||||
continue; |
||||
} |
||||
ILCode code = (ILCode)inst.OpCode.Code; |
||||
object operand = inst.Operand; |
||||
ILCodeUtil.ExpandMacro(ref code, ref operand, methodDef.Body); |
||||
ByteCode byteCode = new ByteCode() { |
||||
Offset = inst.Offset, |
||||
EndOffset = inst.Next != null ? inst.Next.Offset : methodDef.Body.CodeSize, |
||||
Code = code, |
||||
Operand = operand, |
||||
PopCount = inst.GetPopDelta(methodDef), |
||||
PushCount = inst.GetPushDelta() |
||||
}; |
||||
if (prefixes != null) { |
||||
instrToByteCode[prefixes[0]] = byteCode; |
||||
byteCode.Offset = prefixes[0].Offset; |
||||
byteCode.Prefixes = prefixes.ToArray(); |
||||
prefixes = null; |
||||
} else { |
||||
instrToByteCode[inst] = byteCode; |
||||
} |
||||
body.Add(byteCode); |
||||
} |
||||
for (int i = 0; i < body.Count - 1; i++) { |
||||
body[i].Next = body[i + 1]; |
||||
} |
||||
|
||||
Stack<ByteCode> agenda = new Stack<ByteCode>(); |
||||
|
||||
int varCount = methodDef.Body.Variables.Count; |
||||
|
||||
var exceptionHandlerStarts = new HashSet<ByteCode>(methodDef.Body.ExceptionHandlers.Select(eh => instrToByteCode[eh.HandlerStart])); |
||||
|
||||
// Add known states
|
||||
if(methodDef.Body.HasExceptionHandlers) { |
||||
foreach(ExceptionHandler ex in methodDef.Body.ExceptionHandlers) { |
||||
ByteCode handlerStart = instrToByteCode[ex.HandlerStart]; |
||||
handlerStart.StackBefore = new StackSlot[0]; |
||||
handlerStart.VariablesBefore = VariableSlot.MakeUknownState(varCount); |
||||
if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter) { |
||||
// Catch and Filter handlers start with the exeption on the stack
|
||||
ByteCode ldexception = new ByteCode() { |
||||
Code = ILCode.Ldexception, |
||||
Operand = ex.CatchType, |
||||
PopCount = 0, |
||||
PushCount = 1 |
||||
}; |
||||
ldexceptions[ex] = ldexception; |
||||
handlerStart.StackBefore = new StackSlot[] { new StackSlot(new [] { ldexception }, null) }; |
||||
} |
||||
agenda.Push(handlerStart); |
||||
|
||||
if (ex.HandlerType == ExceptionHandlerType.Filter) |
||||
{ |
||||
ByteCode filterStart = instrToByteCode[ex.FilterStart]; |
||||
ByteCode ldexception = new ByteCode() { |
||||
Code = ILCode.Ldexception, |
||||
Operand = ex.CatchType, |
||||
PopCount = 0, |
||||
PushCount = 1 |
||||
}; |
||||
// TODO: ldexceptions[ex] = ldexception;
|
||||
filterStart.StackBefore = new StackSlot[] { new StackSlot(new [] { ldexception }, null) }; |
||||
filterStart.VariablesBefore = VariableSlot.MakeUknownState(varCount); |
||||
agenda.Push(filterStart); |
||||
} |
||||
} |
||||
} |
||||
|
||||
body[0].StackBefore = new StackSlot[0]; |
||||
body[0].VariablesBefore = VariableSlot.MakeUknownState(varCount); |
||||
agenda.Push(body[0]); |
||||
|
||||
// Process agenda
|
||||
while(agenda.Count > 0) { |
||||
ByteCode byteCode = agenda.Pop(); |
||||
|
||||
// Calculate new stack
|
||||
StackSlot[] newStack = StackSlot.ModifyStack(byteCode.StackBefore, byteCode.PopCount ?? byteCode.StackBefore.Length, byteCode.PushCount, byteCode); |
||||
|
||||
// Calculate new variable state
|
||||
VariableSlot[] newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore); |
||||
if (byteCode.IsVariableDefinition) { |
||||
newVariableState[((VariableReference)byteCode.Operand).Index] = new VariableSlot(new [] { byteCode }, false); |
||||
} |
||||
|
||||
// After the leave, finally block might have touched the variables
|
||||
if (byteCode.Code == ILCode.Leave) { |
||||
newVariableState = VariableSlot.MakeUknownState(varCount); |
||||
} |
||||
|
||||
// Find all successors
|
||||
List<ByteCode> branchTargets = new List<ByteCode>(); |
||||
if (!byteCode.Code.IsUnconditionalControlFlow()) { |
||||
if (exceptionHandlerStarts.Contains(byteCode.Next)) { |
||||
// Do not fall though down to exception handler
|
||||
// It is invalid IL as per ECMA-335 §12.4.2.8.1, but some obfuscators produce it
|
||||
} else { |
||||
branchTargets.Add(byteCode.Next); |
||||
} |
||||
} |
||||
if (byteCode.Operand is Instruction[]) { |
||||
foreach(Instruction inst in (Instruction[])byteCode.Operand) { |
||||
ByteCode target = instrToByteCode[inst]; |
||||
branchTargets.Add(target); |
||||
// The target of a branch must have label
|
||||
if (target.Label == null) { |
||||
target.Label = new ILLabel() { Name = target.Name }; |
||||
} |
||||
} |
||||
} else if (byteCode.Operand is Instruction) { |
||||
ByteCode target = instrToByteCode[(Instruction)byteCode.Operand]; |
||||
branchTargets.Add(target); |
||||
// The target of a branch must have label
|
||||
if (target.Label == null) { |
||||
target.Label = new ILLabel() { Name = target.Name }; |
||||
} |
||||
} |
||||
|
||||
// Apply the state to successors
|
||||
foreach (ByteCode branchTarget in branchTargets) { |
||||
if (branchTarget.StackBefore == null && branchTarget.VariablesBefore == null) { |
||||
if (branchTargets.Count == 1) { |
||||
branchTarget.StackBefore = newStack; |
||||
branchTarget.VariablesBefore = newVariableState; |
||||
} else { |
||||
// Do not share data for several bytecodes
|
||||
branchTarget.StackBefore = StackSlot.ModifyStack(newStack, 0, 0, null); |
||||
branchTarget.VariablesBefore = VariableSlot.CloneVariableState(newVariableState); |
||||
} |
||||
agenda.Push(branchTarget); |
||||
} else { |
||||
if (branchTarget.StackBefore.Length != newStack.Length) { |
||||
throw new Exception("Inconsistent stack size at " + byteCode.Name); |
||||
} |
||||
|
||||
// Be careful not to change our new data - it might be reused for several branch targets.
|
||||
// In general, be careful that two bytecodes never share data structures.
|
||||
|
||||
bool modified = false; |
||||
|
||||
// Merge stacks - modify the target
|
||||
for (int i = 0; i < newStack.Length; i++) { |
||||
ByteCode[] oldDefs = branchTarget.StackBefore[i].Definitions; |
||||
ByteCode[] newDefs = oldDefs.Union(newStack[i].Definitions); |
||||
if (newDefs.Length > oldDefs.Length) { |
||||
branchTarget.StackBefore[i] = new StackSlot(newDefs, null); |
||||
modified = true; |
||||
} |
||||
} |
||||
|
||||
// Merge variables - modify the target
|
||||
for (int i = 0; i < newVariableState.Length; i++) { |
||||
VariableSlot oldSlot = branchTarget.VariablesBefore[i]; |
||||
VariableSlot newSlot = newVariableState[i]; |
||||
if (!oldSlot.UnknownDefinition) { |
||||
if (newSlot.UnknownDefinition) { |
||||
branchTarget.VariablesBefore[i] = newSlot; |
||||
modified = true; |
||||
} else { |
||||
ByteCode[] oldDefs = oldSlot.Definitions; |
||||
ByteCode[] newDefs = oldDefs.Union(newSlot.Definitions); |
||||
if (newDefs.Length > oldDefs.Length) { |
||||
branchTarget.VariablesBefore[i] = new VariableSlot(newDefs, false); |
||||
modified = true; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (modified) { |
||||
agenda.Push(branchTarget); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Occasionally the compilers or obfuscators generate unreachable code (which might be intentionally invalid)
|
||||
// I believe it is safe to just remove it
|
||||
body.RemoveAll(b => b.StackBefore == null); |
||||
|
||||
// Generate temporary variables to replace stack
|
||||
foreach(ByteCode byteCode in body) { |
||||
int argIdx = 0; |
||||
int popCount = byteCode.PopCount ?? byteCode.StackBefore.Length; |
||||
for (int i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++) { |
||||
ILVariable tmpVar = new ILVariable() { Name = string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), IsGenerated = true }; |
||||
byteCode.StackBefore[i] = new StackSlot(byteCode.StackBefore[i].Definitions, tmpVar); |
||||
foreach(ByteCode pushedBy in byteCode.StackBefore[i].Definitions) { |
||||
if (pushedBy.StoreTo == null) { |
||||
pushedBy.StoreTo = new List<ILVariable>(1); |
||||
} |
||||
pushedBy.StoreTo.Add(tmpVar); |
||||
} |
||||
argIdx++; |
||||
} |
||||
} |
||||
|
||||
// Try to use single temporary variable insted of several if possilbe (especially useful for dup)
|
||||
// This has to be done after all temporary variables are assigned so we know about all loads
|
||||
foreach(ByteCode byteCode in body) { |
||||
if (byteCode.StoreTo != null && byteCode.StoreTo.Count > 1) { |
||||
var locVars = byteCode.StoreTo; |
||||
// For each of the variables, find the location where it is loaded - there should be preciesly one
|
||||
var loadedBy = locVars.Select(locVar => body.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList(); |
||||
// We now know that all the variables have a single load,
|
||||
// Let's make sure that they have also a single store - us
|
||||
if (loadedBy.All(slot => slot.Definitions.Length == 1 && slot.Definitions[0] == byteCode)) { |
||||
// Great - we can reduce everything into single variable
|
||||
ILVariable tmpVar = new ILVariable() { Name = string.Format("expr_{0:X2}", byteCode.Offset), IsGenerated = true }; |
||||
byteCode.StoreTo = new List<ILVariable>() { tmpVar }; |
||||
foreach(ByteCode bc in body) { |
||||
for (int i = 0; i < bc.StackBefore.Length; i++) { |
||||
// Is it one of the variable to be merged?
|
||||
if (locVars.Contains(bc.StackBefore[i].LoadFrom)) { |
||||
// Replace with the new temp variable
|
||||
bc.StackBefore[i] = new StackSlot(bc.StackBefore[i].Definitions, tmpVar); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Split and convert the normal local variables
|
||||
ConvertLocalVariables(body); |
||||
|
||||
// Convert branch targets to labels
|
||||
foreach(ByteCode byteCode in body) { |
||||
if (byteCode.Operand is Instruction[]) { |
||||
List<ILLabel> newOperand = new List<ILLabel>(); |
||||
foreach(Instruction target in (Instruction[])byteCode.Operand) { |
||||
newOperand.Add(instrToByteCode[target].Label); |
||||
} |
||||
byteCode.Operand = newOperand.ToArray(); |
||||
} else if (byteCode.Operand is Instruction) { |
||||
byteCode.Operand = instrToByteCode[(Instruction)byteCode.Operand].Label; |
||||
} |
||||
} |
||||
|
||||
// Convert parameters to ILVariables
|
||||
ConvertParameters(body); |
||||
|
||||
return body; |
||||
} |
||||
|
||||
static bool IsDeterministicLdloca(ByteCode b) |
||||
{ |
||||
var v = b.Operand; |
||||
b = b.Next; |
||||
if (b.Code == ILCode.Initobj) return true; |
||||
|
||||
// instance method calls on value types use the variable ref deterministically
|
||||
int stack = 1; |
||||
while (true) { |
||||
if (b.PopCount == null) return false; |
||||
stack -= b.PopCount.GetValueOrDefault(); |
||||
if (stack == 0) break; |
||||
if (stack < 0) return false; |
||||
if (b.Code.IsConditionalControlFlow() || b.Code.IsUnconditionalControlFlow()) return false; |
||||
switch (b.Code) { |
||||
case ILCode.Ldloc: |
||||
case ILCode.Ldloca: |
||||
case ILCode.Stloc: |
||||
if (b.Operand == v) return false; |
||||
break; |
||||
} |
||||
stack += b.PushCount; |
||||
b = b.Next; |
||||
if (b == null) return false; |
||||
} |
||||
if (b.Code == ILCode.Ldfld || b.Code == ILCode.Stfld) |
||||
return true; |
||||
return (b.Code == ILCode.Call || b.Code == ILCode.Callvirt) && ((MethodReference)b.Operand).HasThis; |
||||
} |
||||
|
||||
sealed class VariableInfo |
||||
{ |
||||
public ILVariable Variable; |
||||
public List<ByteCode> Defs; |
||||
public List<ByteCode> Uses; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// If possible, separates local variables into several independent variables.
|
||||
/// It should undo any compilers merging.
|
||||
/// </summary>
|
||||
void ConvertLocalVariables(List<ByteCode> body) |
||||
{ |
||||
foreach(VariableDefinition varDef in methodDef.Body.Variables) { |
||||
|
||||
// Find all definitions and uses of this variable
|
||||
var defs = body.Where(b => b.Operand == varDef && b.IsVariableDefinition).ToList(); |
||||
var uses = body.Where(b => b.Operand == varDef && !b.IsVariableDefinition).ToList(); |
||||
|
||||
List<VariableInfo> newVars; |
||||
|
||||
// If the variable is pinned, use single variable.
|
||||
// If any of the uses is from unknown definition, use single variable
|
||||
// If any of the uses is ldloca with a nondeterministic usage pattern, use single variable
|
||||
if (!optimize || varDef.IsPinned || uses.Any(b => b.VariablesBefore[varDef.Index].UnknownDefinition || (b.Code == ILCode.Ldloca && !IsDeterministicLdloca(b)))) { |
||||
newVars = new List<VariableInfo>(1) { new VariableInfo() { |
||||
Variable = new ILVariable() { |
||||
Name = string.IsNullOrEmpty(varDef.Name) ? "var_" + varDef.Index : varDef.Name, |
||||
Type = varDef.IsPinned ? ((PinnedType)varDef.VariableType).ElementType : varDef.VariableType, |
||||
OriginalVariable = varDef |
||||
}, |
||||
Defs = defs, |
||||
Uses = uses |
||||
}}; |
||||
} else { |
||||
// Create a new variable for each definition
|
||||
newVars = defs.Select(def => new VariableInfo() { |
||||
Variable = new ILVariable() { |
||||
Name = (string.IsNullOrEmpty(varDef.Name) ? "var_" + varDef.Index : varDef.Name) + "_" + def.Offset.ToString("X2"), |
||||
Type = varDef.VariableType, |
||||
OriginalVariable = varDef |
||||
}, |
||||
Defs = new List<ByteCode>() { def }, |
||||
Uses = new List<ByteCode>() |
||||
}).ToList(); |
||||
|
||||
// VB.NET uses the 'init' to allow use of uninitialized variables.
|
||||
// We do not really care about them too much - if the original variable
|
||||
// was uninitialized at that point it means that no store was called and
|
||||
// thus all our new variables must be uninitialized as well.
|
||||
// So it does not matter which one we load.
|
||||
|
||||
// TODO: We should add explicit initialization so that C# code compiles.
|
||||
// Remember to handle cases where one path inits the variable, but other does not.
|
||||
|
||||
// Add loads to the data structure; merge variables if necessary
|
||||
foreach(ByteCode use in uses) { |
||||
ByteCode[] useDefs = use.VariablesBefore[varDef.Index].Definitions; |
||||
if (useDefs.Length == 1) { |
||||
VariableInfo newVar = newVars.Single(v => v.Defs.Contains(useDefs[0])); |
||||
newVar.Uses.Add(use); |
||||
} else { |
||||
List<VariableInfo> mergeVars = newVars.Where(v => v.Defs.Intersect(useDefs).Any()).ToList(); |
||||
VariableInfo mergedVar = new VariableInfo() { |
||||
Variable = mergeVars[0].Variable, |
||||
Defs = mergeVars.SelectMany(v => v.Defs).ToList(), |
||||
Uses = mergeVars.SelectMany(v => v.Uses).ToList() |
||||
}; |
||||
mergedVar.Uses.Add(use); |
||||
newVars = newVars.Except(mergeVars).ToList(); |
||||
newVars.Add(mergedVar); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Set bytecode operands
|
||||
foreach(VariableInfo newVar in newVars) { |
||||
foreach(ByteCode def in newVar.Defs) { |
||||
def.Operand = newVar.Variable; |
||||
} |
||||
foreach(ByteCode use in newVar.Uses) { |
||||
use.Operand = newVar.Variable; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
public List<ILVariable> Parameters = new List<ILVariable>(); |
||||
|
||||
void ConvertParameters(List<ByteCode> body) |
||||
{ |
||||
ILVariable thisParameter = null; |
||||
if (methodDef.HasThis) { |
||||
TypeReference type = methodDef.DeclaringType; |
||||
thisParameter = new ILVariable(); |
||||
thisParameter.Type = type.IsValueType ? new ByReferenceType(type) : type; |
||||
thisParameter.Name = "this"; |
||||
thisParameter.OriginalParameter = methodDef.Body.ThisParameter; |
||||
} |
||||
foreach (ParameterDefinition p in methodDef.Parameters) { |
||||
this.Parameters.Add(new ILVariable { Type = p.ParameterType, Name = p.Name, OriginalParameter = p }); |
||||
} |
||||
if (this.Parameters.Count > 0 && (methodDef.IsSetter || methodDef.IsAddOn || methodDef.IsRemoveOn)) { |
||||
// last parameter must be 'value', so rename it
|
||||
this.Parameters.Last().Name = "value"; |
||||
} |
||||
foreach (ByteCode byteCode in body) { |
||||
ParameterDefinition p; |
||||
switch (byteCode.Code) { |
||||
case ILCode.__Ldarg: |
||||
p = (ParameterDefinition)byteCode.Operand; |
||||
byteCode.Code = ILCode.Ldloc; |
||||
byteCode.Operand = p.Index < 0 ? thisParameter : this.Parameters[p.Index]; |
||||
break; |
||||
case ILCode.__Starg: |
||||
p = (ParameterDefinition)byteCode.Operand; |
||||
byteCode.Code = ILCode.Stloc; |
||||
byteCode.Operand = p.Index < 0 ? thisParameter : this.Parameters[p.Index]; |
||||
break; |
||||
case ILCode.__Ldarga: |
||||
p = (ParameterDefinition)byteCode.Operand; |
||||
byteCode.Code = ILCode.Ldloca; |
||||
byteCode.Operand = p.Index < 0 ? thisParameter : this.Parameters[p.Index]; |
||||
break; |
||||
} |
||||
} |
||||
if (thisParameter != null) |
||||
this.Parameters.Add(thisParameter); |
||||
} |
||||
|
||||
List<ILNode> ConvertToAst(List<ByteCode> body, HashSet<ExceptionHandler> ehs) |
||||
{ |
||||
List<ILNode> ast = new List<ILNode>(); |
||||
|
||||
while (ehs.Any()) { |
||||
ILTryCatchBlock tryCatchBlock = new ILTryCatchBlock(); |
||||
|
||||
// Find the first and widest scope
|
||||
int tryStart = ehs.Min(eh => eh.TryStart.Offset); |
||||
int tryEnd = ehs.Where(eh => eh.TryStart.Offset == tryStart).Max(eh => eh.TryEnd.Offset); |
||||
var handlers = ehs.Where(eh => eh.TryStart.Offset == tryStart && eh.TryEnd.Offset == tryEnd).ToList(); |
||||
|
||||
// Remember that any part of the body migt have been removed due to unreachability
|
||||
|
||||
// Cut all instructions up to the try block
|
||||
{ |
||||
int tryStartIdx = 0; |
||||
while (tryStartIdx < body.Count && body[tryStartIdx].Offset < tryStart) tryStartIdx++; |
||||
ast.AddRange(ConvertToAst(body.CutRange(0, tryStartIdx))); |
||||
} |
||||
|
||||
// Cut the try block
|
||||
{ |
||||
HashSet<ExceptionHandler> nestedEHs = new HashSet<ExceptionHandler>(ehs.Where(eh => (tryStart <= eh.TryStart.Offset && eh.TryEnd.Offset < tryEnd) || (tryStart < eh.TryStart.Offset && eh.TryEnd.Offset <= tryEnd))); |
||||
ehs.ExceptWith(nestedEHs); |
||||
int tryEndIdx = 0; |
||||
while (tryEndIdx < body.Count && body[tryEndIdx].Offset < tryEnd) tryEndIdx++; |
||||
tryCatchBlock.TryBlock = new ILBlock(ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs)); |
||||
} |
||||
|
||||
// Cut all handlers
|
||||
tryCatchBlock.CatchBlocks = new List<ILTryCatchBlock.CatchBlock>(); |
||||
foreach(ExceptionHandler eh in handlers) { |
||||
int handlerEndOffset = eh.HandlerEnd == null ? methodDef.Body.CodeSize : eh.HandlerEnd.Offset; |
||||
int startIdx = 0; |
||||
while (startIdx < body.Count && body[startIdx].Offset < eh.HandlerStart.Offset) startIdx++; |
||||
int endIdx = 0; |
||||
while (endIdx < body.Count && body[endIdx].Offset < handlerEndOffset) endIdx++; |
||||
HashSet<ExceptionHandler> nestedEHs = new HashSet<ExceptionHandler>(ehs.Where(e => (eh.HandlerStart.Offset <= e.TryStart.Offset && e.TryEnd.Offset < handlerEndOffset) || (eh.HandlerStart.Offset < e.TryStart.Offset && e.TryEnd.Offset <= handlerEndOffset))); |
||||
ehs.ExceptWith(nestedEHs); |
||||
List<ILNode> handlerAst = ConvertToAst(body.CutRange(startIdx, endIdx - startIdx), nestedEHs); |
||||
if (eh.HandlerType == ExceptionHandlerType.Catch) { |
||||
ILTryCatchBlock.CatchBlock catchBlock = new ILTryCatchBlock.CatchBlock() { |
||||
ExceptionType = eh.CatchType, |
||||
Body = handlerAst |
||||
}; |
||||
// Handle the automatically pushed exception on the stack
|
||||
ByteCode ldexception = ldexceptions[eh]; |
||||
if (ldexception.StoreTo == null || ldexception.StoreTo.Count == 0) { |
||||
// Exception is not used
|
||||
catchBlock.ExceptionVariable = null; |
||||
} else if (ldexception.StoreTo.Count == 1) { |
||||
ILExpression first = catchBlock.Body[0] as ILExpression; |
||||
if (first != null && |
||||
first.Code == ILCode.Pop && |
||||
first.Arguments[0].Code == ILCode.Ldloc && |
||||
first.Arguments[0].Operand == ldexception.StoreTo[0]) |
||||
{ |
||||
// The exception is just poped - optimize it all away;
|
||||
if (context.Settings.AlwaysGenerateExceptionVariableForCatchBlocks) |
||||
catchBlock.ExceptionVariable = new ILVariable() { Name = "ex_" + eh.HandlerStart.Offset.ToString("X2"), IsGenerated = true }; |
||||
else |
||||
catchBlock.ExceptionVariable = null; |
||||
catchBlock.Body.RemoveAt(0); |
||||
} else { |
||||
catchBlock.ExceptionVariable = ldexception.StoreTo[0]; |
||||
} |
||||
} else { |
||||
ILVariable exTemp = new ILVariable() { Name = "ex_" + eh.HandlerStart.Offset.ToString("X2"), IsGenerated = true }; |
||||
catchBlock.ExceptionVariable = exTemp; |
||||
foreach(ILVariable storeTo in ldexception.StoreTo) { |
||||
catchBlock.Body.Insert(0, new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, exTemp))); |
||||
} |
||||
} |
||||
tryCatchBlock.CatchBlocks.Add(catchBlock); |
||||
} else if (eh.HandlerType == ExceptionHandlerType.Finally) { |
||||
tryCatchBlock.FinallyBlock = new ILBlock(handlerAst); |
||||
} else if (eh.HandlerType == ExceptionHandlerType.Fault) { |
||||
tryCatchBlock.FaultBlock = new ILBlock(handlerAst); |
||||
} else { |
||||
// TODO: ExceptionHandlerType.Filter
|
||||
} |
||||
} |
||||
|
||||
ehs.ExceptWith(handlers); |
||||
|
||||
ast.Add(tryCatchBlock); |
||||
} |
||||
|
||||
// Add whatever is left
|
||||
ast.AddRange(ConvertToAst(body)); |
||||
|
||||
return ast; |
||||
} |
||||
|
||||
List<ILNode> ConvertToAst(List<ByteCode> body) |
||||
{ |
||||
List<ILNode> ast = new List<ILNode>(); |
||||
|
||||
// Convert stack-based IL code to ILAst tree
|
||||
foreach(ByteCode byteCode in body) { |
||||
ILRange ilRange = new ILRange(byteCode.Offset, byteCode.EndOffset); |
||||
|
||||
if (byteCode.StackBefore == null) { |
||||
// Unreachable code
|
||||
continue; |
||||
} |
||||
|
||||
ILExpression expr = new ILExpression(byteCode.Code, byteCode.Operand); |
||||
expr.ILRanges.Add(ilRange); |
||||
if (byteCode.Prefixes != null && byteCode.Prefixes.Length > 0) { |
||||
ILExpressionPrefix[] prefixes = new ILExpressionPrefix[byteCode.Prefixes.Length]; |
||||
for (int i = 0; i < prefixes.Length; i++) { |
||||
prefixes[i] = new ILExpressionPrefix((ILCode)byteCode.Prefixes[i].OpCode.Code, byteCode.Prefixes[i].Operand); |
||||
} |
||||
expr.Prefixes = prefixes; |
||||
} |
||||
|
||||
// Label for this instruction
|
||||
if (byteCode.Label != null) { |
||||
ast.Add(byteCode.Label); |
||||
} |
||||
|
||||
// Reference arguments using temporary variables
|
||||
int popCount = byteCode.PopCount ?? byteCode.StackBefore.Length; |
||||
for (int i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++) { |
||||
StackSlot slot = byteCode.StackBefore[i]; |
||||
expr.Arguments.Add(new ILExpression(ILCode.Ldloc, slot.LoadFrom)); |
||||
} |
||||
|
||||
// Store the result to temporary variable(s) if needed
|
||||
if (byteCode.StoreTo == null || byteCode.StoreTo.Count == 0) { |
||||
ast.Add(expr); |
||||
} else if (byteCode.StoreTo.Count == 1) { |
||||
ast.Add(new ILExpression(ILCode.Stloc, byteCode.StoreTo[0], expr)); |
||||
} else { |
||||
ILVariable tmpVar = new ILVariable() { Name = "expr_" + byteCode.Offset.ToString("X2"), IsGenerated = true }; |
||||
ast.Add(new ILExpression(ILCode.Stloc, tmpVar, expr)); |
||||
foreach(ILVariable storeTo in byteCode.StoreTo.AsEnumerable().Reverse()) { |
||||
ast.Add(new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, tmpVar))); |
||||
} |
||||
} |
||||
} |
||||
|
||||
return ast; |
||||
} |
||||
} |
||||
|
||||
public static class ILAstBuilderExtensionMethods |
||||
{ |
||||
public static List<T> CutRange<T>(this List<T> list, int start, int count) |
||||
{ |
||||
List<T> ret = new List<T>(count); |
||||
for (int i = 0; i < count; i++) { |
||||
ret.Add(list[start + i]); |
||||
} |
||||
list.RemoveRange(start, count); |
||||
return ret; |
||||
} |
||||
|
||||
public static T[] Union<T>(this T[] a, T b) |
||||
{ |
||||
if (a.Length == 0) |
||||
return new[] { b }; |
||||
if (Array.IndexOf(a, b) >= 0) |
||||
return a; |
||||
var res = new T[a.Length + 1]; |
||||
Array.Copy(a, 0, res, 0, a.Length); |
||||
res[res.Length - 1] = b; |
||||
return res; |
||||
} |
||||
|
||||
public static T[] Union<T>(this T[] a, T[] b) |
||||
{ |
||||
if (a == b) |
||||
return a; |
||||
if (a.Length == 0) |
||||
return b; |
||||
if (b.Length == 0) |
||||
return a; |
||||
if (a.Length == 1) { |
||||
if (b.Length == 1) |
||||
return a[0].Equals(b[0]) ? a : new[] { a[0], b[0] }; |
||||
return b.Union(a[0]); |
||||
} |
||||
if (b.Length == 1) |
||||
return a.Union(b[0]); |
||||
return Enumerable.Union(a, b).ToArray(); |
||||
} |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -1,600 +0,0 @@
@@ -1,600 +0,0 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.IO; |
||||
using System.Linq; |
||||
using System.Text; |
||||
using ICSharpCode.Decompiler; |
||||
using ICSharpCode.Decompiler.Disassembler; |
||||
using ICSharpCode.NRefactory.Utils; |
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
using Cecil = Mono.Cecil; |
||||
|
||||
namespace ICSharpCode.Decompiler.ILAst |
||||
{ |
||||
public abstract class ILNode |
||||
{ |
||||
public IEnumerable<T> GetSelfAndChildrenRecursive<T>(Func<T, bool> predicate = null) where T: ILNode |
||||
{ |
||||
List<T> result = new List<T>(16); |
||||
AccumulateSelfAndChildrenRecursive(result, predicate); |
||||
return result; |
||||
} |
||||
|
||||
void AccumulateSelfAndChildrenRecursive<T>(List<T> list, Func<T, bool> predicate) where T:ILNode |
||||
{ |
||||
// Note: RemoveEndFinally depends on self coming before children
|
||||
T thisAsT = this as T; |
||||
if (thisAsT != null && (predicate == null || predicate(thisAsT))) |
||||
list.Add(thisAsT); |
||||
foreach (ILNode node in this.GetChildren()) { |
||||
if (node != null) |
||||
node.AccumulateSelfAndChildrenRecursive(list, predicate); |
||||
} |
||||
} |
||||
|
||||
public virtual IEnumerable<ILNode> GetChildren() |
||||
{ |
||||
yield break; |
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
StringWriter w = new StringWriter(); |
||||
WriteTo(new PlainTextOutput(w)); |
||||
return w.ToString().Replace("\r\n", "; "); |
||||
} |
||||
|
||||
public abstract void WriteTo(ITextOutput output); |
||||
} |
||||
|
||||
public class ILBlock: ILNode |
||||
{ |
||||
public ILExpression EntryGoto; |
||||
|
||||
public List<ILNode> Body; |
||||
|
||||
public ILBlock(params ILNode[] body) |
||||
{ |
||||
this.Body = new List<ILNode>(body); |
||||
} |
||||
|
||||
public ILBlock(List<ILNode> body) |
||||
{ |
||||
this.Body = body; |
||||
} |
||||
|
||||
public override IEnumerable<ILNode> GetChildren() |
||||
{ |
||||
if (this.EntryGoto != null) |
||||
yield return this.EntryGoto; |
||||
foreach(ILNode child in this.Body) { |
||||
yield return child; |
||||
} |
||||
} |
||||
|
||||
public override void WriteTo(ITextOutput output) |
||||
{ |
||||
foreach(ILNode child in this.GetChildren()) { |
||||
child.WriteTo(output); |
||||
output.WriteLine(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public class ILBasicBlock: ILNode |
||||
{ |
||||
/// <remarks> Body has to start with a label and end with unconditional control flow </remarks>
|
||||
public List<ILNode> Body = new List<ILNode>(); |
||||
|
||||
public override IEnumerable<ILNode> GetChildren() |
||||
{ |
||||
return this.Body; |
||||
} |
||||
|
||||
public override void WriteTo(ITextOutput output) |
||||
{ |
||||
foreach(ILNode child in this.GetChildren()) { |
||||
child.WriteTo(output); |
||||
output.WriteLine(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public class ILLabel: ILNode |
||||
{ |
||||
public string Name; |
||||
|
||||
public override void WriteTo(ITextOutput output) |
||||
{ |
||||
output.WriteDefinition(Name + ":", this); |
||||
} |
||||
} |
||||
|
||||
public class ILTryCatchBlock: ILNode |
||||
{ |
||||
public class CatchBlock: ILBlock |
||||
{ |
||||
public TypeReference ExceptionType; |
||||
public ILVariable ExceptionVariable; |
||||
|
||||
public override void WriteTo(ITextOutput output) |
||||
{ |
||||
output.Write("catch "); |
||||
output.WriteReference(ExceptionType.FullName, ExceptionType); |
||||
if (ExceptionVariable != null) { |
||||
output.Write(' '); |
||||
output.Write(ExceptionVariable.Name); |
||||
} |
||||
output.WriteLine(" {"); |
||||
output.Indent(); |
||||
base.WriteTo(output); |
||||
output.Unindent(); |
||||
output.WriteLine("}"); |
||||
} |
||||
} |
||||
|
||||
public ILBlock TryBlock; |
||||
public List<CatchBlock> CatchBlocks; |
||||
public ILBlock FinallyBlock; |
||||
public ILBlock FaultBlock; |
||||
|
||||
public override IEnumerable<ILNode> GetChildren() |
||||
{ |
||||
if (this.TryBlock != null) |
||||
yield return this.TryBlock; |
||||
foreach (var catchBlock in this.CatchBlocks) { |
||||
yield return catchBlock; |
||||
} |
||||
if (this.FaultBlock != null) |
||||
yield return this.FaultBlock; |
||||
if (this.FinallyBlock != null) |
||||
yield return this.FinallyBlock; |
||||
} |
||||
|
||||
public override void WriteTo(ITextOutput output) |
||||
{ |
||||
output.WriteLine(".try {"); |
||||
output.Indent(); |
||||
TryBlock.WriteTo(output); |
||||
output.Unindent(); |
||||
output.WriteLine("}"); |
||||
foreach (CatchBlock block in CatchBlocks) { |
||||
block.WriteTo(output); |
||||
} |
||||
if (FaultBlock != null) { |
||||
output.WriteLine("fault {"); |
||||
output.Indent(); |
||||
FaultBlock.WriteTo(output); |
||||
output.Unindent(); |
||||
output.WriteLine("}"); |
||||
} |
||||
if (FinallyBlock != null) { |
||||
output.WriteLine("finally {"); |
||||
output.Indent(); |
||||
FinallyBlock.WriteTo(output); |
||||
output.Unindent(); |
||||
output.WriteLine("}"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public class ILVariable |
||||
{ |
||||
public string Name; |
||||
public bool IsGenerated; |
||||
public TypeReference Type; |
||||
public VariableDefinition OriginalVariable; |
||||
public ParameterDefinition OriginalParameter; |
||||
|
||||
public bool IsPinned { |
||||
get { return OriginalVariable != null && OriginalVariable.IsPinned; } |
||||
} |
||||
|
||||
public bool IsParameter { |
||||
get { return OriginalParameter != null; } |
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
return Name; |
||||
} |
||||
} |
||||
|
||||
public struct ILRange |
||||
{ |
||||
public readonly int From; |
||||
public readonly int To; // Exlusive
|
||||
|
||||
public ILRange(int @from, int to) |
||||
{ |
||||
this.From = @from; |
||||
this.To = to; |
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
return string.Format("{0:X2}-{1:X2}", From, To); |
||||
} |
||||
|
||||
public static List<ILRange> OrderAndJoin(IEnumerable<ILRange> input) |
||||
{ |
||||
if (input == null) |
||||
throw new ArgumentNullException("Input is null!"); |
||||
|
||||
List<ILRange> result = new List<ILRange>(); |
||||
foreach(ILRange curr in input.OrderBy(r => r.From)) { |
||||
if (result.Count > 0) { |
||||
// Merge consequtive ranges if possible
|
||||
ILRange last = result[result.Count - 1]; |
||||
if (curr.From <= last.To) { |
||||
result[result.Count - 1] = new ILRange(last.From, Math.Max(last.To, curr.To)); |
||||
continue; |
||||
} |
||||
} |
||||
result.Add(curr); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
public static List<ILRange> Invert(IEnumerable<ILRange> input, int codeSize) |
||||
{ |
||||
if (input == null) |
||||
throw new ArgumentNullException("Input is null!"); |
||||
|
||||
if (codeSize <= 0) |
||||
throw new ArgumentException("Code size must be grater than 0"); |
||||
|
||||
List<ILRange> ordered = OrderAndJoin(input); |
||||
List<ILRange> result = new List<ILRange>(ordered.Count + 1); |
||||
if (ordered.Count == 0) { |
||||
result.Add(new ILRange(0, codeSize)); |
||||
} else { |
||||
// Gap before the first element
|
||||
if (ordered.First().From != 0) |
||||
result.Add(new ILRange(0, ordered.First().From)); |
||||
|
||||
// Gaps between elements
|
||||
for (int i = 0; i < ordered.Count - 1; i++) |
||||
result.Add(new ILRange(ordered[i].To, ordered[i + 1].From)); |
||||
|
||||
// Gap after the last element
|
||||
Debug.Assert(ordered.Last().To <= codeSize); |
||||
if (ordered.Last().To != codeSize) |
||||
result.Add(new ILRange(ordered.Last().To, codeSize)); |
||||
} |
||||
return result; |
||||
} |
||||
} |
||||
|
||||
public class ILExpressionPrefix |
||||
{ |
||||
public readonly ILCode Code; |
||||
public readonly object Operand; |
||||
|
||||
public ILExpressionPrefix(ILCode code, object operand = null) |
||||
{ |
||||
this.Code = code; |
||||
this.Operand = operand; |
||||
} |
||||
} |
||||
|
||||
public class ILExpression : ILNode |
||||
{ |
||||
public ILCode Code { get; set; } |
||||
public object Operand { get; set; } |
||||
public List<ILExpression> Arguments { get; set; } |
||||
public ILExpressionPrefix[] Prefixes { get; set; } |
||||
// Mapping to the original instructions (useful for debugging)
|
||||
public List<ILRange> ILRanges { get; set; } |
||||
|
||||
public TypeReference ExpectedType { get; set; } |
||||
public TypeReference InferredType { get; set; } |
||||
|
||||
public static readonly object AnyOperand = new object(); |
||||
|
||||
public ILExpression(ILCode code, object operand, List<ILExpression> args) |
||||
{ |
||||
if (operand is ILExpression) |
||||
throw new ArgumentException("operand"); |
||||
|
||||
this.Code = code; |
||||
this.Operand = operand; |
||||
this.Arguments = new List<ILExpression>(args); |
||||
this.ILRanges = new List<ILRange>(1); |
||||
} |
||||
|
||||
public ILExpression(ILCode code, object operand, params ILExpression[] args) |
||||
{ |
||||
if (operand is ILExpression) |
||||
throw new ArgumentException("operand"); |
||||
|
||||
this.Code = code; |
||||
this.Operand = operand; |
||||
this.Arguments = new List<ILExpression>(args); |
||||
this.ILRanges = new List<ILRange>(1); |
||||
} |
||||
|
||||
public void AddPrefix(ILExpressionPrefix prefix) |
||||
{ |
||||
ILExpressionPrefix[] arr = this.Prefixes; |
||||
if (arr == null) |
||||
arr = new ILExpressionPrefix[1]; |
||||
else |
||||
Array.Resize(ref arr, arr.Length + 1); |
||||
arr[arr.Length - 1] = prefix; |
||||
this.Prefixes = arr; |
||||
} |
||||
|
||||
public ILExpressionPrefix GetPrefix(ILCode code) |
||||
{ |
||||
var prefixes = this.Prefixes; |
||||
if (prefixes != null) { |
||||
foreach (ILExpressionPrefix p in prefixes) { |
||||
if (p.Code == code) |
||||
return p; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public override IEnumerable<ILNode> GetChildren() |
||||
{ |
||||
return Arguments; |
||||
} |
||||
|
||||
public bool IsBranch() |
||||
{ |
||||
return this.Operand is ILLabel || this.Operand is ILLabel[]; |
||||
} |
||||
|
||||
public IEnumerable<ILLabel> GetBranchTargets() |
||||
{ |
||||
if (this.Operand is ILLabel) { |
||||
return new ILLabel[] { (ILLabel)this.Operand }; |
||||
} else if (this.Operand is ILLabel[]) { |
||||
return (ILLabel[])this.Operand; |
||||
} else { |
||||
return new ILLabel[] { }; |
||||
} |
||||
} |
||||
|
||||
public override void WriteTo(ITextOutput output) |
||||
{ |
||||
if (Operand is ILVariable && ((ILVariable)Operand).IsGenerated) { |
||||
if (Code == ILCode.Stloc && this.InferredType == null) { |
||||
output.Write(((ILVariable)Operand).Name); |
||||
output.Write(" = "); |
||||
Arguments.First().WriteTo(output); |
||||
return; |
||||
} else if (Code == ILCode.Ldloc) { |
||||
output.Write(((ILVariable)Operand).Name); |
||||
if (this.InferredType != null) { |
||||
output.Write(':'); |
||||
this.InferredType.WriteTo(output, ILNameSyntax.ShortTypeName); |
||||
if (this.ExpectedType != null && this.ExpectedType.FullName != this.InferredType.FullName) { |
||||
output.Write("[exp:"); |
||||
this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName); |
||||
output.Write(']'); |
||||
} |
||||
} |
||||
return; |
||||
} |
||||
} |
||||
|
||||
if (this.Prefixes != null) { |
||||
foreach (var prefix in this.Prefixes) { |
||||
output.Write(prefix.Code.GetName()); |
||||
output.Write(". "); |
||||
} |
||||
} |
||||
|
||||
output.Write(Code.GetName()); |
||||
if (this.InferredType != null) { |
||||
output.Write(':'); |
||||
this.InferredType.WriteTo(output, ILNameSyntax.ShortTypeName); |
||||
if (this.ExpectedType != null && this.ExpectedType.FullName != this.InferredType.FullName) { |
||||
output.Write("[exp:"); |
||||
this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName); |
||||
output.Write(']'); |
||||
} |
||||
} else if (this.ExpectedType != null) { |
||||
output.Write("[exp:"); |
||||
this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName); |
||||
output.Write(']'); |
||||
} |
||||
output.Write('('); |
||||
bool first = true; |
||||
if (Operand != null) { |
||||
if (Operand is ILLabel) { |
||||
output.WriteReference(((ILLabel)Operand).Name, Operand); |
||||
} else if (Operand is ILLabel[]) { |
||||
ILLabel[] labels = (ILLabel[])Operand; |
||||
for (int i = 0; i < labels.Length; i++) { |
||||
if (i > 0) |
||||
output.Write(", "); |
||||
output.WriteReference(labels[i].Name, labels[i]); |
||||
} |
||||
} else if (Operand is MethodReference) { |
||||
MethodReference method = (MethodReference)Operand; |
||||
if (method.DeclaringType != null) { |
||||
method.DeclaringType.WriteTo(output, ILNameSyntax.ShortTypeName); |
||||
output.Write("::"); |
||||
} |
||||
output.WriteReference(method.Name, method); |
||||
} else if (Operand is FieldReference) { |
||||
FieldReference field = (FieldReference)Operand; |
||||
field.DeclaringType.WriteTo(output, ILNameSyntax.ShortTypeName); |
||||
output.Write("::"); |
||||
output.WriteReference(field.Name, field); |
||||
} else { |
||||
DisassemblerHelpers.WriteOperand(output, Operand); |
||||
} |
||||
first = false; |
||||
} |
||||
foreach (ILExpression arg in this.Arguments) { |
||||
if (!first) output.Write(", "); |
||||
arg.WriteTo(output); |
||||
first = false; |
||||
} |
||||
output.Write(')'); |
||||
} |
||||
} |
||||
|
||||
public class ILWhileLoop : ILNode |
||||
{ |
||||
public ILExpression Condition; |
||||
public ILBlock BodyBlock; |
||||
|
||||
public override IEnumerable<ILNode> GetChildren() |
||||
{ |
||||
if (this.Condition != null) |
||||
yield return this.Condition; |
||||
if (this.BodyBlock != null) |
||||
yield return this.BodyBlock; |
||||
} |
||||
|
||||
public override void WriteTo(ITextOutput output) |
||||
{ |
||||
output.WriteLine(""); |
||||
output.Write("loop ("); |
||||
if (this.Condition != null) |
||||
this.Condition.WriteTo(output); |
||||
output.WriteLine(") {"); |
||||
output.Indent(); |
||||
this.BodyBlock.WriteTo(output); |
||||
output.Unindent(); |
||||
output.WriteLine("}"); |
||||
} |
||||
} |
||||
|
||||
public class ILCondition : ILNode |
||||
{ |
||||
public ILExpression Condition; |
||||
public ILBlock TrueBlock; // Branch was taken
|
||||
public ILBlock FalseBlock; // Fall-though
|
||||
|
||||
public override IEnumerable<ILNode> GetChildren() |
||||
{ |
||||
if (this.Condition != null) |
||||
yield return this.Condition; |
||||
if (this.TrueBlock != null) |
||||
yield return this.TrueBlock; |
||||
if (this.FalseBlock != null) |
||||
yield return this.FalseBlock; |
||||
} |
||||
|
||||
public override void WriteTo(ITextOutput output) |
||||
{ |
||||
output.Write("if ("); |
||||
Condition.WriteTo(output); |
||||
output.WriteLine(") {"); |
||||
output.Indent(); |
||||
TrueBlock.WriteTo(output); |
||||
output.Unindent(); |
||||
output.Write("}"); |
||||
if (FalseBlock != null) { |
||||
output.WriteLine(" else {"); |
||||
output.Indent(); |
||||
FalseBlock.WriteTo(output); |
||||
output.Unindent(); |
||||
output.WriteLine("}"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public class ILSwitch: ILNode |
||||
{ |
||||
public class CaseBlock: ILBlock |
||||
{ |
||||
public List<int> Values; // null for the default case
|
||||
|
||||
public override void WriteTo(ITextOutput output) |
||||
{ |
||||
if (this.Values != null) { |
||||
foreach (int i in this.Values) { |
||||
output.WriteLine("case {0}:", i); |
||||
} |
||||
} else { |
||||
output.WriteLine("default:"); |
||||
} |
||||
output.Indent(); |
||||
base.WriteTo(output); |
||||
output.Unindent(); |
||||
} |
||||
} |
||||
|
||||
public ILExpression Condition; |
||||
public List<CaseBlock> CaseBlocks = new List<CaseBlock>(); |
||||
|
||||
public override IEnumerable<ILNode> GetChildren() |
||||
{ |
||||
if (this.Condition != null) |
||||
yield return this.Condition; |
||||
foreach (ILBlock caseBlock in this.CaseBlocks) { |
||||
yield return caseBlock; |
||||
} |
||||
} |
||||
|
||||
public override void WriteTo(ITextOutput output) |
||||
{ |
||||
output.Write("switch ("); |
||||
Condition.WriteTo(output); |
||||
output.WriteLine(") {"); |
||||
output.Indent(); |
||||
foreach (CaseBlock caseBlock in this.CaseBlocks) { |
||||
caseBlock.WriteTo(output); |
||||
} |
||||
output.Unindent(); |
||||
output.WriteLine("}"); |
||||
} |
||||
} |
||||
|
||||
public class ILFixedStatement : ILNode |
||||
{ |
||||
public List<ILExpression> Initializers = new List<ILExpression>(); |
||||
public ILBlock BodyBlock; |
||||
|
||||
public override IEnumerable<ILNode> GetChildren() |
||||
{ |
||||
foreach (ILExpression initializer in this.Initializers) |
||||
yield return initializer; |
||||
if (this.BodyBlock != null) |
||||
yield return this.BodyBlock; |
||||
} |
||||
|
||||
public override void WriteTo(ITextOutput output) |
||||
{ |
||||
output.Write("fixed ("); |
||||
for (int i = 0; i < this.Initializers.Count; i++) { |
||||
if (i > 0) |
||||
output.Write(", "); |
||||
this.Initializers[i].WriteTo(output); |
||||
} |
||||
output.WriteLine(") {"); |
||||
output.Indent(); |
||||
this.BodyBlock.WriteTo(output); |
||||
output.Unindent(); |
||||
output.WriteLine("}"); |
||||
} |
||||
} |
||||
} |
@ -1,490 +0,0 @@
@@ -1,490 +0,0 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using Mono.Cecil; |
||||
using Mono.Cecil.Cil; |
||||
|
||||
namespace ICSharpCode.Decompiler.ILAst |
||||
{ |
||||
public enum ILCode |
||||
{ |
||||
// For convenience, the start is exactly identical to Mono.Cecil.Cil.Code
|
||||
// Instructions that should not be used are prepended by __
|
||||
Nop, |
||||
Break, |
||||
__Ldarg_0, |
||||
__Ldarg_1, |
||||
__Ldarg_2, |
||||
__Ldarg_3, |
||||
__Ldloc_0, |
||||
__Ldloc_1, |
||||
__Ldloc_2, |
||||
__Ldloc_3, |
||||
__Stloc_0, |
||||
__Stloc_1, |
||||
__Stloc_2, |
||||
__Stloc_3, |
||||
__Ldarg_S, |
||||
__Ldarga_S, |
||||
__Starg_S, |
||||
__Ldloc_S, |
||||
__Ldloca_S, |
||||
__Stloc_S, |
||||
Ldnull, |
||||
__Ldc_I4_M1, |
||||
__Ldc_I4_0, |
||||
__Ldc_I4_1, |
||||
__Ldc_I4_2, |
||||
__Ldc_I4_3, |
||||
__Ldc_I4_4, |
||||
__Ldc_I4_5, |
||||
__Ldc_I4_6, |
||||
__Ldc_I4_7, |
||||
__Ldc_I4_8, |
||||
__Ldc_I4_S, |
||||
Ldc_I4, |
||||
Ldc_I8, |
||||
Ldc_R4, |
||||
Ldc_R8, |
||||
Dup, |
||||
Pop, |
||||
Jmp, |
||||
Call, |
||||
Calli, |
||||
Ret, |
||||
__Br_S, |
||||
__Brfalse_S, |
||||
__Brtrue_S, |
||||
__Beq_S, |
||||
__Bge_S, |
||||
__Bgt_S, |
||||
__Ble_S, |
||||
__Blt_S, |
||||
__Bne_Un_S, |
||||
__Bge_Un_S, |
||||
__Bgt_Un_S, |
||||
__Ble_Un_S, |
||||
__Blt_Un_S, |
||||
Br, |
||||
__Brfalse, |
||||
Brtrue, |
||||
__Beq, |
||||
__Bge, |
||||
__Bgt, |
||||
__Ble, |
||||
__Blt, |
||||
__Bne_Un, |
||||
__Bge_Un, |
||||
__Bgt_Un, |
||||
__Ble_Un, |
||||
__Blt_Un, |
||||
Switch, |
||||
__Ldind_I1, |
||||
__Ldind_U1, |
||||
__Ldind_I2, |
||||
__Ldind_U2, |
||||
__Ldind_I4, |
||||
__Ldind_U4, |
||||
__Ldind_I8, |
||||
__Ldind_I, |
||||
__Ldind_R4, |
||||
__Ldind_R8, |
||||
Ldind_Ref, |
||||
Stind_Ref, |
||||
__Stind_I1, |
||||
__Stind_I2, |
||||
__Stind_I4, |
||||
__Stind_I8, |
||||
__Stind_R4, |
||||
__Stind_R8, |
||||
Add, |
||||
Sub, |
||||
Mul, |
||||
Div, |
||||
Div_Un, |
||||
Rem, |
||||
Rem_Un, |
||||
And, |
||||
Or, |
||||
Xor, |
||||
Shl, |
||||
Shr, |
||||
Shr_Un, |
||||
Neg, |
||||
Not, |
||||
Conv_I1, |
||||
Conv_I2, |
||||
Conv_I4, |
||||
Conv_I8, |
||||
Conv_R4, |
||||
Conv_R8, |
||||
Conv_U4, |
||||
Conv_U8, |
||||
Callvirt, |
||||
Cpobj, |
||||
Ldobj, |
||||
Ldstr, |
||||
Newobj, |
||||
Castclass, |
||||
Isinst, |
||||
Conv_R_Un, |
||||
Unbox, |
||||
Throw, |
||||
Ldfld, |
||||
Ldflda, |
||||
Stfld, |
||||
Ldsfld, |
||||
Ldsflda, |
||||
Stsfld, |
||||
Stobj, |
||||
Conv_Ovf_I1_Un, |
||||
Conv_Ovf_I2_Un, |
||||
Conv_Ovf_I4_Un, |
||||
Conv_Ovf_I8_Un, |
||||
Conv_Ovf_U1_Un, |
||||
Conv_Ovf_U2_Un, |
||||
Conv_Ovf_U4_Un, |
||||
Conv_Ovf_U8_Un, |
||||
Conv_Ovf_I_Un, |
||||
Conv_Ovf_U_Un, |
||||
Box, |
||||
Newarr, |
||||
Ldlen, |
||||
Ldelema, |
||||
Ldelem_I1, |
||||
Ldelem_U1, |
||||
Ldelem_I2, |
||||
Ldelem_U2, |
||||
Ldelem_I4, |
||||
Ldelem_U4, |
||||
Ldelem_I8, |
||||
Ldelem_I, |
||||
Ldelem_R4, |
||||
Ldelem_R8, |
||||
Ldelem_Ref, |
||||
Stelem_I, |
||||
Stelem_I1, |
||||
Stelem_I2, |
||||
Stelem_I4, |
||||
Stelem_I8, |
||||
Stelem_R4, |
||||
Stelem_R8, |
||||
Stelem_Ref, |
||||
Ldelem_Any, |
||||
Stelem_Any, |
||||
Unbox_Any, |
||||
Conv_Ovf_I1, |
||||
Conv_Ovf_U1, |
||||
Conv_Ovf_I2, |
||||
Conv_Ovf_U2, |
||||
Conv_Ovf_I4, |
||||
Conv_Ovf_U4, |
||||
Conv_Ovf_I8, |
||||
Conv_Ovf_U8, |
||||
Refanyval, |
||||
Ckfinite, |
||||
Mkrefany, |
||||
Ldtoken, |
||||
Conv_U2, |
||||
Conv_U1, |
||||
Conv_I, |
||||
Conv_Ovf_I, |
||||
Conv_Ovf_U, |
||||
Add_Ovf, |
||||
Add_Ovf_Un, |
||||
Mul_Ovf, |
||||
Mul_Ovf_Un, |
||||
Sub_Ovf, |
||||
Sub_Ovf_Un, |
||||
Endfinally, |
||||
Leave, |
||||
__Leave_S, |
||||
__Stind_I, |
||||
Conv_U, |
||||
Arglist, |
||||
Ceq, |
||||
Cgt, |
||||
Cgt_Un, |
||||
Clt, |
||||
Clt_Un, |
||||
Ldftn, |
||||
Ldvirtftn, |
||||
__Ldarg, |
||||
__Ldarga, |
||||
__Starg, |
||||
Ldloc, |
||||
Ldloca, |
||||
Stloc, |
||||
Localloc, |
||||
Endfilter, |
||||
Unaligned, |
||||
Volatile, |
||||
Tail, |
||||
Initobj, |
||||
Constrained, |
||||
Cpblk, |
||||
Initblk, |
||||
No, |
||||
Rethrow, |
||||
Sizeof, |
||||
Refanytype, |
||||
Readonly, |
||||
|
||||
// Virtual codes - defined for convenience
|
||||
Cne, |
||||
Cge, |
||||
Cge_Un, |
||||
Cle, |
||||
Cle_Un, |
||||
Ldexception, // Operand holds the CatchType for catch handler, null for filter
|
||||
LogicNot, |
||||
LogicAnd, |
||||
LogicOr, |
||||
NullCoalescing, |
||||
InitArray, // Array Initializer
|
||||
|
||||
/// <summary>
|
||||
/// Defines a barrier between the parent expression and the argument expression that prevents combining them
|
||||
/// </summary>
|
||||
Wrap, |
||||
|
||||
// new Class { Prop = 1, Collection = { { 2, 3 }, {4, 5} }}
|
||||
// is represented as:
|
||||
// InitObject(newobj Class,
|
||||
// CallSetter(Prop, InitializedObject, 1),
|
||||
// InitCollection(CallGetter(Collection, InitializedObject))),
|
||||
// Call(Add, InitializedObject, 2, 3),
|
||||
// Call(Add, InitializedObject, 4, 5)))
|
||||
InitObject, // Object initializer: first arg is newobj/defaultvalue, remaining args are the initializing statements
|
||||
InitCollection, // Collection initializer: first arg is newobj/defaultvalue, remaining args are the initializing statements
|
||||
InitializedObject, // Refers the the object being initialized (refers to first arg in parent InitObject or InitCollection instruction)
|
||||
|
||||
TernaryOp, // ?:
|
||||
LoopOrSwitchBreak, |
||||
LoopContinue, |
||||
Ldc_Decimal, |
||||
YieldBreak, |
||||
YieldReturn, |
||||
/// <summary>
|
||||
/// Represents the 'default(T)' instruction.
|
||||
/// </summary>
|
||||
/// <remarks>Introduced by SimplifyLdObjAndStObj step</remarks>
|
||||
DefaultValue, |
||||
/// <summary>
|
||||
/// ILExpression with a single child: binary operator.
|
||||
/// This expression means that the binary operator will also assign the new value to its left-hand side.
|
||||
/// 'CompoundAssignment' must not be used for local variables, as inlining (and other) optimizations don't know that it modifies the variable.
|
||||
/// </summary>
|
||||
/// <remarks>Introduced by MakeCompoundAssignments step</remarks>
|
||||
CompoundAssignment, |
||||
/// <summary>
|
||||
/// Represents the post-increment operator.
|
||||
/// The first argument is the address of the variable to increment (ldloca instruction).
|
||||
/// The second arugment is the amount the variable is incremented by (ldc.i4 instruction)
|
||||
/// </summary>
|
||||
/// <remarks>Introduced by IntroducePostIncrement step</remarks>
|
||||
PostIncrement, |
||||
PostIncrement_Ovf, // checked variant of PostIncrement
|
||||
PostIncrement_Ovf_Un, // checked variant of PostIncrement, for unsigned integers
|
||||
/// <summary>Calls the getter of a static property (or indexer), or of an instance property on 'base'</summary>
|
||||
CallGetter, |
||||
/// <summary>Calls the getter of an instance property (or indexer)</summary>
|
||||
CallvirtGetter, |
||||
/// <summary>Calls the setter of a static property (or indexer), or of an instance property on 'base'</summary>
|
||||
/// <remarks>This allows us to represent "while ((SomeProperty = val) != null) {}"</remarks>
|
||||
CallSetter, |
||||
/// <summary>Calls the setter of a instance property (or indexer)</summary>
|
||||
CallvirtSetter, |
||||
/// <summary>Simulates getting the address of the argument instruction.</summary>
|
||||
/// <remarks>
|
||||
/// Used for postincrement for properties, and to represent the Address() method on multi-dimensional arrays.
|
||||
/// Also used when inlining a method call on a value type: "stloc(v, ...); call(M, ldloca(v));" becomes "call(M, AddressOf(...))"
|
||||
/// </remarks>
|
||||
AddressOf, |
||||
/// <summary>Simulates getting the value of a lifted operator's nullable argument</summary>
|
||||
/// <remarks>
|
||||
/// For example "stloc(v1, ...); stloc(v2, ...); logicand(ceq(call(Nullable`1::GetValueOrDefault, ldloca(v1)), ldloc(v2)), callgetter(Nullable`1::get_HasValue, ldloca(v1)))" becomes "wrap(ceq(ValueOf(...), ...))"
|
||||
/// </remarks>
|
||||
ValueOf, |
||||
/// <summary>Simulates creating a new nullable value from a value type argument</summary>
|
||||
/// <remarks>
|
||||
/// For example "stloc(v1, ...); stloc(v2, ...); ternaryop(callgetter(Nullable`1::get_HasValue, ldloca(v1)), newobj(Nullable`1::.ctor, add(call(Nullable`1::GetValueOrDefault, ldloca(v1)), ldloc(v2))), defaultvalue(Nullable`1))"
|
||||
/// becomes "NullableOf(add(valueof(...), ...))"
|
||||
/// </remarks>
|
||||
NullableOf, |
||||
/// <summary>
|
||||
/// Declares parameters that are used in an expression tree.
|
||||
/// The last child of this node is the call constructing the expression tree, all other children are the
|
||||
/// assignments to the ParameterExpression variables.
|
||||
/// </summary>
|
||||
ExpressionTreeParameterDeclarations, |
||||
/// <summary>
|
||||
/// C# 5 await
|
||||
/// </summary>
|
||||
Await |
||||
} |
||||
|
||||
public static class ILCodeUtil |
||||
{ |
||||
public static string GetName(this ILCode code) |
||||
{ |
||||
return code.ToString().ToLowerInvariant().TrimStart('_').Replace('_','.'); |
||||
} |
||||
|
||||
public static bool IsConditionalControlFlow(this ILCode code) |
||||
{ |
||||
switch(code) { |
||||
case ILCode.__Brfalse_S: |
||||
case ILCode.__Brtrue_S: |
||||
case ILCode.__Beq_S: |
||||
case ILCode.__Bge_S: |
||||
case ILCode.__Bgt_S: |
||||
case ILCode.__Ble_S: |
||||
case ILCode.__Blt_S: |
||||
case ILCode.__Bne_Un_S: |
||||
case ILCode.__Bge_Un_S: |
||||
case ILCode.__Bgt_Un_S: |
||||
case ILCode.__Ble_Un_S: |
||||
case ILCode.__Blt_Un_S: |
||||
case ILCode.__Brfalse: |
||||
case ILCode.Brtrue: |
||||
case ILCode.__Beq: |
||||
case ILCode.__Bge: |
||||
case ILCode.__Bgt: |
||||
case ILCode.__Ble: |
||||
case ILCode.__Blt: |
||||
case ILCode.__Bne_Un: |
||||
case ILCode.__Bge_Un: |
||||
case ILCode.__Bgt_Un: |
||||
case ILCode.__Ble_Un: |
||||
case ILCode.__Blt_Un: |
||||
case ILCode.Switch: |
||||
return true; |
||||
default: |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
public static bool IsUnconditionalControlFlow(this ILCode code) |
||||
{ |
||||
switch(code) { |
||||
case ILCode.Br: |
||||
case ILCode.__Br_S: |
||||
case ILCode.Leave: |
||||
case ILCode.__Leave_S: |
||||
case ILCode.Ret: |
||||
case ILCode.Endfilter: |
||||
case ILCode.Endfinally: |
||||
case ILCode.Throw: |
||||
case ILCode.Rethrow: |
||||
case ILCode.LoopContinue: |
||||
case ILCode.LoopOrSwitchBreak: |
||||
case ILCode.YieldBreak: |
||||
return true; |
||||
default: |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
public static void ExpandMacro(ref ILCode code, ref object operand, MethodBody methodBody) |
||||
{ |
||||
switch (code) { |
||||
case ILCode.__Ldarg_0: code = ILCode.__Ldarg; operand = methodBody.GetParameter(0); break; |
||||
case ILCode.__Ldarg_1: code = ILCode.__Ldarg; operand = methodBody.GetParameter(1); break; |
||||
case ILCode.__Ldarg_2: code = ILCode.__Ldarg; operand = methodBody.GetParameter(2); break; |
||||
case ILCode.__Ldarg_3: code = ILCode.__Ldarg; operand = methodBody.GetParameter(3); break; |
||||
case ILCode.__Ldloc_0: code = ILCode.Ldloc; operand = methodBody.Variables[0]; break; |
||||
case ILCode.__Ldloc_1: code = ILCode.Ldloc; operand = methodBody.Variables[1]; break; |
||||
case ILCode.__Ldloc_2: code = ILCode.Ldloc; operand = methodBody.Variables[2]; break; |
||||
case ILCode.__Ldloc_3: code = ILCode.Ldloc; operand = methodBody.Variables[3]; break; |
||||
case ILCode.__Stloc_0: code = ILCode.Stloc; operand = methodBody.Variables[0]; break; |
||||
case ILCode.__Stloc_1: code = ILCode.Stloc; operand = methodBody.Variables[1]; break; |
||||
case ILCode.__Stloc_2: code = ILCode.Stloc; operand = methodBody.Variables[2]; break; |
||||
case ILCode.__Stloc_3: code = ILCode.Stloc; operand = methodBody.Variables[3]; break; |
||||
case ILCode.__Ldarg_S: code = ILCode.__Ldarg; break; |
||||
case ILCode.__Ldarga_S: code = ILCode.__Ldarga; break; |
||||
case ILCode.__Starg_S: code = ILCode.__Starg; break; |
||||
case ILCode.__Ldloc_S: code = ILCode.Ldloc; break; |
||||
case ILCode.__Ldloca_S: code = ILCode.Ldloca; break; |
||||
case ILCode.__Stloc_S: code = ILCode.Stloc; break; |
||||
case ILCode.__Ldc_I4_M1: code = ILCode.Ldc_I4; operand = -1; break; |
||||
case ILCode.__Ldc_I4_0: code = ILCode.Ldc_I4; operand = 0; break; |
||||
case ILCode.__Ldc_I4_1: code = ILCode.Ldc_I4; operand = 1; break; |
||||
case ILCode.__Ldc_I4_2: code = ILCode.Ldc_I4; operand = 2; break; |
||||
case ILCode.__Ldc_I4_3: code = ILCode.Ldc_I4; operand = 3; break; |
||||
case ILCode.__Ldc_I4_4: code = ILCode.Ldc_I4; operand = 4; break; |
||||
case ILCode.__Ldc_I4_5: code = ILCode.Ldc_I4; operand = 5; break; |
||||
case ILCode.__Ldc_I4_6: code = ILCode.Ldc_I4; operand = 6; break; |
||||
case ILCode.__Ldc_I4_7: code = ILCode.Ldc_I4; operand = 7; break; |
||||
case ILCode.__Ldc_I4_8: code = ILCode.Ldc_I4; operand = 8; break; |
||||
case ILCode.__Ldc_I4_S: code = ILCode.Ldc_I4; operand = (int) (sbyte) operand; break; |
||||
case ILCode.__Br_S: code = ILCode.Br; break; |
||||
case ILCode.__Brfalse_S: code = ILCode.__Brfalse; break; |
||||
case ILCode.__Brtrue_S: code = ILCode.Brtrue; break; |
||||
case ILCode.__Beq_S: code = ILCode.__Beq; break; |
||||
case ILCode.__Bge_S: code = ILCode.__Bge; break; |
||||
case ILCode.__Bgt_S: code = ILCode.__Bgt; break; |
||||
case ILCode.__Ble_S: code = ILCode.__Ble; break; |
||||
case ILCode.__Blt_S: code = ILCode.__Blt; break; |
||||
case ILCode.__Bne_Un_S: code = ILCode.__Bne_Un; break; |
||||
case ILCode.__Bge_Un_S: code = ILCode.__Bge_Un; break; |
||||
case ILCode.__Bgt_Un_S: code = ILCode.__Bgt_Un; break; |
||||
case ILCode.__Ble_Un_S: code = ILCode.__Ble_Un; break; |
||||
case ILCode.__Blt_Un_S: code = ILCode.__Blt_Un; break; |
||||
case ILCode.__Leave_S: code = ILCode.Leave; break; |
||||
case ILCode.__Ldind_I: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.IntPtr; break; |
||||
case ILCode.__Ldind_I1: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.SByte; break; |
||||
case ILCode.__Ldind_I2: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.Int16; break; |
||||
case ILCode.__Ldind_I4: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.Int32; break; |
||||
case ILCode.__Ldind_I8: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.Int64; break; |
||||
case ILCode.__Ldind_U1: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.Byte; break; |
||||
case ILCode.__Ldind_U2: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.UInt16; break; |
||||
case ILCode.__Ldind_U4: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.UInt32; break; |
||||
case ILCode.__Ldind_R4: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.Single; break; |
||||
case ILCode.__Ldind_R8: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.Double; break; |
||||
case ILCode.__Stind_I: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.IntPtr; break; |
||||
case ILCode.__Stind_I1: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Byte; break; |
||||
case ILCode.__Stind_I2: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Int16; break; |
||||
case ILCode.__Stind_I4: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Int32; break; |
||||
case ILCode.__Stind_I8: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Int64; break; |
||||
case ILCode.__Stind_R4: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Single; break; |
||||
case ILCode.__Stind_R8: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Double; break; |
||||
} |
||||
} |
||||
|
||||
public static ParameterDefinition GetParameter (this MethodBody self, int index) |
||||
{ |
||||
var method = self.Method; |
||||
|
||||
if (method.HasThis) { |
||||
if (index == 0) |
||||
return self.ThisParameter; |
||||
|
||||
index--; |
||||
} |
||||
|
||||
var parameters = method.Parameters; |
||||
|
||||
if (index < 0 || index >= parameters.Count) |
||||
return null; |
||||
|
||||
return parameters [index]; |
||||
} |
||||
} |
||||
} |
@ -1,543 +0,0 @@
@@ -1,543 +0,0 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.Linq; |
||||
using Mono.Cecil; |
||||
|
||||
namespace ICSharpCode.Decompiler.ILAst |
||||
{ |
||||
/// <summary>
|
||||
/// IL AST transformation that introduces array, object and collection initializers.
|
||||
/// </summary>
|
||||
partial class ILAstOptimizer |
||||
{ |
||||
#region Array Initializers
|
||||
bool TransformArrayInitializers(List<ILNode> body, ILExpression expr, int pos) |
||||
{ |
||||
ILVariable v, v3; |
||||
ILExpression newarrExpr; |
||||
TypeReference elementType; |
||||
ILExpression lengthExpr; |
||||
int arrayLength; |
||||
if (expr.Match(ILCode.Stloc, out v, out newarrExpr) && |
||||
newarrExpr.Match(ILCode.Newarr, out elementType, out lengthExpr) && |
||||
lengthExpr.Match(ILCode.Ldc_I4, out arrayLength) && |
||||
arrayLength > 0) { |
||||
ILExpression[] newArr; |
||||
int initArrayPos; |
||||
if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, elementType, arrayLength, out newArr, out initArrayPos)) { |
||||
var arrayType = new ArrayType(elementType, 1); |
||||
arrayType.Dimensions[0] = new ArrayDimension(0, arrayLength); |
||||
body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr)); |
||||
body.RemoveAt(initArrayPos); |
||||
} |
||||
// Put in a limit so that we don't consume too much memory if the code allocates a huge array
|
||||
// and populates it extremely sparsly. However, 255 "null" elements in a row actually occur in the Mono C# compiler!
|
||||
const int maxConsecutiveDefaultValueExpressions = 300; |
||||
List<ILExpression> operands = new List<ILExpression>(); |
||||
int numberOfInstructionsToRemove = 0; |
||||
for (int j = pos + 1; j < body.Count; j++) { |
||||
ILExpression nextExpr = body[j] as ILExpression; |
||||
int arrayPos; |
||||
if (nextExpr != null && |
||||
nextExpr.Code.IsStoreToArray() && |
||||
nextExpr.Arguments[0].Match(ILCode.Ldloc, out v3) && |
||||
v == v3 && |
||||
nextExpr.Arguments[1].Match(ILCode.Ldc_I4, out arrayPos) && |
||||
arrayPos >= operands.Count && |
||||
arrayPos <= operands.Count + maxConsecutiveDefaultValueExpressions && |
||||
!nextExpr.Arguments[2].ContainsReferenceTo(v3)) |
||||
{ |
||||
while (operands.Count < arrayPos) |
||||
operands.Add(new ILExpression(ILCode.DefaultValue, elementType)); |
||||
operands.Add(nextExpr.Arguments[2]); |
||||
numberOfInstructionsToRemove++; |
||||
} else { |
||||
break; |
||||
} |
||||
} |
||||
if (operands.Count == arrayLength) { |
||||
var arrayType = new ArrayType(elementType, 1); |
||||
arrayType.Dimensions[0] = new ArrayDimension(0, arrayLength); |
||||
expr.Arguments[0] = new ILExpression(ILCode.InitArray, arrayType, operands); |
||||
body.RemoveRange(pos + 1, numberOfInstructionsToRemove); |
||||
|
||||
new ILInlining(method).InlineIfPossible(body, ref pos); |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
bool TransformMultidimensionalArrayInitializers(List<ILNode> body, ILExpression expr, int pos) |
||||
{ |
||||
ILVariable v; |
||||
ILExpression newarrExpr; |
||||
MethodReference ctor; |
||||
List<ILExpression> ctorArgs; |
||||
ArrayType arrayType; |
||||
if (expr.Match(ILCode.Stloc, out v, out newarrExpr) && |
||||
newarrExpr.Match(ILCode.Newobj, out ctor, out ctorArgs) && |
||||
(arrayType = (ctor.DeclaringType as ArrayType)) != null && |
||||
arrayType.Rank == ctorArgs.Count) { |
||||
// Clone the type, so we can muck about with the Dimensions
|
||||
arrayType = new ArrayType(arrayType.ElementType, arrayType.Rank); |
||||
var arrayLengths = new int[arrayType.Rank]; |
||||
for (int i = 0; i < arrayType.Rank; i++) { |
||||
if (!ctorArgs[i].Match(ILCode.Ldc_I4, out arrayLengths[i])) return false; |
||||
if (arrayLengths[i] <= 0) return false; |
||||
arrayType.Dimensions[i] = new ArrayDimension(0, arrayLengths[i]); |
||||
} |
||||
|
||||
var totalElements = arrayLengths.Aggregate(1, (t, l) => t * l); |
||||
ILExpression[] newArr; |
||||
int initArrayPos; |
||||
if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, arrayType, totalElements, out newArr, out initArrayPos)) { |
||||
body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr)); |
||||
body.RemoveAt(initArrayPos); |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
bool ForwardScanInitializeArrayRuntimeHelper(List<ILNode> body, int pos, ILVariable array, TypeReference arrayType, int arrayLength, out ILExpression[] values, out int foundPos) |
||||
{ |
||||
ILVariable v2; |
||||
MethodReference methodRef; |
||||
ILExpression methodArg1; |
||||
ILExpression methodArg2; |
||||
FieldReference fieldRef; |
||||
if (body.ElementAtOrDefault(pos).Match(ILCode.Call, out methodRef, out methodArg1, out methodArg2) && |
||||
methodRef.DeclaringType.FullName == "System.Runtime.CompilerServices.RuntimeHelpers" && |
||||
methodRef.Name == "InitializeArray" && |
||||
methodArg1.Match(ILCode.Ldloc, out v2) && |
||||
array == v2 && |
||||
methodArg2.Match(ILCode.Ldtoken, out fieldRef)) |
||||
{ |
||||
FieldDefinition fieldDef = fieldRef.ResolveWithinSameModule(); |
||||
if (fieldDef != null && fieldDef.InitialValue != null) { |
||||
ILExpression[] newArr = new ILExpression[arrayLength]; |
||||
if (DecodeArrayInitializer(arrayType.GetElementType(), fieldDef.InitialValue, newArr)) |
||||
{ |
||||
values = newArr; |
||||
foundPos = pos; |
||||
return true; |
||||
} |
||||
} |
||||
} |
||||
values = null; |
||||
foundPos = -1; |
||||
return false; |
||||
} |
||||
|
||||
static bool DecodeArrayInitializer(TypeReference elementTypeRef, byte[] initialValue, ILExpression[] output) |
||||
{ |
||||
TypeCode elementType = TypeAnalysis.GetTypeCode(elementTypeRef); |
||||
switch (elementType) { |
||||
case TypeCode.Boolean: |
||||
case TypeCode.Byte: |
||||
return DecodeArrayInitializer(initialValue, output, elementType, (d, i) => (int)d[i]); |
||||
case TypeCode.SByte: |
||||
return DecodeArrayInitializer(initialValue, output, elementType, (d, i) => (int)unchecked((sbyte)d[i])); |
||||
case TypeCode.Int16: |
||||
return DecodeArrayInitializer(initialValue, output, elementType, (d, i) => (int)BitConverter.ToInt16(d, i)); |
||||
case TypeCode.Char: |
||||
case TypeCode.UInt16: |
||||
return DecodeArrayInitializer(initialValue, output, elementType, (d, i) => (int)BitConverter.ToUInt16(d, i)); |
||||
case TypeCode.Int32: |
||||
case TypeCode.UInt32: |
||||
return DecodeArrayInitializer(initialValue, output, elementType, BitConverter.ToInt32); |
||||
case TypeCode.Int64: |
||||
case TypeCode.UInt64: |
||||
return DecodeArrayInitializer(initialValue, output, elementType, BitConverter.ToInt64); |
||||
case TypeCode.Single: |
||||
return DecodeArrayInitializer(initialValue, output, elementType, BitConverter.ToSingle); |
||||
case TypeCode.Double: |
||||
return DecodeArrayInitializer(initialValue, output, elementType, BitConverter.ToDouble); |
||||
case TypeCode.Object: |
||||
var typeDef = elementTypeRef.ResolveWithinSameModule(); |
||||
if (typeDef != null && typeDef.IsEnum) |
||||
return DecodeArrayInitializer(typeDef.GetEnumUnderlyingType(), initialValue, output); |
||||
|
||||
return false; |
||||
default: |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
static bool DecodeArrayInitializer<T>(byte[] initialValue, ILExpression[] output, TypeCode elementType, Func<byte[], int, T> decoder) |
||||
{ |
||||
int elementSize = ElementSizeOf(elementType); |
||||
if (initialValue.Length < (output.Length * elementSize)) |
||||
return false; |
||||
|
||||
ILCode code = LoadCodeFor(elementType); |
||||
for (int i = 0; i < output.Length; i++) |
||||
output[i] = new ILExpression(code, decoder(initialValue, i * elementSize)); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
private static ILCode LoadCodeFor(TypeCode elementType) |
||||
{ |
||||
switch (elementType) { |
||||
case TypeCode.Boolean: |
||||
case TypeCode.Byte: |
||||
case TypeCode.SByte: |
||||
case TypeCode.Char: |
||||
case TypeCode.Int16: |
||||
case TypeCode.UInt16: |
||||
case TypeCode.Int32: |
||||
case TypeCode.UInt32: |
||||
return ILCode.Ldc_I4; |
||||
case TypeCode.Int64: |
||||
case TypeCode.UInt64: |
||||
return ILCode.Ldc_I8; |
||||
case TypeCode.Single: |
||||
return ILCode.Ldc_R4; |
||||
case TypeCode.Double: |
||||
return ILCode.Ldc_R8; |
||||
default: |
||||
throw new ArgumentOutOfRangeException("elementType"); |
||||
} |
||||
} |
||||
|
||||
private static int ElementSizeOf(TypeCode elementType) |
||||
{ |
||||
switch (elementType) { |
||||
case TypeCode.Boolean: |
||||
case TypeCode.Byte: |
||||
case TypeCode.SByte: |
||||
return 1; |
||||
case TypeCode.Char: |
||||
case TypeCode.Int16: |
||||
case TypeCode.UInt16: |
||||
return 2; |
||||
case TypeCode.Int32: |
||||
case TypeCode.UInt32: |
||||
case TypeCode.Single: |
||||
return 4; |
||||
case TypeCode.Int64: |
||||
case TypeCode.UInt64: |
||||
case TypeCode.Double: |
||||
return 8; |
||||
default: |
||||
throw new ArgumentOutOfRangeException("elementType"); |
||||
} |
||||
} |
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Handles both object and collection initializers.
|
||||
/// </summary>
|
||||
bool TransformObjectInitializers(List<ILNode> body, ILExpression expr, int pos) |
||||
{ |
||||
if (!context.Settings.ObjectOrCollectionInitializers) |
||||
return false; |
||||
|
||||
Debug.Assert(body[pos] == expr); // should be called for top-level expressions only
|
||||
ILVariable v; |
||||
ILExpression newObjExpr; |
||||
TypeReference newObjType; |
||||
bool isValueType; |
||||
MethodReference ctor; |
||||
List<ILExpression> ctorArgs; |
||||
if (expr.Match(ILCode.Stloc, out v, out newObjExpr)) { |
||||
if (newObjExpr.Match(ILCode.Newobj, out ctor, out ctorArgs)) { |
||||
// v = newObj(ctor, ctorArgs)
|
||||
newObjType = ctor.DeclaringType; |
||||
isValueType = false; |
||||
} else if (newObjExpr.Match(ILCode.DefaultValue, out newObjType)) { |
||||
// v = defaultvalue(type)
|
||||
isValueType = true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} else if (expr.Match(ILCode.Call, out ctor, out ctorArgs)) { |
||||
// call(SomeStruct::.ctor, ldloca(v), remainingArgs)
|
||||
if (ctorArgs.Count > 0 && ctorArgs[0].Match(ILCode.Ldloca, out v)) { |
||||
isValueType = true; |
||||
newObjType = ctor.DeclaringType; |
||||
ctorArgs = new List<ILExpression>(ctorArgs); |
||||
ctorArgs.RemoveAt(0); |
||||
newObjExpr = new ILExpression(ILCode.Newobj, ctor, ctorArgs); |
||||
} else { |
||||
return false; |
||||
} |
||||
} else { |
||||
return false; |
||||
} |
||||
if (newObjType.IsValueType != isValueType) |
||||
return false; |
||||
|
||||
int originalPos = pos; |
||||
|
||||
// don't use object initializer syntax for closures
|
||||
if (Ast.Transforms.DelegateConstruction.IsPotentialClosure(context, newObjType.ResolveWithinSameModule())) |
||||
return false; |
||||
|
||||
ILExpression initializer = ParseObjectInitializer(body, ref pos, v, newObjExpr, IsCollectionType(newObjType), isValueType); |
||||
|
||||
if (initializer.Arguments.Count == 1) // only newobj argument, no initializer elements
|
||||
return false; |
||||
int totalElementCount = pos - originalPos - 1; // totalElementCount: includes elements from nested collections
|
||||
Debug.Assert(totalElementCount >= initializer.Arguments.Count - 1); |
||||
|
||||
// Verify that we can inline 'v' into the next instruction:
|
||||
|
||||
if (pos >= body.Count) |
||||
return false; // reached end of block, but there should be another instruction which consumes the initialized object
|
||||
|
||||
ILInlining inlining = new ILInlining(method); |
||||
if (isValueType) { |
||||
// one ldloc for the use of the initialized object
|
||||
if (inlining.numLdloc.GetOrDefault(v) != 1) |
||||
return false; |
||||
// one ldloca for each initializer argument, and also for the ctor call (if it exists)
|
||||
if (inlining.numLdloca.GetOrDefault(v) != totalElementCount + (expr.Code == ILCode.Call ? 1 : 0)) |
||||
return false; |
||||
// one stloc for the initial store (if no ctor call was used)
|
||||
if (inlining.numStloc.GetOrDefault(v) != (expr.Code == ILCode.Call ? 0 : 1)) |
||||
return false; |
||||
} else { |
||||
// one ldloc for each initializer argument, and another ldloc for the use of the initialized object
|
||||
if (inlining.numLdloc.GetOrDefault(v) != totalElementCount + 1) |
||||
return false; |
||||
if (!(inlining.numStloc.GetOrDefault(v) == 1 && inlining.numLdloca.GetOrDefault(v) == 0)) |
||||
return false; |
||||
} |
||||
ILExpression nextExpr = body[pos] as ILExpression; |
||||
if (!inlining.CanInlineInto(nextExpr, v, initializer)) |
||||
return false; |
||||
|
||||
if (expr.Code == ILCode.Stloc) { |
||||
expr.Arguments[0] = initializer; |
||||
} else { |
||||
Debug.Assert(expr.Code == ILCode.Call); |
||||
expr.Code = ILCode.Stloc; |
||||
expr.Operand = v; |
||||
expr.Arguments.Clear(); |
||||
expr.Arguments.Add(initializer); |
||||
} |
||||
// remove all the instructions that were pulled into the initializer
|
||||
body.RemoveRange(originalPos + 1, pos - originalPos - 1); |
||||
|
||||
// now that we know that it's an object initializer, change all the first arguments to 'InitializedObject'
|
||||
ChangeFirstArgumentToInitializedObject(initializer); |
||||
|
||||
inlining = new ILInlining(method); |
||||
inlining.InlineIfPossible(body, ref originalPos); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets whether the type supports collection initializers.
|
||||
/// </summary>
|
||||
static bool IsCollectionType(TypeReference tr) |
||||
{ |
||||
if (tr == null) |
||||
return false; |
||||
TypeDefinition td = tr.Resolve(); |
||||
while (td != null) { |
||||
if (td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections")) |
||||
return true; |
||||
td = td.BaseType != null ? td.BaseType.Resolve() : null; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets whether 'expr' represents a setter in an object initializer.
|
||||
/// ('CallvirtSetter(Property, v, value)')
|
||||
/// </summary>
|
||||
static bool IsSetterInObjectInitializer(ILExpression expr) |
||||
{ |
||||
if (expr == null) |
||||
return false; |
||||
if (expr.Code == ILCode.CallvirtSetter || expr.Code == ILCode.CallSetter || expr.Code == ILCode.Stfld) { |
||||
return expr.Arguments.Count == 2; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Gets whether 'expr' represents the invocation of an 'Add' method in a collection initializer.
|
||||
/// </summary>
|
||||
static bool IsAddMethodCall(ILExpression expr) |
||||
{ |
||||
MethodReference addMethod; |
||||
List<ILExpression> args; |
||||
if (expr.Match(ILCode.Callvirt, out addMethod, out args) || expr.Match(ILCode.Call, out addMethod, out args)) { |
||||
if (addMethod.Name == "Add" && addMethod.HasThis) { |
||||
return args.Count >= 2; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Parses an object initializer.
|
||||
/// </summary>
|
||||
/// <param name="body">ILAst block</param>
|
||||
/// <param name="pos">
|
||||
/// Input: position of the instruction assigning to 'v'.
|
||||
/// Output: first position after the object initializer
|
||||
/// </param>
|
||||
/// <param name="v">The variable that holds the object being initialized</param>
|
||||
/// <param name="newObjExpr">The newobj instruction</param>
|
||||
/// <returns>InitObject instruction</returns>
|
||||
ILExpression ParseObjectInitializer(List<ILNode> body, ref int pos, ILVariable v, ILExpression newObjExpr, bool isCollection, bool isValueType) |
||||
{ |
||||
// Take care not to modify any existing ILExpressions in here.
|
||||
// We just construct new ones around the old ones, any modifications must wait until the whole
|
||||
// object/collection initializer was analyzed.
|
||||
ILExpression objectInitializer = new ILExpression(isCollection ? ILCode.InitCollection : ILCode.InitObject, null, newObjExpr); |
||||
List<ILExpression> initializerStack = new List<ILExpression>(); |
||||
initializerStack.Add(objectInitializer); |
||||
while (++pos < body.Count) { |
||||
ILExpression nextExpr = body[pos] as ILExpression; |
||||
if (IsSetterInObjectInitializer(nextExpr)) { |
||||
if (!AdjustInitializerStack(initializerStack, nextExpr.Arguments[0], v, false, isValueType)) { |
||||
CleanupInitializerStackAfterFailedAdjustment(initializerStack); |
||||
break; |
||||
} |
||||
initializerStack[initializerStack.Count - 1].Arguments.Add(nextExpr); |
||||
} else if (IsAddMethodCall(nextExpr)) { |
||||
if (!AdjustInitializerStack(initializerStack, nextExpr.Arguments[0], v, true, isValueType)) { |
||||
CleanupInitializerStackAfterFailedAdjustment(initializerStack); |
||||
break; |
||||
} |
||||
initializerStack[initializerStack.Count - 1].Arguments.Add(nextExpr); |
||||
} else { |
||||
// can't match any more initializers: end of object initializer
|
||||
break; |
||||
} |
||||
} |
||||
return objectInitializer; |
||||
} |
||||
|
||||
static bool AdjustInitializerStack(List<ILExpression> initializerStack, ILExpression argument, ILVariable v, bool isCollection, bool isValueType) |
||||
{ |
||||
// Argument is of the form 'getter(getter(...(v)))'
|
||||
// Unpack it into a list of getters:
|
||||
List<ILExpression> getters = new List<ILExpression>(); |
||||
while (argument.Code == ILCode.CallvirtGetter || argument.Code == ILCode.CallGetter || argument.Code == ILCode.Ldfld) { |
||||
getters.Add(argument); |
||||
if (argument.Arguments.Count != 1) |
||||
return false; |
||||
argument = argument.Arguments[0]; |
||||
} |
||||
// Ensure that the final argument is 'v'
|
||||
if (isValueType) { |
||||
ILVariable loadedVar; |
||||
if (!(argument.Match(ILCode.Ldloca, out loadedVar) && loadedVar == v)) |
||||
return false; |
||||
} else { |
||||
if (!argument.MatchLdloc(v)) |
||||
return false; |
||||
} |
||||
// Now compare the getters with those that are currently active on the initializer stack:
|
||||
int i; |
||||
for (i = 1; i <= Math.Min(getters.Count, initializerStack.Count - 1); i++) { |
||||
ILExpression g1 = initializerStack[i].Arguments[0]; // getter stored in initializer
|
||||
ILExpression g2 = getters[getters.Count - i]; // matching getter from argument
|
||||
if (g1.Operand != g2.Operand) { |
||||
// operands differ, so we abort the comparison
|
||||
break; |
||||
} |
||||
} |
||||
// Remove all initializers from the stack that were not matched with one from the argument:
|
||||
initializerStack.RemoveRange(i, initializerStack.Count - i); |
||||
// Now create new initializers for the remaining arguments:
|
||||
for (; i <= getters.Count; i++) { |
||||
ILExpression g = getters[getters.Count - i]; |
||||
MemberReference mr = (MemberReference)g.Operand; |
||||
TypeReference returnType; |
||||
if (mr is FieldReference) |
||||
returnType = TypeAnalysis.GetFieldType((FieldReference)mr); |
||||
else |
||||
returnType = TypeAnalysis.SubstituteTypeArgs(((MethodReference)mr).ReturnType, mr); |
||||
|
||||
ILExpression nestedInitializer = new ILExpression( |
||||
IsCollectionType(returnType) ? ILCode.InitCollection : ILCode.InitObject, |
||||
null, g); |
||||
// add new initializer to its parent:
|
||||
ILExpression parentInitializer = initializerStack[initializerStack.Count - 1]; |
||||
if (parentInitializer.Code == ILCode.InitCollection) { |
||||
// can't add children to collection initializer
|
||||
if (parentInitializer.Arguments.Count == 1) { |
||||
// convert empty collection initializer to object initializer
|
||||
parentInitializer.Code = ILCode.InitObject; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
parentInitializer.Arguments.Add(nestedInitializer); |
||||
initializerStack.Add(nestedInitializer); |
||||
} |
||||
ILExpression lastInitializer = initializerStack[initializerStack.Count - 1]; |
||||
if (isCollection) { |
||||
return lastInitializer.Code == ILCode.InitCollection; |
||||
} else { |
||||
if (lastInitializer.Code == ILCode.InitCollection) { |
||||
if (lastInitializer.Arguments.Count == 1) { |
||||
// convert empty collection initializer to object initializer
|
||||
lastInitializer.Code = ILCode.InitObject; |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} else { |
||||
return true; |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void CleanupInitializerStackAfterFailedAdjustment(List<ILExpression> initializerStack) |
||||
{ |
||||
// There might be empty nested initializers left over; so we'll remove those:
|
||||
while (initializerStack.Count > 1 && initializerStack[initializerStack.Count - 1].Arguments.Count == 1) { |
||||
ILExpression parent = initializerStack[initializerStack.Count - 2]; |
||||
Debug.Assert(parent.Arguments.Last() == initializerStack[initializerStack.Count - 1]); |
||||
parent.Arguments.RemoveAt(parent.Arguments.Count - 1); |
||||
initializerStack.RemoveAt(initializerStack.Count - 1); |
||||
} |
||||
} |
||||
|
||||
static void ChangeFirstArgumentToInitializedObject(ILExpression initializer) |
||||
{ |
||||
// Go through all elements in the initializer (so skip the newobj-instr. at the start)
|
||||
for (int i = 1; i < initializer.Arguments.Count; i++) { |
||||
ILExpression element = initializer.Arguments[i]; |
||||
if (element.Code == ILCode.InitCollection || element.Code == ILCode.InitObject) { |
||||
// nested collection/object initializer
|
||||
ILExpression getCollection = element.Arguments[0]; |
||||
getCollection.Arguments[0] = new ILExpression(ILCode.InitializedObject, null); |
||||
ChangeFirstArgumentToInitializedObject(element); // handle the collection elements
|
||||
} else { |
||||
element.Arguments[0] = new ILExpression(ILCode.InitializedObject, null); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,528 +0,0 @@
@@ -1,528 +0,0 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
|
||||
using Mono.Cecil; |
||||
|
||||
namespace ICSharpCode.Decompiler.ILAst |
||||
{ |
||||
partial class ILAstOptimizer |
||||
{ |
||||
bool SimplifyLiftedOperators(List<ILNode> body, ILExpression expr, int pos) |
||||
{ |
||||
if (!new PatternMatcher(typeSystem).SimplifyLiftedOperators(expr)) return false; |
||||
|
||||
var inlining = new ILInlining(method); |
||||
while (--pos >= 0 && inlining.InlineIfPossible(body, ref pos)) ; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
sealed class PatternMatcher |
||||
{ |
||||
readonly TypeSystem typeSystem; |
||||
public PatternMatcher(TypeSystem typeSystem) |
||||
{ |
||||
this.typeSystem = typeSystem; |
||||
} |
||||
|
||||
public bool SimplifyLiftedOperators(ILExpression expr) |
||||
{ |
||||
if (Simplify(expr)) return true; |
||||
|
||||
bool modified = false; |
||||
foreach (var a in expr.Arguments) |
||||
modified |= SimplifyLiftedOperators(a); |
||||
return modified; |
||||
} |
||||
|
||||
abstract class Pattern |
||||
{ |
||||
public readonly Pattern[] Arguments; |
||||
|
||||
protected Pattern(Pattern[] arguments) |
||||
{ |
||||
this.Arguments = arguments; |
||||
} |
||||
|
||||
public virtual bool Match(PatternMatcher pm, ILExpression e) |
||||
{ |
||||
if (e.Arguments.Count != this.Arguments.Length || e.Prefixes != null) return false; |
||||
for (int i = 0; i < this.Arguments.Length; i++) |
||||
if (!this.Arguments[i].Match(pm, e.Arguments[i])) return false; |
||||
return true; |
||||
} |
||||
|
||||
public virtual ILExpression BuildNew(PatternMatcher pm) |
||||
{ |
||||
throw new NotSupportedException(); |
||||
} |
||||
|
||||
public static Pattern operator &(Pattern a, Pattern b) |
||||
{ |
||||
return new ILPattern(ILCode.LogicAnd, a, b); |
||||
} |
||||
|
||||
public static Pattern operator |(Pattern a, Pattern b) |
||||
{ |
||||
return new ILPattern(ILCode.LogicOr, a, b); |
||||
} |
||||
|
||||
public static Pattern operator !(Pattern a) |
||||
{ |
||||
return new ILPattern(ILCode.LogicNot, a); |
||||
} |
||||
} |
||||
|
||||
sealed class ILPattern : Pattern |
||||
{ |
||||
readonly ILCode code; |
||||
|
||||
public ILPattern(ILCode code, params Pattern[] arguments) |
||||
: base(arguments) |
||||
{ |
||||
this.code = code; |
||||
} |
||||
|
||||
public override bool Match(PatternMatcher pm, ILExpression e) |
||||
{ |
||||
return e.Code == this.code && base.Match(pm, e); |
||||
} |
||||
|
||||
public override ILExpression BuildNew(PatternMatcher pm) |
||||
{ |
||||
var args = new ILExpression[this.Arguments.Length]; |
||||
for (int i = 0; i < args.Length; i++) args[i] = this.Arguments[i].BuildNew(pm); |
||||
TypeReference t = null; |
||||
switch (code) { |
||||
case ILCode.Ceq: |
||||
case ILCode.Cne: |
||||
t = pm.typeSystem.Boolean; |
||||
break; |
||||
case ILCode.NullCoalescing: |
||||
t = args[1].InferredType; |
||||
break; |
||||
} |
||||
return new ILExpression(code, null, args) { InferredType = t }; |
||||
} |
||||
} |
||||
|
||||
sealed class MethodPattern : Pattern |
||||
{ |
||||
readonly ILCode code; |
||||
readonly string method; |
||||
|
||||
public MethodPattern(ILCode code, string method, params Pattern[] arguments) |
||||
: base(arguments) |
||||
{ |
||||
this.code = code; |
||||
this.method = method; |
||||
} |
||||
|
||||
public override bool Match(PatternMatcher pm, ILExpression e) |
||||
{ |
||||
if (e.Code != this.code) return false; |
||||
var m = (MethodReference)e.Operand; |
||||
return m.Name == this.method && TypeAnalysis.IsNullableType(m.DeclaringType) && base.Match(pm, e); |
||||
} |
||||
} |
||||
|
||||
enum OperatorType |
||||
{ |
||||
Equality, InEquality, Comparison, Other |
||||
} |
||||
|
||||
sealed class OperatorPattern : Pattern |
||||
{ |
||||
OperatorType type; |
||||
bool simple; |
||||
|
||||
public OperatorPattern() : base(null) { } |
||||
|
||||
public OperatorPattern(OperatorType type, bool simple) |
||||
: this() |
||||
{ |
||||
this.type = type; |
||||
this.simple = simple; |
||||
} |
||||
|
||||
public override bool Match(PatternMatcher pm, ILExpression e) |
||||
{ |
||||
switch (e.Code) { |
||||
case ILCode.Ceq: |
||||
if (type != OperatorType.Equality) return false; |
||||
break; |
||||
case ILCode.Cne: |
||||
if (type != OperatorType.InEquality) return false; |
||||
break; |
||||
case ILCode.Cgt: |
||||
case ILCode.Cgt_Un: |
||||
case ILCode.Cge: |
||||
case ILCode.Cge_Un: |
||||
case ILCode.Clt: |
||||
case ILCode.Clt_Un: |
||||
case ILCode.Cle: |
||||
case ILCode.Cle_Un: |
||||
if (type != OperatorType.Comparison) return false; |
||||
break; |
||||
case ILCode.Add: |
||||
case ILCode.Add_Ovf: |
||||
case ILCode.Add_Ovf_Un: |
||||
case ILCode.Sub: |
||||
case ILCode.Sub_Ovf: |
||||
case ILCode.Sub_Ovf_Un: |
||||
case ILCode.Mul: |
||||
case ILCode.Mul_Ovf: |
||||
case ILCode.Mul_Ovf_Un: |
||||
case ILCode.Div: |
||||
case ILCode.Div_Un: |
||||
case ILCode.Rem: |
||||
case ILCode.Rem_Un: |
||||
case ILCode.And: |
||||
case ILCode.Or: |
||||
case ILCode.Xor: |
||||
case ILCode.Shl: |
||||
case ILCode.Shr: |
||||
case ILCode.Shr_Un: |
||||
case ILCode.Not: |
||||
case ILCode.Neg: |
||||
case ILCode.LogicNot: |
||||
if (type != OperatorType.Other) return false; |
||||
break; |
||||
case ILCode.Call: |
||||
var m = e.Operand as MethodReference; |
||||
if (m == null || m.HasThis || !m.HasParameters || e.Arguments.Count > 2 || !IsCustomOperator(m.Name)) return false; |
||||
break; |
||||
default: return false; |
||||
} |
||||
if (pm.Operator != null) throw new InvalidOperationException(); |
||||
pm.Operator = e; |
||||
|
||||
var a0 = e.Arguments[0]; |
||||
if (!simple) return VariableAGetValueOrDefault.Match(pm, a0) && VariableBGetValueOrDefault.Match(pm, e.Arguments[1]); |
||||
if (e.Arguments.Count == 1) return VariableAGetValueOrDefault.Match(pm, a0); |
||||
if (VariableAGetValueOrDefault.Match(pm, a0)) { |
||||
pm.SimpleOperand = e.Arguments[1]; |
||||
pm.SimpleLeftOperand = false; |
||||
return true; |
||||
} |
||||
if (VariableAGetValueOrDefault.Match(pm, e.Arguments[1])) { |
||||
pm.SimpleOperand = a0; |
||||
pm.SimpleLeftOperand = true; |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
bool IsCustomOperator(string s) |
||||
{ |
||||
switch (type) { |
||||
case OperatorType.Equality: return s == "op_Equality"; |
||||
case OperatorType.InEquality: return s == "op_Inequality"; |
||||
case OperatorType.Comparison: |
||||
if (s.Length < 11 || !s.StartsWith("op_", StringComparison.Ordinal)) return false; |
||||
switch (s) { |
||||
case "op_GreaterThan": |
||||
case "op_GreaterThanOrEqual": |
||||
case "op_LessThan": |
||||
case "op_LessThanOrEqual": return true; |
||||
default: return false; |
||||
} |
||||
default: |
||||
if (s.Length < 10 || !s.StartsWith("op_", StringComparison.Ordinal)) return false; |
||||
switch (s) { |
||||
case "op_Addition": |
||||
case "op_Subtraction": |
||||
case "op_Multiply": |
||||
case "op_Division": |
||||
case "op_Modulus": |
||||
case "op_BitwiseAnd": |
||||
case "op_BitwiseOr": |
||||
case "op_ExclusiveOr": |
||||
case "op_LeftShift": |
||||
case "op_RightShift": |
||||
case "op_UnaryNegation": |
||||
case "op_UnaryPlus": |
||||
case "op_LogicalNot": |
||||
case "op_OnesComplement": |
||||
case "op_Increment": |
||||
case "op_Decrement": return true; |
||||
default: return false; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public override ILExpression BuildNew(PatternMatcher pm) |
||||
{ |
||||
var res = pm.Operator; |
||||
res.Arguments.Clear(); |
||||
if (pm.SimpleLeftOperand) res.Arguments.Add(pm.SimpleOperand); |
||||
res.Arguments.Add(VariableA.BuildNew(pm)); |
||||
if (pm.B != null) res.Arguments.Add(VariableB.BuildNew(pm)); |
||||
else if (pm.SimpleOperand != null && !pm.SimpleLeftOperand) res.Arguments.Add(pm.SimpleOperand); |
||||
return res; |
||||
} |
||||
} |
||||
|
||||
sealed class AnyPattern : Pattern |
||||
{ |
||||
public AnyPattern() : base(null) { } |
||||
|
||||
public override bool Match(PatternMatcher pm, ILExpression e) |
||||
{ |
||||
if (pm.SimpleOperand != null) throw new InvalidOperationException(); |
||||
pm.SimpleOperand = e; |
||||
return true; |
||||
} |
||||
|
||||
public override ILExpression BuildNew(PatternMatcher pm) |
||||
{ |
||||
return pm.SimpleOperand; |
||||
} |
||||
} |
||||
|
||||
sealed class VariablePattern : Pattern |
||||
{ |
||||
readonly ILCode code; |
||||
readonly bool b; |
||||
|
||||
public VariablePattern(ILCode code, bool b) |
||||
: base(null) |
||||
{ |
||||
this.code = code; |
||||
this.b = b; |
||||
} |
||||
|
||||
public override bool Match(PatternMatcher pm, ILExpression e) |
||||
{ |
||||
if (e.Code != this.code) return false; |
||||
var v = e.Operand as ILVariable; |
||||
return v != null && (this.b ? Capture(ref pm.B, v) : Capture(ref pm.A, v)); |
||||
} |
||||
|
||||
static bool Capture(ref ILVariable pmvar, ILVariable v) |
||||
{ |
||||
if (pmvar != null) return pmvar == v; |
||||
pmvar = v; |
||||
return true; |
||||
} |
||||
|
||||
static readonly ILExpression[] EmptyArguments = new ILExpression[0]; |
||||
public override ILExpression BuildNew(PatternMatcher pm) |
||||
{ |
||||
var v = this.b ? pm.B : pm.A; |
||||
var e = new ILExpression(ILCode.Ldloc, v, EmptyArguments); |
||||
if (TypeAnalysis.IsNullableType(v.Type)) e = new ILExpression(ILCode.ValueOf, null, e); |
||||
return e; |
||||
} |
||||
} |
||||
|
||||
sealed class BooleanPattern : Pattern |
||||
{ |
||||
public static readonly Pattern False = new BooleanPattern(false), True = new BooleanPattern(true); |
||||
|
||||
readonly object value; |
||||
BooleanPattern(bool value) |
||||
: base(null) |
||||
{ |
||||
this.value = Convert.ToInt32(value); |
||||
} |
||||
|
||||
public override bool Match(PatternMatcher pm, ILExpression e) |
||||
{ |
||||
return e.Code == ILCode.Ldc_I4 && TypeAnalysis.IsBoolean(e.InferredType) && object.Equals(e.Operand, value); |
||||
} |
||||
|
||||
public override ILExpression BuildNew(PatternMatcher pm) |
||||
{ |
||||
// boolean constants are wrapped inside a container to disable simplyfication of equality comparisons
|
||||
return new ILExpression(ILCode.Wrap, null, new ILExpression(ILCode.Ldc_I4, value)); |
||||
} |
||||
} |
||||
|
||||
static readonly Pattern VariableRefA = new VariablePattern(ILCode.Ldloca, false), VariableRefB = new VariablePattern(ILCode.Ldloca, true); |
||||
static readonly Pattern VariableA = new VariablePattern(ILCode.Ldloc, false), VariableB = new VariablePattern(ILCode.Ldloc, true); |
||||
static readonly Pattern VariableAHasValue = new MethodPattern(ILCode.CallGetter, "get_HasValue", VariableRefA); |
||||
static readonly Pattern VariableAGetValueOrDefault = new MethodPattern(ILCode.Call, "GetValueOrDefault", VariableRefA); |
||||
static readonly Pattern VariableBHasValue = new MethodPattern(ILCode.CallGetter, "get_HasValue", VariableRefB); |
||||
static readonly Pattern VariableBGetValueOrDefault = new MethodPattern(ILCode.Call, "GetValueOrDefault", VariableRefB); |
||||
static readonly Pattern CeqHasValue = new ILPattern(ILCode.Ceq, VariableAHasValue, VariableBHasValue); |
||||
static readonly Pattern CneHasValue = new ILPattern(ILCode.Cne, VariableAHasValue, VariableBHasValue); |
||||
static readonly Pattern AndHasValue = new ILPattern(ILCode.And, VariableAHasValue, VariableBHasValue); |
||||
static readonly Pattern Any = new AnyPattern(); |
||||
static readonly Pattern OperatorVariableAB = new OperatorPattern(); |
||||
|
||||
static OperatorPattern OperatorNN(OperatorType type) |
||||
{ |
||||
return new OperatorPattern(type, false); |
||||
} |
||||
|
||||
static OperatorPattern OperatorNV(OperatorType type) |
||||
{ |
||||
return new OperatorPattern(type, true); |
||||
} |
||||
|
||||
static Pattern NewObj(Pattern p) |
||||
{ |
||||
return new MethodPattern(ILCode.Newobj, ".ctor", p); |
||||
} |
||||
|
||||
static readonly Pattern[] Comparisons = new Pattern[] { |
||||
/* both operands nullable */ |
||||
// == (primitive, decimal)
|
||||
OperatorNN(OperatorType.Equality) & CeqHasValue, |
||||
// == (struct)
|
||||
CeqHasValue & (!VariableAHasValue | OperatorNN(OperatorType.Equality)), |
||||
// != (primitive, decimal)
|
||||
OperatorNN(OperatorType.InEquality) | CneHasValue, |
||||
// != (struct)
|
||||
CneHasValue | (VariableAHasValue & OperatorNN(OperatorType.InEquality)), |
||||
// > , < , >= , <= (primitive, decimal)
|
||||
OperatorNN(OperatorType.Comparison) & AndHasValue, |
||||
// > , < , >= , <= (struct)
|
||||
AndHasValue & OperatorNN(OperatorType.Comparison), |
||||
|
||||
/* only one operand nullable */ |
||||
// == (primitive, decimal)
|
||||
OperatorNV(OperatorType.Equality) & VariableAHasValue, |
||||
// == (struct)
|
||||
VariableAHasValue & OperatorNV(OperatorType.Equality), |
||||
// != (primitive, decimal)
|
||||
OperatorNV(OperatorType.InEquality) | !VariableAHasValue, |
||||
// != (struct)
|
||||
!VariableAHasValue | OperatorNV(OperatorType.InEquality), |
||||
// > , <, >= , <= (primitive, decimal)
|
||||
OperatorNV(OperatorType.Comparison) & VariableAHasValue, |
||||
// > , < , >= , <= (struct)
|
||||
VariableAHasValue & OperatorNV(OperatorType.Comparison), |
||||
}; |
||||
|
||||
static readonly Pattern[] Other = new Pattern[] { |
||||
/* both operands nullable */ |
||||
// & (bool)
|
||||
new ILPattern(ILCode.TernaryOp, VariableAGetValueOrDefault | (!VariableBGetValueOrDefault & !VariableAHasValue), VariableB, VariableA), |
||||
new ILPattern(ILCode.And, VariableA, VariableB), |
||||
// | (bool)
|
||||
new ILPattern(ILCode.TernaryOp, VariableAGetValueOrDefault | (!VariableBGetValueOrDefault & !VariableAHasValue), VariableA, VariableB), |
||||
new ILPattern(ILCode.Or, VariableA, VariableB), |
||||
// null coalescing
|
||||
new ILPattern(ILCode.TernaryOp, VariableAHasValue, NewObj(VariableAGetValueOrDefault), VariableB), |
||||
new ILPattern(ILCode.NullCoalescing, VariableA, VariableB), |
||||
// all other
|
||||
new ILPattern(ILCode.TernaryOp, AndHasValue, NewObj(OperatorNN(OperatorType.Other)), new ILPattern(ILCode.DefaultValue)), |
||||
OperatorVariableAB, |
||||
|
||||
/* only one operand nullable */ |
||||
// & (bool)
|
||||
new ILPattern(ILCode.TernaryOp, Any, VariableA, NewObj(BooleanPattern.False)), |
||||
new ILPattern(ILCode.And, VariableA, Any), |
||||
// | (bool)
|
||||
new ILPattern(ILCode.TernaryOp, Any, NewObj(BooleanPattern.True), VariableA), |
||||
new ILPattern(ILCode.Or, VariableA, Any), |
||||
// == true
|
||||
VariableAGetValueOrDefault & VariableAHasValue, |
||||
new ILPattern(ILCode.Ceq, VariableA, BooleanPattern.True), |
||||
// != true
|
||||
!VariableAGetValueOrDefault | !VariableAHasValue, |
||||
new ILPattern(ILCode.Cne, VariableA, BooleanPattern.True), |
||||
// == false
|
||||
!VariableAGetValueOrDefault & VariableAHasValue, |
||||
new ILPattern(ILCode.Ceq, VariableA, BooleanPattern.False), |
||||
// != false
|
||||
VariableAGetValueOrDefault | !VariableAHasValue, |
||||
new ILPattern(ILCode.Cne, VariableA, BooleanPattern.False), |
||||
// ?? true
|
||||
!VariableAHasValue | VariableAGetValueOrDefault, |
||||
new ILPattern(ILCode.NullCoalescing, VariableA, BooleanPattern.True), |
||||
// ?? false
|
||||
VariableAHasValue & VariableAGetValueOrDefault, |
||||
new ILPattern(ILCode.NullCoalescing, VariableA, BooleanPattern.False), |
||||
// null coalescing
|
||||
new ILPattern(ILCode.TernaryOp, VariableAHasValue, VariableAGetValueOrDefault, Any), |
||||
new ILPattern(ILCode.NullCoalescing, VariableA, Any), |
||||
// all other
|
||||
new ILPattern(ILCode.TernaryOp, VariableAHasValue, NewObj(OperatorNV(OperatorType.Other)), new ILPattern(ILCode.DefaultValue)), |
||||
OperatorVariableAB, |
||||
}; |
||||
|
||||
ILVariable A, B; |
||||
ILExpression Operator, SimpleOperand; |
||||
bool SimpleLeftOperand; |
||||
|
||||
void Reset() |
||||
{ |
||||
this.A = null; |
||||
this.B = null; |
||||
this.Operator = null; |
||||
this.SimpleOperand = null; |
||||
this.SimpleLeftOperand = false; |
||||
} |
||||
|
||||
bool Simplify(ILExpression expr) |
||||
{ |
||||
if (expr.Code == ILCode.TernaryOp || expr.Code == ILCode.LogicAnd || expr.Code == ILCode.LogicOr) { |
||||
Pattern[] ps; |
||||
if (expr.Code != ILCode.TernaryOp) { |
||||
ps = Comparisons; |
||||
for (int i = 0; i < ps.Length; i++) { |
||||
this.Reset(); |
||||
if (!ps[i].Match(this, expr)) continue; |
||||
SetResult(expr, OperatorVariableAB.BuildNew(this)); |
||||
return true; |
||||
} |
||||
} |
||||
ps = Other; |
||||
for (int i = 0; i < ps.Length; i += 2) { |
||||
this.Reset(); |
||||
if (!ps[i].Match(this, expr)) continue; |
||||
var n = ps[i + 1].BuildNew(this); |
||||
SetResult(expr, n); |
||||
if (n.Code == ILCode.NullCoalescing) { |
||||
// if both operands are nullable then the result is also nullable
|
||||
if (n.Arguments[1].Code == ILCode.ValueOf) { |
||||
n.Arguments[0] = n.Arguments[0].Arguments[0]; |
||||
n.Arguments[1] = n.Arguments[1].Arguments[0]; |
||||
} |
||||
} else if (n.Code != ILCode.Ceq && n.Code != ILCode.Cne) { |
||||
expr.Code = ILCode.NullableOf; |
||||
expr.InferredType = expr.ExpectedType = null; |
||||
} |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
static void SetResult(ILExpression expr, ILExpression n) |
||||
{ |
||||
// IL ranges from removed nodes are assigned to the new operator expression
|
||||
var removednodes = expr.GetSelfAndChildrenRecursive<ILExpression>().Except(n.GetSelfAndChildrenRecursive<ILExpression>()); |
||||
n.ILRanges.AddRange(removednodes.SelectMany(el => el.ILRanges)); |
||||
// the new expression is wrapped in a container so that negations aren't pushed through lifted comparison operations
|
||||
expr.Code = ILCode.Wrap; |
||||
expr.Arguments.Clear(); |
||||
expr.Arguments.Add(n); |
||||
expr.ILRanges.Clear(); |
||||
expr.InferredType = n.InferredType; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,443 +0,0 @@
@@ -1,443 +0,0 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
|
||||
using ICSharpCode.Decompiler.FlowAnalysis; |
||||
|
||||
namespace ICSharpCode.Decompiler.ILAst |
||||
{ |
||||
/// <summary>
|
||||
/// Description of LoopsAndConditions.
|
||||
/// </summary>
|
||||
public class LoopsAndConditions |
||||
{ |
||||
Dictionary<ILLabel, ControlFlowNode> labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>(); |
||||
|
||||
readonly DecompilerContext context; |
||||
|
||||
uint nextLabelIndex = 0; |
||||
|
||||
public LoopsAndConditions(DecompilerContext context) |
||||
{ |
||||
this.context = context; |
||||
} |
||||
|
||||
public void FindLoops(ILBlock block) |
||||
{ |
||||
if (block.Body.Count > 0) { |
||||
ControlFlowGraph graph; |
||||
graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand); |
||||
graph.ComputeDominance(context.CancellationToken); |
||||
graph.ComputeDominanceFrontier(); |
||||
block.Body = FindLoops(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint, false); |
||||
} |
||||
} |
||||
|
||||
public void FindConditions(ILBlock block) |
||||
{ |
||||
if (block.Body.Count > 0) { |
||||
ControlFlowGraph graph; |
||||
graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand); |
||||
graph.ComputeDominance(context.CancellationToken); |
||||
graph.ComputeDominanceFrontier(); |
||||
block.Body = FindConditions(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint); |
||||
} |
||||
} |
||||
|
||||
ControlFlowGraph BuildGraph(List<ILNode> nodes, ILLabel entryLabel) |
||||
{ |
||||
int index = 0; |
||||
List<ControlFlowNode> cfNodes = new List<ControlFlowNode>(); |
||||
ControlFlowNode entryPoint = new ControlFlowNode(index++, 0, ControlFlowNodeType.EntryPoint); |
||||
cfNodes.Add(entryPoint); |
||||
ControlFlowNode regularExit = new ControlFlowNode(index++, -1, ControlFlowNodeType.RegularExit); |
||||
cfNodes.Add(regularExit); |
||||
ControlFlowNode exceptionalExit = new ControlFlowNode(index++, -1, ControlFlowNodeType.ExceptionalExit); |
||||
cfNodes.Add(exceptionalExit); |
||||
|
||||
// Create graph nodes
|
||||
labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>(); |
||||
Dictionary<ILNode, ControlFlowNode> astNodeToCfNode = new Dictionary<ILNode, ControlFlowNode>(); |
||||
foreach(ILBasicBlock node in nodes) { |
||||
ControlFlowNode cfNode = new ControlFlowNode(index++, -1, ControlFlowNodeType.Normal); |
||||
cfNodes.Add(cfNode); |
||||
astNodeToCfNode[node] = cfNode; |
||||
cfNode.UserData = node; |
||||
|
||||
// Find all contained labels
|
||||
foreach(ILLabel label in node.GetSelfAndChildrenRecursive<ILLabel>()) { |
||||
labelToCfNode[label] = cfNode; |
||||
} |
||||
} |
||||
|
||||
// Entry endge
|
||||
ControlFlowNode entryNode = labelToCfNode[entryLabel]; |
||||
ControlFlowEdge entryEdge = new ControlFlowEdge(entryPoint, entryNode, JumpType.Normal); |
||||
entryPoint.Outgoing.Add(entryEdge); |
||||
entryNode.Incoming.Add(entryEdge); |
||||
|
||||
// Create edges
|
||||
foreach(ILBasicBlock node in nodes) { |
||||
ControlFlowNode source = astNodeToCfNode[node]; |
||||
|
||||
// Find all branches
|
||||
foreach(ILLabel target in node.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { |
||||
ControlFlowNode destination; |
||||
// Labels which are out of out scope will not be in the collection
|
||||
// Insert self edge only if we are sure we are a loop
|
||||
if (labelToCfNode.TryGetValue(target, out destination) && (destination != source || target == node.Body.FirstOrDefault())) { |
||||
ControlFlowEdge edge = new ControlFlowEdge(source, destination, JumpType.Normal); |
||||
source.Outgoing.Add(edge); |
||||
destination.Incoming.Add(edge); |
||||
} |
||||
} |
||||
} |
||||
|
||||
return new ControlFlowGraph(cfNodes.ToArray()); |
||||
} |
||||
|
||||
List<ILNode> FindLoops(HashSet<ControlFlowNode> scope, ControlFlowNode entryPoint, bool excludeEntryPoint) |
||||
{ |
||||
List<ILNode> result = new List<ILNode>(); |
||||
|
||||
// Do not modify entry data
|
||||
scope = new HashSet<ControlFlowNode>(scope); |
||||
|
||||
Queue<ControlFlowNode> agenda = new Queue<ControlFlowNode>(); |
||||
agenda.Enqueue(entryPoint); |
||||
while(agenda.Count > 0) { |
||||
ControlFlowNode node = agenda.Dequeue(); |
||||
|
||||
// If the node is a loop header
|
||||
if (scope.Contains(node) |
||||
&& node.DominanceFrontier.Contains(node) |
||||
&& (node != entryPoint || !excludeEntryPoint)) |
||||
{ |
||||
HashSet<ControlFlowNode> loopContents = FindLoopContent(scope, node); |
||||
|
||||
// If the first expression is a loop condition
|
||||
ILBasicBlock basicBlock = (ILBasicBlock)node.UserData; |
||||
ILExpression condExpr; |
||||
ILLabel trueLabel; |
||||
ILLabel falseLabel; |
||||
// It has to be just brtrue - any preceding code would introduce goto
|
||||
if(basicBlock.MatchSingleAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) |
||||
{ |
||||
ControlFlowNode trueTarget; |
||||
labelToCfNode.TryGetValue(trueLabel, out trueTarget); |
||||
ControlFlowNode falseTarget; |
||||
labelToCfNode.TryGetValue(falseLabel, out falseTarget); |
||||
|
||||
// If one point inside the loop and the other outside
|
||||
if ((!loopContents.Contains(trueTarget) && loopContents.Contains(falseTarget)) || |
||||
(loopContents.Contains(trueTarget) && !loopContents.Contains(falseTarget)) ) |
||||
{ |
||||
loopContents.RemoveOrThrow(node); |
||||
scope.RemoveOrThrow(node); |
||||
|
||||
// If false means enter the loop
|
||||
if (loopContents.Contains(falseTarget) || falseTarget == node) |
||||
{ |
||||
// Negate the condition
|
||||
condExpr = new ILExpression(ILCode.LogicNot, null, condExpr); |
||||
ILLabel tmp = trueLabel; |
||||
trueLabel = falseLabel; |
||||
falseLabel = tmp; |
||||
} |
||||
|
||||
ControlFlowNode postLoopTarget; |
||||
labelToCfNode.TryGetValue(falseLabel, out postLoopTarget); |
||||
if (postLoopTarget != null) { |
||||
// Pull more nodes into the loop
|
||||
HashSet<ControlFlowNode> postLoopContents = FindDominatedNodes(scope, postLoopTarget); |
||||
var pullIn = scope.Except(postLoopContents).Where(n => node.Dominates(n)); |
||||
loopContents.UnionWith(pullIn); |
||||
} |
||||
|
||||
// Use loop to implement the brtrue
|
||||
basicBlock.Body.RemoveTail(ILCode.Brtrue, ILCode.Br); |
||||
basicBlock.Body.Add(new ILWhileLoop() { |
||||
Condition = condExpr, |
||||
BodyBlock = new ILBlock() { |
||||
EntryGoto = new ILExpression(ILCode.Br, trueLabel), |
||||
Body = FindLoops(loopContents, node, false) |
||||
} |
||||
}); |
||||
basicBlock.Body.Add(new ILExpression(ILCode.Br, falseLabel)); |
||||
result.Add(basicBlock); |
||||
|
||||
scope.ExceptWith(loopContents); |
||||
} |
||||
} |
||||
|
||||
// Fallback method: while(true)
|
||||
if (scope.Contains(node)) { |
||||
result.Add(new ILBasicBlock() { |
||||
Body = new List<ILNode>() { |
||||
new ILLabel() { Name = "Loop_" + (nextLabelIndex++) }, |
||||
new ILWhileLoop() { |
||||
BodyBlock = new ILBlock() { |
||||
EntryGoto = new ILExpression(ILCode.Br, (ILLabel)basicBlock.Body.First()), |
||||
Body = FindLoops(loopContents, node, true) |
||||
} |
||||
}, |
||||
}, |
||||
}); |
||||
|
||||
scope.ExceptWith(loopContents); |
||||
} |
||||
} |
||||
|
||||
// Using the dominator tree should ensure we find the the widest loop first
|
||||
foreach(var child in node.DominatorTreeChildren) { |
||||
agenda.Enqueue(child); |
||||
} |
||||
} |
||||
|
||||
// Add whatever is left
|
||||
foreach(var node in scope) { |
||||
result.Add((ILNode)node.UserData); |
||||
} |
||||
scope.Clear(); |
||||
|
||||
return result; |
||||
} |
||||
|
||||
List<ILNode> FindConditions(HashSet<ControlFlowNode> scope, ControlFlowNode entryNode) |
||||
{ |
||||
List<ILNode> result = new List<ILNode>(); |
||||
|
||||
// Do not modify entry data
|
||||
scope = new HashSet<ControlFlowNode>(scope); |
||||
|
||||
Stack<ControlFlowNode> agenda = new Stack<ControlFlowNode>(); |
||||
agenda.Push(entryNode); |
||||
while(agenda.Count > 0) { |
||||
ControlFlowNode node = agenda.Pop(); |
||||
|
||||
// Find a block that represents a simple condition
|
||||
if (scope.Contains(node)) { |
||||
|
||||
ILBasicBlock block = (ILBasicBlock)node.UserData; |
||||
|
||||
{ |
||||
// Switch
|
||||
ILLabel[] caseLabels; |
||||
ILExpression switchArg; |
||||
ILLabel fallLabel; |
||||
if (block.MatchLastAndBr(ILCode.Switch, out caseLabels, out switchArg, out fallLabel)) { |
||||
|
||||
// Replace the switch code with ILSwitch
|
||||
ILSwitch ilSwitch = new ILSwitch() { Condition = switchArg }; |
||||
block.Body.RemoveTail(ILCode.Switch, ILCode.Br); |
||||
block.Body.Add(ilSwitch); |
||||
block.Body.Add(new ILExpression(ILCode.Br, fallLabel)); |
||||
result.Add(block); |
||||
|
||||
// Remove the item so that it is not picked up as content
|
||||
scope.RemoveOrThrow(node); |
||||
|
||||
// Find the switch offset
|
||||
int addValue = 0; |
||||
List<ILExpression> subArgs; |
||||
if (ilSwitch.Condition.Match(ILCode.Sub, out subArgs) && subArgs[1].Match(ILCode.Ldc_I4, out addValue)) { |
||||
ilSwitch.Condition = subArgs[0]; |
||||
} |
||||
|
||||
// Pull in code of cases
|
||||
ControlFlowNode fallTarget = null; |
||||
labelToCfNode.TryGetValue(fallLabel, out fallTarget); |
||||
|
||||
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>(); |
||||
if (fallTarget != null) |
||||
frontiers.UnionWith(fallTarget.DominanceFrontier.Except(new [] { fallTarget })); |
||||
|
||||
foreach(ILLabel condLabel in caseLabels) { |
||||
ControlFlowNode condTarget = null; |
||||
labelToCfNode.TryGetValue(condLabel, out condTarget); |
||||
if (condTarget != null) |
||||
frontiers.UnionWith(condTarget.DominanceFrontier.Except(new [] { condTarget })); |
||||
} |
||||
|
||||
for (int i = 0; i < caseLabels.Length; i++) { |
||||
ILLabel condLabel = caseLabels[i]; |
||||
|
||||
// Find or create new case block
|
||||
ILSwitch.CaseBlock caseBlock = ilSwitch.CaseBlocks.FirstOrDefault(b => b.EntryGoto.Operand == condLabel); |
||||
if (caseBlock == null) { |
||||
caseBlock = new ILSwitch.CaseBlock() { |
||||
Values = new List<int>(), |
||||
EntryGoto = new ILExpression(ILCode.Br, condLabel) |
||||
}; |
||||
ilSwitch.CaseBlocks.Add(caseBlock); |
||||
|
||||
ControlFlowNode condTarget = null; |
||||
labelToCfNode.TryGetValue(condLabel, out condTarget); |
||||
if (condTarget != null && !frontiers.Contains(condTarget)) { |
||||
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, condTarget); |
||||
scope.ExceptWith(content); |
||||
caseBlock.Body.AddRange(FindConditions(content, condTarget)); |
||||
// Add explicit break which should not be used by default, but the goto removal might decide to use it
|
||||
caseBlock.Body.Add(new ILBasicBlock() { |
||||
Body = { |
||||
new ILLabel() { Name = "SwitchBreak_" + (nextLabelIndex++) }, |
||||
new ILExpression(ILCode.LoopOrSwitchBreak, null) |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
caseBlock.Values.Add(i + addValue); |
||||
} |
||||
|
||||
// Heuristis to determine if we want to use fallthough as default case
|
||||
if (fallTarget != null && !frontiers.Contains(fallTarget)) { |
||||
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, fallTarget); |
||||
if (content.Any()) { |
||||
var caseBlock = new ILSwitch.CaseBlock() { EntryGoto = new ILExpression(ILCode.Br, fallLabel) }; |
||||
ilSwitch.CaseBlocks.Add(caseBlock); |
||||
block.Body.RemoveTail(ILCode.Br); |
||||
|
||||
scope.ExceptWith(content); |
||||
caseBlock.Body.AddRange(FindConditions(content, fallTarget)); |
||||
// Add explicit break which should not be used by default, but the goto removal might decide to use it
|
||||
caseBlock.Body.Add(new ILBasicBlock() { |
||||
Body = { |
||||
new ILLabel() { Name = "SwitchBreak_" + (nextLabelIndex++) }, |
||||
new ILExpression(ILCode.LoopOrSwitchBreak, null) |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Two-way branch
|
||||
ILExpression condExpr; |
||||
ILLabel trueLabel; |
||||
ILLabel falseLabel; |
||||
if(block.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { |
||||
|
||||
// Swap bodies since that seems to be the usual C# order
|
||||
ILLabel temp = trueLabel; |
||||
trueLabel = falseLabel; |
||||
falseLabel = temp; |
||||
condExpr = new ILExpression(ILCode.LogicNot, null, condExpr); |
||||
|
||||
// Convert the brtrue to ILCondition
|
||||
ILCondition ilCond = new ILCondition() { |
||||
Condition = condExpr, |
||||
TrueBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel) }, |
||||
FalseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, falseLabel) } |
||||
}; |
||||
block.Body.RemoveTail(ILCode.Brtrue, ILCode.Br); |
||||
block.Body.Add(ilCond); |
||||
result.Add(block); |
||||
|
||||
// Remove the item immediately so that it is not picked up as content
|
||||
scope.RemoveOrThrow(node); |
||||
|
||||
ControlFlowNode trueTarget = null; |
||||
labelToCfNode.TryGetValue(trueLabel, out trueTarget); |
||||
ControlFlowNode falseTarget = null; |
||||
labelToCfNode.TryGetValue(falseLabel, out falseTarget); |
||||
|
||||
// Pull in the conditional code
|
||||
if (trueTarget != null && HasSingleEdgeEnteringBlock(trueTarget)) { |
||||
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, trueTarget); |
||||
scope.ExceptWith(content); |
||||
ilCond.TrueBlock.Body.AddRange(FindConditions(content, trueTarget)); |
||||
} |
||||
if (falseTarget != null && HasSingleEdgeEnteringBlock(falseTarget)) { |
||||
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, falseTarget); |
||||
scope.ExceptWith(content); |
||||
ilCond.FalseBlock.Body.AddRange(FindConditions(content, falseTarget)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Add the node now so that we have good ordering
|
||||
if (scope.Contains(node)) { |
||||
result.Add((ILNode)node.UserData); |
||||
scope.Remove(node); |
||||
} |
||||
} |
||||
|
||||
// depth-first traversal of dominator tree
|
||||
for (int i = node.DominatorTreeChildren.Count - 1; i >= 0; i--) { |
||||
agenda.Push(node.DominatorTreeChildren[i]); |
||||
} |
||||
} |
||||
|
||||
// Add whatever is left
|
||||
foreach(var node in scope) { |
||||
result.Add((ILNode)node.UserData); |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
static bool HasSingleEdgeEnteringBlock(ControlFlowNode node) |
||||
{ |
||||
return node.Incoming.Count(edge => !node.Dominates(edge.Source)) == 1; |
||||
} |
||||
|
||||
static HashSet<ControlFlowNode> FindDominatedNodes(HashSet<ControlFlowNode> scope, ControlFlowNode head) |
||||
{ |
||||
HashSet<ControlFlowNode> agenda = new HashSet<ControlFlowNode>(); |
||||
HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>(); |
||||
agenda.Add(head); |
||||
|
||||
while(agenda.Count > 0) { |
||||
ControlFlowNode addNode = agenda.First(); |
||||
agenda.Remove(addNode); |
||||
|
||||
if (scope.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) { |
||||
foreach (var successor in addNode.Successors) { |
||||
agenda.Add(successor); |
||||
} |
||||
} |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
static HashSet<ControlFlowNode> FindLoopContent(HashSet<ControlFlowNode> scope, ControlFlowNode head) |
||||
{ |
||||
var viaBackEdges = head.Predecessors.Where(p => head.Dominates(p)); |
||||
HashSet<ControlFlowNode> agenda = new HashSet<ControlFlowNode>(viaBackEdges); |
||||
HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>(); |
||||
|
||||
while(agenda.Count > 0) { |
||||
ControlFlowNode addNode = agenda.First(); |
||||
agenda.Remove(addNode); |
||||
|
||||
if (scope.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) { |
||||
foreach (var predecessor in addNode.Predecessors) { |
||||
agenda.Add(predecessor); |
||||
} |
||||
} |
||||
} |
||||
if (scope.Contains(head)) |
||||
result.Add(head); |
||||
|
||||
return result; |
||||
} |
||||
} |
||||
} |
@ -1,177 +0,0 @@
@@ -1,177 +0,0 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.Linq; |
||||
using Mono.Cecil; |
||||
|
||||
namespace ICSharpCode.Decompiler.ILAst |
||||
{ |
||||
public static class PatternMatching |
||||
{ |
||||
public static bool Match(this ILNode node, ILCode code) |
||||
{ |
||||
ILExpression expr = node as ILExpression; |
||||
return expr != null && expr.Prefixes == null && expr.Code == code; |
||||
} |
||||
|
||||
public static bool Match<T>(this ILNode node, ILCode code, out T operand) |
||||
{ |
||||
ILExpression expr = node as ILExpression; |
||||
if (expr != null && expr.Prefixes == null && expr.Code == code && expr.Arguments.Count == 0) { |
||||
operand = (T)expr.Operand; |
||||
return true; |
||||
} |
||||
operand = default(T); |
||||
return false; |
||||
} |
||||
|
||||
public static bool Match(this ILNode node, ILCode code, out List<ILExpression> args) |
||||
{ |
||||
ILExpression expr = node as ILExpression; |
||||
if (expr != null && expr.Prefixes == null && expr.Code == code) { |
||||
Debug.Assert(expr.Operand == null); |
||||
args = expr.Arguments; |
||||
return true; |
||||
} |
||||
args = null; |
||||
return false; |
||||
} |
||||
|
||||
public static bool Match(this ILNode node, ILCode code, out ILExpression arg) |
||||
{ |
||||
List<ILExpression> args; |
||||
if (node.Match(code, out args) && args.Count == 1) { |
||||
arg = args[0]; |
||||
return true; |
||||
} |
||||
arg = null; |
||||
return false; |
||||
} |
||||
|
||||
public static bool Match<T>(this ILNode node, ILCode code, out T operand, out List<ILExpression> args) |
||||
{ |
||||
ILExpression expr = node as ILExpression; |
||||
if (expr != null && expr.Prefixes == null && expr.Code == code) { |
||||
operand = (T)expr.Operand; |
||||
args = expr.Arguments; |
||||
return true; |
||||
} |
||||
operand = default(T); |
||||
args = null; |
||||
return false; |
||||
} |
||||
|
||||
public static bool Match<T>(this ILNode node, ILCode code, out T operand, out ILExpression arg) |
||||
{ |
||||
List<ILExpression> args; |
||||
if (node.Match(code, out operand, out args) && args.Count == 1) { |
||||
arg = args[0]; |
||||
return true; |
||||
} |
||||
arg = null; |
||||
return false; |
||||
} |
||||
|
||||
public static bool Match<T>(this ILNode node, ILCode code, out T operand, out ILExpression arg1, out ILExpression arg2) |
||||
{ |
||||
List<ILExpression> args; |
||||
if (node.Match(code, out operand, out args) && args.Count == 2) { |
||||
arg1 = args[0]; |
||||
arg2 = args[1]; |
||||
return true; |
||||
} |
||||
arg1 = null; |
||||
arg2 = null; |
||||
return false; |
||||
} |
||||
|
||||
public static bool MatchSingle<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg) |
||||
{ |
||||
if (bb.Body.Count == 2 && |
||||
bb.Body[0] is ILLabel && |
||||
bb.Body[1].Match(code, out operand, out arg)) |
||||
{ |
||||
return true; |
||||
} |
||||
operand = default(T); |
||||
arg = null; |
||||
return false; |
||||
} |
||||
|
||||
public static bool MatchSingleAndBr<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel brLabel) |
||||
{ |
||||
if (bb.Body.Count == 3 && |
||||
bb.Body[0] is ILLabel && |
||||
bb.Body[1].Match(code, out operand, out arg) && |
||||
bb.Body[2].Match(ILCode.Br, out brLabel)) |
||||
{ |
||||
return true; |
||||
} |
||||
operand = default(T); |
||||
arg = null; |
||||
brLabel = null; |
||||
return false; |
||||
} |
||||
|
||||
public static bool MatchLastAndBr<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel brLabel) |
||||
{ |
||||
if (bb.Body.ElementAtOrDefault(bb.Body.Count - 2).Match(code, out operand, out arg) && |
||||
bb.Body.LastOrDefault().Match(ILCode.Br, out brLabel)) |
||||
{ |
||||
return true; |
||||
} |
||||
operand = default(T); |
||||
arg = null; |
||||
brLabel = null; |
||||
return false; |
||||
} |
||||
|
||||
public static bool MatchThis(this ILNode node) |
||||
{ |
||||
ILVariable v; |
||||
return node.Match(ILCode.Ldloc, out v) && v.IsParameter && v.OriginalParameter.Index == -1; |
||||
} |
||||
|
||||
public static bool MatchLdloc(this ILNode node, ILVariable expectedVar) |
||||
{ |
||||
ILVariable v; |
||||
return node.Match(ILCode.Ldloc, out v) && v == expectedVar; |
||||
} |
||||
|
||||
public static bool MatchLdloca(this ILNode node, ILVariable expectedVar) |
||||
{ |
||||
ILVariable v; |
||||
return node.Match(ILCode.Ldloca, out v) && v == expectedVar; |
||||
} |
||||
|
||||
public static bool MatchStloc(this ILNode node, ILVariable expectedVar, out ILExpression expr) |
||||
{ |
||||
ILVariable v; |
||||
return node.Match(ILCode.Stloc, out v, out expr) && v == expectedVar; |
||||
} |
||||
|
||||
public static bool MatchLdcI4(this ILNode node, int expectedValue) |
||||
{ |
||||
int v; |
||||
return node.Match(ILCode.Ldc_I4, out v) && v == expectedValue; |
||||
} |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -1,376 +0,0 @@
@@ -1,376 +0,0 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.Linq; |
||||
|
||||
using Mono.Cecil; |
||||
|
||||
namespace ICSharpCode.Decompiler.ILAst |
||||
{ |
||||
public class SimpleControlFlow |
||||
{ |
||||
Dictionary<ILLabel, int> labelGlobalRefCount = new Dictionary<ILLabel, int>(); |
||||
Dictionary<ILLabel, ILBasicBlock> labelToBasicBlock = new Dictionary<ILLabel, ILBasicBlock>(); |
||||
|
||||
DecompilerContext context; |
||||
TypeSystem typeSystem; |
||||
|
||||
public SimpleControlFlow(DecompilerContext context, ILBlock method) |
||||
{ |
||||
this.context = context; |
||||
this.typeSystem = context.CurrentMethod.Module.TypeSystem; |
||||
|
||||
foreach(ILLabel target in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { |
||||
labelGlobalRefCount[target] = labelGlobalRefCount.GetOrDefault(target) + 1; |
||||
} |
||||
foreach(ILBasicBlock bb in method.GetSelfAndChildrenRecursive<ILBasicBlock>()) { |
||||
foreach(ILLabel label in bb.GetChildren().OfType<ILLabel>()) { |
||||
labelToBasicBlock[label] = bb; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public bool SimplifyTernaryOperator(List<ILNode> body, ILBasicBlock head, int pos) |
||||
{ |
||||
Debug.Assert(body.Contains(head)); |
||||
|
||||
ILExpression condExpr; |
||||
ILLabel trueLabel; |
||||
ILLabel falseLabel; |
||||
ILVariable trueLocVar = null; |
||||
ILExpression trueExpr; |
||||
ILLabel trueFall; |
||||
ILVariable falseLocVar = null; |
||||
ILExpression falseExpr; |
||||
ILLabel falseFall; |
||||
object unused; |
||||
|
||||
if (head.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) && |
||||
labelGlobalRefCount[trueLabel] == 1 && |
||||
labelGlobalRefCount[falseLabel] == 1 && |
||||
((labelToBasicBlock[trueLabel].MatchSingleAndBr(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) && |
||||
labelToBasicBlock[falseLabel].MatchSingleAndBr(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall) && |
||||
trueLocVar == falseLocVar && trueFall == falseFall) || |
||||
(labelToBasicBlock[trueLabel].MatchSingle(ILCode.Ret, out unused, out trueExpr) && |
||||
labelToBasicBlock[falseLabel].MatchSingle(ILCode.Ret, out unused, out falseExpr))) && |
||||
body.Contains(labelToBasicBlock[trueLabel]) && |
||||
body.Contains(labelToBasicBlock[falseLabel]) |
||||
) |
||||
{ |
||||
bool isStloc = trueLocVar != null; |
||||
ILCode opCode = isStloc ? ILCode.Stloc : ILCode.Ret; |
||||
TypeReference retType = isStloc ? trueLocVar.Type : this.context.CurrentMethod.ReturnType; |
||||
bool retTypeIsBoolean = TypeAnalysis.IsBoolean(retType); |
||||
int leftBoolVal; |
||||
int rightBoolVal; |
||||
ILExpression newExpr; |
||||
// a ? true:false is equivalent to a
|
||||
// a ? false:true is equivalent to !a
|
||||
// a ? true : b is equivalent to a || b
|
||||
// a ? b : true is equivalent to !a || b
|
||||
// a ? b : false is equivalent to a && b
|
||||
// a ? false : b is equivalent to !a && b
|
||||
if (retTypeIsBoolean && |
||||
trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) && |
||||
falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) && |
||||
((leftBoolVal != 0 && rightBoolVal == 0) || (leftBoolVal == 0 && rightBoolVal != 0)) |
||||
) |
||||
{ |
||||
// It can be expressed as trivilal expression
|
||||
if (leftBoolVal != 0) { |
||||
newExpr = condExpr; |
||||
} else { |
||||
newExpr = new ILExpression(ILCode.LogicNot, null, condExpr) { InferredType = typeSystem.Boolean }; |
||||
} |
||||
} else if ((retTypeIsBoolean || TypeAnalysis.IsBoolean(falseExpr.InferredType)) && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) && (leftBoolVal == 0 || leftBoolVal == 1)) { |
||||
// It can be expressed as logical expression
|
||||
if (leftBoolVal != 0) { |
||||
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, condExpr, falseExpr); |
||||
} else { |
||||
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, new ILExpression(ILCode.LogicNot, null, condExpr), falseExpr); |
||||
} |
||||
} else if ((retTypeIsBoolean || TypeAnalysis.IsBoolean(trueExpr.InferredType)) && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) && (rightBoolVal == 0 || rightBoolVal == 1)) { |
||||
// It can be expressed as logical expression
|
||||
if (rightBoolVal != 0) { |
||||
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, new ILExpression(ILCode.LogicNot, null, condExpr), trueExpr); |
||||
} else { |
||||
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, condExpr, trueExpr); |
||||
} |
||||
} else { |
||||
// Ternary operator tends to create long complicated return statements
|
||||
if (opCode == ILCode.Ret) |
||||
return false; |
||||
|
||||
// Only simplify generated variables
|
||||
if (opCode == ILCode.Stloc && !trueLocVar.IsGenerated) |
||||
return false; |
||||
|
||||
// Create ternary expression
|
||||
newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr); |
||||
} |
||||
|
||||
head.Body.RemoveTail(ILCode.Brtrue, ILCode.Br); |
||||
head.Body.Add(new ILExpression(opCode, trueLocVar, newExpr)); |
||||
if (isStloc) |
||||
head.Body.Add(new ILExpression(ILCode.Br, trueFall)); |
||||
|
||||
// Remove the old basic blocks
|
||||
body.RemoveOrThrow(labelToBasicBlock[trueLabel]); |
||||
body.RemoveOrThrow(labelToBasicBlock[falseLabel]); |
||||
|
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
public bool SimplifyNullCoalescing(List<ILNode> body, ILBasicBlock head, int pos) |
||||
{ |
||||
// ...
|
||||
// v = ldloc(leftVar)
|
||||
// brtrue(endBBLabel, ldloc(leftVar))
|
||||
// br(rightBBLabel)
|
||||
//
|
||||
// rightBBLabel:
|
||||
// v = rightExpr
|
||||
// br(endBBLabel)
|
||||
// ...
|
||||
// =>
|
||||
// ...
|
||||
// v = NullCoalescing(ldloc(leftVar), rightExpr)
|
||||
// br(endBBLabel)
|
||||
|
||||
ILVariable v, v2; |
||||
ILExpression leftExpr, leftExpr2; |
||||
ILVariable leftVar; |
||||
ILLabel endBBLabel, endBBLabel2; |
||||
ILLabel rightBBLabel; |
||||
ILBasicBlock rightBB; |
||||
ILExpression rightExpr; |
||||
if (head.Body.Count >= 3 && |
||||
head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out v, out leftExpr) && |
||||
leftExpr.Match(ILCode.Ldloc, out leftVar) && |
||||
head.MatchLastAndBr(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) && |
||||
leftExpr2.MatchLdloc(leftVar) && |
||||
labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) && |
||||
rightBB.MatchSingleAndBr(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) && |
||||
v == v2 && |
||||
endBBLabel == endBBLabel2 && |
||||
labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 && |
||||
body.Contains(rightBB) |
||||
) |
||||
{ |
||||
head.Body.RemoveTail(ILCode.Stloc, ILCode.Brtrue, ILCode.Br); |
||||
head.Body.Add(new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.NullCoalescing, null, leftExpr, rightExpr))); |
||||
head.Body.Add(new ILExpression(ILCode.Br, endBBLabel)); |
||||
|
||||
body.RemoveOrThrow(labelToBasicBlock[rightBBLabel]); |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
public bool SimplifyShortCircuit(List<ILNode> body, ILBasicBlock head, int pos) |
||||
{ |
||||
Debug.Assert(body.Contains(head)); |
||||
|
||||
ILExpression condExpr; |
||||
ILLabel trueLabel; |
||||
ILLabel falseLabel; |
||||
if(head.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { |
||||
for (int pass = 0; pass < 2; pass++) { |
||||
|
||||
// On the second pass, swap labels and negate expression of the first branch
|
||||
// It is slightly ugly, but much better then copy-pasting this whole block
|
||||
ILLabel nextLabel = (pass == 0) ? trueLabel : falseLabel; |
||||
ILLabel otherLablel = (pass == 0) ? falseLabel : trueLabel; |
||||
bool negate = (pass == 1); |
||||
|
||||
ILBasicBlock nextBasicBlock = labelToBasicBlock[nextLabel]; |
||||
ILExpression nextCondExpr; |
||||
ILLabel nextTrueLablel; |
||||
ILLabel nextFalseLabel; |
||||
if (body.Contains(nextBasicBlock) && |
||||
nextBasicBlock != head && |
||||
labelGlobalRefCount[(ILLabel)nextBasicBlock.Body.First()] == 1 && |
||||
nextBasicBlock.MatchSingleAndBr(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) && |
||||
(otherLablel == nextFalseLabel || otherLablel == nextTrueLablel)) |
||||
{ |
||||
// Create short cicuit branch
|
||||
ILExpression logicExpr; |
||||
if (otherLablel == nextFalseLabel) { |
||||
logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, negate ? new ILExpression(ILCode.LogicNot, null, condExpr) : condExpr, nextCondExpr); |
||||
} else { |
||||
logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, negate ? condExpr : new ILExpression(ILCode.LogicNot, null, condExpr), nextCondExpr); |
||||
} |
||||
head.Body.RemoveTail(ILCode.Brtrue, ILCode.Br); |
||||
head.Body.Add(new ILExpression(ILCode.Brtrue, nextTrueLablel, logicExpr)); |
||||
head.Body.Add(new ILExpression(ILCode.Br, nextFalseLabel)); |
||||
|
||||
// Remove the inlined branch from scope
|
||||
body.RemoveOrThrow(nextBasicBlock); |
||||
|
||||
return true; |
||||
} |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
public bool SimplifyCustomShortCircuit(List<ILNode> body, ILBasicBlock head, int pos) |
||||
{ |
||||
Debug.Assert(body.Contains(head)); |
||||
|
||||
// --- looking for the following pattern ---
|
||||
// stloc(targetVar, leftVar)
|
||||
// brtrue(exitLabel, call(op_False, leftVar)
|
||||
// br(followingBlock)
|
||||
//
|
||||
// FollowingBlock:
|
||||
// stloc(targetVar, call(op_BitwiseAnd, leftVar, rightExpression))
|
||||
// br(exitLabel)
|
||||
// ---
|
||||
|
||||
if (head.Body.Count < 3) |
||||
return false; |
||||
|
||||
// looking for:
|
||||
// stloc(targetVar, leftVar)
|
||||
ILVariable targetVar; |
||||
ILExpression targetVarInitExpr; |
||||
if (!head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out targetVar, out targetVarInitExpr)) |
||||
return false; |
||||
|
||||
ILVariable leftVar; |
||||
if (!targetVarInitExpr.Match(ILCode.Ldloc, out leftVar)) |
||||
return false; |
||||
|
||||
// looking for:
|
||||
// brtrue(exitLabel, call(op_False, leftVar)
|
||||
// br(followingBlock)
|
||||
ILExpression callExpr; |
||||
ILLabel exitLabel; |
||||
ILLabel followingBlock; |
||||
if(!head.MatchLastAndBr(ILCode.Brtrue, out exitLabel, out callExpr, out followingBlock)) |
||||
return false; |
||||
|
||||
if (labelGlobalRefCount[followingBlock] > 1) |
||||
return false; |
||||
|
||||
MethodReference opFalse; |
||||
ILExpression opFalseArg; |
||||
if (!callExpr.Match(ILCode.Call, out opFalse, out opFalseArg)) |
||||
return false; |
||||
|
||||
// ignore operators other than op_False and op_True
|
||||
if (opFalse.Name != "op_False" && opFalse.Name != "op_True") |
||||
return false; |
||||
|
||||
if (!opFalseArg.MatchLdloc(leftVar)) |
||||
return false; |
||||
|
||||
ILBasicBlock followingBasicBlock = labelToBasicBlock[followingBlock]; |
||||
|
||||
// FollowingBlock:
|
||||
// stloc(targetVar, call(op_BitwiseAnd, leftVar, rightExpression))
|
||||
// br(exitLabel)
|
||||
ILVariable _targetVar; |
||||
ILExpression opBitwiseCallExpr; |
||||
ILLabel _exitLabel; |
||||
if (!followingBasicBlock.MatchSingleAndBr(ILCode.Stloc, out _targetVar, out opBitwiseCallExpr, out _exitLabel)) |
||||
return false; |
||||
|
||||
if (_targetVar != targetVar || exitLabel != _exitLabel) |
||||
return false; |
||||
|
||||
MethodReference opBitwise; |
||||
ILExpression leftVarExpression; |
||||
ILExpression rightExpression; |
||||
if (!opBitwiseCallExpr.Match(ILCode.Call, out opBitwise, out leftVarExpression, out rightExpression)) |
||||
return false; |
||||
|
||||
if (!leftVarExpression.MatchLdloc(leftVar)) |
||||
return false; |
||||
|
||||
// ignore operators other than op_BitwiseAnd and op_BitwiseOr
|
||||
if (opBitwise.Name != "op_BitwiseAnd" && opBitwise.Name != "op_BitwiseOr") |
||||
return false; |
||||
|
||||
// insert:
|
||||
// stloc(targetVar, LogicAnd(C::op_BitwiseAnd, leftVar, rightExpression)
|
||||
// br(exitLabel)
|
||||
ILCode op = opBitwise.Name == "op_BitwiseAnd" ? ILCode.LogicAnd : ILCode.LogicOr; |
||||
|
||||
if (op == ILCode.LogicAnd && opFalse.Name != "op_False") |
||||
return false; |
||||
|
||||
if (op == ILCode.LogicOr && opFalse.Name != "op_True") |
||||
return false; |
||||
|
||||
ILExpression shortCircuitExpr = MakeLeftAssociativeShortCircuit(op, opFalseArg, rightExpression); |
||||
shortCircuitExpr.Operand = opBitwise; |
||||
|
||||
head.Body.RemoveTail(ILCode.Stloc, ILCode.Brtrue, ILCode.Br); |
||||
head.Body.Add(new ILExpression(ILCode.Stloc, targetVar, shortCircuitExpr)); |
||||
head.Body.Add(new ILExpression(ILCode.Br, exitLabel)); |
||||
body.Remove(followingBasicBlock); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
ILExpression MakeLeftAssociativeShortCircuit(ILCode code, ILExpression left, ILExpression right) |
||||
{ |
||||
// Assuming that the inputs are already left associative
|
||||
if (right.Match(code)) { |
||||
// Find the leftmost logical expression
|
||||
ILExpression current = right; |
||||
while(current.Arguments[0].Match(code)) |
||||
current = current.Arguments[0]; |
||||
current.Arguments[0] = new ILExpression(code, null, left, current.Arguments[0]) { InferredType = typeSystem.Boolean }; |
||||
return right; |
||||
} else { |
||||
return new ILExpression(code, null, left, right) { InferredType = typeSystem.Boolean }; |
||||
} |
||||
} |
||||
|
||||
public bool JoinBasicBlocks(List<ILNode> body, ILBasicBlock head, int pos) |
||||
{ |
||||
ILLabel nextLabel; |
||||
ILBasicBlock nextBB; |
||||
if (!head.Body.ElementAtOrDefault(head.Body.Count - 2).IsConditionalControlFlow() && |
||||
head.Body.Last().Match(ILCode.Br, out nextLabel) && |
||||
labelGlobalRefCount[nextLabel] == 1 && |
||||
labelToBasicBlock.TryGetValue(nextLabel, out nextBB) && |
||||
body.Contains(nextBB) && |
||||
nextBB.Body.First() == nextLabel && |
||||
!nextBB.Body.OfType<ILTryCatchBlock>().Any() |
||||
) |
||||
{ |
||||
head.Body.RemoveTail(ILCode.Br); |
||||
nextBB.Body.RemoveAt(0); // Remove label
|
||||
head.Body.AddRange(nextBB.Body); |
||||
|
||||
body.RemoveOrThrow(nextBB); |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
} |
||||
} |
@ -1,321 +0,0 @@
@@ -1,321 +0,0 @@
|
||||
// Copyright (c) 2012 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.Linq; |
||||
using Mono.Cecil; |
||||
|
||||
namespace ICSharpCode.Decompiler.ILAst |
||||
{ |
||||
struct Interval |
||||
{ |
||||
public readonly int Start, End; |
||||
|
||||
public Interval(int start, int end) |
||||
{ |
||||
Debug.Assert(start <= end || (start == 0 && end == -1)); |
||||
this.Start = start; |
||||
this.End = end; |
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
return string.Format("({0} to {1})", Start, End); |
||||
} |
||||
} |
||||
|
||||
class StateRange |
||||
{ |
||||
readonly List<Interval> data = new List<Interval>(); |
||||
|
||||
public StateRange() |
||||
{ |
||||
} |
||||
|
||||
public StateRange(int start, int end) |
||||
{ |
||||
this.data.Add(new Interval(start, end)); |
||||
} |
||||
|
||||
public bool IsEmpty { |
||||
get { return data.Count == 0; } |
||||
} |
||||
|
||||
public bool Contains(int val) |
||||
{ |
||||
foreach (Interval v in data) { |
||||
if (v.Start <= val && val <= v.End) |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
public void UnionWith(StateRange other) |
||||
{ |
||||
data.AddRange(other.data); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Unions this state range with (other intersect (minVal to maxVal))
|
||||
/// </summary>
|
||||
public void UnionWith(StateRange other, int minVal, int maxVal) |
||||
{ |
||||
foreach (Interval v in other.data) { |
||||
int start = Math.Max(v.Start, minVal); |
||||
int end = Math.Min(v.End, maxVal); |
||||
if (start <= end) |
||||
data.Add(new Interval(start, end)); |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Merges overlapping interval ranges.
|
||||
/// </summary>
|
||||
public void Simplify() |
||||
{ |
||||
if (data.Count < 2) |
||||
return; |
||||
data.Sort((a, b) => a.Start.CompareTo(b.Start)); |
||||
Interval prev = data[0]; |
||||
int prevIndex = 0; |
||||
for (int i = 1; i < data.Count; i++) { |
||||
Interval next = data[i]; |
||||
Debug.Assert(prev.Start <= next.Start); |
||||
if (next.Start <= prev.End + 1) { // intervals overlapping or touching
|
||||
prev = new Interval(prev.Start, Math.Max(prev.End, next.End)); |
||||
data[prevIndex] = prev; |
||||
data[i] = new Interval(0, -1); // mark as deleted
|
||||
} else { |
||||
prev = next; |
||||
prevIndex = i; |
||||
} |
||||
} |
||||
data.RemoveAll(i => i.Start > i.End); // remove all entries that were marked as deleted
|
||||
} |
||||
|
||||
public override string ToString() |
||||
{ |
||||
return string.Join(",", data); |
||||
} |
||||
|
||||
public Interval ToEnclosingInterval() |
||||
{ |
||||
if (data.Count == 0) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
return new Interval(data[0].Start, data[data.Count - 1].End); |
||||
} |
||||
} |
||||
|
||||
enum StateRangeAnalysisMode |
||||
{ |
||||
IteratorMoveNext, |
||||
IteratorDispose, |
||||
AsyncMoveNext |
||||
} |
||||
|
||||
class StateRangeAnalysis |
||||
{ |
||||
readonly StateRangeAnalysisMode mode; |
||||
readonly FieldDefinition stateField; |
||||
internal DefaultDictionary<ILNode, StateRange> ranges; |
||||
SymbolicEvaluationContext evalContext; |
||||
|
||||
internal Dictionary<MethodDefinition, StateRange> finallyMethodToStateRange; // used only for IteratorDispose
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the state range logic:
|
||||
/// Clears 'ranges' and sets 'ranges[entryPoint]' to the full range (int.MinValue to int.MaxValue)
|
||||
/// </summary>
|
||||
public StateRangeAnalysis(ILNode entryPoint, StateRangeAnalysisMode mode, FieldDefinition stateField, ILVariable cachedStateVar = null) |
||||
{ |
||||
this.mode = mode; |
||||
this.stateField = stateField; |
||||
if (mode == StateRangeAnalysisMode.IteratorDispose) { |
||||
finallyMethodToStateRange = new Dictionary<MethodDefinition, StateRange>(); |
||||
} |
||||
|
||||
ranges = new DefaultDictionary<ILNode, StateRange>(n => new StateRange()); |
||||
ranges[entryPoint] = new StateRange(int.MinValue, int.MaxValue); |
||||
evalContext = new SymbolicEvaluationContext(stateField); |
||||
if (cachedStateVar != null) |
||||
evalContext.AddStateVariable(cachedStateVar); |
||||
} |
||||
|
||||
public int AssignStateRanges(List<ILNode> body, int bodyLength) |
||||
{ |
||||
if (bodyLength == 0) |
||||
return 0; |
||||
for (int i = 0; i < bodyLength; i++) { |
||||
StateRange nodeRange = ranges[body[i]]; |
||||
nodeRange.Simplify(); |
||||
|
||||
ILLabel label = body[i] as ILLabel; |
||||
if (label != null) { |
||||
ranges[body[i + 1]].UnionWith(nodeRange); |
||||
continue; |
||||
} |
||||
|
||||
ILTryCatchBlock tryFinally = body[i] as ILTryCatchBlock; |
||||
if (tryFinally != null) { |
||||
if (mode == StateRangeAnalysisMode.IteratorDispose) { |
||||
if (tryFinally.CatchBlocks.Count != 0 || tryFinally.FaultBlock != null || tryFinally.FinallyBlock == null) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
ranges[tryFinally.TryBlock].UnionWith(nodeRange); |
||||
if (tryFinally.TryBlock.Body.Count != 0) { |
||||
ranges[tryFinally.TryBlock.Body[0]].UnionWith(nodeRange); |
||||
AssignStateRanges(tryFinally.TryBlock.Body, tryFinally.TryBlock.Body.Count); |
||||
} |
||||
continue; |
||||
} else if (mode == StateRangeAnalysisMode.AsyncMoveNext) { |
||||
return i; |
||||
} else { |
||||
throw new SymbolicAnalysisFailedException(); |
||||
} |
||||
} |
||||
|
||||
ILExpression expr = body[i] as ILExpression; |
||||
if (expr == null) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
switch (expr.Code) { |
||||
case ILCode.Switch: |
||||
{ |
||||
SymbolicValue val = evalContext.Eval(expr.Arguments[0]); |
||||
if (val.Type != SymbolicValueType.State) |
||||
goto default; |
||||
ILLabel[] targetLabels = (ILLabel[])expr.Operand; |
||||
for (int j = 0; j < targetLabels.Length; j++) { |
||||
int state = j - val.Constant; |
||||
ranges[targetLabels[j]].UnionWith(nodeRange, state, state); |
||||
} |
||||
StateRange nextRange = ranges[body[i + 1]]; |
||||
nextRange.UnionWith(nodeRange, int.MinValue, -1 - val.Constant); |
||||
nextRange.UnionWith(nodeRange, targetLabels.Length - val.Constant, int.MaxValue); |
||||
break; |
||||
} |
||||
case ILCode.Br: |
||||
case ILCode.Leave: |
||||
ranges[(ILLabel)expr.Operand].UnionWith(nodeRange); |
||||
break; |
||||
case ILCode.Brtrue: |
||||
{ |
||||
SymbolicValue val = evalContext.Eval(expr.Arguments[0]).AsBool(); |
||||
if (val.Type == SymbolicValueType.StateEquals) { |
||||
ranges[(ILLabel)expr.Operand].UnionWith(nodeRange, val.Constant, val.Constant); |
||||
StateRange nextRange = ranges[body[i + 1]]; |
||||
nextRange.UnionWith(nodeRange, int.MinValue, val.Constant - 1); |
||||
nextRange.UnionWith(nodeRange, val.Constant + 1, int.MaxValue); |
||||
break; |
||||
} else if (val.Type == SymbolicValueType.StateInEquals) { |
||||
ranges[body[i + 1]].UnionWith(nodeRange, val.Constant, val.Constant); |
||||
StateRange targetRange = ranges[(ILLabel)expr.Operand]; |
||||
targetRange.UnionWith(nodeRange, int.MinValue, val.Constant - 1); |
||||
targetRange.UnionWith(nodeRange, val.Constant + 1, int.MaxValue); |
||||
break; |
||||
} else { |
||||
goto default; |
||||
} |
||||
} |
||||
case ILCode.Nop: |
||||
ranges[body[i + 1]].UnionWith(nodeRange); |
||||
break; |
||||
case ILCode.Ret: |
||||
break; |
||||
case ILCode.Stloc: |
||||
{ |
||||
SymbolicValue val = evalContext.Eval(expr.Arguments[0]); |
||||
if (val.Type == SymbolicValueType.State && val.Constant == 0) { |
||||
evalContext.AddStateVariable((ILVariable)expr.Operand); |
||||
goto case ILCode.Nop; |
||||
} else { |
||||
goto default; |
||||
} |
||||
} |
||||
case ILCode.Call: |
||||
// in some cases (e.g. foreach over array) the C# compiler produces a finally method outside of try-finally blocks
|
||||
if (mode == StateRangeAnalysisMode.IteratorDispose) { |
||||
MethodDefinition mdef = (expr.Operand as MethodReference).ResolveWithinSameModule(); |
||||
if (mdef == null || finallyMethodToStateRange.ContainsKey(mdef)) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
finallyMethodToStateRange.Add(mdef, nodeRange); |
||||
break; |
||||
} else { |
||||
goto default; |
||||
} |
||||
default: |
||||
if (mode == StateRangeAnalysisMode.IteratorDispose) { |
||||
throw new SymbolicAnalysisFailedException(); |
||||
} else { |
||||
return i; |
||||
} |
||||
} |
||||
} |
||||
return bodyLength; |
||||
} |
||||
|
||||
public void EnsureLabelAtPos(List<ILNode> body, ref int pos, ref int bodyLength) |
||||
{ |
||||
if (pos > 0 && body[pos - 1] is ILLabel) { |
||||
pos--; |
||||
return; // label found
|
||||
} |
||||
|
||||
// ensure that the first element at body[pos] is a label:
|
||||
ILLabel newLabel = new ILLabel(); |
||||
newLabel.Name = "YieldReturnEntryPoint"; |
||||
|
||||
ILExpression expr = pos == 1 && body.Count == 1 ? body[0] as ILExpression : null; |
||||
if (expr != null && expr.Code == ILCode.Leave && expr.Operand is ILLabel) { |
||||
ranges[newLabel] = ranges[(ILLabel)expr.Operand]; |
||||
pos = 0; |
||||
} else { |
||||
ranges[newLabel] = ranges[body[pos]]; // give the label the range of the instruction at body[pos]
|
||||
} |
||||
|
||||
body.Insert(pos, newLabel); |
||||
bodyLength++; |
||||
} |
||||
|
||||
public LabelRangeMapping CreateLabelRangeMapping(List<ILNode> body, int pos, int bodyLength) |
||||
{ |
||||
LabelRangeMapping result = new LabelRangeMapping(); |
||||
CreateLabelRangeMapping(body, pos, bodyLength, result, false); |
||||
return result; |
||||
} |
||||
|
||||
void CreateLabelRangeMapping(List<ILNode> body, int pos, int bodyLength, LabelRangeMapping result, bool onlyInitialLabels) |
||||
{ |
||||
for (int i = pos; i < bodyLength; i++) { |
||||
ILLabel label = body[i] as ILLabel; |
||||
if (label != null) { |
||||
result.Add(new KeyValuePair<ILLabel, StateRange>(label, ranges[label])); |
||||
} else { |
||||
ILTryCatchBlock tryCatchBlock = body[i] as ILTryCatchBlock; |
||||
if (tryCatchBlock != null) { |
||||
CreateLabelRangeMapping(tryCatchBlock.TryBlock.Body, 0, tryCatchBlock.TryBlock.Body.Count, result, true); |
||||
} else if (onlyInitialLabels) { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
class LabelRangeMapping : List<KeyValuePair<ILLabel, StateRange>> {} |
||||
} |
@ -1,157 +0,0 @@
@@ -1,157 +0,0 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using Mono.Cecil; |
||||
|
||||
namespace ICSharpCode.Decompiler.ILAst |
||||
{ |
||||
/// <summary>
|
||||
/// This exception is thrown when we find something else than we expect from the C# compiler.
|
||||
/// This aborts the analysis and makes the whole transform fail.
|
||||
/// </summary>
|
||||
class SymbolicAnalysisFailedException : Exception {} |
||||
|
||||
enum SymbolicValueType |
||||
{ |
||||
/// <summary>
|
||||
/// Unknown value
|
||||
/// </summary>
|
||||
Unknown, |
||||
/// <summary>
|
||||
/// int: Constant (result of ldc.i4)
|
||||
/// </summary>
|
||||
IntegerConstant, |
||||
/// <summary>
|
||||
/// int: State + Constant
|
||||
/// </summary>
|
||||
State, |
||||
/// <summary>
|
||||
/// This pointer (result of ldarg.0)
|
||||
/// </summary>
|
||||
This, |
||||
/// <summary>
|
||||
/// bool: State == Constant
|
||||
/// </summary>
|
||||
StateEquals, |
||||
/// <summary>
|
||||
/// bool: State != Constant
|
||||
/// </summary>
|
||||
StateInEquals |
||||
} |
||||
|
||||
struct SymbolicValue |
||||
{ |
||||
public readonly int Constant; |
||||
public readonly SymbolicValueType Type; |
||||
|
||||
public SymbolicValue(SymbolicValueType type, int constant = 0) |
||||
{ |
||||
this.Type = type; |
||||
this.Constant = constant; |
||||
} |
||||
|
||||
public SymbolicValue AsBool() |
||||
{ |
||||
if (Type == SymbolicValueType.State) { |
||||
// convert state integer to bool:
|
||||
// if (state + c) = if (state + c != 0) = if (state != -c)
|
||||
return new SymbolicValue(SymbolicValueType.StateInEquals, unchecked(-Constant)); |
||||
} |
||||
return this; |
||||
} |
||||
public override string ToString() |
||||
{ |
||||
return string.Format("[SymbolicValue {0}: {1}]", this.Type, this.Constant); |
||||
} |
||||
} |
||||
|
||||
class SymbolicEvaluationContext |
||||
{ |
||||
readonly FieldDefinition stateField; |
||||
readonly List<ILVariable> stateVariables = new List<ILVariable>(); |
||||
|
||||
public SymbolicEvaluationContext(FieldDefinition stateField) |
||||
{ |
||||
this.stateField = stateField; |
||||
} |
||||
|
||||
public void AddStateVariable(ILVariable v) |
||||
{ |
||||
if (!stateVariables.Contains(v)) |
||||
stateVariables.Add(v); |
||||
} |
||||
|
||||
SymbolicValue Failed() |
||||
{ |
||||
return new SymbolicValue(SymbolicValueType.Unknown); |
||||
} |
||||
|
||||
public SymbolicValue Eval(ILExpression expr) |
||||
{ |
||||
SymbolicValue left, right; |
||||
switch (expr.Code) { |
||||
case ILCode.Sub: |
||||
left = Eval(expr.Arguments[0]); |
||||
right = Eval(expr.Arguments[1]); |
||||
if (left.Type != SymbolicValueType.State && left.Type != SymbolicValueType.IntegerConstant) |
||||
return Failed(); |
||||
if (right.Type != SymbolicValueType.IntegerConstant) |
||||
return Failed(); |
||||
return new SymbolicValue(left.Type, unchecked ( left.Constant - right.Constant )); |
||||
case ILCode.Ldfld: |
||||
if (Eval(expr.Arguments[0]).Type != SymbolicValueType.This) |
||||
return Failed(); |
||||
if (CecilExtensions.ResolveWithinSameModule(expr.Operand as FieldReference) != stateField) |
||||
return Failed(); |
||||
return new SymbolicValue(SymbolicValueType.State); |
||||
case ILCode.Ldloc: |
||||
ILVariable loadedVariable = (ILVariable)expr.Operand; |
||||
if (stateVariables.Contains(loadedVariable)) |
||||
return new SymbolicValue(SymbolicValueType.State); |
||||
else if (loadedVariable.IsParameter && loadedVariable.OriginalParameter.Index < 0) |
||||
return new SymbolicValue(SymbolicValueType.This); |
||||
else |
||||
return Failed(); |
||||
case ILCode.Ldc_I4: |
||||
return new SymbolicValue(SymbolicValueType.IntegerConstant, (int)expr.Operand); |
||||
case ILCode.Ceq: |
||||
case ILCode.Cne: |
||||
left = Eval(expr.Arguments[0]); |
||||
right = Eval(expr.Arguments[1]); |
||||
if (left.Type != SymbolicValueType.State || right.Type != SymbolicValueType.IntegerConstant) |
||||
return Failed(); |
||||
// bool: (state + left.Constant == right.Constant)
|
||||
// bool: (state == right.Constant - left.Constant)
|
||||
return new SymbolicValue(expr.Code == ILCode.Ceq ? SymbolicValueType.StateEquals : SymbolicValueType.StateInEquals, unchecked(right.Constant - left.Constant)); |
||||
case ILCode.LogicNot: |
||||
SymbolicValue val = Eval(expr.Arguments[0]).AsBool(); |
||||
if (val.Type == SymbolicValueType.StateEquals) |
||||
return new SymbolicValue(SymbolicValueType.StateInEquals, val.Constant); |
||||
else if (val.Type == SymbolicValueType.StateInEquals) |
||||
return new SymbolicValue(SymbolicValueType.StateEquals, val.Constant); |
||||
else |
||||
return Failed(); |
||||
default: |
||||
return Failed(); |
||||
} |
||||
} |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -1,635 +0,0 @@
@@ -1,635 +0,0 @@
|
||||
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.Linq; |
||||
using Mono.Cecil; |
||||
|
||||
namespace ICSharpCode.Decompiler.ILAst |
||||
{ |
||||
class YieldReturnDecompiler |
||||
{ |
||||
// For a description on the code generated by the C# compiler for yield return:
|
||||
// http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx
|
||||
|
||||
// The idea here is:
|
||||
// - Figure out whether the current method is instanciating an enumerator
|
||||
// - Figure out which of the fields is the state field
|
||||
// - Construct an exception table based on states. This allows us to determine, for each state, what the parent try block is.
|
||||
|
||||
// See http://community.sharpdevelop.net/blogs/danielgrunwald/archive/2011/03/06/ilspy-yield-return.aspx
|
||||
// for a description of this step.
|
||||
|
||||
DecompilerContext context; |
||||
TypeDefinition enumeratorType; |
||||
MethodDefinition enumeratorCtor; |
||||
MethodDefinition disposeMethod; |
||||
FieldDefinition stateField; |
||||
FieldDefinition currentField; |
||||
Dictionary<FieldDefinition, ILVariable> fieldToParameterMap = new Dictionary<FieldDefinition, ILVariable>(); |
||||
List<ILNode> newBody; |
||||
|
||||
#region Run() method
|
||||
public static void Run(DecompilerContext context, ILBlock method) |
||||
{ |
||||
if (!context.Settings.YieldReturn) |
||||
return; // abort if enumerator decompilation is disabled
|
||||
var yrd = new YieldReturnDecompiler(); |
||||
yrd.context = context; |
||||
if (!yrd.MatchEnumeratorCreationPattern(method)) |
||||
return; |
||||
yrd.enumeratorType = yrd.enumeratorCtor.DeclaringType; |
||||
#if DEBUG
|
||||
if (Debugger.IsAttached) { |
||||
yrd.Run(); |
||||
} else { |
||||
#endif
|
||||
try { |
||||
yrd.Run(); |
||||
} catch (SymbolicAnalysisFailedException) { |
||||
return; |
||||
} |
||||
#if DEBUG
|
||||
} |
||||
#endif
|
||||
method.Body.Clear(); |
||||
method.EntryGoto = null; |
||||
method.Body.AddRange(yrd.newBody); |
||||
|
||||
// Repeat the inlining/copy propagation optimization because the conversion of field access
|
||||
// to local variables can open up additional inlining possibilities.
|
||||
ILInlining inlining = new ILInlining(method); |
||||
inlining.InlineAllVariables(); |
||||
inlining.CopyPropagation(); |
||||
} |
||||
|
||||
void Run() |
||||
{ |
||||
AnalyzeCtor(); |
||||
AnalyzeCurrentProperty(); |
||||
ResolveIEnumerableIEnumeratorFieldMapping(); |
||||
ConstructExceptionTable(); |
||||
AnalyzeMoveNext(); |
||||
TranslateFieldsToLocalAccess(); |
||||
} |
||||
#endregion
|
||||
|
||||
#region Match the enumerator creation pattern
|
||||
bool MatchEnumeratorCreationPattern(ILBlock method) |
||||
{ |
||||
if (method.Body.Count == 0) |
||||
return false; |
||||
ILExpression newObj; |
||||
if (method.Body.Count == 1) { |
||||
// ret(newobj(...))
|
||||
if (method.Body[0].Match(ILCode.Ret, out newObj)) |
||||
return MatchEnumeratorCreationNewObj(newObj, out enumeratorCtor); |
||||
else |
||||
return false; |
||||
} |
||||
// stloc(var_1, newobj(..)
|
||||
ILVariable var1; |
||||
if (!method.Body[0].Match(ILCode.Stloc, out var1, out newObj)) |
||||
return false; |
||||
if (!MatchEnumeratorCreationNewObj(newObj, out enumeratorCtor)) |
||||
return false; |
||||
|
||||
int i; |
||||
for (i = 1; i < method.Body.Count; i++) { |
||||
// stfld(..., ldloc(var_1), ldloc(parameter))
|
||||
FieldReference storedField; |
||||
ILExpression ldloc, loadParameter; |
||||
if (!method.Body[i].Match(ILCode.Stfld, out storedField, out ldloc, out loadParameter)) |
||||
break; |
||||
ILVariable loadedVar, loadedArg; |
||||
if (!ldloc.Match(ILCode.Ldloc, out loadedVar) || !loadParameter.Match(ILCode.Ldloc, out loadedArg)) |
||||
return false; |
||||
storedField = GetFieldDefinition(storedField); |
||||
if (loadedVar != var1 || storedField == null || !loadedArg.IsParameter) |
||||
return false; |
||||
fieldToParameterMap[(FieldDefinition)storedField] = loadedArg; |
||||
} |
||||
ILVariable var2; |
||||
ILExpression ldlocForStloc2; |
||||
if (i < method.Body.Count && method.Body[i].Match(ILCode.Stloc, out var2, out ldlocForStloc2)) { |
||||
// stloc(var_2, ldloc(var_1))
|
||||
if (ldlocForStloc2.Code != ILCode.Ldloc || ldlocForStloc2.Operand != var1) |
||||
return false; |
||||
i++; |
||||
} else { |
||||
// the compiler might skip the above instruction in release builds; in that case, it directly returns stloc.Operand
|
||||
var2 = var1; |
||||
} |
||||
ILExpression retArg; |
||||
if (i < method.Body.Count && method.Body[i].Match(ILCode.Ret, out retArg)) { |
||||
// ret(ldloc(var_2))
|
||||
if (retArg.Code == ILCode.Ldloc && retArg.Operand == var2) { |
||||
return true; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
static FieldDefinition GetFieldDefinition(FieldReference field) |
||||
{ |
||||
return CecilExtensions.ResolveWithinSameModule(field); |
||||
} |
||||
|
||||
static MethodDefinition GetMethodDefinition(MethodReference method) |
||||
{ |
||||
return CecilExtensions.ResolveWithinSameModule(method); |
||||
} |
||||
|
||||
bool MatchEnumeratorCreationNewObj(ILExpression expr, out MethodDefinition ctor) |
||||
{ |
||||
// newobj(CurrentType/...::.ctor, ldc.i4(-2))
|
||||
ctor = null; |
||||
if (expr.Code != ILCode.Newobj || expr.Arguments.Count != 1) |
||||
return false; |
||||
if (expr.Arguments[0].Code != ILCode.Ldc_I4) |
||||
return false; |
||||
int initialState = (int)expr.Arguments[0].Operand; |
||||
if (!(initialState == -2 || initialState == 0)) |
||||
return false; |
||||
ctor = GetMethodDefinition(expr.Operand as MethodReference); |
||||
if (ctor == null || ctor.DeclaringType.DeclaringType != context.CurrentType) |
||||
return false; |
||||
return IsCompilerGeneratorEnumerator(ctor.DeclaringType); |
||||
} |
||||
|
||||
public static bool IsCompilerGeneratorEnumerator(TypeDefinition type) |
||||
{ |
||||
if (!(type.DeclaringType != null && type.IsCompilerGenerated())) |
||||
return false; |
||||
foreach (TypeReference i in type.Interfaces) { |
||||
if (i.Namespace == "System.Collections" && i.Name == "IEnumerator") |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
#endregion
|
||||
|
||||
#region Figure out what the 'state' field is (analysis of .ctor())
|
||||
/// <summary>
|
||||
/// Looks at the enumerator's ctor and figures out which of the fields holds the state.
|
||||
/// </summary>
|
||||
void AnalyzeCtor() |
||||
{ |
||||
ILBlock method = CreateILAst(enumeratorCtor); |
||||
|
||||
foreach (ILNode node in method.Body) { |
||||
FieldReference field; |
||||
ILExpression instExpr; |
||||
ILExpression stExpr; |
||||
ILVariable arg; |
||||
if (node.Match(ILCode.Stfld, out field, out instExpr, out stExpr) && |
||||
instExpr.MatchThis() && |
||||
stExpr.Match(ILCode.Ldloc, out arg) && |
||||
arg.IsParameter && arg.OriginalParameter.Index == 0) |
||||
{ |
||||
stateField = GetFieldDefinition(field); |
||||
} |
||||
} |
||||
if (stateField == null) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Creates ILAst for the specified method, optimized up to before the 'YieldReturn' step.
|
||||
/// </summary>
|
||||
ILBlock CreateILAst(MethodDefinition method) |
||||
{ |
||||
if (method == null || !method.HasBody) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
|
||||
ILBlock ilMethod = new ILBlock(); |
||||
ILAstBuilder astBuilder = new ILAstBuilder(); |
||||
ilMethod.Body = astBuilder.Build(method, true, context); |
||||
ILAstOptimizer optimizer = new ILAstOptimizer(); |
||||
optimizer.Optimize(context, ilMethod, ILAstOptimizationStep.YieldReturn); |
||||
return ilMethod; |
||||
} |
||||
#endregion
|
||||
|
||||
#region Figure out what the 'current' field is (analysis of get_Current())
|
||||
/// <summary>
|
||||
/// Looks at the enumerator's get_Current method and figures out which of the fields holds the current value.
|
||||
/// </summary>
|
||||
void AnalyzeCurrentProperty() |
||||
{ |
||||
MethodDefinition getCurrentMethod = enumeratorType.Methods.FirstOrDefault( |
||||
m => m.Name.StartsWith("System.Collections.Generic.IEnumerator", StringComparison.Ordinal) |
||||
&& m.Name.EndsWith(".get_Current", StringComparison.Ordinal)); |
||||
ILBlock method = CreateILAst(getCurrentMethod); |
||||
if (method.Body.Count == 1) { |
||||
// release builds directly return the current field
|
||||
ILExpression retExpr; |
||||
FieldReference field; |
||||
ILExpression ldFromObj; |
||||
if (method.Body[0].Match(ILCode.Ret, out retExpr) && |
||||
retExpr.Match(ILCode.Ldfld, out field, out ldFromObj) && |
||||
ldFromObj.MatchThis()) |
||||
{ |
||||
currentField = GetFieldDefinition(field); |
||||
} |
||||
} else if (method.Body.Count == 2) { |
||||
ILVariable v, v2; |
||||
ILExpression stExpr; |
||||
FieldReference field; |
||||
ILExpression ldFromObj; |
||||
ILExpression retExpr; |
||||
if (method.Body[0].Match(ILCode.Stloc, out v, out stExpr) && |
||||
stExpr.Match(ILCode.Ldfld, out field, out ldFromObj) && |
||||
ldFromObj.MatchThis() && |
||||
method.Body[1].Match(ILCode.Ret, out retExpr) && |
||||
retExpr.Match(ILCode.Ldloc, out v2) && |
||||
v == v2) |
||||
{ |
||||
currentField = GetFieldDefinition(field); |
||||
} |
||||
} |
||||
if (currentField == null) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
} |
||||
#endregion
|
||||
|
||||
#region Figure out the mapping of IEnumerable fields to IEnumerator fields (analysis of GetEnumerator())
|
||||
void ResolveIEnumerableIEnumeratorFieldMapping() |
||||
{ |
||||
MethodDefinition getEnumeratorMethod = enumeratorType.Methods.FirstOrDefault( |
||||
m => m.Name.StartsWith("System.Collections.Generic.IEnumerable", StringComparison.Ordinal) |
||||
&& m.Name.EndsWith(".GetEnumerator", StringComparison.Ordinal)); |
||||
if (getEnumeratorMethod == null) |
||||
return; // no mappings (maybe it's just an IEnumerator implementation?)
|
||||
|
||||
ILBlock method = CreateILAst(getEnumeratorMethod); |
||||
foreach (ILNode node in method.Body) { |
||||
FieldReference stField; |
||||
ILExpression stToObj; |
||||
ILExpression stExpr; |
||||
FieldReference ldField; |
||||
ILExpression ldFromObj; |
||||
if (node.Match(ILCode.Stfld, out stField, out stToObj, out stExpr) && |
||||
stExpr.Match(ILCode.Ldfld, out ldField, out ldFromObj) && |
||||
ldFromObj.MatchThis()) |
||||
{ |
||||
FieldDefinition storedField = GetFieldDefinition(stField); |
||||
FieldDefinition loadedField = GetFieldDefinition(ldField); |
||||
if (storedField != null && loadedField != null) { |
||||
ILVariable mappedParameter; |
||||
if (fieldToParameterMap.TryGetValue(loadedField, out mappedParameter)) |
||||
fieldToParameterMap[storedField] = mappedParameter; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
#endregion
|
||||
|
||||
#region Construction of the exception table (analysis of Dispose())
|
||||
// We construct the exception table by analyzing the enumerator's Dispose() method.
|
||||
|
||||
// Assumption: there are no loops/backward jumps
|
||||
// We 'run' the code, with "state" being a symbolic variable
|
||||
// so it can form expressions like "state + x" (when there's a sub instruction)
|
||||
// For each instruction, we maintain a list of value ranges for state for which the instruction is reachable.
|
||||
// This is (int.MinValue, int.MaxValue) for the first instruction.
|
||||
// These ranges are propagated depending on the conditional jumps performed by the code.
|
||||
|
||||
Dictionary<MethodDefinition, StateRange> finallyMethodToStateRange; |
||||
|
||||
void ConstructExceptionTable() |
||||
{ |
||||
disposeMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "System.IDisposable.Dispose"); |
||||
ILBlock ilMethod = CreateILAst(disposeMethod); |
||||
|
||||
var rangeAnalysis = new StateRangeAnalysis(ilMethod.Body[0], StateRangeAnalysisMode.IteratorDispose, stateField); |
||||
rangeAnalysis.AssignStateRanges(ilMethod.Body, ilMethod.Body.Count); |
||||
finallyMethodToStateRange = rangeAnalysis.finallyMethodToStateRange; |
||||
|
||||
// Now look at the finally blocks:
|
||||
foreach (var tryFinally in ilMethod.GetSelfAndChildrenRecursive<ILTryCatchBlock>()) { |
||||
var range = rangeAnalysis.ranges[tryFinally.TryBlock.Body[0]]; |
||||
var finallyBody = tryFinally.FinallyBlock.Body; |
||||
if (finallyBody.Count != 2) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
ILExpression call = finallyBody[0] as ILExpression; |
||||
if (call == null || call.Code != ILCode.Call || call.Arguments.Count != 1) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
if (!call.Arguments[0].MatchThis()) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
if (!finallyBody[1].Match(ILCode.Endfinally)) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
|
||||
MethodDefinition mdef = GetMethodDefinition(call.Operand as MethodReference); |
||||
if (mdef == null || finallyMethodToStateRange.ContainsKey(mdef)) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
finallyMethodToStateRange.Add(mdef, range); |
||||
} |
||||
rangeAnalysis = null; |
||||
} |
||||
#endregion
|
||||
|
||||
#region Analysis of MoveNext()
|
||||
ILVariable returnVariable; |
||||
ILLabel returnLabel; |
||||
ILLabel returnFalseLabel; |
||||
|
||||
void AnalyzeMoveNext() |
||||
{ |
||||
MethodDefinition moveNextMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "MoveNext"); |
||||
ILBlock ilMethod = CreateILAst(moveNextMethod); |
||||
|
||||
if (ilMethod.Body.Count == 0) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
ILExpression lastReturnArg; |
||||
if (!ilMethod.Body.Last().Match(ILCode.Ret, out lastReturnArg)) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
|
||||
// There are two possibilities:
|
||||
if (lastReturnArg.Code == ILCode.Ldloc) { |
||||
// a) the compiler uses a variable for returns (in debug builds, or when there are try-finally blocks)
|
||||
returnVariable = (ILVariable)lastReturnArg.Operand; |
||||
returnLabel = ilMethod.Body.ElementAtOrDefault(ilMethod.Body.Count - 2) as ILLabel; |
||||
if (returnLabel == null) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
} else { |
||||
// b) the compiler directly returns constants
|
||||
returnVariable = null; |
||||
returnLabel = null; |
||||
// In this case, the last return must return false.
|
||||
if (lastReturnArg.Code != ILCode.Ldc_I4 || (int)lastReturnArg.Operand != 0) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
} |
||||
|
||||
ILTryCatchBlock tryFaultBlock = ilMethod.Body[0] as ILTryCatchBlock; |
||||
List<ILNode> body; |
||||
int bodyLength; |
||||
if (tryFaultBlock != null) { |
||||
// there are try-finally blocks
|
||||
if (returnVariable == null) // in this case, we must use a return variable
|
||||
throw new SymbolicAnalysisFailedException(); |
||||
// must be a try-fault block:
|
||||
if (tryFaultBlock.CatchBlocks.Count != 0 || tryFaultBlock.FinallyBlock != null || tryFaultBlock.FaultBlock == null) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
|
||||
ILBlock faultBlock = tryFaultBlock.FaultBlock; |
||||
// Ensure the fault block contains the call to Dispose().
|
||||
if (faultBlock.Body.Count != 2) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
MethodReference disposeMethodRef; |
||||
ILExpression disposeArg; |
||||
if (!faultBlock.Body[0].Match(ILCode.Call, out disposeMethodRef, out disposeArg)) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
if (GetMethodDefinition(disposeMethodRef) != disposeMethod || !disposeArg.MatchThis()) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
if (!faultBlock.Body[1].Match(ILCode.Endfinally)) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
|
||||
body = tryFaultBlock.TryBlock.Body; |
||||
bodyLength = body.Count; |
||||
} else { |
||||
// no try-finally blocks
|
||||
body = ilMethod.Body; |
||||
if (returnVariable == null) |
||||
bodyLength = body.Count - 1; // all except for the return statement
|
||||
else |
||||
bodyLength = body.Count - 2; // all except for the return label and statement
|
||||
} |
||||
|
||||
// Now verify that the last instruction in the body is 'ret(false)'
|
||||
if (returnVariable != null) { |
||||
// If we don't have a return variable, we already verified that above.
|
||||
// If we do have one, check for 'stloc(returnVariable, ldc.i4(0))'
|
||||
|
||||
// Maybe might be a jump to the return label after the stloc:
|
||||
ILExpression leave = body.ElementAtOrDefault(bodyLength - 1) as ILExpression; |
||||
if (leave != null && (leave.Code == ILCode.Br || leave.Code == ILCode.Leave) && leave.Operand == returnLabel) |
||||
bodyLength--; |
||||
ILExpression store0 = body.ElementAtOrDefault(bodyLength - 1) as ILExpression; |
||||
if (store0 == null || store0.Code != ILCode.Stloc || store0.Operand != returnVariable) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
if (store0.Arguments[0].Code != ILCode.Ldc_I4 || (int)store0.Arguments[0].Operand != 0) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
|
||||
bodyLength--; // don't conside the stloc instruction to be part of the body
|
||||
} |
||||
// The last element in the body usually is a label pointing to the 'ret(false)'
|
||||
returnFalseLabel = body.ElementAtOrDefault(bodyLength - 1) as ILLabel; |
||||
// Note: in Roslyn-compiled code, returnFalseLabel may be null.
|
||||
|
||||
var rangeAnalysis = new StateRangeAnalysis(body[0], StateRangeAnalysisMode.IteratorMoveNext, stateField); |
||||
int pos = rangeAnalysis.AssignStateRanges(body, bodyLength); |
||||
rangeAnalysis.EnsureLabelAtPos(body, ref pos, ref bodyLength); |
||||
|
||||
var labels = rangeAnalysis.CreateLabelRangeMapping(body, pos, bodyLength); |
||||
ConvertBody(body, pos, bodyLength, labels); |
||||
} |
||||
#endregion
|
||||
|
||||
#region ConvertBody
|
||||
struct SetState |
||||
{ |
||||
public readonly int NewBodyPos; |
||||
public readonly int NewState; |
||||
|
||||
public SetState(int newBodyPos, int newState) |
||||
{ |
||||
this.NewBodyPos = newBodyPos; |
||||
this.NewState = newState; |
||||
} |
||||
} |
||||
|
||||
void ConvertBody(List<ILNode> body, int startPos, int bodyLength, List<KeyValuePair<ILLabel, StateRange>> labels) |
||||
{ |
||||
newBody = new List<ILNode>(); |
||||
newBody.Add(MakeGoTo(labels, 0)); |
||||
List<SetState> stateChanges = new List<SetState>(); |
||||
int currentState = -1; |
||||
// Copy all instructions from the old body to newBody.
|
||||
for (int pos = startPos; pos < bodyLength; pos++) { |
||||
ILExpression expr = body[pos] as ILExpression; |
||||
if (expr != null && expr.Code == ILCode.Stfld && expr.Arguments[0].MatchThis()) { |
||||
// Handle stores to 'state' or 'current'
|
||||
if (GetFieldDefinition(expr.Operand as FieldReference) == stateField) { |
||||
if (expr.Arguments[1].Code != ILCode.Ldc_I4) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
currentState = (int)expr.Arguments[1].Operand; |
||||
stateChanges.Add(new SetState(newBody.Count, currentState)); |
||||
} else if (GetFieldDefinition(expr.Operand as FieldReference) == currentField) { |
||||
newBody.Add(new ILExpression(ILCode.YieldReturn, null, expr.Arguments[1])); |
||||
} else { |
||||
newBody.Add(body[pos]); |
||||
} |
||||
} else if (returnVariable != null && expr != null && expr.Code == ILCode.Stloc && expr.Operand == returnVariable) { |
||||
// handle store+branch to the returnVariable
|
||||
ILExpression br = body.ElementAtOrDefault(++pos) as ILExpression; |
||||
if (br == null || !(br.Code == ILCode.Br || br.Code == ILCode.Leave) || br.Operand != returnLabel || expr.Arguments[0].Code != ILCode.Ldc_I4) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
int val = (int)expr.Arguments[0].Operand; |
||||
if (val == 0) { |
||||
newBody.Add(new ILExpression(ILCode.YieldBreak, null)); |
||||
} else if (val == 1) { |
||||
newBody.Add(MakeGoTo(labels, currentState)); |
||||
} else { |
||||
throw new SymbolicAnalysisFailedException(); |
||||
} |
||||
} else if (expr != null && expr.Code == ILCode.Ret) { |
||||
if (expr.Arguments.Count != 1 || expr.Arguments[0].Code != ILCode.Ldc_I4) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
// handle direct return (e.g. in release builds)
|
||||
int val = (int)expr.Arguments[0].Operand; |
||||
if (val == 0) { |
||||
newBody.Add(new ILExpression(ILCode.YieldBreak, null)); |
||||
} else if (val == 1) { |
||||
newBody.Add(MakeGoTo(labels, currentState)); |
||||
} else { |
||||
throw new SymbolicAnalysisFailedException(); |
||||
} |
||||
} else if (expr != null && expr.Code == ILCode.Call && expr.Arguments.Count == 1 && expr.Arguments[0].MatchThis()) { |
||||
MethodDefinition method = GetMethodDefinition(expr.Operand as MethodReference); |
||||
if (method == null) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
StateRange stateRange; |
||||
if (method == disposeMethod) { |
||||
// Explicit call to dispose is used for "yield break;" within the method.
|
||||
ILExpression br = body.ElementAtOrDefault(++pos) as ILExpression; |
||||
if (br == null || !(br.Code == ILCode.Br || br.Code == ILCode.Leave) || br.Operand != returnFalseLabel) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
newBody.Add(new ILExpression(ILCode.YieldBreak, null)); |
||||
} else if (finallyMethodToStateRange.TryGetValue(method, out stateRange)) { |
||||
// Call to Finally-method
|
||||
int index = stateChanges.FindIndex(ss => stateRange.Contains(ss.NewState)); |
||||
if (index < 0) |
||||
throw new SymbolicAnalysisFailedException(); |
||||
|
||||
ILLabel label = new ILLabel(); |
||||
label.Name = "JumpOutOfTryFinally" + stateChanges[index].NewState; |
||||
newBody.Add(new ILExpression(ILCode.Leave, label)); |
||||
|
||||
SetState stateChange = stateChanges[index]; |
||||
// Move all instructions from stateChange.Pos to newBody.Count into a try-block
|
||||
stateChanges.RemoveRange(index, stateChanges.Count - index); // remove all state changes up to the one we found
|
||||
ILTryCatchBlock tryFinally = new ILTryCatchBlock(); |
||||
tryFinally.TryBlock = new ILBlock(newBody.GetRange(stateChange.NewBodyPos, newBody.Count - stateChange.NewBodyPos)); |
||||
newBody.RemoveRange(stateChange.NewBodyPos, newBody.Count - stateChange.NewBodyPos); // remove all nodes that we just moved into the try block
|
||||
tryFinally.CatchBlocks = new List<ILTryCatchBlock.CatchBlock>(); |
||||
tryFinally.FinallyBlock = ConvertFinallyBlock(method); |
||||
newBody.Add(tryFinally); |
||||
newBody.Add(label); |
||||
} |
||||
} else { |
||||
newBody.Add(body[pos]); |
||||
} |
||||
} |
||||
newBody.Add(new ILExpression(ILCode.YieldBreak, null)); |
||||
} |
||||
|
||||
ILExpression MakeGoTo(ILLabel targetLabel) |
||||
{ |
||||
Debug.Assert(targetLabel != null); |
||||
if (targetLabel == returnFalseLabel) |
||||
return new ILExpression(ILCode.YieldBreak, null); |
||||
else |
||||
return new ILExpression(ILCode.Br, targetLabel); |
||||
} |
||||
|
||||
ILExpression MakeGoTo(List<KeyValuePair<ILLabel, StateRange>> labels, int state) |
||||
{ |
||||
foreach (var pair in labels) { |
||||
if (pair.Value.Contains(state)) |
||||
return MakeGoTo(pair.Key); |
||||
} |
||||
throw new SymbolicAnalysisFailedException(); |
||||
} |
||||
|
||||
ILBlock ConvertFinallyBlock(MethodDefinition finallyMethod) |
||||
{ |
||||
ILBlock block = CreateILAst(finallyMethod); |
||||
// Get rid of assignment to state
|
||||
FieldReference stfld; |
||||
List<ILExpression> args; |
||||
if (block.Body.Count > 0 && block.Body[0].Match(ILCode.Stfld, out stfld, out args)) { |
||||
if (GetFieldDefinition(stfld) == stateField && args[0].MatchThis()) |
||||
block.Body.RemoveAt(0); |
||||
} |
||||
// Convert ret to endfinally
|
||||
foreach (ILExpression expr in block.GetSelfAndChildrenRecursive<ILExpression>()) { |
||||
if (expr.Code == ILCode.Ret) |
||||
expr.Code = ILCode.Endfinally; |
||||
} |
||||
return block; |
||||
} |
||||
#endregion
|
||||
|
||||
#region TranslateFieldsToLocalAccess
|
||||
void TranslateFieldsToLocalAccess() |
||||
{ |
||||
TranslateFieldsToLocalAccess(newBody, fieldToParameterMap); |
||||
} |
||||
|
||||
internal static void TranslateFieldsToLocalAccess(List<ILNode> newBody, Dictionary<FieldDefinition, ILVariable> fieldToParameterMap) |
||||
{ |
||||
var fieldToLocalMap = new DefaultDictionary<FieldDefinition, ILVariable>(f => new ILVariable { Name = f.Name, Type = f.FieldType }); |
||||
foreach (ILNode node in newBody) { |
||||
foreach (ILExpression expr in node.GetSelfAndChildrenRecursive<ILExpression>()) { |
||||
FieldDefinition field = GetFieldDefinition(expr.Operand as FieldReference); |
||||
if (field != null) { |
||||
switch (expr.Code) { |
||||
case ILCode.Ldfld: |
||||
if (expr.Arguments[0].MatchThis()) { |
||||
expr.Code = ILCode.Ldloc; |
||||
if (fieldToParameterMap.ContainsKey(field)) { |
||||
expr.Operand = fieldToParameterMap[field]; |
||||
} else { |
||||
expr.Operand = fieldToLocalMap[field]; |
||||
} |
||||
expr.Arguments.Clear(); |
||||
} |
||||
break; |
||||
case ILCode.Stfld: |
||||
if (expr.Arguments[0].MatchThis()) { |
||||
expr.Code = ILCode.Stloc; |
||||
if (fieldToParameterMap.ContainsKey(field)) { |
||||
expr.Operand = fieldToParameterMap[field]; |
||||
} else { |
||||
expr.Operand = fieldToLocalMap[field]; |
||||
} |
||||
expr.Arguments.RemoveAt(0); |
||||
} |
||||
break; |
||||
case ILCode.Ldflda: |
||||
if (expr.Arguments[0].MatchThis()) { |
||||
expr.Code = ILCode.Ldloca; |
||||
if (fieldToParameterMap.ContainsKey(field)) { |
||||
expr.Operand = fieldToParameterMap[field]; |
||||
} else { |
||||
expr.Operand = fieldToLocalMap[field]; |
||||
} |
||||
expr.Arguments.Clear(); |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
#endregion
|
||||
} |
||||
} |
Loading…
Reference in new issue