[XamlC] drop ICSharpCode.Decompiler (#586)

* [XamlC] drop ICSharpCode.Decompiler

* update nuspec

* fix typo
This commit is contained in:
Stephane Delcroix 2016-12-05 13:31:31 +01:00 коммит произвёл GitHub
Родитель 3692786c3a
Коммит 1a5bead2f2
127 изменённых файлов: 10 добавлений и 34110 удалений

Просмотреть файл

@ -118,11 +118,6 @@
<file src="..\Xamarin.Forms.Build.Tasks\bin\$Configuration$\Mono.Cecil.Mdb.dll" target="build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20" />
<file src="..\Xamarin.Forms.Build.Tasks\bin\$Configuration$\Mono.Cecil.Pdb.dll" target="build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20" />
<file src="..\Xamarin.Forms.Build.Tasks\bin\$Configuration$\Mono.Cecil.Rocks.dll" target="build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20" />
<file src="..\ICSharpCode.Decompiler\bin\$Configuration$\ICSharpCode.Decompiler.dll" target="build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20" />
<file src="..\ICSharpCode.Decompiler\bin\$Configuration$\ICSharpCode.NRefactory.dll" target="build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20" />
<file src="..\ICSharpCode.Decompiler\bin\$Configuration$\ICSharpCode.NRefactory.Cecil.dll" target="build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20" />
<file src="..\ICSharpCode.Decompiler\bin\$Configuration$\ICSharpCode.NRefactory.CSharp.dll" target="build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20" />
<file src="..\ICSharpCode.Decompiler\bin\$Configuration$\ICSharpCode.NRefactory.Xml.dll" target="build\portable-win+net45+wp80+win81+wpa81+MonoAndroid10+Xamarin.iOS10+xamarinmac20" />
<!--Android-->
<file src="..\Xamarin.Forms.Platform.Android\bin\$Configuration$\Xamarin.Forms.Platform.Android.dll" target="lib\MonoAndroid10" />

Просмотреть файл

@ -1,52 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast
{
public class TypeInformation
{
public readonly TypeReference InferredType;
public readonly TypeReference ExpectedType;
public TypeInformation(TypeReference inferredType, TypeReference expectedType)
{
InferredType = inferredType;
ExpectedType = expectedType;
}
}
public class LdTokenAnnotation {}
/// <summary>
/// Annotation that is applied to the body expression of an Expression.Lambda() call.
/// </summary>
public class ParameterDeclarationAnnotation
{
public readonly List<ParameterDeclaration> Parameters = new List<ParameterDeclaration>();
public ParameterDeclarationAnnotation(ILExpression expr)
{
Debug.Assert(expr.Code == ILCode.ExpressionTreeParameterDeclarations);
for (int i = 0; i < expr.Arguments.Count - 1; i++) {
ILExpression p = expr.Arguments[i];
// p looks like this:
// stloc(v, call(Expression::Parameter, call(Type::GetTypeFromHandle, ldtoken(...)), ldstr(...)))
ILVariable v = (ILVariable)p.Operand;
TypeReference typeRef = (TypeReference)p.Arguments[0].Arguments[0].Arguments[0].Operand;
string name = (string)p.Arguments[0].Arguments[1].Operand;
Parameters.Add(new ParameterDeclaration(AstBuilder.ConvertType(typeRef), name).WithAnnotation(v));
}
}
}
/// <summary>
/// Annotation that is applied to a LambdaExpression that was produced by an expression tree.
/// </summary>
public class ExpressionTreeLambdaAnnotation
{
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,69 +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.Linq;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
namespace ICSharpCode.Decompiler.Ast
{
/// <summary>
/// Allows storing comments inside IEnumerable{Statement}. Used in the AstMethodBuilder.
/// CommentStatement nodes are replaced with regular comments later on.
/// </summary>
internal class CommentStatement : Statement
{
string comment;
public CommentStatement(string comment)
{
if (comment == null)
throw new ArgumentNullException("comment");
this.comment = comment;
}
public override void AcceptVisitor(IAstVisitor visitor)
{
}
public override T AcceptVisitor<T>(IAstVisitor<T> visitor)
{
return default(T);
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return default(S);
}
public static void ReplaceAll(AstNode tree)
{
foreach (var cs in tree.Descendants.OfType<CommentStatement>()) {
cs.Parent.InsertChildBefore(cs, new Comment(cs.comment), Roles.Comment);
cs.Remove();
}
}
protected override bool DoMatch(AstNode other, Match match)
{
CommentStatement o = other as CommentStatement;
return o != null && MatchString(comment, o.comment);
}
}
}

Просмотреть файл

@ -1,71 +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.Threading;
using ICSharpCode.Decompiler.Ast;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using Mono.Cecil;
namespace ICSharpCode.Decompiler
{
public class DecompilerContext
{
public ModuleDefinition CurrentModule;
public CancellationToken CancellationToken;
public TypeDefinition CurrentType;
public MethodDefinition CurrentMethod;
public DecompilerSettings Settings = new DecompilerSettings();
public bool CurrentMethodIsAsync;
// public ITypeResolveContext TypeResolveContext;
// public IProjectContent ProjectContent;
public DecompilerContext(ModuleDefinition currentModule)
{
if (currentModule == null)
throw new ArgumentNullException("currentModule");
CurrentModule = currentModule;
// this.ProjectContent = new CecilTypeResolveContext(currentModule);
// List<ITypeResolveContext> resolveContexts = new List<ITypeResolveContext>();
// resolveContexts.Add(this.ProjectContent);
// foreach (AssemblyNameReference r in currentModule.AssemblyReferences) {
// AssemblyDefinition d = currentModule.AssemblyResolver.Resolve(r);
// if (d != null) {
// resolveContexts.Add(new CecilTypeResolveContext(d.MainModule));
// }
// }
// this.TypeResolveContext = new CompositeTypeResolveContext(resolveContexts);
}
/// <summary>
/// Used to pass variable names from a method to its anonymous methods.
/// </summary>
internal List<string> ReservedVariableNames = new List<string>();
public DecompilerContext Clone()
{
DecompilerContext ctx = (DecompilerContext)MemberwiseClone();
ctx.ReservedVariableNames = new List<string>(ctx.ReservedVariableNames);
return ctx;
}
}
}

Просмотреть файл

@ -1,86 +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 ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
namespace ICSharpCode.Decompiler.Ast
{
internal static class NRefactoryExtensions
{
public static T WithAnnotation<T>(this T node, object annotation) where T : AstNode
{
if (annotation != null)
node.AddAnnotation(annotation);
return node;
}
public static T CopyAnnotationsFrom<T>(this T node, AstNode other) where T : AstNode
{
foreach (object annotation in other.Annotations) {
node.AddAnnotation(annotation);
}
return node;
}
public static T Detach<T>(this T node) where T : AstNode
{
node.Remove();
return node;
}
public static Expression WithName(this Expression node, string patternGroupName)
{
return new NamedNode(patternGroupName, node);
}
public static Statement WithName(this Statement node, string patternGroupName)
{
return new NamedNode(patternGroupName, node);
}
public static void AddNamedArgument(this NRefactory.CSharp.Attribute attribute, string name, Expression argument)
{
attribute.Arguments.Add(new AssignmentExpression(new IdentifierExpression(name), argument));
}
public static AstType ToType(this Pattern pattern)
{
return pattern;
}
public static Expression ToExpression(this Pattern pattern)
{
return pattern;
}
public static Statement ToStatement(this Pattern pattern)
{
return pattern;
}
public static Statement GetNextStatement(this Statement statement)
{
AstNode next = statement.NextSibling;
while (next != null && !(next is Statement))
next = next.NextSibling;
return (Statement)next;
}
}
}

Просмотреть файл

@ -1,347 +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.ILAst;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast
{
public class NameVariables
{
static readonly Dictionary<string, string> typeNameToVariableNameDict = new Dictionary<string, string> {
{ "System.Boolean", "flag" },
{ "System.Byte", "b" },
{ "System.SByte", "b" },
{ "System.Int16", "num" },
{ "System.Int32", "num" },
{ "System.Int64", "num" },
{ "System.UInt16", "num" },
{ "System.UInt32", "num" },
{ "System.UInt64", "num" },
{ "System.Single", "num" },
{ "System.Double", "num" },
{ "System.Decimal", "num" },
{ "System.String", "text" },
{ "System.Object", "obj" },
{ "System.Char", "c" }
};
public static void AssignNamesToVariables(DecompilerContext context, IEnumerable<ILVariable> parameters, IEnumerable<ILVariable> variables, ILBlock methodBody)
{
NameVariables nv = new NameVariables();
nv.context = context;
nv.fieldNamesInCurrentType = context.CurrentType.Fields.Select(f => f.Name).ToList();
// First mark existing variable names as reserved.
foreach (string name in context.ReservedVariableNames)
nv.AddExistingName(name);
foreach (var p in parameters)
nv.AddExistingName(p.Name);
foreach (var v in variables) {
if (v.IsGenerated) {
// don't introduce names for variables generated by ILSpy - keep "expr"/"arg"
nv.AddExistingName(v.Name);
} else if (v.OriginalVariable != null && context.Settings.UseDebugSymbols) {
string varName = v.OriginalVariable.Name;
if (string.IsNullOrEmpty(varName) || varName.StartsWith("V_", StringComparison.Ordinal) || !IsValidName(varName))
{
// don't use the name from the debug symbols if it looks like a generated name
v.Name = null;
} else {
// use the name from the debug symbols
// (but ensure we don't use the same name for two variables)
v.Name = nv.GetAlternativeName(varName);
}
} else {
v.Name = null;
}
}
// Now generate names:
foreach (ILVariable p in parameters) {
if (string.IsNullOrEmpty(p.Name))
p.Name = nv.GenerateNameForVariable(p, methodBody);
}
foreach (ILVariable varDef in variables) {
if (string.IsNullOrEmpty(varDef.Name))
varDef.Name = nv.GenerateNameForVariable(varDef, methodBody);
}
}
static bool IsValidName(string varName)
{
if (string.IsNullOrEmpty(varName))
return false;
if (!(char.IsLetter(varName[0]) || varName[0] == '_'))
return false;
for (int i = 1; i < varName.Length; i++) {
if (!(char.IsLetterOrDigit(varName[i]) || varName[i] == '_'))
return false;
}
return true;
}
DecompilerContext context;
List<string> fieldNamesInCurrentType;
Dictionary<string, int> typeNames = new Dictionary<string, int>();
public void AddExistingName(string name)
{
if (string.IsNullOrEmpty(name))
return;
int number;
string nameWithoutDigits = SplitName(name, out number);
int existingNumber;
if (typeNames.TryGetValue(nameWithoutDigits, out existingNumber)) {
typeNames[nameWithoutDigits] = Math.Max(number, existingNumber);
} else {
typeNames.Add(nameWithoutDigits, number);
}
}
string SplitName(string name, out int number)
{
// First, identify whether the name already ends with a number:
int pos = name.Length;
while (pos > 0 && name[pos-1] >= '0' && name[pos-1] <= '9')
pos--;
if (pos < name.Length) {
if (int.TryParse(name.Substring(pos), out number)) {
return name.Substring(0, pos);
}
}
number = 1;
return name;
}
const char maxLoopVariableName = 'n';
public string GetAlternativeName(string oldVariableName)
{
if (oldVariableName.Length == 1 && oldVariableName[0] >= 'i' && oldVariableName[0] <= maxLoopVariableName) {
for (char c = 'i'; c <= maxLoopVariableName; c++) {
if (!typeNames.ContainsKey(c.ToString())) {
typeNames.Add(c.ToString(), 1);
return c.ToString();
}
}
}
int number;
string nameWithoutDigits = SplitName(oldVariableName, out number);
if (!typeNames.ContainsKey(nameWithoutDigits)) {
typeNames.Add(nameWithoutDigits, number - 1);
}
int count = ++typeNames[nameWithoutDigits];
if (count != 1) {
return nameWithoutDigits + count.ToString();
} else {
return nameWithoutDigits;
}
}
string GenerateNameForVariable(ILVariable variable, ILBlock methodBody)
{
string proposedName = null;
if (variable.Type == context.CurrentType.Module.TypeSystem.Int32) {
// test whether the variable might be a loop counter
bool isLoopCounter = false;
foreach (ILWhileLoop loop in methodBody.GetSelfAndChildrenRecursive<ILWhileLoop>()) {
ILExpression expr = loop.Condition;
while (expr != null && expr.Code == ILCode.LogicNot)
expr = expr.Arguments[0];
if (expr != null) {
switch (expr.Code) {
case ILCode.Clt:
case ILCode.Clt_Un:
case ILCode.Cgt:
case ILCode.Cgt_Un:
case ILCode.Cle:
case ILCode.Cle_Un:
case ILCode.Cge:
case ILCode.Cge_Un:
ILVariable loadVar;
if (expr.Arguments[0].Match(ILCode.Ldloc, out loadVar) && loadVar == variable) {
isLoopCounter = true;
}
break;
}
}
}
if (isLoopCounter) {
// For loop variables, use i,j,k,l,m,n
for (char c = 'i'; c <= maxLoopVariableName; c++) {
if (!typeNames.ContainsKey(c.ToString())) {
proposedName = c.ToString();
break;
}
}
}
}
if (string.IsNullOrEmpty(proposedName)) {
var proposedNameForStores =
(from expr in methodBody.GetSelfAndChildrenRecursive<ILExpression>()
where expr.Code == ILCode.Stloc && expr.Operand == variable
select GetNameFromExpression(expr.Arguments.Single())
).Except(fieldNamesInCurrentType).ToList();
if (proposedNameForStores.Count == 1) {
proposedName = proposedNameForStores[0];
}
}
if (string.IsNullOrEmpty(proposedName)) {
var proposedNameForLoads =
(from expr in methodBody.GetSelfAndChildrenRecursive<ILExpression>()
from i in Enumerable.Range(0, expr.Arguments.Count)
let arg = expr.Arguments[i]
where arg.Code == ILCode.Ldloc && arg.Operand == variable
select GetNameForArgument(expr, i)
).Except(fieldNamesInCurrentType).ToList();
if (proposedNameForLoads.Count == 1) {
proposedName = proposedNameForLoads[0];
}
}
if (string.IsNullOrEmpty(proposedName)) {
proposedName = GetNameByType(variable.Type);
}
// remove any numbers from the proposed name
int number;
proposedName = SplitName(proposedName, out number);
if (!typeNames.ContainsKey(proposedName)) {
typeNames.Add(proposedName, 0);
}
int count = ++typeNames[proposedName];
if (count > 1) {
return proposedName + count.ToString();
} else {
return proposedName;
}
}
static string GetNameFromExpression(ILExpression expr)
{
switch (expr.Code) {
case ILCode.Ldfld:
case ILCode.Ldsfld:
return CleanUpVariableName(((FieldReference)expr.Operand).Name);
case ILCode.Call:
case ILCode.Callvirt:
case ILCode.CallGetter:
case ILCode.CallvirtGetter:
MethodReference mr = (MethodReference)expr.Operand;
if (mr.Name.StartsWith("get_", StringComparison.OrdinalIgnoreCase) && mr.Parameters.Count == 0) {
// use name from properties, but not from indexers
return CleanUpVariableName(mr.Name.Substring(4));
} else if (mr.Name.StartsWith("Get", StringComparison.OrdinalIgnoreCase) && mr.Name.Length >= 4 && char.IsUpper(mr.Name[3])) {
// use name from Get-methods
return CleanUpVariableName(mr.Name.Substring(3));
}
break;
}
return null;
}
static string GetNameForArgument(ILExpression parent, int i)
{
switch (parent.Code) {
case ILCode.Stfld:
case ILCode.Stsfld:
if (i == parent.Arguments.Count - 1) // last argument is stored value
return CleanUpVariableName(((FieldReference)parent.Operand).Name);
else
break;
case ILCode.Call:
case ILCode.Callvirt:
case ILCode.Newobj:
case ILCode.CallGetter:
case ILCode.CallvirtGetter:
case ILCode.CallSetter:
case ILCode.CallvirtSetter:
MethodReference methodRef = (MethodReference)parent.Operand;
if (methodRef.Parameters.Count == 1 && i == parent.Arguments.Count - 1) {
// argument might be value of a setter
if (methodRef.Name.StartsWith("set_", StringComparison.OrdinalIgnoreCase)) {
return CleanUpVariableName(methodRef.Name.Substring(4));
} else if (methodRef.Name.StartsWith("Set", StringComparison.OrdinalIgnoreCase) && methodRef.Name.Length >= 4 && char.IsUpper(methodRef.Name[3])) {
return CleanUpVariableName(methodRef.Name.Substring(3));
}
}
MethodDefinition methodDef = methodRef.Resolve();
if (methodDef != null) {
var p = methodDef.Parameters.ElementAtOrDefault((parent.Code != ILCode.Newobj && methodDef.HasThis) ? i - 1 : i);
if (p != null && !string.IsNullOrEmpty(p.Name))
return CleanUpVariableName(p.Name);
}
break;
case ILCode.Ret:
return "result";
}
return null;
}
string GetNameByType(TypeReference type)
{
type = TypeAnalysis.UnpackModifiers(type);
GenericInstanceType git = type as GenericInstanceType;
if (git != null && git.ElementType.FullName == "System.Nullable`1" && git.GenericArguments.Count == 1) {
type = ((GenericInstanceType)type).GenericArguments[0];
}
string name;
if (type.IsArray) {
name = "array";
} else if (type.IsPointer || type.IsByReference) {
name = "ptr";
} else if (type.Name.EndsWith("Exception", StringComparison.Ordinal)) {
name = "ex";
} else if (!typeNameToVariableNameDict.TryGetValue(type.FullName, out name)) {
name = type.Name;
// remove the 'I' for interfaces
if (name.Length >= 3 && name[0] == 'I' && char.IsUpper(name[1]) && char.IsLower(name[2]))
name = name.Substring(1);
name = CleanUpVariableName(name);
}
return name;
}
static string CleanUpVariableName(string name)
{
// remove the backtick (generics)
int pos = name.IndexOf('`');
if (pos >= 0)
name = name.Substring(0, pos);
// remove field prefix:
if (name.Length > 2 && name.StartsWith("m_", StringComparison.Ordinal))
name = name.Substring(2);
else if (name.Length > 1 && name[0] == '_')
name = name.Substring(1);
if (name.Length == 0)
return "obj";
else
return char.ToLower(name[0]) + name.Substring(1);
}
}
}

Просмотреть файл

@ -1,369 +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;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast
{
public class TextTokenWriter : TokenWriter
{
readonly ITextOutput output;
readonly DecompilerContext context;
readonly Stack<AstNode> nodeStack = new Stack<AstNode>();
int braceLevelWithinType = -1;
bool inDocumentationComment = false;
bool firstUsingDeclaration;
bool lastUsingDeclaration;
TextLocation? lastEndOfLine;
public bool FoldBraces = false;
public TextTokenWriter(ITextOutput output, DecompilerContext context)
{
if (output == null)
throw new ArgumentNullException("output");
if (context == null)
throw new ArgumentNullException("context");
this.output = output;
this.context = context;
}
public override void WriteIdentifier(Identifier identifier)
{
var definition = GetCurrentDefinition();
if (definition != null) {
output.WriteDefinition(identifier.Name, definition, false);
return;
}
object memberRef = GetCurrentMemberReference();
if (memberRef != null) {
output.WriteReference(identifier.Name, memberRef);
return;
}
definition = GetCurrentLocalDefinition();
if (definition != null) {
output.WriteDefinition(identifier.Name, definition);
return;
}
memberRef = GetCurrentLocalReference();
if (memberRef != null) {
output.WriteReference(identifier.Name, memberRef, true);
return;
}
if (firstUsingDeclaration) {
output.MarkFoldStart(defaultCollapsed: true);
firstUsingDeclaration = false;
}
output.Write(identifier.Name);
}
MemberReference GetCurrentMemberReference()
{
AstNode node = nodeStack.Peek();
MemberReference memberRef = node.Annotation<MemberReference>();
if (memberRef == null && node.Role == Roles.TargetExpression && (node.Parent is InvocationExpression || node.Parent is ObjectCreateExpression)) {
memberRef = node.Parent.Annotation<MemberReference>();
}
if (node is IdentifierExpression && node.Role == Roles.TargetExpression && node.Parent is InvocationExpression && memberRef != null) {
var declaringType = memberRef.DeclaringType.Resolve();
if (declaringType != null && declaringType.IsDelegate())
return null;
}
return FilterMemberReference(memberRef);
}
MemberReference FilterMemberReference(MemberReference memberRef)
{
if (memberRef == null)
return null;
if (context.Settings.AutomaticEvents && memberRef is FieldDefinition) {
var field = (FieldDefinition)memberRef;
return field.DeclaringType.Events.FirstOrDefault(ev => ev.Name == field.Name) ?? memberRef;
}
return memberRef;
}
object GetCurrentLocalReference()
{
AstNode node = nodeStack.Peek();
ILVariable variable = node.Annotation<ILVariable>();
if (variable != null) {
if (variable.OriginalParameter != null)
return variable.OriginalParameter;
//if (variable.OriginalVariable != null)
// return variable.OriginalVariable;
return variable;
}
var gotoStatement = node as GotoStatement;
if (gotoStatement != null)
{
var method = nodeStack.Select(nd => nd.Annotation<MethodReference>()).FirstOrDefault(mr => mr != null);
if (method != null)
return method.ToString() + gotoStatement.Label;
}
return null;
}
object GetCurrentLocalDefinition()
{
AstNode node = nodeStack.Peek();
if (node is Identifier && node.Parent != null)
node = node.Parent;
var parameterDef = node.Annotation<ParameterDefinition>();
if (parameterDef != null)
return parameterDef;
if (node is VariableInitializer || node is CatchClause || node is ForeachStatement) {
var variable = node.Annotation<ILVariable>();
if (variable != null) {
if (variable.OriginalParameter != null)
return variable.OriginalParameter;
//if (variable.OriginalVariable != null)
// return variable.OriginalVariable;
return variable;
}
}
var label = node as LabelStatement;
if (label != null) {
var method = nodeStack.Select(nd => nd.Annotation<MethodReference>()).FirstOrDefault(mr => mr != null);
if (method != null)
return method.ToString() + label.Label;
}
return null;
}
object GetCurrentDefinition()
{
if (nodeStack == null || nodeStack.Count == 0)
return null;
var node = nodeStack.Peek();
if (node is Identifier)
node = node.Parent;
if (IsDefinition(node))
return node.Annotation<MemberReference>();
return null;
}
public override void WriteKeyword(Role role, string keyword)
{
output.Write(keyword);
}
public override void WriteToken(Role role, string token)
{
// Attach member reference to token only if there's no identifier in the current node.
MemberReference memberRef = GetCurrentMemberReference();
var node = nodeStack.Peek();
if (memberRef != null && node.GetChildByRole(Roles.Identifier).IsNull)
output.WriteReference(token, memberRef);
else
output.Write(token);
}
public override void Space()
{
output.Write(' ');
}
public void OpenBrace(BraceStyle style)
{
if (braceLevelWithinType >= 0 || nodeStack.Peek() is TypeDeclaration)
braceLevelWithinType++;
if (nodeStack.OfType<BlockStatement>().Count() <= 1 || FoldBraces) {
output.MarkFoldStart(defaultCollapsed: braceLevelWithinType == 1);
}
output.WriteLine();
output.WriteLine("{");
output.Indent();
}
public void CloseBrace(BraceStyle style)
{
output.Unindent();
output.Write('}');
if (nodeStack.OfType<BlockStatement>().Count() <= 1 || FoldBraces)
output.MarkFoldEnd();
if (braceLevelWithinType >= 0)
braceLevelWithinType--;
}
public override void Indent()
{
output.Indent();
}
public override void Unindent()
{
output.Unindent();
}
public override void NewLine()
{
if (lastUsingDeclaration) {
output.MarkFoldEnd();
lastUsingDeclaration = false;
}
lastEndOfLine = output.Location;
output.WriteLine();
}
public override void WriteComment(CommentType commentType, string content)
{
switch (commentType) {
case CommentType.SingleLine:
output.Write("//");
output.WriteLine(content);
break;
case CommentType.MultiLine:
output.Write("/*");
output.Write(content);
output.Write("*/");
break;
case CommentType.Documentation:
bool isLastLine = !(nodeStack.Peek().NextSibling is Comment);
if (!inDocumentationComment && !isLastLine) {
inDocumentationComment = true;
output.MarkFoldStart("///" + content, true);
}
output.Write("///");
output.Write(content);
if (inDocumentationComment && isLastLine) {
inDocumentationComment = false;
output.MarkFoldEnd();
}
output.WriteLine();
break;
default:
output.Write(content);
break;
}
}
public override void WritePreProcessorDirective(PreProcessorDirectiveType type, string argument)
{
// pre-processor directive must start on its own line
output.Write('#');
output.Write(type.ToString().ToLowerInvariant());
if (!string.IsNullOrEmpty(argument)) {
output.Write(' ');
output.Write(argument);
}
output.WriteLine();
}
public override void WritePrimitiveValue(object value, string literalValue = null)
{
new TextWriterTokenWriter(new TextOutputWriter(output)).WritePrimitiveValue(value, literalValue);
}
public override void WritePrimitiveType(string type)
{
output.Write(type);
if (type == "new") {
output.Write("()");
}
}
Stack<TextLocation> startLocations = new Stack<TextLocation>();
Stack<MethodDebugSymbols> symbolsStack = new Stack<MethodDebugSymbols>();
public override void StartNode(AstNode node)
{
if (nodeStack.Count == 0) {
if (IsUsingDeclaration(node)) {
firstUsingDeclaration = !IsUsingDeclaration(node.PrevSibling);
lastUsingDeclaration = !IsUsingDeclaration(node.NextSibling);
} else {
firstUsingDeclaration = false;
lastUsingDeclaration = false;
}
}
nodeStack.Push(node);
startLocations.Push(output.Location);
if (node is EntityDeclaration && node.Annotation<MemberReference>() != null && node.GetChildByRole(Roles.Identifier).IsNull)
output.WriteDefinition("", node.Annotation<MemberReference>(), false);
if (node.Annotation<MethodDebugSymbols>() != null) {
symbolsStack.Push(node.Annotation<MethodDebugSymbols>());
symbolsStack.Peek().StartLocation = startLocations.Peek();
}
}
bool IsUsingDeclaration(AstNode node)
{
return node is UsingDeclaration || node is UsingAliasDeclaration;
}
public override void EndNode(AstNode node)
{
if (nodeStack.Pop() != node)
throw new InvalidOperationException();
var startLocation = startLocations.Pop();
// code mappings
var ranges = node.Annotation<List<ILRange>>();
if (symbolsStack.Count > 0 && ranges != null && ranges.Count > 0) {
// Ignore the newline which was printed at the end of the statement
TextLocation endLocation = (node is Statement) ? (lastEndOfLine ?? output.Location) : output.Location;
symbolsStack.Peek().SequencePoints.Add(
new SequencePoint() {
ILRanges = ILRange.OrderAndJoin(ranges).ToArray(),
StartLocation = startLocation,
EndLocation = endLocation
});
}
if (node.Annotation<MethodDebugSymbols>() != null) {
symbolsStack.Peek().EndLocation = output.Location;
output.AddDebugSymbols(symbolsStack.Pop());
}
}
static bool IsDefinition(AstNode node)
{
return node is EntityDeclaration
|| (node is VariableInitializer && node.Parent is FieldDeclaration)
|| node is FixedVariableInitializer;
}
}
}

Просмотреть файл

@ -1,368 +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.Linq;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory.CSharp;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Add checked/unchecked blocks.
/// </summary>
public class AddCheckedBlocks : IAstTransform
{
#region Annotation
sealed class CheckedUncheckedAnnotation {
/// <summary>
/// true=checked, false=unchecked
/// </summary>
public bool IsChecked;
}
public static readonly object CheckedAnnotation = new CheckedUncheckedAnnotation { IsChecked = true };
public static readonly object UncheckedAnnotation = new CheckedUncheckedAnnotation { IsChecked = false };
#endregion
/*
We treat placing checked/unchecked blocks as an optimization problem, with the following goals:
1. Use minimum number of checked blocks+expressions
2. Prefer checked expressions over checked blocks
3. Make the scope of checked expressions as small as possible
4. Open checked blocks as late as possible, and close checked blocks as late as possible
(where goal 1 has the highest priority)
Goal 4a (open checked blocks as late as possible) is necessary so that we don't move variable declarations
into checked blocks, as the variable might still be used after the checked block.
(this could cause DeclareVariables to omit the variable declaration, producing incorrect code)
Goal 4b (close checked blocks as late as possible) makes the code look nicer in this case:
checked {
int c = a + b;
int r = a + c;
return r;
}
If the checked block was closed as early as possible, the variable r would have to be declared outside
(this would work, but look badly)
*/
#region struct Cost
struct Cost
{
// highest possible cost so that the Blocks+Expressions addition doesn't overflow
public static readonly Cost Infinite = new Cost(0x3fffffff, 0x3fffffff);
public readonly int Blocks;
public readonly int Expressions;
public Cost(int blocks, int expressions)
{
Blocks = blocks;
Expressions = expressions;
}
public static bool operator <(Cost a, Cost b)
{
return a.Blocks + a.Expressions < b.Blocks + b.Expressions
|| a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks < b.Blocks;
}
public static bool operator >(Cost a, Cost b)
{
return a.Blocks + a.Expressions > b.Blocks + b.Expressions
|| a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks > b.Blocks;
}
public static bool operator <=(Cost a, Cost b)
{
return a.Blocks + a.Expressions < b.Blocks + b.Expressions
|| a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks <= b.Blocks;
}
public static bool operator >=(Cost a, Cost b)
{
return a.Blocks + a.Expressions > b.Blocks + b.Expressions
|| a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks >= b.Blocks;
}
public static Cost operator +(Cost a, Cost b)
{
return new Cost(a.Blocks + b.Blocks, a.Expressions + b.Expressions);
}
public override string ToString()
{
return string.Format("[{0} + {1}]", Blocks, Expressions);
}
}
#endregion
#region class InsertedNode
/// <summary>
/// Holds the blocks and expressions that should be inserted
/// </summary>
abstract class InsertedNode
{
public static InsertedNode operator +(InsertedNode a, InsertedNode b)
{
if (a == null)
return b;
if (b == null)
return a;
return new InsertedNodeList(a, b);
}
public abstract void Insert();
}
class InsertedNodeList : InsertedNode
{
readonly InsertedNode child1, child2;
public InsertedNodeList(InsertedNode child1, InsertedNode child2)
{
this.child1 = child1;
this.child2 = child2;
}
public override void Insert()
{
child1.Insert();
child2.Insert();
}
}
class InsertedExpression : InsertedNode
{
readonly Expression expression;
readonly bool isChecked;
public InsertedExpression(Expression expression, bool isChecked)
{
this.expression = expression;
this.isChecked = isChecked;
}
public override void Insert()
{
if (isChecked)
expression.ReplaceWith(e => new CheckedExpression { Expression = e });
else
expression.ReplaceWith(e => new UncheckedExpression { Expression = e });
}
}
class ConvertCompoundAssignment : InsertedNode
{
readonly Expression expression;
readonly bool isChecked;
public ConvertCompoundAssignment(Expression expression, bool isChecked)
{
this.expression = expression;
this.isChecked = isChecked;
}
public override void Insert()
{
AssignmentExpression assign = expression.Annotation<ReplaceMethodCallsWithOperators.RestoreOriginalAssignOperatorAnnotation>().Restore(expression);
expression.ReplaceWith(assign);
if (isChecked)
assign.Right = new CheckedExpression { Expression = assign.Right.Detach() };
else
assign.Right = new UncheckedExpression { Expression = assign.Right.Detach() };
}
}
class InsertedBlock : InsertedNode
{
readonly Statement firstStatement; // inclusive
readonly Statement lastStatement; // exclusive
readonly bool isChecked;
public InsertedBlock(Statement firstStatement, Statement lastStatement, bool isChecked)
{
this.firstStatement = firstStatement;
this.lastStatement = lastStatement;
this.isChecked = isChecked;
}
public override void Insert()
{
BlockStatement newBlock = new BlockStatement();
// Move all statements except for the first
Statement next;
for (Statement stmt = firstStatement.GetNextStatement(); stmt != lastStatement; stmt = next) {
next = stmt.GetNextStatement();
newBlock.Add(stmt.Detach());
}
// Replace the first statement with the new (un)checked block
if (isChecked)
firstStatement.ReplaceWith(new CheckedStatement { Body = newBlock });
else
firstStatement.ReplaceWith(new UncheckedStatement { Body = newBlock });
// now also move the first node into the new block
newBlock.Statements.InsertAfter(null, firstStatement);
}
}
#endregion
#region class Result
/// <summary>
/// Holds the result of an insertion operation.
/// </summary>
class Result
{
public Cost CostInCheckedContext;
public InsertedNode NodesToInsertInCheckedContext;
public Cost CostInUncheckedContext;
public InsertedNode NodesToInsertInUncheckedContext;
}
#endregion
public void Run(AstNode node)
{
BlockStatement block = node as BlockStatement;
if (block == null) {
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
Run(child);
}
} else {
Result r = GetResultFromBlock(block);
if (r.NodesToInsertInUncheckedContext != null)
r.NodesToInsertInUncheckedContext.Insert();
}
}
Result GetResultFromBlock(BlockStatement block)
{
// For a block, we are tracking 4 possibilities:
// a) context is checked, no unchecked block open
Cost costCheckedContext = new Cost(0, 0);
InsertedNode nodesCheckedContext = null;
// b) context is checked, an unchecked block is open
Cost costCheckedContextUncheckedBlockOpen = Cost.Infinite;
InsertedNode nodesCheckedContextUncheckedBlockOpen = null;
Statement uncheckedBlockStart = null;
// c) context is unchecked, no checked block open
Cost costUncheckedContext = new Cost(0, 0);
InsertedNode nodesUncheckedContext = null;
// d) context is unchecked, a checked block is open
Cost costUncheckedContextCheckedBlockOpen = Cost.Infinite;
InsertedNode nodesUncheckedContextCheckedBlockOpen = null;
Statement checkedBlockStart = null;
Statement statement = block.Statements.FirstOrDefault();
while (true) {
// Blocks can be closed 'for free'. We use '<=' so that blocks are closed as late as possible (goal 4b)
if (costCheckedContextUncheckedBlockOpen <= costCheckedContext) {
costCheckedContext = costCheckedContextUncheckedBlockOpen;
nodesCheckedContext = nodesCheckedContextUncheckedBlockOpen + new InsertedBlock(uncheckedBlockStart, statement, false);
}
if (costUncheckedContextCheckedBlockOpen <= costUncheckedContext) {
costUncheckedContext = costUncheckedContextCheckedBlockOpen;
nodesUncheckedContext = nodesUncheckedContextCheckedBlockOpen + new InsertedBlock(checkedBlockStart, statement, true);
}
if (statement == null)
break;
// Now try opening blocks. We use '<=' so that blocks are opened as late as possible. (goal 4a)
if (costCheckedContext + new Cost(1, 0) <= costCheckedContextUncheckedBlockOpen) {
costCheckedContextUncheckedBlockOpen = costCheckedContext + new Cost(1, 0);
nodesCheckedContextUncheckedBlockOpen = nodesCheckedContext;
uncheckedBlockStart = statement;
}
if (costUncheckedContext + new Cost(1, 0) <= costUncheckedContextCheckedBlockOpen) {
costUncheckedContextCheckedBlockOpen = costUncheckedContext + new Cost(1, 0);
nodesUncheckedContextCheckedBlockOpen = nodesUncheckedContext;
checkedBlockStart = statement;
}
// Now handle the statement
Result stmtResult = GetResult(statement);
costCheckedContext += stmtResult.CostInCheckedContext;
nodesCheckedContext += stmtResult.NodesToInsertInCheckedContext;
costCheckedContextUncheckedBlockOpen += stmtResult.CostInUncheckedContext;
nodesCheckedContextUncheckedBlockOpen += stmtResult.NodesToInsertInUncheckedContext;
costUncheckedContext += stmtResult.CostInUncheckedContext;
nodesUncheckedContext += stmtResult.NodesToInsertInUncheckedContext;
costUncheckedContextCheckedBlockOpen += stmtResult.CostInCheckedContext;
nodesUncheckedContextCheckedBlockOpen += stmtResult.NodesToInsertInCheckedContext;
statement = statement.GetNextStatement();
}
return new Result {
CostInCheckedContext = costCheckedContext, NodesToInsertInCheckedContext = nodesCheckedContext,
CostInUncheckedContext = costUncheckedContext, NodesToInsertInUncheckedContext = nodesUncheckedContext
};
}
Result GetResult(AstNode node)
{
if (node is BlockStatement)
return GetResultFromBlock((BlockStatement)node);
Result result = new Result();
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
Result childResult = GetResult(child);
result.CostInCheckedContext += childResult.CostInCheckedContext;
result.NodesToInsertInCheckedContext += childResult.NodesToInsertInCheckedContext;
result.CostInUncheckedContext += childResult.CostInUncheckedContext;
result.NodesToInsertInUncheckedContext += childResult.NodesToInsertInUncheckedContext;
}
Expression expr = node as Expression;
if (expr != null) {
CheckedUncheckedAnnotation annotation = expr.Annotation<CheckedUncheckedAnnotation>();
if (annotation != null) {
// If the annotation requires this node to be in a specific context, add a huge cost to the other context
// That huge cost gives us the option to ignore a required checked/unchecked expression when there wouldn't be any
// solution otherwise. (e.g. "for (checked(M().x += 1); true; unchecked(M().x += 2)) {}")
if (annotation.IsChecked)
result.CostInUncheckedContext += new Cost(10000, 0);
else
result.CostInCheckedContext += new Cost(10000, 0);
}
// Embed this node in an checked/unchecked expression:
if (expr.Parent is ExpressionStatement) {
// We cannot use checked/unchecked for top-level-expressions.
// However, we could try converting a compound assignment (checked(a+=b);) or unary operator (checked(a++);)
// back to its old form.
if (expr.Annotation<ReplaceMethodCallsWithOperators.RestoreOriginalAssignOperatorAnnotation>() != null) {
// We use '<' so that expressions are introduced on the deepest level possible (goal 3)
if (result.CostInCheckedContext + new Cost(1, 1) < result.CostInUncheckedContext) {
result.CostInUncheckedContext = result.CostInCheckedContext + new Cost(1, 1);
result.NodesToInsertInUncheckedContext = result.NodesToInsertInCheckedContext + new ConvertCompoundAssignment(expr, true);
} else if (result.CostInUncheckedContext + new Cost(1, 1) < result.CostInCheckedContext) {
result.CostInCheckedContext = result.CostInUncheckedContext + new Cost(1, 1);
result.NodesToInsertInCheckedContext = result.NodesToInsertInUncheckedContext + new ConvertCompoundAssignment(expr, false);
}
}
} else if (expr.Role.IsValid(Expression.Null)) {
// We use '<' so that expressions are introduced on the deepest level possible (goal 3)
if (result.CostInCheckedContext + new Cost(0, 1) < result.CostInUncheckedContext) {
result.CostInUncheckedContext = result.CostInCheckedContext + new Cost(0, 1);
result.NodesToInsertInUncheckedContext = result.NodesToInsertInCheckedContext + new InsertedExpression(expr, true);
} else if (result.CostInUncheckedContext + new Cost(0, 1) < result.CostInCheckedContext) {
result.CostInCheckedContext = result.CostInUncheckedContext + new Cost(0, 1);
result.NodesToInsertInCheckedContext = result.NodesToInsertInUncheckedContext + new InsertedExpression(expr, false);
}
}
}
return result;
}
}
}

Просмотреть файл

@ -1,179 +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.Linq;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Combines query expressions and removes transparent identifiers.
/// </summary>
public class CombineQueryExpressions : IAstTransform
{
readonly DecompilerContext context;
public CombineQueryExpressions(DecompilerContext context)
{
this.context = context;
}
public void Run(AstNode compilationUnit)
{
if (!context.Settings.QueryExpressions)
return;
CombineQueries(compilationUnit);
}
static readonly InvocationExpression castPattern = new InvocationExpression {
Target = new MemberReferenceExpression {
Target = new AnyNode("inExpr"),
MemberName = "Cast",
TypeArguments = { new AnyNode("targetType") }
}};
void CombineQueries(AstNode node)
{
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
CombineQueries(child);
}
QueryExpression query = node as QueryExpression;
if (query != null) {
QueryFromClause fromClause = (QueryFromClause)query.Clauses.First();
QueryExpression innerQuery = fromClause.Expression as QueryExpression;
if (innerQuery != null) {
if (TryRemoveTransparentIdentifier(query, fromClause, innerQuery)) {
RemoveTransparentIdentifierReferences(query);
} else {
QueryContinuationClause continuation = new QueryContinuationClause();
continuation.PrecedingQuery = innerQuery.Detach();
continuation.Identifier = fromClause.Identifier;
fromClause.ReplaceWith(continuation);
}
} else {
Match m = castPattern.Match(fromClause.Expression);
if (m.Success) {
fromClause.Type = m.Get<AstType>("targetType").Single().Detach();
fromClause.Expression = m.Get<Expression>("inExpr").Single().Detach();
}
}
}
}
static readonly QuerySelectClause selectTransparentIdentifierPattern = new QuerySelectClause {
Expression = new Choice {
new AnonymousTypeCreateExpression {
Initializers = {
new NamedExpression {
Name = Pattern.AnyString,
Expression = new IdentifierExpression(Pattern.AnyString)
}.WithName("nae1"),
new NamedExpression {
Name = Pattern.AnyString,
Expression = new AnyNode("nae2Expr")
}.WithName("nae2")
}
},
new AnonymousTypeCreateExpression {
Initializers = {
new NamedNode("identifier", new IdentifierExpression(Pattern.AnyString)),
new AnyNode("nae2Expr")
}
}
}};
bool IsTransparentIdentifier(string identifier)
{
return identifier.StartsWith("<>", StringComparison.Ordinal) && identifier.Contains("TransparentIdentifier");
}
bool TryRemoveTransparentIdentifier(QueryExpression query, QueryFromClause fromClause, QueryExpression innerQuery)
{
if (!IsTransparentIdentifier(fromClause.Identifier))
return false;
Match match = selectTransparentIdentifierPattern.Match(innerQuery.Clauses.Last());
if (!match.Success)
return false;
QuerySelectClause selectClause = (QuerySelectClause)innerQuery.Clauses.Last();
NamedExpression nae1 = match.Get<NamedExpression>("nae1").SingleOrDefault();
NamedExpression nae2 = match.Get<NamedExpression>("nae2").SingleOrDefault();
if (nae1 != null && nae1.Name != ((IdentifierExpression)nae1.Expression).Identifier)
return false;
Expression nae2Expr = match.Get<Expression>("nae2Expr").Single();
IdentifierExpression nae2IdentExpr = nae2Expr as IdentifierExpression;
if (nae2IdentExpr != null && (nae2 == null || nae2.Name == nae2IdentExpr.Identifier)) {
// from * in (from x in ... select new { x = x, y = y }) ...
// =>
// from x in ... ...
fromClause.Remove();
selectClause.Remove();
// Move clauses from innerQuery to query
QueryClause insertionPos = null;
foreach (var clause in innerQuery.Clauses) {
query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach());
}
} else {
// from * in (from x in ... select new { x = x, y = expr }) ...
// =>
// from x in ... let y = expr ...
fromClause.Remove();
selectClause.Remove();
// Move clauses from innerQuery to query
QueryClause insertionPos = null;
foreach (var clause in innerQuery.Clauses) {
query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach());
}
string ident;
if (nae2 != null)
ident = nae2.Name;
else if (nae2Expr is IdentifierExpression)
ident = ((IdentifierExpression)nae2Expr).Identifier;
else if (nae2Expr is MemberReferenceExpression)
ident = ((MemberReferenceExpression)nae2Expr).MemberName;
else
throw new InvalidOperationException("Could not infer name from initializer in AnonymousTypeCreateExpression");
query.Clauses.InsertAfter(insertionPos, new QueryLetClause { Identifier = ident, Expression = nae2Expr.Detach() });
}
return true;
}
/// <summary>
/// Removes all occurrences of transparent identifiers
/// </summary>
void RemoveTransparentIdentifierReferences(AstNode node)
{
foreach (AstNode child in node.Children) {
RemoveTransparentIdentifierReferences(child);
}
MemberReferenceExpression mre = node as MemberReferenceExpression;
if (mre != null) {
IdentifierExpression ident = mre.Target as IdentifierExpression;
if (ident != null && IsTransparentIdentifier(ident.Identifier)) {
IdentifierExpression newIdent = new IdentifierExpression(mre.MemberName);
mre.TypeArguments.MoveTo(newIdent.TypeArguments);
newIdent.CopyAnnotationsFrom(mre);
newIdent.RemoveAnnotations<PropertyDeclaration>(); // remove the reference to the property of the anonymous type
mre.ReplaceWith(newIdent);
return;
}
}
}
}
}

Просмотреть файл

@ -1,111 +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 ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Base class for AST visitors that need the current type/method context info.
/// </summary>
public abstract class ContextTrackingVisitor<TResult> : DepthFirstAstVisitor<object, TResult>, IAstTransform
{
protected readonly DecompilerContext context;
protected ContextTrackingVisitor(DecompilerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
this.context = context;
}
public override TResult VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
{
TypeDefinition oldType = context.CurrentType;
try {
context.CurrentType = typeDeclaration.Annotation<TypeDefinition>();
return base.VisitTypeDeclaration(typeDeclaration, data);
} finally {
context.CurrentType = oldType;
}
}
public override TResult VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data)
{
Debug.Assert(context.CurrentMethod == null);
try {
context.CurrentMethod = methodDeclaration.Annotation<MethodDefinition>();
return base.VisitMethodDeclaration(methodDeclaration, data);
} finally {
context.CurrentMethod = null;
}
}
public override TResult VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data)
{
Debug.Assert(context.CurrentMethod == null);
try {
context.CurrentMethod = constructorDeclaration.Annotation<MethodDefinition>();
return base.VisitConstructorDeclaration(constructorDeclaration, data);
} finally {
context.CurrentMethod = null;
}
}
public override TResult VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration, object data)
{
Debug.Assert(context.CurrentMethod == null);
try {
context.CurrentMethod = destructorDeclaration.Annotation<MethodDefinition>();
return base.VisitDestructorDeclaration(destructorDeclaration, data);
} finally {
context.CurrentMethod = null;
}
}
public override TResult VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, object data)
{
Debug.Assert(context.CurrentMethod == null);
try {
context.CurrentMethod = operatorDeclaration.Annotation<MethodDefinition>();
return base.VisitOperatorDeclaration(operatorDeclaration, data);
} finally {
context.CurrentMethod = null;
}
}
public override TResult VisitAccessor(Accessor accessor, object data)
{
Debug.Assert(context.CurrentMethod == null);
try {
context.CurrentMethod = accessor.Annotation<MethodDefinition>();
return base.VisitAccessor(accessor, data);
} finally {
context.CurrentMethod = null;
}
}
void IAstTransform.Run(AstNode node)
{
node.AcceptVisitor(this, null);
}
}
}

Просмотреть файл

@ -1,183 +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.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// If the first element of a constructor is a chained constructor call, convert it into a constructor initializer.
/// </summary>
public class ConvertConstructorCallIntoInitializer : DepthFirstAstVisitor<object, object>, IAstTransform
{
public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data)
{
ExpressionStatement stmt = constructorDeclaration.Body.Statements.FirstOrDefault() as ExpressionStatement;
if (stmt == null)
return null;
InvocationExpression invocation = stmt.Expression as InvocationExpression;
if (invocation == null)
return null;
MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression;
if (mre != null && mre.MemberName == ".ctor") {
ConstructorInitializer ci = new ConstructorInitializer();
if (mre.Target is ThisReferenceExpression)
ci.ConstructorInitializerType = ConstructorInitializerType.This;
else if (mre.Target is BaseReferenceExpression)
ci.ConstructorInitializerType = ConstructorInitializerType.Base;
else
return null;
// Move arguments from invocation to initializer:
invocation.Arguments.MoveTo(ci.Arguments);
// Add the initializer: (unless it is the default 'base()')
if (!(ci.ConstructorInitializerType == ConstructorInitializerType.Base && ci.Arguments.Count == 0))
constructorDeclaration.Initializer = ci.WithAnnotation(invocation.Annotation<MethodReference>());
// Remove the statement:
stmt.Remove();
}
return null;
}
static readonly ExpressionStatement fieldInitializerPattern = new ExpressionStatement {
Expression = new AssignmentExpression {
Left = new NamedNode("fieldAccess", new MemberReferenceExpression {
Target = new ThisReferenceExpression(),
MemberName = Pattern.AnyString
}),
Operator = AssignmentOperatorType.Assign,
Right = new AnyNode("initializer")
}
};
static readonly AstNode thisCallPattern = new ExpressionStatement(new ThisReferenceExpression().Invoke(".ctor", new Repeat(new AnyNode())));
public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
{
// Handle initializers on instance fields
HandleInstanceFieldInitializers(typeDeclaration.Members);
// Now convert base constructor calls to initializers:
base.VisitTypeDeclaration(typeDeclaration, data);
// Remove single empty constructor:
RemoveSingleEmptyConstructor(typeDeclaration);
// Handle initializers on static fields:
HandleStaticFieldInitializers(typeDeclaration.Members);
return null;
}
void HandleInstanceFieldInitializers(IEnumerable<AstNode> members)
{
var instanceCtors = members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
var instanceCtorsNotChainingWithThis = instanceCtors.Where(ctor => !thisCallPattern.IsMatch(ctor.Body.Statements.FirstOrDefault())).ToArray();
if (instanceCtorsNotChainingWithThis.Length > 0) {
MethodDefinition ctorMethodDef = instanceCtorsNotChainingWithThis[0].Annotation<MethodDefinition>();
if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsValueType)
return;
// Recognize field initializers:
// Convert first statement in all ctors (if all ctors have the same statement) into a field initializer.
bool allSame;
do {
Match m = fieldInitializerPattern.Match(instanceCtorsNotChainingWithThis[0].Body.FirstOrDefault());
if (!m.Success)
break;
FieldDefinition fieldDef = m.Get<AstNode>("fieldAccess").Single().Annotation<FieldReference>().ResolveWithinSameModule();
if (fieldDef == null)
break;
AstNode fieldOrEventDecl = members.FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
if (fieldOrEventDecl == null)
break;
Expression initializer = m.Get<Expression>("initializer").Single();
// 'this'/'base' cannot be used in field initializers
if (initializer.DescendantsAndSelf.Any(n => n is ThisReferenceExpression || n is BaseReferenceExpression))
break;
allSame = true;
for (int i = 1; i < instanceCtorsNotChainingWithThis.Length; i++) {
if (!instanceCtors[0].Body.First().IsMatch(instanceCtorsNotChainingWithThis[i].Body.FirstOrDefault()))
allSame = false;
}
if (allSame) {
foreach (var ctor in instanceCtorsNotChainingWithThis)
ctor.Body.First().Remove();
fieldOrEventDecl.GetChildrenByRole(Roles.Variable).Single().Initializer = initializer.Detach();
}
} while (allSame);
}
}
void RemoveSingleEmptyConstructor(TypeDeclaration typeDeclaration)
{
var instanceCtors = typeDeclaration.Members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
if (instanceCtors.Length == 1) {
ConstructorDeclaration emptyCtor = new ConstructorDeclaration();
emptyCtor.Modifiers = ((typeDeclaration.Modifiers & Modifiers.Abstract) == Modifiers.Abstract ? Modifiers.Protected : Modifiers.Public);
emptyCtor.Body = new BlockStatement();
if (emptyCtor.IsMatch(instanceCtors[0]))
instanceCtors[0].Remove();
}
}
void HandleStaticFieldInitializers(IEnumerable<AstNode> members)
{
// Convert static constructor into field initializers if the class is BeforeFieldInit
var staticCtor = members.OfType<ConstructorDeclaration>().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static);
if (staticCtor != null) {
MethodDefinition ctorMethodDef = staticCtor.Annotation<MethodDefinition>();
if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsBeforeFieldInit) {
while (true) {
ExpressionStatement es = staticCtor.Body.Statements.FirstOrDefault() as ExpressionStatement;
if (es == null)
break;
AssignmentExpression assignment = es.Expression as AssignmentExpression;
if (assignment == null || assignment.Operator != AssignmentOperatorType.Assign)
break;
FieldDefinition fieldDef = assignment.Left.Annotation<FieldReference>().ResolveWithinSameModule();
if (fieldDef == null || !fieldDef.IsStatic)
break;
FieldDeclaration fieldDecl = members.OfType<FieldDeclaration>().FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
if (fieldDecl == null)
break;
fieldDecl.Variables.Single().Initializer = assignment.Right.Detach();
es.Remove();
}
if (staticCtor.Body.Statements.Count == 0)
staticCtor.Remove();
}
}
}
void IAstTransform.Run(AstNode node)
{
// If we're viewing some set of members (fields are direct children of CompilationUnit),
// we also need to handle those:
HandleInstanceFieldInitializers(node.Children);
HandleStaticFieldInitializers(node.Children);
node.AcceptVisitor(this, null);
}
}
}

Просмотреть файл

@ -1,109 +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.Linq;
using System.Reflection;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
internal sealed class TypePattern : Pattern
{
readonly string ns;
readonly string name;
public TypePattern(Type type)
{
ns = type.Namespace;
name = type.Name;
}
public override bool DoMatch(INode other, Match match)
{
ComposedType ct = other as ComposedType;
AstType o;
if (ct != null && !ct.HasNullableSpecifier && ct.PointerRank == 0 && !ct.ArraySpecifiers.Any()) {
// Special case: ILSpy sometimes produces a ComposedType but then removed all array specifiers
// from it. In that case, we need to look at the base type for the annotations.
o = ct.BaseType;
} else {
o = other as AstType;
if (o == null)
return false;
}
TypeReference tr = o.Annotation<TypeReference>();
return tr != null && tr.Namespace == ns && tr.Name == name;
}
public override string ToString()
{
return name;
}
}
internal sealed class LdTokenPattern : Pattern
{
AnyNode childNode;
public LdTokenPattern(string groupName)
{
childNode = new AnyNode(groupName);
}
public override bool DoMatch(INode other, Match match)
{
InvocationExpression ie = other as InvocationExpression;
if (ie != null && ie.Annotation<LdTokenAnnotation>() != null && ie.Arguments.Count == 1) {
return childNode.DoMatch(ie.Arguments.Single(), match);
}
return false;
}
public override string ToString()
{
return "ldtoken(...)";
}
}
/// <summary>
/// typeof-Pattern that applies on the expanded form of typeof (prior to ReplaceMethodCallsWithOperators)
/// </summary>
internal sealed class TypeOfPattern : Pattern
{
INode childNode;
public TypeOfPattern(string groupName)
{
childNode = new TypePattern(typeof(Type)).ToType().Invoke(
"GetTypeFromHandle", new TypeOfExpression(new AnyNode(groupName)).Member("TypeHandle"));
}
public override bool DoMatch(INode other, Match match)
{
return childNode.DoMatch(other, match);
}
public override string ToString()
{
return "typeof(...)";
}
}
}

Просмотреть файл

@ -1,58 +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 ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Transforms decimal constant fields.
/// </summary>
public class DecimalConstantTransform : DepthFirstAstVisitor<object, object>, IAstTransform
{
static readonly PrimitiveType decimalType = new PrimitiveType("decimal");
public override object VisitFieldDeclaration(FieldDeclaration fieldDeclaration, object data)
{
const Modifiers staticReadOnly = Modifiers.Static | Modifiers.Readonly;
if ((fieldDeclaration.Modifiers & staticReadOnly) == staticReadOnly && decimalType.IsMatch(fieldDeclaration.ReturnType)) {
foreach (var attributeSection in fieldDeclaration.Attributes) {
foreach (var attribute in attributeSection.Attributes) {
TypeReference tr = attribute.Type.Annotation<TypeReference>();
if (tr != null && tr.Name == "DecimalConstantAttribute" && tr.Namespace == "System.Runtime.CompilerServices") {
attribute.Remove();
if (attributeSection.Attributes.Count == 0)
attributeSection.Remove();
fieldDeclaration.Modifiers = (fieldDeclaration.Modifiers & ~staticReadOnly) | Modifiers.Const;
return null;
}
}
}
}
return null;
}
public void Run(AstNode compilationUnit)
{
compilationUnit.AcceptVisitor(this, null);
}
}
}

Просмотреть файл

@ -1,368 +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 System.Threading;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.Analysis;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Moves variable declarations to improved positions.
/// </summary>
public class DeclareVariables : IAstTransform
{
sealed class VariableToDeclare
{
public AstType Type;
public string Name;
public ILVariable ILVariable;
public AssignmentExpression ReplacedAssignment;
public Statement InsertionPoint;
}
readonly CancellationToken cancellationToken;
List<VariableToDeclare> variablesToDeclare = new List<VariableToDeclare>();
public DeclareVariables(DecompilerContext context)
{
cancellationToken = context.CancellationToken;
}
public void Run(AstNode node)
{
Run(node, null);
// Declare all the variables at the end, after all the logic has run.
// This is done so that definite assignment analysis can work on a single representation and doesn't have to be updated
// when we change the AST.
foreach (var v in variablesToDeclare) {
if (v.ReplacedAssignment == null) {
BlockStatement block = (BlockStatement)v.InsertionPoint.Parent;
var decl = new VariableDeclarationStatement((AstType)v.Type.Clone(), v.Name);
if (v.ILVariable != null)
decl.Variables.Single().AddAnnotation(v.ILVariable);
block.Statements.InsertBefore(
v.InsertionPoint,
decl);
}
}
// First do all the insertions, then do all the replacements. This is necessary because a replacement might remove our reference point from the AST.
foreach (var v in variablesToDeclare) {
if (v.ReplacedAssignment != null) {
// We clone the right expression so that it doesn't get removed from the old ExpressionStatement,
// which might be still in use by the definite assignment graph.
VariableInitializer initializer = new VariableInitializer(v.Name, v.ReplacedAssignment.Right.Detach()).CopyAnnotationsFrom(v.ReplacedAssignment).WithAnnotation(v.ILVariable);
VariableDeclarationStatement varDecl = new VariableDeclarationStatement {
Type = (AstType)v.Type.Clone(),
Variables = { initializer }
};
ExpressionStatement es = v.ReplacedAssignment.Parent as ExpressionStatement;
if (es != null) {
// Note: if this crashes with 'Cannot replace the root node', check whether two variables were assigned the same name
es.ReplaceWith(varDecl.CopyAnnotationsFrom(es));
} else {
v.ReplacedAssignment.ReplaceWith(varDecl);
}
}
}
variablesToDeclare = null;
}
void Run(AstNode node, DefiniteAssignmentAnalysis daa)
{
BlockStatement block = node as BlockStatement;
if (block != null) {
var variables = block.Statements.TakeWhile(stmt => stmt is VariableDeclarationStatement)
.Cast<VariableDeclarationStatement>().ToList();
if (variables.Count > 0) {
// remove old variable declarations:
foreach (VariableDeclarationStatement varDecl in variables) {
Debug.Assert(varDecl.Variables.Single().Initializer.IsNull);
varDecl.Remove();
}
if (daa == null) {
// If possible, reuse the DefiniteAssignmentAnalysis that was created for the parent block
daa = new DefiniteAssignmentAnalysis(block, cancellationToken);
}
foreach (VariableDeclarationStatement varDecl in variables) {
VariableInitializer initializer = varDecl.Variables.Single();
string variableName = initializer.Name;
ILVariable v = initializer.Annotation<ILVariable>();
bool allowPassIntoLoops = initializer.Annotation<DelegateConstruction.CapturedVariableAnnotation>() == null;
DeclareVariableInBlock(daa, block, varDecl.Type, variableName, v, allowPassIntoLoops);
}
}
}
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
Run(child, daa);
}
}
void DeclareVariableInBlock(DefiniteAssignmentAnalysis daa, BlockStatement block, AstType type, string variableName, ILVariable v, bool allowPassIntoLoops)
{
// declarationPoint: The point where the variable would be declared, if we decide to declare it in this block
Statement declarationPoint = null;
// Check whether we can move down the variable into the sub-blocks
bool canMoveVariableIntoSubBlocks = FindDeclarationPoint(daa, variableName, allowPassIntoLoops, block, out declarationPoint);
if (declarationPoint == null) {
// The variable isn't used at all
return;
}
if (canMoveVariableIntoSubBlocks) {
// Declare the variable within the sub-blocks
foreach (Statement stmt in block.Statements) {
ForStatement forStmt = stmt as ForStatement;
if (forStmt != null && forStmt.Initializers.Count == 1) {
// handle the special case of moving a variable into the for initializer
if (TryConvertAssignmentExpressionIntoVariableDeclaration(forStmt.Initializers.Single(), type, variableName))
continue;
}
UsingStatement usingStmt = stmt as UsingStatement;
if (usingStmt != null && usingStmt.ResourceAcquisition is AssignmentExpression) {
// handle the special case of moving a variable into a using statement
if (TryConvertAssignmentExpressionIntoVariableDeclaration((Expression)usingStmt.ResourceAcquisition, type, variableName))
continue;
}
IfElseStatement ies = stmt as IfElseStatement;
if (ies != null) {
foreach (var child in IfElseChainChildren(ies)) {
BlockStatement subBlock = child as BlockStatement;
if (subBlock != null)
DeclareVariableInBlock(daa, subBlock, type, variableName, v, allowPassIntoLoops);
}
continue;
}
foreach (AstNode child in stmt.Children) {
BlockStatement subBlock = child as BlockStatement;
if (subBlock != null) {
DeclareVariableInBlock(daa, subBlock, type, variableName, v, allowPassIntoLoops);
} else if (HasNestedBlocks(child)) {
foreach (BlockStatement nestedSubBlock in child.Children.OfType<BlockStatement>()) {
DeclareVariableInBlock(daa, nestedSubBlock, type, variableName, v, allowPassIntoLoops);
}
}
}
}
} else {
// Try converting an assignment expression into a VariableDeclarationStatement
if (!TryConvertAssignmentExpressionIntoVariableDeclaration(declarationPoint, type, variableName)) {
// Declare the variable in front of declarationPoint
variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, ILVariable = v, InsertionPoint = declarationPoint });
}
}
}
bool TryConvertAssignmentExpressionIntoVariableDeclaration(Statement declarationPoint, AstType type, string variableName)
{
// convert the declarationPoint into a VariableDeclarationStatement
ExpressionStatement es = declarationPoint as ExpressionStatement;
if (es != null) {
return TryConvertAssignmentExpressionIntoVariableDeclaration(es.Expression, type, variableName);
}
return false;
}
bool TryConvertAssignmentExpressionIntoVariableDeclaration(Expression expression, AstType type, string variableName)
{
AssignmentExpression ae = expression as AssignmentExpression;
if (ae != null && ae.Operator == AssignmentOperatorType.Assign) {
IdentifierExpression ident = ae.Left as IdentifierExpression;
if (ident != null && ident.Identifier == variableName) {
variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, ILVariable = ident.Annotation<ILVariable>(), ReplacedAssignment = ae });
return true;
}
}
return false;
}
/// <summary>
/// Finds the declaration point for the variable within the specified block.
/// </summary>
/// <param name="daa">
/// Definite assignment analysis, must be prepared for 'block' or one of its parents.
/// </param>
/// <param name="varDecl">The variable to declare</param>
/// <param name="block">The block in which the variable should be declared</param>
/// <param name="declarationPoint">
/// Output parameter: the first statement within 'block' where the variable needs to be declared.
/// </param>
/// <returns>
/// Returns whether it is possible to move the variable declaration into sub-blocks.
/// </returns>
public static bool FindDeclarationPoint(DefiniteAssignmentAnalysis daa, VariableDeclarationStatement varDecl, BlockStatement block, out Statement declarationPoint)
{
string variableName = varDecl.Variables.Single().Name;
bool allowPassIntoLoops = varDecl.Variables.Single().Annotation<DelegateConstruction.CapturedVariableAnnotation>() == null;
return FindDeclarationPoint(daa, variableName, allowPassIntoLoops, block, out declarationPoint);
}
static bool FindDeclarationPoint(DefiniteAssignmentAnalysis daa, string variableName, bool allowPassIntoLoops, BlockStatement block, out Statement declarationPoint)
{
// declarationPoint: The point where the variable would be declared, if we decide to declare it in this block
declarationPoint = null;
foreach (Statement stmt in block.Statements) {
if (UsesVariable(stmt, variableName)) {
if (declarationPoint == null)
declarationPoint = stmt;
if (!CanMoveVariableUseIntoSubBlock(stmt, variableName, allowPassIntoLoops)) {
// If it's not possible to move the variable use into a nested block,
// we need to declare the variable in this block
return false;
}
// If we can move the variable into the sub-block, we need to ensure that the remaining code
// does not use the value that was assigned by the first sub-block
Statement nextStatement = stmt.GetNextStatement();
if (nextStatement != null) {
// Analyze the range from the next statement to the end of the block
daa.SetAnalyzedRange(nextStatement, block);
daa.Analyze(variableName);
if (daa.UnassignedVariableUses.Count > 0) {
return false;
}
}
}
}
return true;
}
static bool CanMoveVariableUseIntoSubBlock(Statement stmt, string variableName, bool allowPassIntoLoops)
{
if (!allowPassIntoLoops && (stmt is ForStatement || stmt is ForeachStatement || stmt is DoWhileStatement || stmt is WhileStatement))
return false;
ForStatement forStatement = stmt as ForStatement;
if (forStatement != null && forStatement.Initializers.Count == 1) {
// for-statement is special case: we can move variable declarations into the initializer
ExpressionStatement es = forStatement.Initializers.Single() as ExpressionStatement;
if (es != null) {
AssignmentExpression ae = es.Expression as AssignmentExpression;
if (ae != null && ae.Operator == AssignmentOperatorType.Assign) {
IdentifierExpression ident = ae.Left as IdentifierExpression;
if (ident != null && ident.Identifier == variableName) {
return !UsesVariable(ae.Right, variableName);
}
}
}
}
UsingStatement usingStatement = stmt as UsingStatement;
if (usingStatement != null) {
// using-statement is special case: we can move variable declarations into the initializer
AssignmentExpression ae = usingStatement.ResourceAcquisition as AssignmentExpression;
if (ae != null && ae.Operator == AssignmentOperatorType.Assign) {
IdentifierExpression ident = ae.Left as IdentifierExpression;
if (ident != null && ident.Identifier == variableName) {
return !UsesVariable(ae.Right, variableName);
}
}
}
IfElseStatement ies = stmt as IfElseStatement;
if (ies != null) {
foreach (var child in IfElseChainChildren(ies)) {
if (!(child is BlockStatement) && UsesVariable(child, variableName))
return false;
}
return true;
}
// We can move the variable into a sub-block only if the variable is used in only that sub-block (and not in expressions such as the loop condition)
for (AstNode child = stmt.FirstChild; child != null; child = child.NextSibling) {
if (!(child is BlockStatement) && UsesVariable(child, variableName)) {
if (HasNestedBlocks(child)) {
// catch clauses/switch sections can contain nested blocks
for (AstNode grandchild = child.FirstChild; grandchild != null; grandchild = grandchild.NextSibling) {
if (!(grandchild is BlockStatement) && UsesVariable(grandchild, variableName))
return false;
}
} else {
return false;
}
}
}
return true;
}
static IEnumerable<AstNode> IfElseChainChildren(IfElseStatement ies)
{
IfElseStatement prev;
do {
yield return ies.Condition;
yield return ies.TrueStatement;
prev = ies;
ies = ies.FalseStatement as IfElseStatement;
} while (ies != null);
if (!prev.FalseStatement.IsNull)
yield return prev.FalseStatement;
}
static bool HasNestedBlocks(AstNode node)
{
return node is CatchClause || node is SwitchSection;
}
static bool UsesVariable(AstNode node, string variableName)
{
IdentifierExpression ie = node as IdentifierExpression;
if (ie != null && ie.Identifier == variableName)
return true;
FixedStatement fixedStatement = node as FixedStatement;
if (fixedStatement != null) {
foreach (VariableInitializer v in fixedStatement.Variables) {
if (v.Name == variableName)
return false; // no need to introduce the variable here
}
}
ForeachStatement foreachStatement = node as ForeachStatement;
if (foreachStatement != null) {
if (foreachStatement.VariableName == variableName)
return false; // no need to introduce the variable here
}
UsingStatement usingStatement = node as UsingStatement;
if (usingStatement != null) {
VariableDeclarationStatement varDecl = usingStatement.ResourceAcquisition as VariableDeclarationStatement;
if (varDecl != null) {
foreach (VariableInitializer v in varDecl.Variables) {
if (v.Name == variableName)
return false; // no need to introduce the variable here
}
}
}
CatchClause catchClause = node as CatchClause;
if (catchClause != null && catchClause.VariableName == variableName) {
return false; // no need to introduce the variable here
}
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
if (UsesVariable(child, variableName))
return true;
}
return false;
}
}
}

Просмотреть файл

@ -1,502 +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 System.Threading;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast.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 DelegateConstruction : ContextTrackingVisitor<object>
{
internal sealed class Annotation
{
/// <summary>
/// ldftn or ldvirtftn?
/// </summary>
public readonly bool IsVirtual;
public Annotation(bool isVirtual)
{
IsVirtual = isVirtual;
}
}
internal sealed class CapturedVariableAnnotation
{
}
List<string> currentlyUsedVariableNames = new List<string>();
public DelegateConstruction(DecompilerContext context) : base(context)
{
}
public override object VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, object data)
{
if (objectCreateExpression.Arguments.Count == 2) {
Expression obj = objectCreateExpression.Arguments.First();
Expression func = objectCreateExpression.Arguments.Last();
Annotation annotation = func.Annotation<Annotation>();
if (annotation != null) {
IdentifierExpression methodIdent = (IdentifierExpression)((InvocationExpression)func).Arguments.Single();
MethodReference method = methodIdent.Annotation<MethodReference>();
if (method != null) {
if (HandleAnonymousMethod(objectCreateExpression, obj, method))
return null;
// Perform the transformation to "new Action(obj.func)".
obj.Remove();
methodIdent.Remove();
if (!annotation.IsVirtual && obj is ThisReferenceExpression) {
// maybe it's getting the pointer of a base method?
if (method.DeclaringType.GetElementType() != context.CurrentType) {
obj = new BaseReferenceExpression();
}
}
if (!annotation.IsVirtual && obj is NullReferenceExpression && !method.HasThis) {
// We're loading a static method.
// However it is possible to load extension methods with an instance, so we compare the number of arguments:
bool isExtensionMethod = false;
TypeReference delegateType = objectCreateExpression.Type.Annotation<TypeReference>();
if (delegateType != null) {
TypeDefinition delegateTypeDef = delegateType.Resolve();
if (delegateTypeDef != null) {
MethodDefinition invokeMethod = delegateTypeDef.Methods.FirstOrDefault(m => m.Name == "Invoke");
if (invokeMethod != null) {
isExtensionMethod = (invokeMethod.Parameters.Count + 1 == method.Parameters.Count);
}
}
}
if (!isExtensionMethod) {
obj = new TypeReferenceExpression { Type = AstBuilder.ConvertType(method.DeclaringType) };
}
}
// now transform the identifier into a member reference
MemberReferenceExpression mre = new MemberReferenceExpression();
mre.Target = obj;
mre.MemberName = methodIdent.Identifier;
methodIdent.TypeArguments.MoveTo(mre.TypeArguments);
mre.AddAnnotation(method);
objectCreateExpression.Arguments.Clear();
objectCreateExpression.Arguments.Add(mre);
return null;
}
}
}
return base.VisitObjectCreateExpression(objectCreateExpression, data);
}
internal static bool IsAnonymousMethod(DecompilerContext context, MethodDefinition method)
{
if (method == null || !(method.HasGeneratedName() || method.Name.Contains("$")))
return false;
if (!(method.IsCompilerGenerated() || IsPotentialClosure(context, method.DeclaringType)))
return false;
return true;
}
bool HandleAnonymousMethod(ObjectCreateExpression objectCreateExpression, Expression target, MethodReference methodRef)
{
if (!context.Settings.AnonymousMethods)
return false; // anonymous method decompilation is disabled
if (target != null && !(target is IdentifierExpression || target is ThisReferenceExpression || target is NullReferenceExpression))
return false; // don't copy arbitrary expressions, deal with identifiers only
// Anonymous methods are defined in the same assembly
MethodDefinition method = methodRef.ResolveWithinSameModule();
if (!IsAnonymousMethod(context, 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(AstBuilder.MakeParameters(method, isLambda: true));
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:
DecompilerContext subContext = context.Clone();
subContext.CurrentMethod = method;
subContext.CurrentMethodIsAsync = false;
subContext.ReservedVariableNames.AddRange(currentlyUsedVariableNames);
BlockStatement body = AstMethodBodyBuilder.CreateMethodBody(method, subContext, ame.Parameters);
TransformationPipeline.RunTransformationsUntil(body, v => v is DelegateConstruction, subContext);
body.AcceptVisitor(this, null);
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.IsParameter && method.Parameters.Contains(v.OriginalParameter)
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.Annotation<TypeInformation>().ExpectedType.Resolve();
if (expectedType != null && !expectedType.IsDelegate()) {
var simplifiedDelegateCreation = (ObjectCreateExpression)objectCreateExpression.Clone();
simplifiedDelegateCreation.Arguments.Clear();
simplifiedDelegateCreation.Arguments.Add(replacement);
replacement = simplifiedDelegateCreation;
}
objectCreateExpression.ReplaceWith(replacement);
return true;
}
internal static bool IsPotentialClosure(DecompilerContext context, TypeDefinition potentialDisplayClass)
{
if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGeneratedOrIsInCompilerGeneratedClass())
return false;
// check that methodContainingType is within containingType
while (potentialDisplayClass != context.CurrentType) {
potentialDisplayClass = potentialDisplayClass.DeclaringType;
if (potentialDisplayClass == null)
return false;
}
return true;
}
public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data)
{
if (context.Settings.ExpressionTrees && ExpressionTreeConverter.CouldBeExpressionTree(invocationExpression)) {
Expression converted = ExpressionTreeConverter.TryConvert(context, invocationExpression);
if (converted != null) {
invocationExpression.ReplaceWith(converted);
return converted.AcceptVisitor(this, data);
}
}
return base.VisitInvocationExpression(invocationExpression, data);
}
#region Track current variables
public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data)
{
Debug.Assert(currentlyUsedVariableNames.Count == 0);
try {
currentlyUsedVariableNames.AddRange(methodDeclaration.Parameters.Select(p => p.Name));
return base.VisitMethodDeclaration(methodDeclaration, data);
} finally {
currentlyUsedVariableNames.Clear();
}
}
public override object VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, object data)
{
Debug.Assert(currentlyUsedVariableNames.Count == 0);
try {
currentlyUsedVariableNames.AddRange(operatorDeclaration.Parameters.Select(p => p.Name));
return base.VisitOperatorDeclaration(operatorDeclaration, data);
} finally {
currentlyUsedVariableNames.Clear();
}
}
public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data)
{
Debug.Assert(currentlyUsedVariableNames.Count == 0);
try {
currentlyUsedVariableNames.AddRange(constructorDeclaration.Parameters.Select(p => p.Name));
return base.VisitConstructorDeclaration(constructorDeclaration, data);
} finally {
currentlyUsedVariableNames.Clear();
}
}
public override object VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration, object data)
{
Debug.Assert(currentlyUsedVariableNames.Count == 0);
try {
currentlyUsedVariableNames.AddRange(indexerDeclaration.Parameters.Select(p => p.Name));
return base.VisitIndexerDeclaration(indexerDeclaration, data);
} finally {
currentlyUsedVariableNames.Clear();
}
}
public override object VisitAccessor(Accessor accessor, object data)
{
try {
currentlyUsedVariableNames.Add("value");
return base.VisitAccessor(accessor, data);
} finally {
currentlyUsedVariableNames.RemoveAt(currentlyUsedVariableNames.Count - 1);
}
}
public override object VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, object data)
{
foreach (VariableInitializer v in variableDeclarationStatement.Variables)
currentlyUsedVariableNames.Add(v.Name);
return base.VisitVariableDeclarationStatement(variableDeclarationStatement, data);
}
public override object VisitFixedStatement(FixedStatement fixedStatement, object data)
{
foreach (VariableInitializer v in fixedStatement.Variables)
currentlyUsedVariableNames.Add(v.Name);
return base.VisitFixedStatement(fixedStatement, data);
}
#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, object data)
{
int numberOfVariablesOutsideBlock = currentlyUsedVariableNames.Count;
base.VisitBlockStatement(blockStatement, data);
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<ILVariable>();
if (variable == null)
continue;
TypeDefinition type = variable.Type.ResolveWithinSameModule();
if (!IsPotentialClosure(context, type))
continue;
if (displayClassAssignmentMatch.Get<AstType>("type").Single().Annotation<TypeReference>().ResolveWithinSameModule() != 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.Annotation<FieldReference>() != null))
ok = false;
}
}
if (!ok)
continue;
Dictionary<FieldReference, AstNode> dict = new Dictionary<FieldReference, 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.IsParameter).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) {
FieldDefinition fieldDef = m.Get<MemberReferenceExpression>("left").Single().Annotation<FieldReference>().ResolveWithinSameModule();
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.IsParameter && parameterOccurrances.Count(c => c == v) == 1;
if (!isParameter && IsPotentialClosure(context, v.Type.ResolveWithinSameModule())) {
// 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
FieldDefinition fieldDef2 = mre.Annotation<FieldReference>().ResolveWithinSameModule();
if (fieldDef2 == null || !IsPotentialClosure(context, fieldDef2.FieldType.ResolveWithinSameModule())) {
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);
}
if (isParameter || isDisplayClassParentPointerAssignment) {
dict[fieldDef] = 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 (FieldDefinition 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
{
IsGenerated = true,
Name = capturedVariableName,
Type = field.FieldType,
};
variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), 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(mre.Annotation<FieldReference>().ResolveWithinSameModule(), 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;
}
// 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,875 +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 System.Reflection;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
public class ExpressionTreeConverter
{
#region static TryConvert method
public static bool CouldBeExpressionTree(InvocationExpression expr)
{
if (expr != null && expr.Arguments.Count == 2) {
MethodReference mr = expr.Annotation<MethodReference>();
return mr != null && mr.Name == "Lambda" && mr.DeclaringType.FullName == "System.Linq.Expressions.Expression";
}
return false;
}
public static Expression TryConvert(DecompilerContext context, Expression expr)
{
Expression converted = new ExpressionTreeConverter(context).Convert(expr);
if (converted != null) {
converted.AddAnnotation(new ExpressionTreeLambdaAnnotation());
}
return converted;
}
#endregion
readonly DecompilerContext context;
Stack<LambdaExpression> activeLambdas = new Stack<LambdaExpression>();
ExpressionTreeConverter(DecompilerContext context)
{
this.context = context;
}
#region Main Convert method
Expression Convert(Expression expr)
{
InvocationExpression invocation = expr as InvocationExpression;
if (invocation != null) {
MethodReference mr = invocation.Annotation<MethodReference>();
if (mr != null && mr.DeclaringType.FullName == "System.Linq.Expressions.Expression") {
switch (mr.Name) {
case "Add":
return ConvertBinaryOperator(invocation, BinaryOperatorType.Add, false);
case "AddChecked":
return ConvertBinaryOperator(invocation, BinaryOperatorType.Add, true);
case "AddAssign":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Add, false);
case "AddAssignChecked":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Add, true);
case "And":
return ConvertBinaryOperator(invocation, BinaryOperatorType.BitwiseAnd);
case "AndAlso":
return ConvertBinaryOperator(invocation, BinaryOperatorType.ConditionalAnd);
case "AndAssign":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.BitwiseAnd);
case "ArrayAccess":
case "ArrayIndex":
return ConvertArrayIndex(invocation);
case "ArrayLength":
return ConvertArrayLength(invocation);
case "Assign":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Assign);
case "Call":
return ConvertCall(invocation);
case "Coalesce":
return ConvertBinaryOperator(invocation, BinaryOperatorType.NullCoalescing);
case "Condition":
return ConvertCondition(invocation);
case "Constant":
if (invocation.Arguments.Count >= 1)
return invocation.Arguments.First().Clone();
else
return NotSupported(expr);
case "Convert":
return ConvertCast(invocation, false);
case "ConvertChecked":
return ConvertCast(invocation, true);
case "Divide":
return ConvertBinaryOperator(invocation, BinaryOperatorType.Divide);
case "DivideAssign":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Divide);
case "Equal":
return ConvertBinaryOperator(invocation, BinaryOperatorType.Equality);
case "ExclusiveOr":
return ConvertBinaryOperator(invocation, BinaryOperatorType.ExclusiveOr);
case "ExclusiveOrAssign":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.ExclusiveOr);
case "Field":
return ConvertField(invocation);
case "GreaterThan":
return ConvertBinaryOperator(invocation, BinaryOperatorType.GreaterThan);
case "GreaterThanOrEqual":
return ConvertBinaryOperator(invocation, BinaryOperatorType.GreaterThanOrEqual);
case "Invoke":
return ConvertInvoke(invocation);
case "Lambda":
return ConvertLambda(invocation);
case "LeftShift":
return ConvertBinaryOperator(invocation, BinaryOperatorType.ShiftLeft);
case "LeftShiftAssign":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.ShiftLeft);
case "LessThan":
return ConvertBinaryOperator(invocation, BinaryOperatorType.LessThan);
case "LessThanOrEqual":
return ConvertBinaryOperator(invocation, BinaryOperatorType.LessThanOrEqual);
case "ListInit":
return ConvertListInit(invocation);
case "MemberInit":
return ConvertMemberInit(invocation);
case "Modulo":
return ConvertBinaryOperator(invocation, BinaryOperatorType.Modulus);
case "ModuloAssign":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Modulus);
case "Multiply":
return ConvertBinaryOperator(invocation, BinaryOperatorType.Multiply, false);
case "MultiplyChecked":
return ConvertBinaryOperator(invocation, BinaryOperatorType.Multiply, true);
case "MultiplyAssign":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Multiply, false);
case "MultiplyAssignChecked":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Multiply, true);
case "Negate":
return ConvertUnaryOperator(invocation, UnaryOperatorType.Minus, false);
case "NegateChecked":
return ConvertUnaryOperator(invocation, UnaryOperatorType.Minus, true);
case "New":
return ConvertNewObject(invocation);
case "NewArrayBounds":
return ConvertNewArrayBounds(invocation);
case "NewArrayInit":
return ConvertNewArrayInit(invocation);
case "Not":
return ConvertUnaryOperator(invocation, UnaryOperatorType.Not);
case "NotEqual":
return ConvertBinaryOperator(invocation, BinaryOperatorType.InEquality);
case "OnesComplement":
return ConvertUnaryOperator(invocation, UnaryOperatorType.BitNot);
case "Or":
return ConvertBinaryOperator(invocation, BinaryOperatorType.BitwiseOr);
case "OrAssign":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.BitwiseOr);
case "OrElse":
return ConvertBinaryOperator(invocation, BinaryOperatorType.ConditionalOr);
case "Property":
return ConvertProperty(invocation);
case "Quote":
if (invocation.Arguments.Count == 1)
return Convert(invocation.Arguments.Single());
else
return NotSupported(invocation);
case "RightShift":
return ConvertBinaryOperator(invocation, BinaryOperatorType.ShiftRight);
case "RightShiftAssign":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.ShiftRight);
case "Subtract":
return ConvertBinaryOperator(invocation, BinaryOperatorType.Subtract, false);
case "SubtractChecked":
return ConvertBinaryOperator(invocation, BinaryOperatorType.Subtract, true);
case "SubtractAssign":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Subtract, false);
case "SubtractAssignChecked":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Subtract, true);
case "TypeAs":
return ConvertTypeAs(invocation);
case "TypeIs":
return ConvertTypeIs(invocation);
}
}
}
IdentifierExpression ident = expr as IdentifierExpression;
if (ident != null) {
ILVariable v = ident.Annotation<ILVariable>();
if (v != null) {
foreach (LambdaExpression lambda in activeLambdas) {
foreach (ParameterDeclaration p in lambda.Parameters) {
if (p.Annotation<ILVariable>() == v)
return new IdentifierExpression(p.Name).WithAnnotation(v);
}
}
}
}
return NotSupported(expr);
}
Expression NotSupported(Expression expr)
{
Debug.WriteLine("Expression Tree Conversion Failed: '" + expr + "' is not supported");
return null;
}
#endregion
#region Convert Lambda
static readonly Expression emptyArrayPattern = new ArrayCreateExpression {
Type = new AnyNode(),
Arguments = { new PrimitiveExpression(0) }
};
Expression ConvertLambda(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 2)
return NotSupported(invocation);
LambdaExpression lambda = new LambdaExpression();
Expression body = invocation.Arguments.First();
ArrayCreateExpression parameterArray = invocation.Arguments.Last() as ArrayCreateExpression;
if (parameterArray == null)
return NotSupported(invocation);
var annotation = body.Annotation<ParameterDeclarationAnnotation>();
if (annotation != null) {
lambda.Parameters.AddRange(annotation.Parameters);
} else {
// No parameter declaration annotation found.
if (!emptyArrayPattern.IsMatch(parameterArray))
return null;
}
activeLambdas.Push(lambda);
Expression convertedBody = Convert(body);
activeLambdas.Pop();
if (convertedBody == null)
return null;
lambda.Body = convertedBody;
return lambda;
}
#endregion
#region Convert Field
static readonly Expression getFieldFromHandlePattern =
new TypePattern(typeof(FieldInfo)).ToType().Invoke(
"GetFieldFromHandle",
new LdTokenPattern("field").ToExpression().Member("FieldHandle"),
new OptionalNode(new TypeOfExpression(new AnyNode("declaringType")).Member("TypeHandle"))
);
Expression ConvertField(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 2)
return NotSupported(invocation);
Expression fieldInfoExpr = invocation.Arguments.ElementAt(1);
Match m = getFieldFromHandlePattern.Match(fieldInfoExpr);
if (!m.Success)
return NotSupported(invocation);
FieldReference fr = m.Get<AstNode>("field").Single().Annotation<FieldReference>();
if (fr == null)
return null;
Expression target = invocation.Arguments.ElementAt(0);
Expression convertedTarget;
if (target is NullReferenceExpression) {
if (m.Has("declaringType"))
convertedTarget = new TypeReferenceExpression(m.Get<AstType>("declaringType").Single().Clone());
else
convertedTarget = new TypeReferenceExpression(AstBuilder.ConvertType(fr.DeclaringType));
} else {
convertedTarget = Convert(target);
if (convertedTarget == null)
return null;
}
return convertedTarget.Member(fr.Name).WithAnnotation(fr);
}
#endregion
#region Convert Property
static readonly Expression getMethodFromHandlePattern =
new TypePattern(typeof(MethodBase)).ToType().Invoke(
"GetMethodFromHandle",
new LdTokenPattern("method").ToExpression().Member("MethodHandle"),
new OptionalNode(new TypeOfExpression(new AnyNode("declaringType")).Member("TypeHandle"))
).CastTo(new TypePattern(typeof(MethodInfo)));
Expression ConvertProperty(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 2)
return NotSupported(invocation);
Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(1));
if (!m.Success)
return NotSupported(invocation);
MethodReference mr = m.Get<AstNode>("method").Single().Annotation<MethodReference>();
if (mr == null)
return null;
Expression target = invocation.Arguments.ElementAt(0);
Expression convertedTarget;
if (target is NullReferenceExpression) {
if (m.Has("declaringType"))
convertedTarget = new TypeReferenceExpression(m.Get<AstType>("declaringType").Single().Clone());
else
convertedTarget = new TypeReferenceExpression(AstBuilder.ConvertType(mr.DeclaringType));
} else {
convertedTarget = Convert(target);
if (convertedTarget == null)
return null;
}
return convertedTarget.Member(GetPropertyName(mr)).WithAnnotation(mr);
}
string GetPropertyName(MethodReference accessor)
{
string name = accessor.Name;
if (name.StartsWith("get_", StringComparison.Ordinal) || name.StartsWith("set_", StringComparison.Ordinal))
name = name.Substring(4);
return name;
}
#endregion
#region Convert Call
Expression ConvertCall(InvocationExpression invocation)
{
if (invocation.Arguments.Count < 2)
return NotSupported(invocation);
Expression target;
int firstArgumentPosition;
Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(0));
if (m.Success) {
target = null;
firstArgumentPosition = 1;
} else {
m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(1));
if (!m.Success)
return NotSupported(invocation);
target = invocation.Arguments.ElementAt(0);
firstArgumentPosition = 2;
}
MethodReference mr = m.Get<AstNode>("method").Single().Annotation<MethodReference>();
if (mr == null)
return null;
Expression convertedTarget;
if (target == null || target is NullReferenceExpression) {
// static method
if (m.Has("declaringType"))
convertedTarget = new TypeReferenceExpression(m.Get<AstType>("declaringType").Single().Clone());
else
convertedTarget = new TypeReferenceExpression(AstBuilder.ConvertType(mr.DeclaringType));
} else {
convertedTarget = Convert(target);
if (convertedTarget == null)
return null;
}
MemberReferenceExpression mre = convertedTarget.Member(mr.Name);
GenericInstanceMethod gim = mr as GenericInstanceMethod;
if (gim != null) {
foreach (TypeReference tr in gim.GenericArguments) {
mre.TypeArguments.Add(AstBuilder.ConvertType(tr));
}
}
IList<Expression> arguments = null;
if (invocation.Arguments.Count == firstArgumentPosition + 1) {
Expression argumentArray = invocation.Arguments.ElementAt(firstArgumentPosition);
arguments = ConvertExpressionsArray(argumentArray);
}
if (arguments == null) {
arguments = new List<Expression>();
foreach (Expression argument in invocation.Arguments.Skip(firstArgumentPosition)) {
Expression convertedArgument = Convert(argument);
if (convertedArgument == null)
return null;
arguments.Add(convertedArgument);
}
}
MethodDefinition methodDef = mr.Resolve();
if (methodDef != null && methodDef.IsGetter) {
PropertyDefinition indexer = AstMethodBodyBuilder.GetIndexer(methodDef);
if (indexer != null)
return new IndexerExpression(mre.Target.Detach(), arguments).WithAnnotation(indexer);
}
return new InvocationExpression(mre, arguments).WithAnnotation(mr);
}
Expression ConvertInvoke(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 2)
return NotSupported(invocation);
Expression convertedTarget = Convert(invocation.Arguments.ElementAt(0));
IList<Expression> convertedArguments = ConvertExpressionsArray(invocation.Arguments.ElementAt(1));
if (convertedTarget != null && convertedArguments != null)
return new InvocationExpression(convertedTarget, convertedArguments);
else
return null;
}
#endregion
#region Convert Binary Operator
static readonly Pattern trueOrFalse = new Choice {
new PrimitiveExpression(true),
new PrimitiveExpression(false)
};
Expression ConvertBinaryOperator(InvocationExpression invocation, BinaryOperatorType op, bool? isChecked = null)
{
if (invocation.Arguments.Count < 2)
return NotSupported(invocation);
Expression left = Convert(invocation.Arguments.ElementAt(0));
if (left == null)
return null;
Expression right = Convert(invocation.Arguments.ElementAt(1));
if (right == null)
return null;
BinaryOperatorExpression boe = new BinaryOperatorExpression(left, op, right);
if (isChecked != null)
boe.AddAnnotation(isChecked.Value ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation);
switch (invocation.Arguments.Count) {
case 2:
return boe;
case 3:
Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(2));
if (m.Success)
return boe.WithAnnotation(m.Get<AstNode>("method").Single().Annotation<MethodReference>());
else
return null;
case 4:
if (!trueOrFalse.IsMatch(invocation.Arguments.ElementAt(2)))
return null;
m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(3));
if (m.Success)
return boe.WithAnnotation(m.Get<AstNode>("method").Single().Annotation<MethodReference>());
else
return null;
default:
return NotSupported(invocation);
}
}
#endregion
#region Convert Assignment Operator
Expression ConvertAssignmentOperator(InvocationExpression invocation, AssignmentOperatorType op, bool? isChecked = null)
{
return NotSupported(invocation);
}
#endregion
#region Convert Unary Operator
Expression ConvertUnaryOperator(InvocationExpression invocation, UnaryOperatorType op, bool? isChecked = null)
{
if (invocation.Arguments.Count < 1)
return NotSupported(invocation);
Expression expr = Convert(invocation.Arguments.ElementAt(0));
if (expr == null)
return null;
UnaryOperatorExpression uoe = new UnaryOperatorExpression(op, expr);
if (isChecked != null)
uoe.AddAnnotation(isChecked.Value ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation);
switch (invocation.Arguments.Count) {
case 1:
return uoe;
case 2:
Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(1));
if (m.Success)
return uoe.WithAnnotation(m.Get<AstNode>("method").Single().Annotation<MethodReference>());
else
return null;
default:
return NotSupported(invocation);
}
}
#endregion
#region Convert Condition Operator
Expression ConvertCondition(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 3)
return NotSupported(invocation);
Expression condition = Convert(invocation.Arguments.ElementAt(0));
Expression trueExpr = Convert(invocation.Arguments.ElementAt(1));
Expression falseExpr = Convert(invocation.Arguments.ElementAt(2));
if (condition != null && trueExpr != null && falseExpr != null)
return new ConditionalExpression(condition, trueExpr, falseExpr);
else
return null;
}
#endregion
#region Convert New Object
static readonly Expression newObjectCtorPattern = new TypePattern(typeof(MethodBase)).ToType().Invoke
(
"GetMethodFromHandle",
new LdTokenPattern("ctor").ToExpression().Member("MethodHandle"),
new OptionalNode(new TypeOfExpression(new AnyNode("declaringType")).Member("TypeHandle"))
).CastTo(new TypePattern(typeof(ConstructorInfo)));
Expression ConvertNewObject(InvocationExpression invocation)
{
if (invocation.Arguments.Count < 1 || invocation.Arguments.Count > 3)
return NotSupported(invocation);
Match m = newObjectCtorPattern.Match(invocation.Arguments.First());
if (!m.Success)
return NotSupported(invocation);
MethodReference ctor = m.Get<AstNode>("ctor").Single().Annotation<MethodReference>();
if (ctor == null)
return null;
AstType declaringTypeNode;
TypeReference declaringType;
if (m.Has("declaringType")) {
declaringTypeNode = m.Get<AstType>("declaringType").Single().Clone();
declaringType = declaringTypeNode.Annotation<TypeReference>();
} else {
declaringTypeNode = AstBuilder.ConvertType(ctor.DeclaringType);
declaringType = ctor.DeclaringType;
}
if (declaringTypeNode == null)
return null;
ObjectCreateExpression oce = new ObjectCreateExpression(declaringTypeNode);
if (invocation.Arguments.Count >= 2) {
IList<Expression> arguments = ConvertExpressionsArray(invocation.Arguments.ElementAtOrDefault(1));
if (arguments == null)
return null;
oce.Arguments.AddRange(arguments);
}
if (invocation.Arguments.Count >= 3 && declaringType.IsAnonymousType()) {
MethodDefinition resolvedCtor = ctor.Resolve();
if (resolvedCtor == null || resolvedCtor.Parameters.Count != oce.Arguments.Count)
return null;
AnonymousTypeCreateExpression atce = new AnonymousTypeCreateExpression();
var arguments = oce.Arguments.ToArray();
if (AstMethodBodyBuilder.CanInferAnonymousTypePropertyNamesFromArguments(arguments, resolvedCtor.Parameters)) {
oce.Arguments.MoveTo(atce.Initializers);
} else {
for (int i = 0; i < resolvedCtor.Parameters.Count; i++) {
atce.Initializers.Add(
new NamedExpression {
Name = resolvedCtor.Parameters[i].Name,
Expression = arguments[i].Detach()
});
}
}
return atce;
}
return oce;
}
#endregion
#region Convert ListInit
static readonly Pattern elementInitArrayPattern = ArrayInitializationPattern(
typeof(System.Linq.Expressions.ElementInit),
new TypePattern(typeof(System.Linq.Expressions.Expression)).ToType().Invoke("ElementInit", new AnyNode("methodInfos"), new AnyNode("addArgumentsArrays"))
);
Expression ConvertListInit(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 2)
return NotSupported(invocation);
ObjectCreateExpression oce = Convert(invocation.Arguments.ElementAt(0)) as ObjectCreateExpression;
if (oce == null)
return null;
Expression elementsArray = invocation.Arguments.ElementAt(1);
ArrayInitializerExpression initializer = ConvertElementInit(elementsArray);
if (initializer != null) {
oce.Initializer = initializer;
return oce;
} else {
return null;
}
}
ArrayInitializerExpression ConvertElementInit(Expression elementsArray)
{
IList<Expression> elements = ConvertExpressionsArray(elementsArray);
if (elements != null) {
return new ArrayInitializerExpression(elements);
}
Match m = elementInitArrayPattern.Match(elementsArray);
if (!m.Success)
return null;
ArrayInitializerExpression result = new ArrayInitializerExpression();
foreach (var elementInit in m.Get<Expression>("addArgumentsArrays")) {
IList<Expression> arguments = ConvertExpressionsArray(elementInit);
if (arguments == null)
return null;
result.Elements.Add(new ArrayInitializerExpression(arguments));
}
return result;
}
#endregion
#region Convert MemberInit
Expression ConvertMemberInit(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 2)
return NotSupported(invocation);
ObjectCreateExpression oce = Convert(invocation.Arguments.ElementAt(0)) as ObjectCreateExpression;
if (oce == null)
return null;
Expression elementsArray = invocation.Arguments.ElementAt(1);
ArrayInitializerExpression bindings = ConvertMemberBindings(elementsArray);
if (bindings == null)
return null;
oce.Initializer = bindings;
return oce;
}
static readonly Pattern memberBindingArrayPattern = ArrayInitializationPattern(typeof(System.Linq.Expressions.MemberBinding), new AnyNode("binding"));
static readonly INode expressionTypeReference = new TypeReferenceExpression(new TypePattern(typeof(System.Linq.Expressions.Expression)));
ArrayInitializerExpression ConvertMemberBindings(Expression elementsArray)
{
Match m = memberBindingArrayPattern.Match(elementsArray);
if (!m.Success)
return null;
ArrayInitializerExpression result = new ArrayInitializerExpression();
foreach (var binding in m.Get<Expression>("binding")) {
InvocationExpression bindingInvocation = binding as InvocationExpression;
if (bindingInvocation == null || bindingInvocation.Arguments.Count != 2)
return null;
MemberReferenceExpression bindingMRE = bindingInvocation.Target as MemberReferenceExpression;
if (bindingMRE == null || !expressionTypeReference.IsMatch(bindingMRE.Target))
return null;
Expression bindingTarget = bindingInvocation.Arguments.ElementAt(0);
Expression bindingValue = bindingInvocation.Arguments.ElementAt(1);
string memberName;
Match m2 = getMethodFromHandlePattern.Match(bindingTarget);
if (m2.Success) {
MethodReference setter = m2.Get<AstNode>("method").Single().Annotation<MethodReference>();
if (setter == null)
return null;
memberName = GetPropertyName(setter);
} else {
return null;
}
Expression convertedValue;
switch (bindingMRE.MemberName) {
case "Bind":
convertedValue = Convert(bindingValue);
break;
case "MemberBind":
convertedValue = ConvertMemberBindings(bindingValue);
break;
case "ListBind":
convertedValue = ConvertElementInit(bindingValue);
break;
default:
return null;
}
if (convertedValue == null)
return null;
result.Elements.Add(new NamedExpression(memberName, convertedValue));
}
return result;
}
#endregion
#region Convert Cast
Expression ConvertCast(InvocationExpression invocation, bool isChecked)
{
if (invocation.Arguments.Count < 2)
return null;
Expression converted = Convert(invocation.Arguments.ElementAt(0));
AstType type = ConvertTypeReference(invocation.Arguments.ElementAt(1));
if (converted != null && type != null) {
CastExpression cast = converted.CastTo(type);
cast.AddAnnotation(isChecked ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation);
switch (invocation.Arguments.Count) {
case 2:
return cast;
case 3:
Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(2));
if (m.Success)
return cast.WithAnnotation(m.Get<AstNode>("method").Single().Annotation<MethodReference>());
else
return null;
}
}
return null;
}
#endregion
#region ConvertExpressionsArray
static Pattern ArrayInitializationPattern(Type arrayElementType, INode elementPattern)
{
return new Choice {
new ArrayCreateExpression {
Type = new TypePattern(arrayElementType),
Arguments = { new PrimitiveExpression(0) }
},
new ArrayCreateExpression {
Type = new TypePattern(arrayElementType),
AdditionalArraySpecifiers = { new ArraySpecifier() },
Initializer = new ArrayInitializerExpression {
Elements = { new Repeat(elementPattern) }
}
}
};
}
static readonly Pattern expressionArrayPattern = ArrayInitializationPattern(typeof(System.Linq.Expressions.Expression), new AnyNode("elements"));
IList<Expression> ConvertExpressionsArray(Expression arrayExpression)
{
Match m = expressionArrayPattern.Match(arrayExpression);
if (m.Success) {
List<Expression> result = new List<Expression>();
foreach (Expression expr in m.Get<Expression>("elements")) {
Expression converted = Convert(expr);
if (converted == null)
return null;
result.Add(converted);
}
return result;
}
return null;
}
#endregion
#region Convert TypeAs/TypeIs
static readonly TypeOfPattern typeOfPattern = new TypeOfPattern("type");
AstType ConvertTypeReference(Expression typeOfExpression)
{
Match m = typeOfPattern.Match(typeOfExpression);
if (m.Success)
return m.Get<AstType>("type").Single().Clone();
else
return null;
}
Expression ConvertTypeAs(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 2)
return null;
Expression converted = Convert(invocation.Arguments.ElementAt(0));
AstType type = ConvertTypeReference(invocation.Arguments.ElementAt(1));
if (converted != null && type != null)
return new AsExpression(converted, type);
return null;
}
Expression ConvertTypeIs(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 2)
return null;
Expression converted = Convert(invocation.Arguments.ElementAt(0));
AstType type = ConvertTypeReference(invocation.Arguments.ElementAt(1));
if (converted != null && type != null)
return new IsExpression { Expression = converted, Type = type };
return null;
}
#endregion
#region Convert Array
Expression ConvertArrayIndex(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 2)
return NotSupported(invocation);
Expression targetConverted = Convert(invocation.Arguments.First());
if (targetConverted == null)
return null;
Expression index = invocation.Arguments.ElementAt(1);
Expression indexConverted = Convert(index);
if (indexConverted != null) {
return new IndexerExpression(targetConverted, indexConverted);
}
IList<Expression> indexesConverted = ConvertExpressionsArray(index);
if (indexesConverted != null) {
return new IndexerExpression(targetConverted, indexesConverted);
}
return null;
}
Expression ConvertArrayLength(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 1)
return NotSupported(invocation);
Expression targetConverted = Convert(invocation.Arguments.Single());
if (targetConverted != null)
return targetConverted.Member("Length");
else
return null;
}
Expression ConvertNewArrayInit(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 2)
return NotSupported(invocation);
AstType elementType = ConvertTypeReference(invocation.Arguments.ElementAt(0));
IList<Expression> elements = ConvertExpressionsArray(invocation.Arguments.ElementAt(1));
if (elementType != null && elements != null) {
if (ContainsAnonymousType(elementType)) {
elementType = null;
}
return new ArrayCreateExpression {
Type = elementType,
AdditionalArraySpecifiers = { new ArraySpecifier() },
Initializer = new ArrayInitializerExpression(elements)
};
}
return null;
}
Expression ConvertNewArrayBounds(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 2)
return NotSupported(invocation);
AstType elementType = ConvertTypeReference(invocation.Arguments.ElementAt(0));
IList<Expression> arguments = ConvertExpressionsArray(invocation.Arguments.ElementAt(1));
if (elementType != null && arguments != null) {
if (ContainsAnonymousType(elementType)) {
elementType = null;
}
ArrayCreateExpression ace = new ArrayCreateExpression();
ace.Type = elementType;
ace.Arguments.AddRange(arguments);
return ace;
}
return null;
}
bool ContainsAnonymousType(AstType type)
{
foreach (AstType t in type.DescendantsAndSelf.OfType<AstType>()) {
TypeReference tr = t.Annotation<TypeReference>();
if (tr != null && tr.IsAnonymousType())
return true;
}
return false;
}
#endregion
}
}

Просмотреть файл

@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ICSharpCode.NRefactory.CSharp;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
internal class FlattenSwitchBlocks : IAstTransform
{
public void Run(AstNode compilationUnit)
{
foreach (var switchSection in compilationUnit.Descendants.OfType<SwitchSection>())
{
if (switchSection.Statements.Count != 1)
continue;
var blockStatement = switchSection.Statements.First() as BlockStatement;
if (blockStatement == null || blockStatement.Statements.Any(st => st is VariableDeclarationStatement))
continue;
blockStatement.Remove();
blockStatement.Statements.MoveTo(switchSection.Statements);
}
}
}
}

Просмотреть файл

@ -1,66 +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.Linq;
using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Converts extension method calls into infix syntax.
/// </summary>
public class IntroduceExtensionMethods : IAstTransform
{
readonly DecompilerContext context;
public IntroduceExtensionMethods(DecompilerContext context)
{
this.context = context;
}
public void Run(AstNode compilationUnit)
{
foreach (InvocationExpression invocation in compilationUnit.Descendants.OfType<InvocationExpression>()) {
MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression;
MethodReference methodReference = invocation.Annotation<MethodReference>();
if (mre != null && mre.Target is TypeReferenceExpression && methodReference != null && invocation.Arguments.Any()) {
MethodDefinition d = methodReference.Resolve();
if (d != null) {
foreach (var ca in d.CustomAttributes) {
if (ca.AttributeType.Name == "ExtensionAttribute" && ca.AttributeType.Namespace == "System.Runtime.CompilerServices") {
var firstArgument = invocation.Arguments.First();
if (firstArgument is NullReferenceExpression)
firstArgument = firstArgument.ReplaceWith(expr => expr.CastTo(AstBuilder.ConvertType(d.Parameters.First().ParameterType)));
else
mre.Target = firstArgument.Detach();
if (invocation.Arguments.Any()) {
// HACK: removing type arguments should be done indepently from whether a method is an extension method,
// just by testing whether the arguments can be inferred
mre.TypeArguments.Clear();
}
break;
}
}
}
}
}
}
}
}

Просмотреть файл

@ -1,295 +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.Linq;
using ICSharpCode.NRefactory.CSharp;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Decompiles query expressions.
/// Based on C# 4.0 spec, §7.16.2 Query expression translation
/// </summary>
public class IntroduceQueryExpressions : IAstTransform
{
readonly DecompilerContext context;
public IntroduceQueryExpressions(DecompilerContext context)
{
this.context = context;
}
public void Run(AstNode compilationUnit)
{
if (!context.Settings.QueryExpressions)
return;
DecompileQueries(compilationUnit);
// After all queries were decompiled, detect degenerate queries (queries not property terminated with 'select' or 'group')
// and fix them, either by adding a degenerate select, or by combining them with another query.
foreach (QueryExpression query in compilationUnit.Descendants.OfType<QueryExpression>()) {
QueryFromClause fromClause = (QueryFromClause)query.Clauses.First();
if (IsDegenerateQuery(query)) {
// introduce select for degenerate query
query.Clauses.Add(new QuerySelectClause { Expression = new IdentifierExpression(fromClause.Identifier) });
}
// See if the data source of this query is a degenerate query,
// and combine the queries if possible.
QueryExpression innerQuery = fromClause.Expression as QueryExpression;
while (IsDegenerateQuery(innerQuery)) {
QueryFromClause innerFromClause = (QueryFromClause)innerQuery.Clauses.First();
if (fromClause.Identifier != innerFromClause.Identifier)
break;
// Replace the fromClause with all clauses from the inner query
fromClause.Remove();
QueryClause insertionPos = null;
foreach (var clause in innerQuery.Clauses) {
query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach());
}
fromClause = innerFromClause;
innerQuery = fromClause.Expression as QueryExpression;
}
}
}
bool IsDegenerateQuery(QueryExpression query)
{
if (query == null)
return false;
var lastClause = query.Clauses.LastOrDefault();
return !(lastClause is QuerySelectClause || lastClause is QueryGroupClause);
}
void DecompileQueries(AstNode node)
{
QueryExpression query = DecompileQuery(node as InvocationExpression);
if (query != null)
node.ReplaceWith(query);
for (AstNode child = (query ?? node).FirstChild; child != null; child = child.NextSibling) {
DecompileQueries(child);
}
}
QueryExpression DecompileQuery(InvocationExpression invocation)
{
if (invocation == null)
return null;
MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression;
if (mre == null)
return null;
switch (mre.MemberName) {
case "Select":
{
if (invocation.Arguments.Count != 1)
return null;
string parameterName;
Expression body;
if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out body)) {
QueryExpression query = new QueryExpression();
query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = mre.Target.Detach() });
query.Clauses.Add(new QuerySelectClause { Expression = body.Detach() });
return query;
}
return null;
}
case "GroupBy":
{
if (invocation.Arguments.Count == 2) {
string parameterName1, parameterName2;
Expression keySelector, elementSelector;
if (MatchSimpleLambda(invocation.Arguments.ElementAt(0), out parameterName1, out keySelector)
&& MatchSimpleLambda(invocation.Arguments.ElementAt(1), out parameterName2, out elementSelector)
&& parameterName1 == parameterName2)
{
QueryExpression query = new QueryExpression();
query.Clauses.Add(new QueryFromClause { Identifier = parameterName1, Expression = mre.Target.Detach() });
query.Clauses.Add(new QueryGroupClause { Projection = elementSelector.Detach(), Key = keySelector.Detach() });
return query;
}
} else if (invocation.Arguments.Count == 1) {
string parameterName;
Expression keySelector;
if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out keySelector)) {
QueryExpression query = new QueryExpression();
query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = mre.Target.Detach() });
query.Clauses.Add(new QueryGroupClause { Projection = new IdentifierExpression(parameterName), Key = keySelector.Detach() });
return query;
}
}
return null;
}
case "SelectMany":
{
if (invocation.Arguments.Count != 2)
return null;
string parameterName;
Expression collectionSelector;
if (!MatchSimpleLambda(invocation.Arguments.ElementAt(0), out parameterName, out collectionSelector))
return null;
LambdaExpression lambda = invocation.Arguments.ElementAt(1) as LambdaExpression;
if (lambda != null && lambda.Parameters.Count == 2 && lambda.Body is Expression) {
ParameterDeclaration p1 = lambda.Parameters.ElementAt(0);
ParameterDeclaration p2 = lambda.Parameters.ElementAt(1);
if (p1.Name == parameterName) {
QueryExpression query = new QueryExpression();
query.Clauses.Add(new QueryFromClause { Identifier = p1.Name, Expression = mre.Target.Detach() });
query.Clauses.Add(new QueryFromClause { Identifier = p2.Name, Expression = collectionSelector.Detach() });
query.Clauses.Add(new QuerySelectClause { Expression = ((Expression)lambda.Body).Detach() });
return query;
}
}
return null;
}
case "Where":
{
if (invocation.Arguments.Count != 1)
return null;
string parameterName;
Expression body;
if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out body)) {
QueryExpression query = new QueryExpression();
query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = mre.Target.Detach() });
query.Clauses.Add(new QueryWhereClause { Condition = body.Detach() });
return query;
}
return null;
}
case "OrderBy":
case "OrderByDescending":
case "ThenBy":
case "ThenByDescending":
{
if (invocation.Arguments.Count != 1)
return null;
string parameterName;
Expression orderExpression;
if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out orderExpression)) {
if (ValidateThenByChain(invocation, parameterName)) {
QueryOrderClause orderClause = new QueryOrderClause();
InvocationExpression tmp = invocation;
while (mre.MemberName == "ThenBy" || mre.MemberName == "ThenByDescending") {
// insert new ordering at beginning
orderClause.Orderings.InsertAfter(
null, new QueryOrdering {
Expression = orderExpression.Detach(),
Direction = (mre.MemberName == "ThenBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending)
});
tmp = (InvocationExpression)mre.Target;
mre = (MemberReferenceExpression)tmp.Target;
MatchSimpleLambda(tmp.Arguments.Single(), out parameterName, out orderExpression);
}
// insert new ordering at beginning
orderClause.Orderings.InsertAfter(
null, new QueryOrdering {
Expression = orderExpression.Detach(),
Direction = (mre.MemberName == "OrderBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending)
});
QueryExpression query = new QueryExpression();
query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = mre.Target.Detach() });
query.Clauses.Add(orderClause);
return query;
}
}
return null;
}
case "Join":
case "GroupJoin":
{
if (invocation.Arguments.Count != 4)
return null;
Expression source1 = mre.Target;
Expression source2 = invocation.Arguments.ElementAt(0);
string elementName1, elementName2;
Expression key1, key2;
if (!MatchSimpleLambda(invocation.Arguments.ElementAt(1), out elementName1, out key1))
return null;
if (!MatchSimpleLambda(invocation.Arguments.ElementAt(2), out elementName2, out key2))
return null;
LambdaExpression lambda = invocation.Arguments.ElementAt(3) as LambdaExpression;
if (lambda != null && lambda.Parameters.Count == 2 && lambda.Body is Expression) {
ParameterDeclaration p1 = lambda.Parameters.ElementAt(0);
ParameterDeclaration p2 = lambda.Parameters.ElementAt(1);
if (p1.Name == elementName1 && (p2.Name == elementName2 || mre.MemberName == "GroupJoin")) {
QueryExpression query = new QueryExpression();
query.Clauses.Add(new QueryFromClause { Identifier = elementName1, Expression = source1.Detach() });
QueryJoinClause joinClause = new QueryJoinClause();
joinClause.JoinIdentifier = elementName2; // join elementName2
joinClause.InExpression = source2.Detach(); // in source2
joinClause.OnExpression = key1.Detach(); // on key1
joinClause.EqualsExpression = key2.Detach(); // equals key2
if (mre.MemberName == "GroupJoin") {
joinClause.IntoIdentifier = p2.Name; // into p2.Name
}
query.Clauses.Add(joinClause);
query.Clauses.Add(new QuerySelectClause { Expression = ((Expression)lambda.Body).Detach() });
return query;
}
}
return null;
}
default:
return null;
}
}
/// <summary>
/// Ensure that all ThenBy's are correct, and that the list of ThenBy's is terminated by an 'OrderBy' invocation.
/// </summary>
bool ValidateThenByChain(InvocationExpression invocation, string expectedParameterName)
{
if (invocation == null || invocation.Arguments.Count != 1)
return false;
MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression;
if (mre == null)
return false;
string parameterName;
Expression body;
if (!MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out body))
return false;
if (parameterName != expectedParameterName)
return false;
if (mre.MemberName == "OrderBy" || mre.MemberName == "OrderByDescending")
return true;
else if (mre.MemberName == "ThenBy" || mre.MemberName == "ThenByDescending")
return ValidateThenByChain(mre.Target as InvocationExpression, expectedParameterName);
else
return false;
}
/// <summary>Matches simple lambdas of the form "a => b"</summary>
bool MatchSimpleLambda(Expression expr, out string parameterName, out Expression body)
{
LambdaExpression lambda = expr as LambdaExpression;
if (lambda != null && lambda.Parameters.Count == 1 && lambda.Body is Expression) {
ParameterDeclaration p = lambda.Parameters.Single();
if (p.ParameterModifier == ParameterModifier.None) {
parameterName = p.Name;
body = (Expression)lambda.Body;
return true;
}
}
parameterName = null;
body = null;
return false;
}
}
}

Просмотреть файл

@ -1,106 +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 ICSharpCode.NRefactory.CSharp;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
public class IntroduceUnsafeModifier : DepthFirstAstVisitor<object, bool>, IAstTransform
{
public static readonly object PointerArithmeticAnnotation = new PointerArithmetic();
sealed class PointerArithmetic {}
public void Run(AstNode compilationUnit)
{
compilationUnit.AcceptVisitor(this, null);
}
protected override bool VisitChildren(AstNode node, object data)
{
bool result = false;
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
result |= child.AcceptVisitor(this, data);
}
if (result && node is EntityDeclaration && !(node is Accessor)) {
((EntityDeclaration)node).Modifiers |= Modifiers.Unsafe;
return false;
}
return result;
}
public override bool VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression, object data)
{
base.VisitPointerReferenceExpression(pointerReferenceExpression, data);
return true;
}
public override bool VisitComposedType(ComposedType composedType, object data)
{
if (composedType.PointerRank > 0)
return true;
else
return base.VisitComposedType(composedType, data);
}
public override bool VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, object data)
{
bool result = base.VisitUnaryOperatorExpression(unaryOperatorExpression, data);
if (unaryOperatorExpression.Operator == UnaryOperatorType.Dereference) {
BinaryOperatorExpression bop = unaryOperatorExpression.Expression as BinaryOperatorExpression;
if (bop != null && bop.Operator == BinaryOperatorType.Add && bop.Annotation<PointerArithmetic>() != null) {
// transform "*(ptr + int)" to "ptr[int]"
IndexerExpression indexer = new IndexerExpression();
indexer.Target = bop.Left.Detach();
indexer.Arguments.Add(bop.Right.Detach());
indexer.CopyAnnotationsFrom(unaryOperatorExpression);
indexer.CopyAnnotationsFrom(bop);
unaryOperatorExpression.ReplaceWith(indexer);
}
return true;
} else if (unaryOperatorExpression.Operator == UnaryOperatorType.AddressOf) {
return true;
} else {
return result;
}
}
public override bool VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, object data)
{
bool result = base.VisitMemberReferenceExpression(memberReferenceExpression, data);
UnaryOperatorExpression uoe = memberReferenceExpression.Target as UnaryOperatorExpression;
if (uoe != null && uoe.Operator == UnaryOperatorType.Dereference) {
PointerReferenceExpression pre = new PointerReferenceExpression();
pre.Target = uoe.Expression.Detach();
pre.MemberName = memberReferenceExpression.MemberName;
memberReferenceExpression.TypeArguments.MoveTo(pre.TypeArguments);
pre.CopyAnnotationsFrom(uoe);
pre.CopyAnnotationsFrom(memberReferenceExpression);
memberReferenceExpression.ReplaceWith(pre);
}
return result;
}
public override bool VisitStackAllocExpression(StackAllocExpression stackAllocExpression, object data)
{
base.VisitStackAllocExpression(stackAllocExpression, data);
return true;
}
}
}

Просмотреть файл

@ -1,359 +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.NRefactory.CSharp;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Introduces using declarations.
/// </summary>
public class IntroduceUsingDeclarations : IAstTransform
{
DecompilerContext context;
public IntroduceUsingDeclarations(DecompilerContext context)
{
this.context = context;
}
public void Run(AstNode compilationUnit)
{
// First determine all the namespaces that need to be imported:
compilationUnit.AcceptVisitor(new FindRequiredImports(this), null);
importedNamespaces.Add("System"); // always import System, even when not necessary
if (context.Settings.UsingDeclarations) {
// Now add using declarations for those namespaces:
foreach (string ns in importedNamespaces.OrderByDescending(n => n)) {
// we go backwards (OrderByDescending) through the list of namespaces because we insert them backwards
// (always inserting at the start of the list)
string[] parts = ns.Split('.');
AstType nsType = new SimpleType(parts[0]);
for (int i = 1; i < parts.Length; i++) {
nsType = new MemberType { Target = nsType, MemberName = parts[i] };
}
compilationUnit.InsertChildAfter(null, new UsingDeclaration { Import = nsType }, SyntaxTree.MemberRole);
}
}
if (!context.Settings.FullyQualifyAmbiguousTypeNames)
return;
FindAmbiguousTypeNames(context.CurrentModule, internalsVisible: true);
foreach (AssemblyNameReference r in context.CurrentModule.AssemblyReferences) {
AssemblyDefinition d = context.CurrentModule.AssemblyResolver.Resolve(r);
if (d != null)
FindAmbiguousTypeNames(d.MainModule, internalsVisible: false);
}
// verify that the SimpleTypes refer to the correct type (no ambiguities)
compilationUnit.AcceptVisitor(new FullyQualifyAmbiguousTypeNamesVisitor(this), null);
}
readonly HashSet<string> declaredNamespaces = new HashSet<string>() { string.Empty };
readonly HashSet<string> importedNamespaces = new HashSet<string>();
// Note that we store type names with `n suffix, so we automatically disambiguate based on number of type parameters.
readonly HashSet<string> availableTypeNames = new HashSet<string>();
readonly HashSet<string> ambiguousTypeNames = new HashSet<string>();
sealed class FindRequiredImports : DepthFirstAstVisitor<object, object>
{
readonly IntroduceUsingDeclarations transform;
string currentNamespace;
public FindRequiredImports(IntroduceUsingDeclarations transform)
{
this.transform = transform;
currentNamespace = transform.context.CurrentType != null ? transform.context.CurrentType.Namespace : string.Empty;
}
bool IsParentOfCurrentNamespace(string ns)
{
if (ns.Length == 0)
return true;
if (currentNamespace.StartsWith(ns, StringComparison.Ordinal)) {
if (currentNamespace.Length == ns.Length)
return true;
if (currentNamespace[ns.Length] == '.')
return true;
}
return false;
}
public override object VisitSimpleType(SimpleType simpleType, object data)
{
TypeReference tr = simpleType.Annotation<TypeReference>();
if (tr != null && !IsParentOfCurrentNamespace(tr.Namespace)) {
transform.importedNamespaces.Add(tr.Namespace);
}
return base.VisitSimpleType(simpleType, data); // also visit type arguments
}
public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data)
{
string oldNamespace = currentNamespace;
foreach (string ident in namespaceDeclaration.Identifiers) {
currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident);
transform.declaredNamespaces.Add(currentNamespace);
}
base.VisitNamespaceDeclaration(namespaceDeclaration, data);
currentNamespace = oldNamespace;
return null;
}
}
void FindAmbiguousTypeNames(ModuleDefinition module, bool internalsVisible)
{
foreach (TypeDefinition type in module.Types) {
if (internalsVisible || type.IsPublic) {
if (importedNamespaces.Contains(type.Namespace) || declaredNamespaces.Contains(type.Namespace)) {
if (!availableTypeNames.Add(type.Name))
ambiguousTypeNames.Add(type.Name);
}
}
}
}
sealed class FullyQualifyAmbiguousTypeNamesVisitor : DepthFirstAstVisitor<object, object>
{
readonly IntroduceUsingDeclarations transform;
string currentNamespace;
HashSet<string> currentMemberTypes;
Dictionary<string, MemberReference> currentMembers;
bool isWithinTypeReferenceExpression;
public FullyQualifyAmbiguousTypeNamesVisitor(IntroduceUsingDeclarations transform)
{
this.transform = transform;
currentNamespace = transform.context.CurrentType != null ? transform.context.CurrentType.Namespace : string.Empty;
}
public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data)
{
string oldNamespace = currentNamespace;
foreach (string ident in namespaceDeclaration.Identifiers) {
currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident);
}
base.VisitNamespaceDeclaration(namespaceDeclaration, data);
currentNamespace = oldNamespace;
return null;
}
public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
{
HashSet<string> oldMemberTypes = currentMemberTypes;
currentMemberTypes = currentMemberTypes != null ? new HashSet<string>(currentMemberTypes) : new HashSet<string>();
Dictionary<string, MemberReference> oldMembers = currentMembers;
currentMembers = new Dictionary<string, MemberReference>();
TypeDefinition typeDef = typeDeclaration.Annotation<TypeDefinition>();
bool privateMembersVisible = true;
ModuleDefinition internalMembersVisibleInModule = typeDef.Module;
while (typeDef != null) {
foreach (GenericParameter gp in typeDef.GenericParameters) {
currentMemberTypes.Add(gp.Name);
}
foreach (TypeDefinition t in typeDef.NestedTypes) {
if (privateMembersVisible || IsVisible(t, internalMembersVisibleInModule))
currentMemberTypes.Add(t.Name.Substring(t.Name.LastIndexOf('+') + 1));
}
foreach (MethodDefinition method in typeDef.Methods) {
if (privateMembersVisible || IsVisible(method, internalMembersVisibleInModule))
AddCurrentMember(method);
}
foreach (PropertyDefinition property in typeDef.Properties) {
if (privateMembersVisible || IsVisible(property.GetMethod, internalMembersVisibleInModule) || IsVisible(property.SetMethod, internalMembersVisibleInModule))
AddCurrentMember(property);
}
foreach (EventDefinition ev in typeDef.Events) {
if (privateMembersVisible || IsVisible(ev.AddMethod, internalMembersVisibleInModule) || IsVisible(ev.RemoveMethod, internalMembersVisibleInModule))
AddCurrentMember(ev);
}
foreach (FieldDefinition f in typeDef.Fields) {
if (privateMembersVisible || IsVisible(f, internalMembersVisibleInModule))
AddCurrentMember(f);
}
// repeat with base class:
if (typeDef.BaseType != null)
typeDef = typeDef.BaseType.Resolve();
else
typeDef = null;
privateMembersVisible = false;
}
// Now add current members from outer classes:
if (oldMembers != null) {
foreach (var pair in oldMembers) {
// add members from outer classes only if the inner class doesn't define the member
if (!currentMembers.ContainsKey(pair.Key))
currentMembers.Add(pair.Key, pair.Value);
}
}
base.VisitTypeDeclaration(typeDeclaration, data);
currentMembers = oldMembers;
return null;
}
void AddCurrentMember(MemberReference m)
{
MemberReference existingMember;
if (currentMembers.TryGetValue(m.Name, out existingMember)) {
// We keep the existing member assignment if it was from another class (=from a derived class),
// because members in derived classes have precedence over members in base classes.
if (existingMember != null && existingMember.DeclaringType == m.DeclaringType) {
// Use null as value to signalize multiple members with the same name
currentMembers[m.Name] = null;
}
} else {
currentMembers.Add(m.Name, m);
}
}
bool IsVisible(MethodDefinition m, ModuleDefinition internalMembersVisibleInModule)
{
if (m == null)
return false;
switch (m.Attributes & MethodAttributes.MemberAccessMask) {
case MethodAttributes.FamANDAssem:
case MethodAttributes.Assembly:
return m.Module == internalMembersVisibleInModule;
case MethodAttributes.Family:
case MethodAttributes.FamORAssem:
case MethodAttributes.Public:
return true;
default:
return false;
}
}
bool IsVisible(FieldDefinition f, ModuleDefinition internalMembersVisibleInModule)
{
if (f == null)
return false;
switch (f.Attributes & FieldAttributes.FieldAccessMask) {
case FieldAttributes.FamANDAssem:
case FieldAttributes.Assembly:
return f.Module == internalMembersVisibleInModule;
case FieldAttributes.Family:
case FieldAttributes.FamORAssem:
case FieldAttributes.Public:
return true;
default:
return false;
}
}
bool IsVisible(TypeDefinition t, ModuleDefinition internalMembersVisibleInModule)
{
if (t == null)
return false;
switch (t.Attributes & TypeAttributes.VisibilityMask) {
case TypeAttributes.NotPublic:
case TypeAttributes.NestedAssembly:
case TypeAttributes.NestedFamANDAssem:
return t.Module == internalMembersVisibleInModule;
case TypeAttributes.NestedFamily:
case TypeAttributes.NestedFamORAssem:
case TypeAttributes.NestedPublic:
case TypeAttributes.Public:
return true;
default:
return false;
}
}
public override object VisitSimpleType(SimpleType simpleType, object data)
{
// Handle type arguments first, so that the fixed-up type arguments get moved over to the MemberType,
// if we're also creating one here.
base.VisitSimpleType(simpleType, data);
TypeReference tr = simpleType.Annotation<TypeReference>();
// Fully qualify any ambiguous type names.
if (tr != null && IsAmbiguous(tr.Namespace, tr.Name)) {
AstType ns;
if (string.IsNullOrEmpty(tr.Namespace)) {
ns = new SimpleType("global");
} else {
string[] parts = tr.Namespace.Split('.');
if (IsAmbiguous(string.Empty, parts[0])) {
// conflict between namespace and type name/member name
ns = new MemberType { Target = new SimpleType("global"), IsDoubleColon = true, MemberName = parts[0] };
} else {
ns = new SimpleType(parts[0]);
}
for (int i = 1; i < parts.Length; i++) {
ns = new MemberType { Target = ns, MemberName = parts[i] };
}
}
MemberType mt = new MemberType();
mt.Target = ns;
mt.IsDoubleColon = string.IsNullOrEmpty(tr.Namespace);
mt.MemberName = simpleType.Identifier;
mt.CopyAnnotationsFrom(simpleType);
simpleType.TypeArguments.MoveTo(mt.TypeArguments);
simpleType.ReplaceWith(mt);
}
return null;
}
public override object VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, object data)
{
isWithinTypeReferenceExpression = true;
base.VisitTypeReferenceExpression(typeReferenceExpression, data);
isWithinTypeReferenceExpression = false;
return null;
}
bool IsAmbiguous(string ns, string name)
{
// If the type name conflicts with an inner class/type parameter, we need to fully-qualify it:
if (currentMemberTypes != null && currentMemberTypes.Contains(name))
return true;
// If the type name conflicts with a field/property etc. on the current class, we need to fully-qualify it,
// if we're inside an expression.
if (isWithinTypeReferenceExpression && currentMembers != null) {
MemberReference mr;
if (currentMembers.TryGetValue(name, out mr)) {
// However, in the special case where the member is a field or property with the same type
// as is requested, then we can use the short name (if it's not otherwise ambiguous)
PropertyDefinition prop = mr as PropertyDefinition;
FieldDefinition field = mr as FieldDefinition;
if (!(prop != null && prop.PropertyType.Namespace == ns && prop.PropertyType.Name == name)
&& !(field != null && field.FieldType.Namespace == ns && field.FieldType.Name == name))
return true;
}
}
// If the type is defined in the current namespace,
// then we can use the short name even if we imported type with same name from another namespace.
if (ns == currentNamespace && !string.IsNullOrEmpty(ns))
return false;
return transform.ambiguousTypeNames.Contains(name);
}
}
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,164 +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.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
public class PushNegation: DepthFirstAstVisitor<object, object>, IAstTransform
{
sealed class LiftedOperator { }
/// <summary>
/// Annotation for lifted operators that cannot be transformed by PushNegation
/// </summary>
public static readonly object LiftedOperatorAnnotation = new LiftedOperator();
public override object VisitUnaryOperatorExpression(UnaryOperatorExpression unary, object data)
{
// lifted operators can't be transformed
if (unary.Annotation<LiftedOperator>() != null || unary.Expression.Annotation<LiftedOperator>() != null)
return base.VisitUnaryOperatorExpression(unary, data);
// Remove double negation
// !!a
if (unary.Operator == UnaryOperatorType.Not &&
unary.Expression is UnaryOperatorExpression &&
(unary.Expression as UnaryOperatorExpression).Operator == UnaryOperatorType.Not)
{
AstNode newNode = (unary.Expression as UnaryOperatorExpression).Expression;
unary.ReplaceWith(newNode);
return newNode.AcceptVisitor(this, data);
}
// Push through binary operation
// !((a) op (b))
BinaryOperatorExpression binaryOp = unary.Expression as BinaryOperatorExpression;
if (unary.Operator == UnaryOperatorType.Not && binaryOp != null) {
bool successful = true;
switch (binaryOp.Operator) {
case BinaryOperatorType.Equality:
binaryOp.Operator = BinaryOperatorType.InEquality;
break;
case BinaryOperatorType.InEquality:
binaryOp.Operator = BinaryOperatorType.Equality;
break;
case BinaryOperatorType.GreaterThan: // TODO: these are invalid for floats (stupid NaN)
binaryOp.Operator = BinaryOperatorType.LessThanOrEqual;
break;
case BinaryOperatorType.GreaterThanOrEqual:
binaryOp.Operator = BinaryOperatorType.LessThan;
break;
case BinaryOperatorType.LessThanOrEqual:
binaryOp.Operator = BinaryOperatorType.GreaterThan;
break;
case BinaryOperatorType.LessThan:
binaryOp.Operator = BinaryOperatorType.GreaterThanOrEqual;
break;
default:
successful = false;
break;
}
if (successful) {
unary.ReplaceWith(binaryOp);
return binaryOp.AcceptVisitor(this, data);
}
successful = true;
switch (binaryOp.Operator) {
case BinaryOperatorType.ConditionalAnd:
binaryOp.Operator = BinaryOperatorType.ConditionalOr;
break;
case BinaryOperatorType.ConditionalOr:
binaryOp.Operator = BinaryOperatorType.ConditionalAnd;
break;
default:
successful = false;
break;
}
if (successful) {
binaryOp.Left.ReplaceWith(e => new UnaryOperatorExpression(UnaryOperatorType.Not, e));
binaryOp.Right.ReplaceWith(e => new UnaryOperatorExpression(UnaryOperatorType.Not, e));
unary.ReplaceWith(binaryOp);
return binaryOp.AcceptVisitor(this, data);
}
}
return base.VisitUnaryOperatorExpression(unary, data);
}
readonly static AstNode asCastIsNullPattern = new BinaryOperatorExpression(
new AnyNode("expr").ToExpression().CastAs(new AnyNode("type")),
BinaryOperatorType.Equality,
new NullReferenceExpression()
);
readonly static AstNode asCastIsNotNullPattern = new BinaryOperatorExpression(
new AnyNode("expr").ToExpression().CastAs(new AnyNode("type")),
BinaryOperatorType.InEquality,
new NullReferenceExpression()
);
public override object VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, object data)
{
// lifted operators can't be transformed
if (binaryOperatorExpression.Annotation<LiftedOperator>() != null)
return base.VisitBinaryOperatorExpression(binaryOperatorExpression, data);
BinaryOperatorType op = binaryOperatorExpression.Operator;
bool? rightOperand = null;
if (binaryOperatorExpression.Right is PrimitiveExpression)
rightOperand = ((PrimitiveExpression)binaryOperatorExpression.Right).Value as bool?;
if (op == BinaryOperatorType.Equality && rightOperand == true || op == BinaryOperatorType.InEquality && rightOperand == false) {
// 'b == true' or 'b != false' is useless
binaryOperatorExpression.Left.AcceptVisitor(this, data);
binaryOperatorExpression.ReplaceWith(binaryOperatorExpression.Left);
return null;
} else if (op == BinaryOperatorType.Equality && rightOperand == false || op == BinaryOperatorType.InEquality && rightOperand == true) {
// 'b == false' or 'b != true' is a negation:
Expression left = binaryOperatorExpression.Left;
left.Remove();
UnaryOperatorExpression uoe = new UnaryOperatorExpression(UnaryOperatorType.Not, left);
binaryOperatorExpression.ReplaceWith(uoe);
return uoe.AcceptVisitor(this, data);
} else {
bool negate = false;
Match m = asCastIsNotNullPattern.Match(binaryOperatorExpression);
if (!m.Success) {
m = asCastIsNullPattern.Match(binaryOperatorExpression);
negate = true;
}
if (m.Success) {
Expression expr = m.Get<Expression>("expr").Single().Detach().IsType(m.Get<AstType>("type").Single().Detach());
if (negate)
expr = new UnaryOperatorExpression(UnaryOperatorType.Not, expr);
binaryOperatorExpression.ReplaceWith(expr);
return expr.AcceptVisitor(this, data);
} else {
return base.VisitBinaryOperatorExpression(binaryOperatorExpression, data);
}
}
}
void IAstTransform.Run(AstNode node)
{
node.AcceptVisitor(this, null);
}
}
}

Просмотреть файл

@ -1,356 +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 System.Reflection;
using ICSharpCode.NRefactory.PatternMatching;
using Mono.Cecil;
using Ast = ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Replaces method calls with the appropriate operator expressions.
/// Also simplifies "x = x op y" into "x op= y" where possible.
/// </summary>
public class ReplaceMethodCallsWithOperators : DepthFirstAstVisitor<object, object>, IAstTransform
{
static readonly MemberReferenceExpression typeHandleOnTypeOfPattern = new MemberReferenceExpression {
Target = new Choice {
new TypeOfExpression(new AnyNode()),
new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.RefType, Arguments = { new AnyNode() } }
},
MemberName = "TypeHandle"
};
DecompilerContext context;
public ReplaceMethodCallsWithOperators(DecompilerContext context)
{
this.context = context;
}
public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data)
{
base.VisitInvocationExpression(invocationExpression, data);
ProcessInvocationExpression(invocationExpression);
return null;
}
internal static void ProcessInvocationExpression(InvocationExpression invocationExpression)
{
MethodReference methodRef = invocationExpression.Annotation<MethodReference>();
if (methodRef == null)
return;
var arguments = invocationExpression.Arguments.ToArray();
// Reduce "String.Concat(a, b)" to "a + b"
if (methodRef.Name == "Concat" && methodRef.DeclaringType.FullName == "System.String" && arguments.Length >= 2)
{
invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression
Expression expr = arguments[0];
for (int i = 1; i < arguments.Length; i++) {
expr = new BinaryOperatorExpression(expr, BinaryOperatorType.Add, arguments[i]);
}
invocationExpression.ReplaceWith(expr);
return;
}
switch (methodRef.FullName) {
case "System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle)":
if (arguments.Length == 1) {
if (typeHandleOnTypeOfPattern.IsMatch(arguments[0])) {
invocationExpression.ReplaceWith(((MemberReferenceExpression)arguments[0]).Target);
return;
}
}
break;
case "System.Reflection.FieldInfo System.Reflection.FieldInfo::GetFieldFromHandle(System.RuntimeFieldHandle)":
if (arguments.Length == 1) {
MemberReferenceExpression mre = arguments[0] as MemberReferenceExpression;
if (mre != null && mre.MemberName == "FieldHandle" && mre.Target.Annotation<LdTokenAnnotation>() != null) {
invocationExpression.ReplaceWith(mre.Target);
return;
}
}
break;
case "System.Reflection.FieldInfo System.Reflection.FieldInfo::GetFieldFromHandle(System.RuntimeFieldHandle,System.RuntimeTypeHandle)":
if (arguments.Length == 2) {
MemberReferenceExpression mre1 = arguments[0] as MemberReferenceExpression;
MemberReferenceExpression mre2 = arguments[1] as MemberReferenceExpression;
if (mre1 != null && mre1.MemberName == "FieldHandle" && mre1.Target.Annotation<LdTokenAnnotation>() != null) {
if (mre2 != null && mre2.MemberName == "TypeHandle" && mre2.Target is TypeOfExpression) {
Expression oldArg = ((InvocationExpression)mre1.Target).Arguments.Single();
FieldReference field = oldArg.Annotation<FieldReference>();
if (field != null) {
AstType declaringType = ((TypeOfExpression)mre2.Target).Type.Detach();
oldArg.ReplaceWith(declaringType.Member(field.Name).WithAnnotation(field));
invocationExpression.ReplaceWith(mre1.Target);
return;
}
}
}
}
break;
}
BinaryOperatorType? bop = GetBinaryOperatorTypeFromMetadataName(methodRef.Name);
if (bop != null && arguments.Length == 2) {
invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression
invocationExpression.ReplaceWith(
new BinaryOperatorExpression(arguments[0], bop.Value, arguments[1]).WithAnnotation(methodRef)
);
return;
}
UnaryOperatorType? uop = GetUnaryOperatorTypeFromMetadataName(methodRef.Name);
if (uop != null && arguments.Length == 1) {
arguments[0].Remove(); // detach argument
invocationExpression.ReplaceWith(
new UnaryOperatorExpression(uop.Value, arguments[0]).WithAnnotation(methodRef)
);
return;
}
if (methodRef.Name == "op_Explicit" && arguments.Length == 1) {
arguments[0].Remove(); // detach argument
invocationExpression.ReplaceWith(
arguments[0].CastTo(AstBuilder.ConvertType(methodRef.ReturnType, methodRef.MethodReturnType))
.WithAnnotation(methodRef)
);
return;
}
if (methodRef.Name == "op_Implicit" && arguments.Length == 1) {
invocationExpression.ReplaceWith(arguments[0]);
return;
}
if (methodRef.Name == "op_True" && arguments.Length == 1 && invocationExpression.Role == Roles.Condition) {
invocationExpression.ReplaceWith(arguments[0]);
return;
}
return;
}
static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name)
{
switch (name) {
case "op_Addition":
return BinaryOperatorType.Add;
case "op_Subtraction":
return BinaryOperatorType.Subtract;
case "op_Multiply":
return BinaryOperatorType.Multiply;
case "op_Division":
return BinaryOperatorType.Divide;
case "op_Modulus":
return BinaryOperatorType.Modulus;
case "op_BitwiseAnd":
return BinaryOperatorType.BitwiseAnd;
case "op_BitwiseOr":
return BinaryOperatorType.BitwiseOr;
case "op_ExclusiveOr":
return BinaryOperatorType.ExclusiveOr;
case "op_LeftShift":
return BinaryOperatorType.ShiftLeft;
case "op_RightShift":
return BinaryOperatorType.ShiftRight;
case "op_Equality":
return BinaryOperatorType.Equality;
case "op_Inequality":
return BinaryOperatorType.InEquality;
case "op_LessThan":
return BinaryOperatorType.LessThan;
case "op_LessThanOrEqual":
return BinaryOperatorType.LessThanOrEqual;
case "op_GreaterThan":
return BinaryOperatorType.GreaterThan;
case "op_GreaterThanOrEqual":
return BinaryOperatorType.GreaterThanOrEqual;
default:
return null;
}
}
static UnaryOperatorType? GetUnaryOperatorTypeFromMetadataName(string name)
{
switch (name) {
case "op_LogicalNot":
return UnaryOperatorType.Not;
case "op_OnesComplement":
return UnaryOperatorType.BitNot;
case "op_UnaryNegation":
return UnaryOperatorType.Minus;
case "op_UnaryPlus":
return UnaryOperatorType.Plus;
case "op_Increment":
return UnaryOperatorType.Increment;
case "op_Decrement":
return UnaryOperatorType.Decrement;
default:
return null;
}
}
/// <summary>
/// This annotation is used to convert a compound assignment "a += 2;" or increment operator "a++;"
/// back to the original "a = a + 2;". This is sometimes necessary when the checked/unchecked semantics
/// cannot be guaranteed otherwise (see CheckedUnchecked.ForWithCheckedInitializerAndUncheckedIterator test)
/// </summary>
public class RestoreOriginalAssignOperatorAnnotation
{
readonly BinaryOperatorExpression binaryOperatorExpression;
public RestoreOriginalAssignOperatorAnnotation(BinaryOperatorExpression binaryOperatorExpression)
{
this.binaryOperatorExpression = binaryOperatorExpression;
}
public AssignmentExpression Restore(Expression expression)
{
expression.RemoveAnnotations<RestoreOriginalAssignOperatorAnnotation>();
AssignmentExpression assign = expression as AssignmentExpression;
if (assign == null) {
UnaryOperatorExpression uoe = (UnaryOperatorExpression)expression;
assign = new AssignmentExpression(uoe.Expression.Detach(), new PrimitiveExpression(1));
} else {
assign.Operator = AssignmentOperatorType.Assign;
}
binaryOperatorExpression.Right = assign.Right.Detach();
assign.Right = binaryOperatorExpression;
return assign;
}
}
public override object VisitAssignmentExpression(AssignmentExpression assignment, object data)
{
base.VisitAssignmentExpression(assignment, data);
// Combine "x = x op y" into "x op= y"
BinaryOperatorExpression binary = assignment.Right as BinaryOperatorExpression;
if (binary != null && assignment.Operator == AssignmentOperatorType.Assign) {
if (CanConvertToCompoundAssignment(assignment.Left) && assignment.Left.IsMatch(binary.Left)) {
assignment.Operator = GetAssignmentOperatorForBinaryOperator(binary.Operator);
if (assignment.Operator != AssignmentOperatorType.Assign) {
// If we found a shorter operator, get rid of the BinaryOperatorExpression:
assignment.CopyAnnotationsFrom(binary);
assignment.Right = binary.Right;
assignment.AddAnnotation(new RestoreOriginalAssignOperatorAnnotation(binary));
}
}
}
if (context.Settings.IntroduceIncrementAndDecrement && (assignment.Operator == AssignmentOperatorType.Add || assignment.Operator == AssignmentOperatorType.Subtract)) {
// detect increment/decrement
if (assignment.Right.IsMatch(new PrimitiveExpression(1))) {
// only if it's not a custom operator
if (assignment.Annotation<MethodReference>() == null) {
UnaryOperatorType type;
// When the parent is an expression statement, pre- or post-increment doesn't matter;
// so we can pick post-increment which is more commonly used (for (int i = 0; i < x; i++))
if (assignment.Parent is ExpressionStatement)
type = (assignment.Operator == AssignmentOperatorType.Add) ? UnaryOperatorType.PostIncrement : UnaryOperatorType.PostDecrement;
else
type = (assignment.Operator == AssignmentOperatorType.Add) ? UnaryOperatorType.Increment : UnaryOperatorType.Decrement;
assignment.ReplaceWith(new UnaryOperatorExpression(type, assignment.Left.Detach()).CopyAnnotationsFrom(assignment));
}
}
}
return null;
}
public static AssignmentOperatorType GetAssignmentOperatorForBinaryOperator(BinaryOperatorType bop)
{
switch (bop) {
case BinaryOperatorType.Add:
return AssignmentOperatorType.Add;
case BinaryOperatorType.Subtract:
return AssignmentOperatorType.Subtract;
case BinaryOperatorType.Multiply:
return AssignmentOperatorType.Multiply;
case BinaryOperatorType.Divide:
return AssignmentOperatorType.Divide;
case BinaryOperatorType.Modulus:
return AssignmentOperatorType.Modulus;
case BinaryOperatorType.ShiftLeft:
return AssignmentOperatorType.ShiftLeft;
case BinaryOperatorType.ShiftRight:
return AssignmentOperatorType.ShiftRight;
case BinaryOperatorType.BitwiseAnd:
return AssignmentOperatorType.BitwiseAnd;
case BinaryOperatorType.BitwiseOr:
return AssignmentOperatorType.BitwiseOr;
case BinaryOperatorType.ExclusiveOr:
return AssignmentOperatorType.ExclusiveOr;
default:
return AssignmentOperatorType.Assign;
}
}
static bool CanConvertToCompoundAssignment(Expression left)
{
MemberReferenceExpression mre = left as MemberReferenceExpression;
if (mre != null)
return IsWithoutSideEffects(mre.Target);
IndexerExpression ie = left as IndexerExpression;
if (ie != null)
return IsWithoutSideEffects(ie.Target) && ie.Arguments.All(IsWithoutSideEffects);
UnaryOperatorExpression uoe = left as UnaryOperatorExpression;
if (uoe != null && uoe.Operator == UnaryOperatorType.Dereference)
return IsWithoutSideEffects(uoe.Expression);
return IsWithoutSideEffects(left);
}
static bool IsWithoutSideEffects(Expression left)
{
return left is ThisReferenceExpression || left is IdentifierExpression || left is TypeReferenceExpression || left is BaseReferenceExpression;
}
static readonly Expression getMethodOrConstructorFromHandlePattern =
new TypePattern(typeof(MethodBase)).ToType().Invoke(
"GetMethodFromHandle",
new NamedNode("ldtokenNode", new LdTokenPattern("method")).ToExpression().Member("MethodHandle"),
new OptionalNode(new TypeOfExpression(new AnyNode("declaringType")).Member("TypeHandle"))
).CastTo(new Choice {
new TypePattern(typeof(MethodInfo)),
new TypePattern(typeof(ConstructorInfo))
});
public override object VisitCastExpression(CastExpression castExpression, object data)
{
base.VisitCastExpression(castExpression, data);
// Handle methodof
Match m = getMethodOrConstructorFromHandlePattern.Match(castExpression);
if (m.Success) {
MethodReference method = m.Get<AstNode>("method").Single().Annotation<MethodReference>();
if (m.Has("declaringType")) {
Expression newNode = m.Get<AstType>("declaringType").Single().Detach().Member(method.Name);
newNode = newNode.Invoke(method.Parameters.Select(p => new TypeReferenceExpression(AstBuilder.ConvertType(p.ParameterType, p))));
newNode.AddAnnotation(method);
m.Get<AstNode>("method").Single().ReplaceWith(newNode);
}
castExpression.ReplaceWith(m.Get<AstNode>("ldtokenNode").Single());
}
return null;
}
void IAstTransform.Run(AstNode node)
{
node.AcceptVisitor(this, null);
}
}
}

Просмотреть файл

@ -1,65 +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.Threading;
using ICSharpCode.NRefactory.CSharp;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
public interface IAstTransform
{
void Run(AstNode compilationUnit);
}
public static class TransformationPipeline
{
public static IAstTransform[] CreatePipeline(DecompilerContext context)
{
return new IAstTransform[] {
new PushNegation(),
new DelegateConstruction(context),
new PatternStatementTransform(context),
new ReplaceMethodCallsWithOperators(context),
new IntroduceUnsafeModifier(),
new AddCheckedBlocks(),
new DeclareVariables(context), // should run after most transforms that modify statements
new ConvertConstructorCallIntoInitializer(), // must run after DeclareVariables
new DecimalConstantTransform(),
new IntroduceUsingDeclarations(context),
new IntroduceExtensionMethods(context), // must run after IntroduceUsingDeclarations
new IntroduceQueryExpressions(context), // must run after IntroduceExtensionMethods
new CombineQueryExpressions(context),
new FlattenSwitchBlocks(),
};
}
public static void RunTransformationsUntil(AstNode node, Predicate<IAstTransform> abortCondition, DecompilerContext context)
{
if (node == null)
return;
foreach (var transform in CreatePipeline(context)) {
context.CancellationToken.ThrowIfCancellationRequested();
if (abortCondition != null && abortCondition(transform))
return;
transform.Run(node);
}
}
}
}

Просмотреть файл

@ -1,536 +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.Collections.ObjectModel;
using System.Linq;
using System.Text;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast
{
public static class TypesHierarchyHelpers
{
public static bool IsBaseType(TypeDefinition baseType, TypeDefinition derivedType, bool resolveTypeArguments)
{
if (resolveTypeArguments)
return BaseTypes(derivedType).Any(t => t.Item == baseType);
else {
var comparableBaseType = baseType.Resolve();
if (comparableBaseType == null)
return false;
while (derivedType.BaseType != null) {
var resolvedBaseType = derivedType.BaseType.Resolve();
if (resolvedBaseType == null)
return false;
if (comparableBaseType == resolvedBaseType)
return true;
derivedType = resolvedBaseType;
}
return false;
}
}
/// <summary>
/// Determines whether one method overrides or hides another method.
/// </summary>
/// <param name="parentMethod">The method declared in a base type.</param>
/// <param name="childMethod">The method declared in a derived type.</param>
/// <returns>true if <paramref name="childMethod"/> hides or overrides <paramref name="parentMethod"/>,
/// otherwise false.</returns>
public static bool IsBaseMethod(MethodDefinition parentMethod, MethodDefinition childMethod)
{
if (parentMethod == null)
throw new ArgumentNullException("parentMethod");
if (childMethod == null)
throw new ArgumentNullException("childMethod");
if (parentMethod.Name != childMethod.Name)
return false;
if (parentMethod.HasParameters || childMethod.HasParameters)
if (!parentMethod.HasParameters || !childMethod.HasParameters || parentMethod.Parameters.Count != childMethod.Parameters.Count)
return false;
return FindBaseMethods(childMethod).Any(m => m == parentMethod);// || (parentMethod.HasGenericParameters && m.);
}
/// <summary>
/// Determines whether a property overrides or hides another property.
/// </summary>
/// <param name="parentProperty">The property declared in a base type.</param>
/// <param name="childProperty">The property declared in a derived type.</param>
/// <returns>true if the <paramref name="childProperty"/> hides or overrides <paramref name="parentProperty"/>,
/// otherwise false.</returns>
public static bool IsBaseProperty(PropertyDefinition parentProperty, PropertyDefinition childProperty)
{
if (parentProperty == null)
throw new ArgumentNullException("parentProperty");
if (childProperty == null)
throw new ArgumentNullException("childProperty");
if (parentProperty.Name != childProperty.Name)
return false;
if (parentProperty.HasParameters || childProperty.HasParameters)
if (!parentProperty.HasParameters || !childProperty.HasParameters || parentProperty.Parameters.Count != childProperty.Parameters.Count)
return false;
return FindBaseProperties(childProperty).Any(m => m == parentProperty);
}
public static bool IsBaseEvent(EventDefinition parentEvent, EventDefinition childEvent)
{
if (parentEvent.Name != childEvent.Name)
return false;
return FindBaseEvents(childEvent).Any(m => m == parentEvent);
}
/// <summary>
/// Finds all methods from base types overridden or hidden by the specified method.
/// </summary>
/// <param name="method">The method which overrides or hides methods from base types.</param>
/// <returns>Methods overriden or hidden by the specified method.</returns>
public static IEnumerable<MethodDefinition> FindBaseMethods(MethodDefinition method)
{
if (method == null)
throw new ArgumentNullException("method");
var typeContext = CreateGenericContext(method.DeclaringType);
var gMethod = typeContext.ApplyTo(method);
foreach (var baseType in BaseTypes(method.DeclaringType))
foreach (var baseMethod in baseType.Item.Methods)
if (MatchMethod(baseType.ApplyTo(baseMethod), gMethod) && IsVisibleFromDerived(baseMethod, method.DeclaringType)) {
yield return baseMethod;
if (baseMethod.IsNewSlot == baseMethod.IsVirtual)
yield break;
}
}
/// <summary>
/// Finds all properties from base types overridden or hidden by the specified property.
/// </summary>
/// <param name="property">The property which overrides or hides properties from base types.</param>
/// <returns>Properties overriden or hidden by the specified property.</returns>
public static IEnumerable<PropertyDefinition> FindBaseProperties(PropertyDefinition property)
{
if (property == null)
throw new ArgumentNullException("property");
if ((property.GetMethod ?? property.SetMethod).HasOverrides)
yield break;
var typeContext = CreateGenericContext(property.DeclaringType);
var gProperty = typeContext.ApplyTo(property);
bool isIndexer = property.IsIndexer();
foreach (var baseType in BaseTypes(property.DeclaringType))
foreach (var baseProperty in baseType.Item.Properties)
if (MatchProperty(baseType.ApplyTo(baseProperty), gProperty)
&& IsVisibleFromDerived(baseProperty, property.DeclaringType)) {
if (isIndexer != baseProperty.IsIndexer())
continue;
yield return baseProperty;
var anyPropertyAccessor = baseProperty.GetMethod ?? baseProperty.SetMethod;
if (anyPropertyAccessor.IsNewSlot == anyPropertyAccessor.IsVirtual)
yield break;
}
}
public static IEnumerable<EventDefinition> FindBaseEvents(EventDefinition eventDef)
{
if (eventDef == null)
throw new ArgumentNullException("eventDef");
var typeContext = CreateGenericContext(eventDef.DeclaringType);
var gEvent = typeContext.ApplyTo(eventDef);
foreach (var baseType in BaseTypes(eventDef.DeclaringType))
foreach (var baseEvent in baseType.Item.Events)
if (MatchEvent(baseType.ApplyTo(baseEvent), gEvent) && IsVisibleFromDerived(baseEvent, eventDef.DeclaringType)) {
yield return baseEvent;
var anyEventAccessor = baseEvent.AddMethod ?? baseEvent.RemoveMethod;
if (anyEventAccessor.IsNewSlot == anyEventAccessor.IsVirtual)
yield break;
}
}
/// <summary>
/// Determinates whether member of the base type is visible from a derived type.
/// </summary>
/// <param name="baseMember">The member which visibility is checked.</param>
/// <param name="derivedType">The derived type.</param>
/// <returns>true if the member is visible from derived type, othewise false.</returns>
public static bool IsVisibleFromDerived(IMemberDefinition baseMember, TypeDefinition derivedType)
{
if (baseMember == null)
throw new ArgumentNullException("baseMember");
if (derivedType == null)
throw new ArgumentNullException("derivedType");
MethodAttributes attrs = GetAccessAttributes(baseMember) & MethodAttributes.MemberAccessMask;
if (attrs == MethodAttributes.Private)
return false;
if (baseMember.DeclaringType.Module == derivedType.Module)
return true;
if (attrs == MethodAttributes.Assembly || attrs == MethodAttributes.FamANDAssem) {
var derivedTypeAsm = derivedType.Module.Assembly;
var asm = baseMember.DeclaringType.Module.Assembly;
if (asm.HasCustomAttributes) {
var attributes = asm.CustomAttributes
.Where(attr => attr.AttributeType.FullName == "System.Runtime.CompilerServices.InternalsVisibleToAttribute");
foreach (var attribute in attributes) {
string assemblyName = attribute.ConstructorArguments[0].Value as string;
assemblyName = assemblyName.Split(',')[0]; // strip off any public key info
if (assemblyName == derivedTypeAsm.Name.Name)
return true;
}
}
return false;
}
return true;
}
static MethodAttributes GetAccessAttributes(IMemberDefinition member)
{
var fld = member as FieldDefinition;
if (fld != null)
return (MethodAttributes)fld.Attributes;
var method = member as MethodDefinition;
if (method != null)
return method.Attributes;
var prop = member as PropertyDefinition;
if (prop != null) {
return (prop.GetMethod ?? prop.SetMethod).Attributes;
}
var evnt = member as EventDefinition;
if (evnt != null) {
return (evnt.AddMethod ?? evnt.RemoveMethod).Attributes;
}
var nestedType = member as TypeDefinition;
if (nestedType != null) {
if (nestedType.IsNestedPrivate)
return MethodAttributes.Private;
if (nestedType.IsNestedAssembly || nestedType.IsNestedFamilyAndAssembly)
return MethodAttributes.Assembly;
return MethodAttributes.Public;
}
throw new NotSupportedException();
}
static bool MatchMethod(GenericContext<MethodDefinition> candidate, GenericContext<MethodDefinition> method)
{
var mCandidate = candidate.Item;
var mMethod = method.Item;
if (mCandidate.Name != mMethod.Name)
return false;
if (mCandidate.HasOverrides)
return false;
if (mCandidate.IsSpecialName != method.Item.IsSpecialName)
return false;
if (mCandidate.HasGenericParameters || mMethod.HasGenericParameters) {
if (!mCandidate.HasGenericParameters || !mMethod.HasGenericParameters || mCandidate.GenericParameters.Count != mMethod.GenericParameters.Count)
return false;
}
if (mCandidate.HasParameters || mMethod.HasParameters) {
if (!mCandidate.HasParameters || !mMethod.HasParameters || mCandidate.Parameters.Count != mMethod.Parameters.Count)
return false;
for (int index = 0; index < mCandidate.Parameters.Count; index++) {
if (!MatchParameters(candidate.ApplyTo(mCandidate.Parameters[index]), method.ApplyTo(mMethod.Parameters[index])))
return false;
}
}
return true;
}
public static bool MatchInterfaceMethod(MethodDefinition candidate, MethodDefinition method, TypeReference interfaceContextType)
{
var candidateContext = CreateGenericContext(candidate.DeclaringType);
var gCandidate = candidateContext.ApplyTo(candidate);
if (interfaceContextType is GenericInstanceType) {
var methodContext = new GenericContext<TypeDefinition>(interfaceContextType.Resolve(), ((GenericInstanceType)interfaceContextType).GenericArguments);
var gMethod = methodContext.ApplyTo(method);
return MatchMethod(gCandidate, gMethod);
} else {
var methodContext = CreateGenericContext(interfaceContextType.Resolve());
var gMethod = candidateContext.ApplyTo(method);
return MatchMethod(gCandidate, gMethod);
}
}
static bool MatchProperty(GenericContext<PropertyDefinition> candidate, GenericContext<PropertyDefinition> property)
{
var mCandidate = candidate.Item;
var mProperty = property.Item;
if (mCandidate.Name != mProperty.Name)
return false;
if ((mCandidate.GetMethod ?? mCandidate.SetMethod).HasOverrides)
return false;
if (mCandidate.HasParameters || mProperty.HasParameters) {
if (!mCandidate.HasParameters || !mProperty.HasParameters || mCandidate.Parameters.Count != mProperty.Parameters.Count)
return false;
for (int index = 0; index < mCandidate.Parameters.Count; index++) {
if (!MatchParameters(candidate.ApplyTo(mCandidate.Parameters[index]), property.ApplyTo(mProperty.Parameters[index])))
return false;
}
}
return true;
}
static bool MatchEvent(GenericContext<EventDefinition> candidate, GenericContext<EventDefinition> ev)
{
var mCandidate = candidate.Item;
var mEvent = ev.Item;
if (mCandidate.Name != mEvent.Name)
return false;
if ((mCandidate.AddMethod ?? mCandidate.RemoveMethod).HasOverrides)
return false;
if (!IsSameType(candidate.ResolveWithContext(mCandidate.EventType), ev.ResolveWithContext(mEvent.EventType)))
return false;
return true;
}
static bool MatchParameters(GenericContext<ParameterDefinition> baseParameterType, GenericContext<ParameterDefinition> parameterType)
{
if (baseParameterType.Item.IsIn != parameterType.Item.IsIn ||
baseParameterType.Item.IsOut != parameterType.Item.IsOut)
return false;
var baseParam = baseParameterType.ResolveWithContext(baseParameterType.Item.ParameterType);
var param = parameterType.ResolveWithContext(parameterType.Item.ParameterType);
return IsSameType(baseParam, param);
}
static bool IsSameType(TypeReference tr1, TypeReference tr2)
{
if (tr1 == tr2)
return true;
if (tr1 == null || tr2 == null)
return false;
if (tr1.GetType() != tr2.GetType())
return false;
if (tr1.Name == tr2.Name && tr1.FullName == tr2.FullName)
return true;
return false;
}
static IEnumerable<GenericContext<TypeDefinition>> BaseTypes(TypeDefinition type)
{
return BaseTypes(CreateGenericContext(type));
}
static IEnumerable<GenericContext<TypeDefinition>> BaseTypes(GenericContext<TypeDefinition> type)
{
while (type.Item.BaseType != null) {
var baseType = type.Item.BaseType;
var genericBaseType = baseType as GenericInstanceType;
if (genericBaseType != null) {
#pragma warning disable 618
type = new GenericContext<TypeDefinition>(genericBaseType.ResolveOrThrow(),
#pragma warning restore 618
genericBaseType.GenericArguments.Select(t => type.ResolveWithContext(t)));
} else
#pragma warning disable 618
type = new GenericContext<TypeDefinition>(baseType.ResolveOrThrow());
#pragma warning restore 618
yield return type;
}
}
static GenericContext<TypeDefinition> CreateGenericContext(TypeDefinition type)
{
return type.HasGenericParameters
? new GenericContext<TypeDefinition>(type, type.GenericParameters)
: new GenericContext<TypeDefinition>(type);
}
struct GenericContext<T> where T : class
{
static readonly ReadOnlyCollection<TypeReference> Empty = new ReadOnlyCollection<TypeReference>(new List<TypeReference>());
static readonly GenericParameter UnresolvedGenericTypeParameter =
new DummyGenericParameterProvider(false).DummyParameter;
static readonly GenericParameter UnresolvedGenericMethodParameter =
new DummyGenericParameterProvider(true).DummyParameter;
public readonly T Item;
public readonly ReadOnlyCollection<TypeReference> TypeArguments;
public GenericContext(T item)
{
if (item == null)
throw new ArgumentNullException("item");
Item = item;
TypeArguments = Empty;
}
public GenericContext(T item, IEnumerable<TypeReference> typeArguments)
{
if (item == null)
throw new ArgumentNullException("item");
Item = item;
var list = new List<TypeReference>();
foreach (var arg in typeArguments) {
var resolved = arg != null ? arg.Resolve() : arg;
list.Add(resolved != null ? resolved : arg);
}
TypeArguments = new ReadOnlyCollection<TypeReference>(list);
}
GenericContext(T item, ReadOnlyCollection<TypeReference> typeArguments)
{
Item = item;
TypeArguments = typeArguments;
}
public TypeReference ResolveWithContext(TypeReference type)
{
var genericParameter = type as GenericParameter;
if (genericParameter != null)
if (genericParameter.Owner.GenericParameterType == GenericParameterType.Type)
return TypeArguments[genericParameter.Position];
else
return genericParameter.Owner.GenericParameterType == GenericParameterType.Type
? UnresolvedGenericTypeParameter : UnresolvedGenericMethodParameter;
var typeSpecification = type as TypeSpecification;
if (typeSpecification != null) {
var resolvedElementType = ResolveWithContext(typeSpecification.ElementType);
return ReplaceElementType(typeSpecification, resolvedElementType);
}
#pragma warning disable 618
return type.ResolveOrThrow();
#pragma warning restore 618
}
TypeReference ReplaceElementType(TypeSpecification ts, TypeReference newElementType)
{
var arrayType = ts as ArrayType;
if (arrayType != null) {
if (newElementType == arrayType.ElementType)
return arrayType;
var newArrayType = new ArrayType(newElementType, arrayType.Rank);
for (int dimension = 0; dimension < arrayType.Rank; dimension++)
newArrayType.Dimensions[dimension] = arrayType.Dimensions[dimension];
return newArrayType;
}
var byReferenceType = ts as ByReferenceType;
if (byReferenceType != null) {
return new ByReferenceType(newElementType);
}
// TODO: should we throw an exception instead calling Resolve method?
#pragma warning disable 618
return ts.ResolveOrThrow();
#pragma warning restore 618
}
public GenericContext<T2> ApplyTo<T2>(T2 item) where T2 : class
{
return new GenericContext<T2>(item, TypeArguments);
}
class DummyGenericParameterProvider : IGenericParameterProvider
{
readonly GenericParameterType type;
readonly Mono.Collections.Generic.Collection<GenericParameter> parameters;
public DummyGenericParameterProvider(bool methodTypeParameter)
{
type = methodTypeParameter ? GenericParameterType.Method :
GenericParameterType.Type;
parameters = new Mono.Collections.Generic.Collection<GenericParameter>(1);
parameters.Add(new GenericParameter(this));
}
public GenericParameter DummyParameter
{
get { return parameters[0]; }
}
bool IGenericParameterProvider.HasGenericParameters
{
get { throw new NotImplementedException(); }
}
bool IGenericParameterProvider.IsDefinition
{
get { throw new NotImplementedException(); }
}
ModuleDefinition IGenericParameterProvider.Module
{
get { throw new NotImplementedException(); }
}
Mono.Collections.Generic.Collection<GenericParameter> IGenericParameterProvider.GenericParameters
{
get { return parameters; }
}
GenericParameterType IGenericParameterProvider.GenericParameterType
{
get { return type; }
}
MetadataToken IMetadataTokenProvider.MetadataToken
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
}
}
}
}

Просмотреть файл

@ -1,374 +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;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler
{
/// <summary>
/// Cecil helper methods.
/// </summary>
public static class CecilExtensions
{
#region GetPushDelta / GetPopDelta
public static int GetPushDelta(this Instruction instruction)
{
OpCode code = instruction.OpCode;
switch (code.StackBehaviourPush) {
case StackBehaviour.Push0:
return 0;
case StackBehaviour.Push1:
case StackBehaviour.Pushi:
case StackBehaviour.Pushi8:
case StackBehaviour.Pushr4:
case StackBehaviour.Pushr8:
case StackBehaviour.Pushref:
return 1;
case StackBehaviour.Push1_push1:
return 2;
case StackBehaviour.Varpush:
if (code.FlowControl != FlowControl.Call)
break;
IMethodSignature method = (IMethodSignature) instruction.Operand;
return IsVoid (method.ReturnType) ? 0 : 1;
}
throw new NotSupportedException ();
}
public static int? GetPopDelta(this Instruction instruction, MethodDefinition methodDef)
{
OpCode code = instruction.OpCode;
switch (code.StackBehaviourPop) {
case StackBehaviour.Pop0:
return 0;
case StackBehaviour.Popi:
case StackBehaviour.Popref:
case StackBehaviour.Pop1:
return 1;
case StackBehaviour.Pop1_pop1:
case StackBehaviour.Popi_pop1:
case StackBehaviour.Popi_popi:
case StackBehaviour.Popi_popi8:
case StackBehaviour.Popi_popr4:
case StackBehaviour.Popi_popr8:
case StackBehaviour.Popref_pop1:
case StackBehaviour.Popref_popi:
return 2;
case StackBehaviour.Popi_popi_popi:
case StackBehaviour.Popref_popi_popi:
case StackBehaviour.Popref_popi_popi8:
case StackBehaviour.Popref_popi_popr4:
case StackBehaviour.Popref_popi_popr8:
case StackBehaviour.Popref_popi_popref:
return 3;
case StackBehaviour.PopAll:
return null;
case StackBehaviour.Varpop:
if (code == OpCodes.Ret)
return methodDef.ReturnType.IsVoid() ? 0 : 1;
if (code.FlowControl != FlowControl.Call)
break;
IMethodSignature method = (IMethodSignature) instruction.Operand;
int count = method.HasParameters ? method.Parameters.Count : 0;
if (method.HasThis && code != OpCodes.Newobj)
++count;
if (code == OpCodes.Calli)
++count; // calli takes a function pointer in additional to the normal args
return count;
}
throw new NotSupportedException ();
}
public static bool IsVoid(this TypeReference type)
{
while (type is OptionalModifierType || type is RequiredModifierType)
type = ((TypeSpecification)type).ElementType;
return type.MetadataType == MetadataType.Void;
}
public static bool IsValueTypeOrVoid(this TypeReference type)
{
while (type is OptionalModifierType || type is RequiredModifierType)
type = ((TypeSpecification)type).ElementType;
if (type is ArrayType)
return false;
return type.IsValueType || type.IsVoid();
}
/// <summary>
/// checks if the given TypeReference is one of the following types:
/// [sbyte, short, int, long, IntPtr]
/// </summary>
public static bool IsSignedIntegralType(this TypeReference type)
{
return type.MetadataType == MetadataType.SByte ||
type.MetadataType == MetadataType.Int16 ||
type.MetadataType == MetadataType.Int32 ||
type.MetadataType == MetadataType.Int64 ||
type.MetadataType == MetadataType.IntPtr;
}
/// <summary>
/// checks if the given value is a numeric zero-value.
/// NOTE that this only works for types: [sbyte, short, int, long, IntPtr, byte, ushort, uint, ulong, float, double and decimal]
/// </summary>
public static bool IsZero(this object value)
{
return value.Equals((sbyte)0) ||
value.Equals((short)0) ||
value.Equals(0) ||
value.Equals(0L) ||
value.Equals(IntPtr.Zero) ||
value.Equals((byte)0) ||
value.Equals((ushort)0) ||
value.Equals(0u) ||
value.Equals(0UL) ||
value.Equals(0.0f) ||
value.Equals(0.0) ||
value.Equals((decimal)0);
}
#endregion
/// <summary>
/// Gets the (exclusive) end offset of this instruction.
/// </summary>
public static int GetEndOffset(this Instruction inst)
{
if (inst == null)
throw new ArgumentNullException("inst");
return inst.Offset + inst.GetSize();
}
public static string OffsetToString(int offset)
{
return string.Format("IL_{0:x4}", offset);
}
public static HashSet<MethodDefinition> GetAccessorMethods(this TypeDefinition type)
{
HashSet<MethodDefinition> accessorMethods = new HashSet<MethodDefinition>();
foreach (var property in type.Properties) {
accessorMethods.Add(property.GetMethod);
accessorMethods.Add(property.SetMethod);
if (property.HasOtherMethods) {
foreach (var m in property.OtherMethods)
accessorMethods.Add(m);
}
}
foreach (EventDefinition ev in type.Events) {
accessorMethods.Add(ev.AddMethod);
accessorMethods.Add(ev.RemoveMethod);
accessorMethods.Add(ev.InvokeMethod);
if (ev.HasOtherMethods) {
foreach (var m in ev.OtherMethods)
accessorMethods.Add(m);
}
}
return accessorMethods;
}
public static TypeDefinition ResolveWithinSameModule(this TypeReference type)
{
if (type != null && type.GetElementType().Module == type.Module)
return type.Resolve();
else
return null;
}
public static FieldDefinition ResolveWithinSameModule(this FieldReference field)
{
if (field != null && field.DeclaringType.GetElementType().Module == field.Module)
return field.Resolve();
else
return null;
}
public static MethodDefinition ResolveWithinSameModule(this MethodReference method)
{
if (method != null && method.DeclaringType.GetElementType().Module == method.Module)
return method.Resolve();
else
return null;
}
[Obsolete("throwing exceptions is considered a bug")]
public static TypeDefinition ResolveOrThrow(this TypeReference typeReference)
{
var resolved = typeReference.Resolve();
if (resolved == null)
throw new ReferenceResolvingException();
return resolved;
}
public static bool IsCompilerGenerated(this ICustomAttributeProvider provider)
{
if (provider != null && provider.HasCustomAttributes) {
foreach (CustomAttribute a in provider.CustomAttributes) {
if (a.AttributeType.FullName == "System.Runtime.CompilerServices.CompilerGeneratedAttribute")
return true;
}
}
return false;
}
public static bool IsCompilerGeneratedOrIsInCompilerGeneratedClass(this IMemberDefinition member)
{
if (member == null)
return false;
if (member.IsCompilerGenerated())
return true;
return IsCompilerGeneratedOrIsInCompilerGeneratedClass(member.DeclaringType);
}
public static TypeReference GetEnumUnderlyingType(this TypeDefinition type)
{
if (!type.IsEnum)
throw new ArgumentException("Type must be an enum", "type");
var fields = type.Fields;
for (int i = 0; i < fields.Count; i++)
{
var field = fields[i];
if (!field.IsStatic)
return field.FieldType;
}
throw new NotSupportedException();
}
public static bool IsAnonymousType(this TypeReference type)
{
if (type == null)
return false;
if (string.IsNullOrEmpty(type.Namespace) && type.HasGeneratedName() && (type.Name.Contains("AnonType") || type.Name.Contains("AnonymousType"))) {
TypeDefinition td = type.Resolve();
return td != null && td.IsCompilerGenerated();
}
return false;
}
public static bool HasGeneratedName(this MemberReference member)
{
return member.Name.StartsWith("<", StringComparison.Ordinal);
}
public static bool ContainsAnonymousType(this TypeReference type)
{
GenericInstanceType git = type as GenericInstanceType;
if (git != null) {
if (IsAnonymousType(git))
return true;
for (int i = 0; i < git.GenericArguments.Count; i++) {
if (git.GenericArguments[i].ContainsAnonymousType())
return true;
}
return false;
}
TypeSpecification typeSpec = type as TypeSpecification;
if (typeSpec != null)
return typeSpec.ElementType.ContainsAnonymousType();
else
return false;
}
public static string GetDefaultMemberName(this TypeDefinition type)
{
CustomAttribute attr;
return type.GetDefaultMemberName(out attr);
}
public static string GetDefaultMemberName(this TypeDefinition type, out CustomAttribute defaultMemberAttribute)
{
if (type.HasCustomAttributes)
foreach (CustomAttribute ca in type.CustomAttributes)
if (ca.Constructor.DeclaringType.Name == "DefaultMemberAttribute" && ca.Constructor.DeclaringType.Namespace == "System.Reflection"
&& ca.Constructor.FullName == @"System.Void System.Reflection.DefaultMemberAttribute::.ctor(System.String)") {
defaultMemberAttribute = ca;
return ca.ConstructorArguments[0].Value as string;
}
defaultMemberAttribute = null;
return null;
}
public static bool IsIndexer(this PropertyDefinition property)
{
CustomAttribute attr;
return property.IsIndexer(out attr);
}
public static bool IsIndexer(this PropertyDefinition property, out CustomAttribute defaultMemberAttribute)
{
defaultMemberAttribute = null;
if (property.HasParameters) {
var accessor = property.GetMethod ?? property.SetMethod;
PropertyDefinition basePropDef = property;
if (accessor.HasOverrides) {
// if the property is explicitly implementing an interface, look up the property in the interface:
MethodDefinition baseAccessor = accessor.Overrides.First().Resolve();
if (baseAccessor != null) {
foreach (PropertyDefinition baseProp in baseAccessor.DeclaringType.Properties) {
if (baseProp.GetMethod == baseAccessor || baseProp.SetMethod == baseAccessor) {
basePropDef = baseProp;
break;
}
}
} else
return false;
}
CustomAttribute attr;
var defaultMemberName = basePropDef.DeclaringType.GetDefaultMemberName(out attr);
if (defaultMemberName == basePropDef.Name) {
defaultMemberAttribute = attr;
return true;
}
}
return false;
}
public static bool IsDelegate(this TypeDefinition type)
{
if (type.BaseType != null && type.BaseType.Namespace == "System") {
if (type.BaseType.Name == "MulticastDelegate")
return true;
if (type.BaseType.Name == "Delegate" && type.Name != "MulticastDelegate")
return true;
}
return false;
}
}
}

Просмотреть файл

@ -1,57 +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.ILAst;
using ICSharpCode.NRefactory;
using Mono.Cecil;
namespace ICSharpCode.Decompiler
{
/// <summary> Maps method's source code to IL </summary>
public class MethodDebugSymbols
{
public MethodDefinition CecilMethod { get; set; }
public List<ILVariable> LocalVariables { get; set; }
public List<SequencePoint> SequencePoints { get; set; }
public TextLocation StartLocation { get; set; }
public TextLocation EndLocation { get; set; }
public MethodDebugSymbols(MethodDefinition methodDef)
{
CecilMethod = methodDef;
LocalVariables = new List<ILVariable>();
SequencePoints = new List<SequencePoint>();
}
}
public class SequencePoint
{
public ILRange[] ILRanges { get; set; }
public TextLocation StartLocation { get; set; }
public TextLocation EndLocation { get; set; }
public int ILOffset { get { return ILRanges[0].From; } }
public override string ToString()
{
return string.Join(" ", ILRanges) + " " + StartLocation + "-" + EndLocation;
}
}
}

Просмотреть файл

@ -1,42 +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.Runtime.Serialization;
using Mono.Cecil;
namespace ICSharpCode.Decompiler
{
/// <summary>
/// Description of DecompilerException.
/// </summary>
public class DecompilerException : Exception, ISerializable
{
public MethodDefinition DecompiledMethod { get; set; }
public DecompilerException(MethodDefinition decompiledMethod, Exception innerException)
: base("Error decompiling " + decompiledMethod.FullName + Environment.NewLine, innerException)
{
}
// This constructor is needed for serialization.
protected DecompilerException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}

Просмотреть файл

@ -1,356 +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.ComponentModel;
using ICSharpCode.NRefactory.CSharp;
namespace ICSharpCode.Decompiler
{
/// <summary>
/// Settings for the decompiler.
/// </summary>
public class DecompilerSettings : INotifyPropertyChanged
{
bool anonymousMethods = true;
/// <summary>
/// Decompile anonymous methods/lambdas.
/// </summary>
public bool AnonymousMethods {
get { return anonymousMethods; }
set {
if (anonymousMethods != value) {
anonymousMethods = value;
OnPropertyChanged("AnonymousMethods");
}
}
}
bool expressionTrees = true;
/// <summary>
/// Decompile expression trees.
/// </summary>
public bool ExpressionTrees {
get { return expressionTrees; }
set {
if (expressionTrees != value) {
expressionTrees = value;
OnPropertyChanged("ExpressionTrees");
}
}
}
bool yieldReturn = true;
/// <summary>
/// Decompile enumerators.
/// </summary>
public bool YieldReturn {
get { return yieldReturn; }
set {
if (yieldReturn != value) {
yieldReturn = value;
OnPropertyChanged("YieldReturn");
}
}
}
bool asyncAwait = true;
/// <summary>
/// Decompile async methods.
/// </summary>
public bool AsyncAwait {
get { return asyncAwait; }
set {
if (asyncAwait != value) {
asyncAwait = value;
OnPropertyChanged("AsyncAwait");
}
}
}
bool automaticProperties = true;
/// <summary>
/// Decompile automatic properties
/// </summary>
public bool AutomaticProperties {
get { return automaticProperties; }
set {
if (automaticProperties != value) {
automaticProperties = value;
OnPropertyChanged("AutomaticProperties");
}
}
}
bool automaticEvents = true;
/// <summary>
/// Decompile automatic events
/// </summary>
public bool AutomaticEvents {
get { return automaticEvents; }
set {
if (automaticEvents != value) {
automaticEvents = value;
OnPropertyChanged("AutomaticEvents");
}
}
}
bool usingStatement = true;
/// <summary>
/// Decompile using statements.
/// </summary>
public bool UsingStatement {
get { return usingStatement; }
set {
if (usingStatement != value) {
usingStatement = value;
OnPropertyChanged("UsingStatement");
}
}
}
bool forEachStatement = true;
/// <summary>
/// Decompile foreach statements.
/// </summary>
public bool ForEachStatement {
get { return forEachStatement; }
set {
if (forEachStatement != value) {
forEachStatement = value;
OnPropertyChanged("ForEachStatement");
}
}
}
bool lockStatement = true;
/// <summary>
/// Decompile lock statements.
/// </summary>
public bool LockStatement {
get { return lockStatement; }
set {
if (lockStatement != value) {
lockStatement = value;
OnPropertyChanged("LockStatement");
}
}
}
bool switchStatementOnString = true;
public bool SwitchStatementOnString {
get { return switchStatementOnString; }
set {
if (switchStatementOnString != value) {
switchStatementOnString = value;
OnPropertyChanged("SwitchStatementOnString");
}
}
}
bool usingDeclarations = true;
public bool UsingDeclarations {
get { return usingDeclarations; }
set {
if (usingDeclarations != value) {
usingDeclarations = value;
OnPropertyChanged("UsingDeclarations");
}
}
}
bool queryExpressions = true;
public bool QueryExpressions {
get { return queryExpressions; }
set {
if (queryExpressions != value) {
queryExpressions = value;
OnPropertyChanged("QueryExpressions");
}
}
}
bool fullyQualifyAmbiguousTypeNames = true;
public bool FullyQualifyAmbiguousTypeNames {
get { return fullyQualifyAmbiguousTypeNames; }
set {
if (fullyQualifyAmbiguousTypeNames != value) {
fullyQualifyAmbiguousTypeNames = value;
OnPropertyChanged("FullyQualifyAmbiguousTypeNames");
}
}
}
bool useDebugSymbols = true;
/// <summary>
/// Gets/Sets whether to use variable names from debug symbols, if available.
/// </summary>
public bool UseDebugSymbols {
get { return useDebugSymbols; }
set {
if (useDebugSymbols != value) {
useDebugSymbols = value;
OnPropertyChanged("UseDebugSymbols");
}
}
}
bool objectCollectionInitializers = true;
/// <summary>
/// Gets/Sets whether to use C# 3.0 object/collection initializers
/// </summary>
public bool ObjectOrCollectionInitializers {
get { return objectCollectionInitializers; }
set {
if (objectCollectionInitializers != value) {
objectCollectionInitializers = value;
OnPropertyChanged("ObjectCollectionInitializers");
}
}
}
bool showXmlDocumentation = true;
/// <summary>
/// Gets/Sets whether to include XML documentation comments in the decompiled code
/// </summary>
public bool ShowXmlDocumentation {
get { return showXmlDocumentation; }
set {
if (showXmlDocumentation != value) {
showXmlDocumentation = value;
OnPropertyChanged("ShowXmlDocumentation");
}
}
}
bool foldBraces = false;
public bool FoldBraces {
get { return foldBraces; }
set {
if (foldBraces != value) {
foldBraces = value;
OnPropertyChanged("FoldBraces");
}
}
}
#region Options to aid VB decompilation
bool introduceIncrementAndDecrement = true;
/// <summary>
/// Gets/Sets whether to use increment and decrement operators
/// </summary>
public bool IntroduceIncrementAndDecrement {
get { return introduceIncrementAndDecrement; }
set {
if (introduceIncrementAndDecrement != value) {
introduceIncrementAndDecrement = value;
OnPropertyChanged("IntroduceIncrementAndDecrement");
}
}
}
bool makeAssignmentExpressions = true;
/// <summary>
/// Gets/Sets whether to use assignment expressions such as in while ((count = Do()) != 0) ;
/// </summary>
public bool MakeAssignmentExpressions {
get { return makeAssignmentExpressions; }
set {
if (makeAssignmentExpressions != value) {
makeAssignmentExpressions = value;
OnPropertyChanged("MakeAssignmentExpressions");
}
}
}
bool alwaysGenerateExceptionVariableForCatchBlocks = false;
/// <summary>
/// Gets/Sets whether to always generate exception variables in catch blocks
/// </summary>
public bool AlwaysGenerateExceptionVariableForCatchBlocks {
get { return alwaysGenerateExceptionVariableForCatchBlocks; }
set {
if (alwaysGenerateExceptionVariableForCatchBlocks != value) {
alwaysGenerateExceptionVariableForCatchBlocks = value;
OnPropertyChanged("AlwaysGenerateExceptionVariableForCatchBlocks");
}
}
}
#endregion
CSharpFormattingOptions csharpFormattingOptions;
public CSharpFormattingOptions CSharpFormattingOptions {
get {
if (csharpFormattingOptions == null) {
csharpFormattingOptions = FormattingOptionsFactory.CreateAllman();
csharpFormattingOptions.IndentSwitchBody = false;
csharpFormattingOptions.ArrayInitializerWrapping = Wrapping.WrapAlways;
}
return csharpFormattingOptions;
}
set {
if (value == null)
throw new ArgumentNullException();
if (csharpFormattingOptions != value) {
csharpFormattingOptions = value;
OnPropertyChanged("CSharpFormattingOptions");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public DecompilerSettings Clone()
{
DecompilerSettings settings = (DecompilerSettings)MemberwiseClone();
if (csharpFormattingOptions != null)
settings.csharpFormattingOptions = csharpFormattingOptions.Clone();
settings.PropertyChanged = null;
return settings;
}
}
}

Просмотреть файл

@ -1,447 +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 Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.Disassembler
{
public enum ILNameSyntax
{
/// <summary>
/// class/valuetype + TypeName (built-in types use keyword syntax)
/// </summary>
Signature,
/// <summary>
/// Like signature, but always refers to type parameters using their position
/// </summary>
SignatureNoNamedTypeParameters,
/// <summary>
/// [assembly]Full.Type.Name (even for built-in types)
/// </summary>
TypeName,
/// <summary>
/// Name (but built-in types use keyword syntax)
/// </summary>
ShortTypeName
}
public static class DisassemblerHelpers
{
public static void WriteOffsetReference(ITextOutput writer, Instruction instruction)
{
writer.WriteReference(CecilExtensions.OffsetToString(instruction.Offset), instruction);
}
public static void WriteTo(this ExceptionHandler exceptionHandler, ITextOutput writer)
{
writer.Write("Try ");
WriteOffsetReference(writer, exceptionHandler.TryStart);
writer.Write('-');
WriteOffsetReference(writer, exceptionHandler.TryEnd);
writer.Write(' ');
writer.Write(exceptionHandler.HandlerType.ToString());
if (exceptionHandler.FilterStart != null) {
writer.Write(' ');
WriteOffsetReference(writer, exceptionHandler.FilterStart);
writer.Write(" handler ");
}
if (exceptionHandler.CatchType != null) {
writer.Write(' ');
exceptionHandler.CatchType.WriteTo(writer);
}
writer.Write(' ');
WriteOffsetReference(writer, exceptionHandler.HandlerStart);
writer.Write('-');
WriteOffsetReference(writer, exceptionHandler.HandlerEnd);
}
public static void WriteTo(this Instruction instruction, ITextOutput writer)
{
writer.WriteDefinition(CecilExtensions.OffsetToString(instruction.Offset), instruction);
writer.Write(": ");
writer.WriteReference(instruction.OpCode.Name, instruction.OpCode);
if (instruction.Operand != null) {
writer.Write(' ');
if (instruction.OpCode == OpCodes.Ldtoken) {
if (instruction.Operand is MethodReference)
writer.Write("method ");
else if (instruction.Operand is FieldReference)
writer.Write("field ");
}
WriteOperand(writer, instruction.Operand);
}
}
static void WriteLabelList(ITextOutput writer, Instruction[] instructions)
{
writer.Write("(");
for(int i = 0; i < instructions.Length; i++) {
if(i != 0) writer.Write(", ");
WriteOffsetReference(writer, instructions[i]);
}
writer.Write(")");
}
static string ToInvariantCultureString(object value)
{
IConvertible convertible = value as IConvertible;
return(null != convertible)
? convertible.ToString(System.Globalization.CultureInfo.InvariantCulture)
: value.ToString();
}
public static void WriteTo(this MethodReference method, ITextOutput writer)
{
if (method.ExplicitThis) {
writer.Write("instance explicit ");
}
else if (method.HasThis) {
writer.Write("instance ");
}
method.ReturnType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters);
writer.Write(' ');
if (method.DeclaringType != null) {
method.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write("::");
}
MethodDefinition md = method as MethodDefinition;
if (md != null && md.IsCompilerControlled) {
writer.WriteReference(Escape(method.Name + "$PST" + method.MetadataToken.ToInt32().ToString("X8")), method);
} else {
writer.WriteReference(Escape(method.Name), method);
}
GenericInstanceMethod gim = method as GenericInstanceMethod;
if (gim != null) {
writer.Write('<');
for (int i = 0; i < gim.GenericArguments.Count; i++) {
if (i > 0)
writer.Write(", ");
gim.GenericArguments[i].WriteTo(writer);
}
writer.Write('>');
}
writer.Write("(");
var parameters = method.Parameters;
for(int i = 0; i < parameters.Count; ++i) {
if (i > 0) writer.Write(", ");
parameters[i].ParameterType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters);
}
writer.Write(")");
}
static void WriteTo(this FieldReference field, ITextOutput writer)
{
field.FieldType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters);
writer.Write(' ');
field.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write("::");
writer.WriteReference(Escape(field.Name), field);
}
static bool IsValidIdentifierCharacter(char c)
{
return c == '_' || c == '$' || c == '@' || c == '?' || c == '`';
}
static bool IsValidIdentifier(string identifier)
{
if (string.IsNullOrEmpty(identifier))
return false;
if (!(char.IsLetter(identifier[0]) || IsValidIdentifierCharacter(identifier[0]))) {
// As a special case, .ctor and .cctor are valid despite starting with a dot
return identifier == ".ctor" || identifier == ".cctor";
}
for (int i = 1; i < identifier.Length; i++) {
if (!(char.IsLetterOrDigit(identifier[i]) || IsValidIdentifierCharacter(identifier[i]) || identifier[i] == '.'))
return false;
}
return true;
}
static readonly HashSet<string> ilKeywords = BuildKeywordList(
"abstract", "algorithm", "alignment", "ansi", "any", "arglist",
"array", "as", "assembly", "assert", "at", "auto", "autochar", "beforefieldinit",
"blob", "blob_object", "bool", "brnull", "brnull.s", "brzero", "brzero.s", "bstr",
"bytearray", "byvalstr", "callmostderived", "carray", "catch", "cdecl", "cf",
"char", "cil", "class", "clsid", "const", "currency", "custom", "date", "decimal",
"default", "demand", "deny", "endmac", "enum", "error", "explicit", "extends", "extern",
"false", "famandassem", "family", "famorassem", "fastcall", "fault", "field", "filetime",
"filter", "final", "finally", "fixed", "float", "float32", "float64", "forwardref",
"fromunmanaged", "handler", "hidebysig", "hresult", "idispatch", "il", "illegal",
"implements", "implicitcom", "implicitres", "import", "in", "inheritcheck", "init",
"initonly", "instance", "int", "int16", "int32", "int64", "int8", "interface", "internalcall",
"iunknown", "lasterr", "lcid", "linkcheck", "literal", "localloc", "lpstr", "lpstruct", "lptstr",
"lpvoid", "lpwstr", "managed", "marshal", "method", "modopt", "modreq", "native", "nested",
"newslot", "noappdomain", "noinlining", "nomachine", "nomangle", "nometadata", "noncasdemand",
"noncasinheritance", "noncaslinkdemand", "noprocess", "not", "not_in_gc_heap", "notremotable",
"notserialized", "null", "nullref", "object", "objectref", "opt", "optil", "out",
"permitonly", "pinned", "pinvokeimpl", "prefix1", "prefix2", "prefix3", "prefix4", "prefix5", "prefix6",
"prefix7", "prefixref", "prejitdeny", "prejitgrant", "preservesig", "private", "privatescope", "protected",
"public", "record", "refany", "reqmin", "reqopt", "reqrefuse", "reqsecobj", "request", "retval",
"rtspecialname", "runtime", "safearray", "sealed", "sequential", "serializable", "special", "specialname",
"static", "stdcall", "storage", "stored_object", "stream", "streamed_object", "string", "struct",
"synchronized", "syschar", "sysstring", "tbstr", "thiscall", "tls", "to", "true", "typedref",
"unicode", "unmanaged", "unmanagedexp", "unsigned", "unused", "userdefined", "value", "valuetype",
"vararg", "variant", "vector", "virtual", "void", "wchar", "winapi", "with", "wrapper",
// These are not listed as keywords in spec, but ILAsm treats them as such
"property", "type", "flags", "callconv", "strict"
);
static HashSet<string> BuildKeywordList(params string[] keywords)
{
HashSet<string> s = new HashSet<string>(keywords);
foreach (var field in typeof(OpCodes).GetFields()) {
s.Add(((OpCode)field.GetValue(null)).Name);
}
return s;
}
public static string Escape(string identifier)
{
if (IsValidIdentifier(identifier) && !ilKeywords.Contains(identifier)) {
return identifier;
} else {
// The ECMA specification says that ' inside SQString should be ecaped using an octal escape sequence,
// but we follow Microsoft's ILDasm and use \'.
return "'" + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(identifier).Replace("'", "\\'") + "'";
}
}
public static void WriteTo(this TypeReference type, ITextOutput writer, ILNameSyntax syntax = ILNameSyntax.Signature)
{
ILNameSyntax syntaxForElementTypes = syntax == ILNameSyntax.SignatureNoNamedTypeParameters ? syntax : ILNameSyntax.Signature;
if (type is PinnedType) {
((PinnedType)type).ElementType.WriteTo(writer, syntaxForElementTypes);
writer.Write(" pinned");
} else if (type is ArrayType) {
ArrayType at = (ArrayType)type;
at.ElementType.WriteTo(writer, syntaxForElementTypes);
writer.Write('[');
writer.Write(string.Join(", ", at.Dimensions));
writer.Write(']');
} else if (type is GenericParameter) {
writer.Write('!');
if (((GenericParameter)type).Owner.GenericParameterType == GenericParameterType.Method)
writer.Write('!');
if (string.IsNullOrEmpty(type.Name) || type.Name[0] == '!' || syntax == ILNameSyntax.SignatureNoNamedTypeParameters)
writer.Write(((GenericParameter)type).Position.ToString());
else
writer.Write(Escape(type.Name));
} else if (type is ByReferenceType) {
((ByReferenceType)type).ElementType.WriteTo(writer, syntaxForElementTypes);
writer.Write('&');
} else if (type is PointerType) {
((PointerType)type).ElementType.WriteTo(writer, syntaxForElementTypes);
writer.Write('*');
} else if (type is GenericInstanceType) {
type.GetElementType().WriteTo(writer, syntaxForElementTypes);
writer.Write('<');
var arguments = ((GenericInstanceType)type).GenericArguments;
for (int i = 0; i < arguments.Count; i++) {
if (i > 0)
writer.Write(", ");
arguments[i].WriteTo(writer, syntaxForElementTypes);
}
writer.Write('>');
} else if (type is OptionalModifierType) {
((OptionalModifierType)type).ElementType.WriteTo(writer, syntax);
writer.Write(" modopt(");
((OptionalModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write(") ");
} else if (type is RequiredModifierType) {
((RequiredModifierType)type).ElementType.WriteTo(writer, syntax);
writer.Write(" modreq(");
((RequiredModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write(") ");
} else {
string name = PrimitiveTypeName(type.FullName);
if (syntax == ILNameSyntax.ShortTypeName) {
if (name != null)
writer.Write(name);
else
writer.WriteReference(Escape(type.Name), type);
} else if ((syntax == ILNameSyntax.Signature || syntax == ILNameSyntax.SignatureNoNamedTypeParameters) && name != null) {
writer.Write(name);
} else {
if (syntax == ILNameSyntax.Signature || syntax == ILNameSyntax.SignatureNoNamedTypeParameters)
writer.Write(type.IsValueType ? "valuetype " : "class ");
if (type.DeclaringType != null) {
type.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write('/');
writer.WriteReference(Escape(type.Name), type);
} else {
if (!type.IsDefinition && type.Scope != null && !(type is TypeSpecification))
writer.Write("[{0}]", Escape(type.Scope.Name));
writer.WriteReference(Escape(type.FullName), type);
}
}
}
}
public static void WriteOperand(ITextOutput writer, object operand)
{
if (operand == null)
throw new ArgumentNullException("operand");
Instruction targetInstruction = operand as Instruction;
if (targetInstruction != null) {
WriteOffsetReference(writer, targetInstruction);
return;
}
Instruction[] targetInstructions = operand as Instruction[];
if (targetInstructions != null) {
WriteLabelList(writer, targetInstructions);
return;
}
VariableReference variableRef = operand as VariableReference;
if (variableRef != null) {
if (string.IsNullOrEmpty(variableRef.Name))
writer.WriteReference(variableRef.Index.ToString(), variableRef);
else
writer.WriteReference(Escape(variableRef.Name), variableRef);
return;
}
ParameterReference paramRef = operand as ParameterReference;
if (paramRef != null) {
if (string.IsNullOrEmpty(paramRef.Name))
writer.WriteReference(paramRef.Index.ToString(), paramRef);
else
writer.WriteReference(Escape(paramRef.Name), paramRef);
return;
}
MethodReference methodRef = operand as MethodReference;
if (methodRef != null) {
methodRef.WriteTo(writer);
return;
}
TypeReference typeRef = operand as TypeReference;
if (typeRef != null) {
typeRef.WriteTo(writer, ILNameSyntax.TypeName);
return;
}
FieldReference fieldRef = operand as FieldReference;
if (fieldRef != null) {
fieldRef.WriteTo(writer);
return;
}
string s = operand as string;
if (s != null) {
writer.Write("\"" + NRefactory.CSharp.TextWriterTokenWriter.ConvertString(s) + "\"");
} else if (operand is char) {
writer.Write(((int)(char)operand).ToString());
} else if (operand is float) {
float val = (float)operand;
if (val == 0) {
if (1 / val == float.NegativeInfinity) {
// negative zero is a special case
writer.Write('-');
}
writer.Write("0.0");
} else if (float.IsInfinity(val) || float.IsNaN(val)) {
byte[] data = BitConverter.GetBytes(val);
writer.Write('(');
for (int i = 0; i < data.Length; i++) {
if (i > 0)
writer.Write(' ');
writer.Write(data[i].ToString("X2"));
}
writer.Write(')');
} else {
writer.Write(val.ToString("R", System.Globalization.CultureInfo.InvariantCulture));
}
} else if (operand is double) {
double val = (double)operand;
if (val == 0) {
if (1 / val == double.NegativeInfinity) {
// negative zero is a special case
writer.Write('-');
}
writer.Write("0.0");
} else if (double.IsInfinity(val) || double.IsNaN(val)) {
byte[] data = BitConverter.GetBytes(val);
writer.Write('(');
for (int i = 0; i < data.Length; i++) {
if (i > 0)
writer.Write(' ');
writer.Write(data[i].ToString("X2"));
}
writer.Write(')');
} else {
writer.Write(val.ToString("R", System.Globalization.CultureInfo.InvariantCulture));
}
} else if (operand is bool) {
writer.Write((bool)operand ? "true" : "false");
} else {
s = ToInvariantCultureString(operand);
writer.Write(s);
}
}
public static string PrimitiveTypeName(string fullName)
{
switch (fullName) {
case "System.SByte":
return "int8";
case "System.Int16":
return "int16";
case "System.Int32":
return "int32";
case "System.Int64":
return "int64";
case "System.Byte":
return "uint8";
case "System.UInt16":
return "uint16";
case "System.UInt32":
return "uint32";
case "System.UInt64":
return "uint64";
case "System.Single":
return "float32";
case "System.Double":
return "float64";
case "System.Void":
return "void";
case "System.Boolean":
return "bool";
case "System.String":
return "string";
case "System.Char":
return "char";
case "System.Object":
return "object";
case "System.IntPtr":
return "native int";
default:
return null;
}
}
}
}

Просмотреть файл

@ -1,228 +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.FlowAnalysis;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.Disassembler
{
/// <summary>
/// Specifies the type of an IL structure.
/// </summary>
public enum ILStructureType
{
/// <summary>
/// The root block of the method
/// </summary>
Root,
/// <summary>
/// A nested control structure representing a loop.
/// </summary>
Loop,
/// <summary>
/// A nested control structure representing a try block.
/// </summary>
Try,
/// <summary>
/// A nested control structure representing a catch, finally, or fault block.
/// </summary>
Handler,
/// <summary>
/// A nested control structure representing an exception filter block.
/// </summary>
Filter
}
/// <summary>
/// An IL structure.
/// </summary>
public class ILStructure
{
public readonly ILStructureType Type;
/// <summary>
/// Start position of the structure.
/// </summary>
public readonly int StartOffset;
/// <summary>
/// End position of the structure. (exclusive)
/// </summary>
public readonly int EndOffset;
/// <summary>
/// The exception handler associated with the Try, Filter or Handler block.
/// </summary>
public readonly ExceptionHandler ExceptionHandler;
/// <summary>
/// The loop's entry point.
/// </summary>
public readonly Instruction LoopEntryPoint;
/// <summary>
/// The list of child structures.
/// </summary>
public readonly List<ILStructure> Children = new List<ILStructure>();
public ILStructure(MethodBody body)
: this(ILStructureType.Root, 0, body.CodeSize)
{
// Build the tree of exception structures:
for (int i = 0; i < body.ExceptionHandlers.Count; i++) {
ExceptionHandler eh = body.ExceptionHandlers[i];
if (!body.ExceptionHandlers.Take(i).Any(oldEh => oldEh.TryStart == eh.TryStart && oldEh.TryEnd == eh.TryEnd))
AddNestedStructure(new ILStructure(ILStructureType.Try, eh.TryStart.Offset, eh.TryEnd.Offset, eh));
if (eh.HandlerType == ExceptionHandlerType.Filter)
AddNestedStructure(new ILStructure(ILStructureType.Filter, eh.FilterStart.Offset, eh.HandlerStart.Offset, eh));
AddNestedStructure(new ILStructure(ILStructureType.Handler, eh.HandlerStart.Offset, eh.HandlerEnd == null ? body.CodeSize : eh.HandlerEnd.Offset, eh));
}
// Very simple loop detection: look for backward branches
List<KeyValuePair<Instruction, Instruction>> allBranches = FindAllBranches(body);
// We go through the branches in reverse so that we find the biggest possible loop boundary first (think loops with "continue;")
for (int i = allBranches.Count - 1; i >= 0; i--) {
int loopEnd = allBranches[i].Key.GetEndOffset();
int loopStart = allBranches[i].Value.Offset;
if (loopStart < loopEnd) {
// We found a backward branch. This is a potential loop.
// Check that is has only one entry point:
Instruction entryPoint = null;
// entry point is first instruction in loop if prev inst isn't an unconditional branch
Instruction prev = allBranches[i].Value.Previous;
if (prev != null && !OpCodeInfo.IsUnconditionalBranch(prev.OpCode))
entryPoint = allBranches[i].Value;
bool multipleEntryPoints = false;
foreach (var pair in allBranches) {
if (pair.Key.Offset < loopStart || pair.Key.Offset >= loopEnd) {
if (loopStart <= pair.Value.Offset && pair.Value.Offset < loopEnd) {
// jump from outside the loop into the loop
if (entryPoint == null)
entryPoint = pair.Value;
else if (pair.Value != entryPoint)
multipleEntryPoints = true;
}
}
}
if (!multipleEntryPoints) {
AddNestedStructure(new ILStructure(ILStructureType.Loop, loopStart, loopEnd, entryPoint));
}
}
}
SortChildren();
}
public ILStructure(ILStructureType type, int startOffset, int endOffset, ExceptionHandler handler = null)
{
Debug.Assert(startOffset < endOffset);
Type = type;
StartOffset = startOffset;
EndOffset = endOffset;
ExceptionHandler = handler;
}
public ILStructure(ILStructureType type, int startOffset, int endOffset, Instruction loopEntryPoint)
{
Debug.Assert(startOffset < endOffset);
Type = type;
StartOffset = startOffset;
EndOffset = endOffset;
LoopEntryPoint = loopEntryPoint;
}
bool AddNestedStructure(ILStructure newStructure)
{
// special case: don't consider the loop-like structure of "continue;" statements to be nested loops
if (Type == ILStructureType.Loop && newStructure.Type == ILStructureType.Loop && newStructure.StartOffset == StartOffset)
return false;
// use <= for end-offset comparisons because both end and EndOffset are exclusive
Debug.Assert(StartOffset <= newStructure.StartOffset && newStructure.EndOffset <= EndOffset);
foreach (ILStructure child in Children) {
if (child.StartOffset <= newStructure.StartOffset && newStructure.EndOffset <= child.EndOffset) {
return child.AddNestedStructure(newStructure);
} else if (!(child.EndOffset <= newStructure.StartOffset || newStructure.EndOffset <= child.StartOffset)) {
// child and newStructure overlap
if (!(newStructure.StartOffset <= child.StartOffset && child.EndOffset <= newStructure.EndOffset)) {
// Invalid nesting, can't build a tree. -> Don't add the new structure.
return false;
}
}
}
// Move existing structures into the new structure:
for (int i = 0; i < Children.Count; i++) {
ILStructure child = Children[i];
if (newStructure.StartOffset <= child.StartOffset && child.EndOffset <= newStructure.EndOffset) {
Children.RemoveAt(i--);
newStructure.Children.Add(child);
}
}
// Add the structure here:
Children.Add(newStructure);
return true;
}
/// <summary>
/// Finds all branches. Returns list of source offset->target offset mapping.
/// Multiple entries for the same source offset are possible (switch statements).
/// The result is sorted by source offset.
/// </summary>
List<KeyValuePair<Instruction, Instruction>> FindAllBranches(MethodBody body)
{
var result = new List<KeyValuePair<Instruction, Instruction>>();
foreach (Instruction inst in body.Instructions) {
switch (inst.OpCode.OperandType) {
case OperandType.InlineBrTarget:
case OperandType.ShortInlineBrTarget:
result.Add(new KeyValuePair<Instruction, Instruction>(inst, (Instruction)inst.Operand));
break;
case OperandType.InlineSwitch:
foreach (Instruction target in (Instruction[])inst.Operand)
result.Add(new KeyValuePair<Instruction, Instruction>(inst, target));
break;
}
}
return result;
}
void SortChildren()
{
Children.Sort((a, b) => a.StartOffset.CompareTo(b.StartOffset));
foreach (ILStructure child in Children)
child.SortChildren();
}
/// <summary>
/// Gets the innermost structure containing the specified offset.
/// </summary>
public ILStructure GetInnermost(int offset)
{
Debug.Assert(StartOffset <= offset && offset < EndOffset);
foreach (ILStructure child in Children) {
if (child.StartOffset <= offset && offset < child.EndOffset)
return child.GetInnermost(offset);
}
return this;
}
}
}

Просмотреть файл

@ -1,239 +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 System.Threading;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.FlowAnalysis;
using ICSharpCode.Decompiler.ILAst;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.Disassembler
{
/// <summary>
/// Disassembles a method body.
/// </summary>
public sealed class MethodBodyDisassembler
{
readonly ITextOutput output;
readonly bool detectControlStructure;
readonly CancellationToken cancellationToken;
public MethodBodyDisassembler(ITextOutput output, bool detectControlStructure, CancellationToken cancellationToken)
{
if (output == null)
throw new ArgumentNullException("output");
this.output = output;
this.detectControlStructure = detectControlStructure;
this.cancellationToken = cancellationToken;
}
public void Disassemble(MethodBody body, MethodDebugSymbols debugSymbols)
{
// start writing IL code
MethodDefinition method = body.Method;
output.WriteLine("// Method begins at RVA 0x{0:x4}", method.RVA);
output.WriteLine("// Code size {0} (0x{0:x})", body.CodeSize);
output.WriteLine(".maxstack {0}", body.MaxStackSize);
if (method.DeclaringType.Module.Assembly != null && method.DeclaringType.Module.Assembly.EntryPoint == method)
output.WriteLine (".entrypoint");
if (method.Body.HasVariables) {
output.Write(".locals ");
if (method.Body.InitLocals)
output.Write("init ");
output.WriteLine("(");
output.Indent();
foreach (var v in method.Body.Variables) {
output.WriteDefinition("[" + v.Index + "] ", v);
v.VariableType.WriteTo(output);
if (!string.IsNullOrEmpty(v.Name)) {
output.Write(' ');
output.Write(DisassemblerHelpers.Escape(v.Name));
}
if (v.Index + 1 < method.Body.Variables.Count)
output.Write(',');
output.WriteLine();
}
output.Unindent();
output.WriteLine(")");
}
output.WriteLine();
if (detectControlStructure && body.Instructions.Count > 0) {
Instruction inst = body.Instructions[0];
HashSet<int> branchTargets = GetBranchTargets(body.Instructions);
WriteStructureBody(new ILStructure(body), branchTargets, ref inst, debugSymbols, method.Body.CodeSize);
} else {
foreach (var inst in method.Body.Instructions) {
var startLocation = output.Location;
inst.WriteTo(output);
if (debugSymbols != null) {
// add IL code mappings - used in debugger
debugSymbols.SequencePoints.Add(
new SequencePoint() {
StartLocation = output.Location,
EndLocation = output.Location,
ILRanges = new ILRange[] { new ILRange(inst.Offset, inst.Next == null ? method.Body.CodeSize : inst.Next.Offset) }
});
}
output.WriteLine();
}
if (method.Body.HasExceptionHandlers) {
output.WriteLine();
foreach (var eh in method.Body.ExceptionHandlers) {
eh.WriteTo(output);
output.WriteLine();
}
}
}
}
HashSet<int> GetBranchTargets(IEnumerable<Instruction> instructions)
{
HashSet<int> branchTargets = new HashSet<int>();
foreach (var inst in instructions) {
Instruction target = inst.Operand as Instruction;
if (target != null)
branchTargets.Add(target.Offset);
Instruction[] targets = inst.Operand as Instruction[];
if (targets != null)
foreach (Instruction t in targets)
branchTargets.Add(t.Offset);
}
return branchTargets;
}
void WriteStructureHeader(ILStructure s)
{
switch (s.Type) {
case ILStructureType.Loop:
output.Write("// loop start");
if (s.LoopEntryPoint != null) {
output.Write(" (head: ");
DisassemblerHelpers.WriteOffsetReference(output, s.LoopEntryPoint);
output.Write(')');
}
output.WriteLine();
break;
case ILStructureType.Try:
output.WriteLine(".try");
output.WriteLine("{");
break;
case ILStructureType.Handler:
switch (s.ExceptionHandler.HandlerType) {
case ExceptionHandlerType.Catch:
case ExceptionHandlerType.Filter:
output.Write("catch");
if (s.ExceptionHandler.CatchType != null) {
output.Write(' ');
s.ExceptionHandler.CatchType.WriteTo(output, ILNameSyntax.TypeName);
}
output.WriteLine();
break;
case ExceptionHandlerType.Finally:
output.WriteLine("finally");
break;
case ExceptionHandlerType.Fault:
output.WriteLine("fault");
break;
default:
throw new NotSupportedException();
}
output.WriteLine("{");
break;
case ILStructureType.Filter:
output.WriteLine("filter");
output.WriteLine("{");
break;
default:
throw new NotSupportedException();
}
output.Indent();
}
void WriteStructureBody(ILStructure s, HashSet<int> branchTargets, ref Instruction inst, MethodDebugSymbols debugSymbols, int codeSize)
{
bool isFirstInstructionInStructure = true;
bool prevInstructionWasBranch = false;
int childIndex = 0;
while (inst != null && inst.Offset < s.EndOffset) {
int offset = inst.Offset;
if (childIndex < s.Children.Count && s.Children[childIndex].StartOffset <= offset && offset < s.Children[childIndex].EndOffset) {
ILStructure child = s.Children[childIndex++];
WriteStructureHeader(child);
WriteStructureBody(child, branchTargets, ref inst, debugSymbols, codeSize);
WriteStructureFooter(child);
} else {
if (!isFirstInstructionInStructure && (prevInstructionWasBranch || branchTargets.Contains(offset))) {
output.WriteLine(); // put an empty line after branches, and in front of branch targets
}
var startLocation = output.Location;
inst.WriteTo(output);
// add IL code mappings - used in debugger
if (debugSymbols != null) {
debugSymbols.SequencePoints.Add(
new SequencePoint() {
StartLocation = startLocation,
EndLocation = output.Location,
ILRanges = new ILRange[] { new ILRange(inst.Offset, inst.Next == null ? codeSize : inst.Next.Offset) }
});
}
output.WriteLine();
prevInstructionWasBranch = inst.OpCode.FlowControl == FlowControl.Branch
|| inst.OpCode.FlowControl == FlowControl.Cond_Branch
|| inst.OpCode.FlowControl == FlowControl.Return
|| inst.OpCode.FlowControl == FlowControl.Throw;
inst = inst.Next;
}
isFirstInstructionInStructure = false;
}
}
void WriteStructureFooter(ILStructure s)
{
output.Unindent();
switch (s.Type) {
case ILStructureType.Loop:
output.WriteLine("// end loop");
break;
case ILStructureType.Try:
output.WriteLine("} // end .try");
break;
case ILStructureType.Handler:
output.WriteLine("} // end handler");
break;
case ILStructureType.Filter:
output.WriteLine("} // end filter");
break;
default:
throw new NotSupportedException();
}
}
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,78 +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;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Describes the type of a control flow egde.
/// </summary>
public enum JumpType
{
/// <summary>
/// A regular control flow edge.
/// </summary>
Normal,
/// <summary>
/// Jump to exception handler (an exception occurred)
/// </summary>
JumpToExceptionHandler,
/// <summary>
/// Jump from try block to leave target:
/// This is not a real jump, as the finally handler is executed first!
/// </summary>
LeaveTry,
/// <summary>
/// Jump at endfinally (to any of the potential leave targets).
/// For any leave-instruction, control flow enters the finally block - the edge to the leave target (LeaveTry) is not a real control flow edge.
/// EndFinally edges are inserted at the end of the finally block, jumping to any of the targets of the leave instruction.
/// This edge type is only used when copying of finally blocks is disabled (with copying, a normal deterministic edge is used at each copy of the endfinally node).
/// </summary>
EndFinally
}
/// <summary>
/// Represents an edge in the control flow graph, pointing from Source to Target.
/// </summary>
public sealed class ControlFlowEdge
{
public readonly ControlFlowNode Source;
public readonly ControlFlowNode Target;
public readonly JumpType Type;
public ControlFlowEdge(ControlFlowNode source, ControlFlowNode target, JumpType type)
{
Source = source;
Target = target;
Type = type;
}
public override string ToString()
{
switch (Type) {
case JumpType.Normal:
return "#" + Target.BlockIndex;
case JumpType.JumpToExceptionHandler:
return "e:#" + Target.BlockIndex;
default:
return Type.ToString() + ":#" + Target.BlockIndex;
}
}
}
}

Просмотреть файл

@ -1,191 +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.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using ICSharpCode.NRefactory.Utils;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Contains the control flow graph.
/// </summary>
/// <remarks>Use ControlFlowGraph builder to create instances of the ControlFlowGraph.</remarks>
public sealed class ControlFlowGraph
{
readonly ReadOnlyCollection<ControlFlowNode> nodes;
public ControlFlowNode EntryPoint {
get { return nodes[0]; }
}
public ControlFlowNode RegularExit {
get { return nodes[1]; }
}
public ControlFlowNode ExceptionalExit {
get { return nodes[2]; }
}
public ReadOnlyCollection<ControlFlowNode> Nodes {
get { return nodes; }
}
internal ControlFlowGraph(ControlFlowNode[] nodes)
{
this.nodes = new ReadOnlyCollection<ControlFlowNode>(nodes);
Debug.Assert(EntryPoint.NodeType == ControlFlowNodeType.EntryPoint);
Debug.Assert(RegularExit.NodeType == ControlFlowNodeType.RegularExit);
Debug.Assert(ExceptionalExit.NodeType == ControlFlowNodeType.ExceptionalExit);
}
public GraphVizGraph ExportGraph()
{
GraphVizGraph graph = new GraphVizGraph();
foreach (ControlFlowNode node in nodes) {
graph.AddNode(new GraphVizNode(node.BlockIndex) { label = node.ToString(), shape = "box" });
}
foreach (ControlFlowNode node in nodes) {
foreach (ControlFlowEdge edge in node.Outgoing) {
GraphVizEdge e = new GraphVizEdge(edge.Source.BlockIndex, edge.Target.BlockIndex);
switch (edge.Type) {
case JumpType.Normal:
break;
case JumpType.LeaveTry:
case JumpType.EndFinally:
e.color = "red";
break;
default:
e.color = "gray";
//e.constraint = false;
break;
}
graph.AddEdge(e);
}
if (node.ImmediateDominator != null) {
graph.AddEdge(new GraphVizEdge(node.ImmediateDominator.BlockIndex, node.BlockIndex) { color = "green", constraint = false });
}
}
return graph;
}
/// <summary>
/// Resets "Visited" to false for all nodes in this graph.
/// </summary>
public void ResetVisited()
{
foreach (ControlFlowNode node in nodes) {
node.Visited = false;
}
}
/// <summary>
/// Computes the dominator tree.
/// </summary>
public void ComputeDominance(CancellationToken cancellationToken = default(CancellationToken))
{
// A Simple, Fast Dominance Algorithm
// Keith D. Cooper, Timothy J. Harvey and Ken Kennedy
EntryPoint.ImmediateDominator = EntryPoint;
bool changed = true;
while (changed) {
changed = false;
ResetVisited();
cancellationToken.ThrowIfCancellationRequested();
// for all nodes b except the entry point
EntryPoint.TraversePreOrder(
b => b.Successors,
b => {
if (b != EntryPoint) {
ControlFlowNode newIdom = b.Predecessors.First(block => block.Visited && block != b);
// for all other predecessors p of b
foreach (ControlFlowNode p in b.Predecessors) {
if (p != b && p.ImmediateDominator != null) {
newIdom = FindCommonDominator(p, newIdom);
}
}
if (b.ImmediateDominator != newIdom) {
b.ImmediateDominator = newIdom;
changed = true;
}
}
});
}
EntryPoint.ImmediateDominator = null;
foreach (ControlFlowNode node in nodes) {
if (node.ImmediateDominator != null)
node.ImmediateDominator.DominatorTreeChildren.Add(node);
}
}
static ControlFlowNode FindCommonDominator(ControlFlowNode b1, ControlFlowNode b2)
{
// Here we could use the postorder numbers to get rid of the hashset, see "A Simple, Fast Dominance Algorithm"
HashSet<ControlFlowNode> path1 = new HashSet<ControlFlowNode>();
while (b1 != null && path1.Add(b1))
b1 = b1.ImmediateDominator;
while (b2 != null) {
if (path1.Contains(b2))
return b2;
else
b2 = b2.ImmediateDominator;
}
throw new Exception("No common dominator found!");
}
/// <summary>
/// Computes dominance frontiers.
/// This method requires that the dominator tree is already computed!
/// </summary>
public void ComputeDominanceFrontier()
{
ResetVisited();
EntryPoint.TraversePostOrder(
b => b.DominatorTreeChildren,
n => {
//logger.WriteLine("Calculating dominance frontier for " + n.Name);
n.DominanceFrontier = new HashSet<ControlFlowNode>();
// DF_local computation
foreach (ControlFlowNode succ in n.Successors) {
if (succ.ImmediateDominator != n) {
//logger.WriteLine(" local: " + succ.Name);
n.DominanceFrontier.Add(succ);
}
}
// DF_up computation
foreach (ControlFlowNode child in n.DominatorTreeChildren) {
foreach (ControlFlowNode p in child.DominanceFrontier) {
if (p.ImmediateDominator != n) {
//logger.WriteLine(" DF_up: " + p.Name + " (child=" + child.Name);
n.DominanceFrontier.Add(p);
}
}
}
});
}
}
}

Просмотреть файл

@ -1,439 +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.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Constructs the Control Flow Graph from a Cecil method body.
/// </summary>
public sealed class ControlFlowGraphBuilder
{
public static ControlFlowGraph Build(MethodBody methodBody)
{
return new ControlFlowGraphBuilder(methodBody).Build();
}
// This option controls how finally blocks are handled:
// false means that the endfinally instruction will jump to any of the leave targets (EndFinally edge type).
// true means that a copy of the whole finally block is created for each leave target. In this case, each endfinally node will be connected with the leave
// target using a normal edge.
bool copyFinallyBlocks = false;
MethodBody methodBody;
int[] offsets; // array index = instruction index; value = IL offset
bool[] hasIncomingJumps; // array index = instruction index
List<ControlFlowNode> nodes = new List<ControlFlowNode>();
ControlFlowNode entryPoint;
ControlFlowNode regularExit;
ControlFlowNode exceptionalExit;
ControlFlowGraphBuilder(MethodBody methodBody)
{
this.methodBody = methodBody;
offsets = methodBody.Instructions.Select(i => i.Offset).ToArray();
hasIncomingJumps = new bool[methodBody.Instructions.Count];
entryPoint = new ControlFlowNode(0, 0, ControlFlowNodeType.EntryPoint);
nodes.Add(entryPoint);
regularExit = new ControlFlowNode(1, -1, ControlFlowNodeType.RegularExit);
nodes.Add(regularExit);
exceptionalExit = new ControlFlowNode(2, -1, ControlFlowNodeType.ExceptionalExit);
nodes.Add(exceptionalExit);
Debug.Assert(nodes.Count == 3);
}
/// <summary>
/// Determines the index of the instruction (for use with the hasIncomingJumps array)
/// </summary>
int GetInstructionIndex(Instruction inst)
{
int index = Array.BinarySearch(offsets, inst.Offset);
Debug.Assert(index >= 0);
return index;
}
/// <summary>
/// Builds the ControlFlowGraph.
/// </summary>
public ControlFlowGraph Build()
{
CalculateHasIncomingJumps();
CreateNodes();
CreateRegularControlFlow();
CreateExceptionalControlFlow();
if (copyFinallyBlocks)
CopyFinallyBlocksIntoLeaveEdges();
else
TransformLeaveEdges();
return new ControlFlowGraph(nodes.ToArray());
}
#region Step 1: calculate which instructions are the targets of jump instructions.
void CalculateHasIncomingJumps()
{
foreach (Instruction inst in methodBody.Instructions) {
if (inst.OpCode.OperandType == OperandType.InlineBrTarget || inst.OpCode.OperandType == OperandType.ShortInlineBrTarget) {
hasIncomingJumps[GetInstructionIndex((Instruction)inst.Operand)] = true;
} else if (inst.OpCode.OperandType == OperandType.InlineSwitch) {
foreach (Instruction i in (Instruction[])inst.Operand)
hasIncomingJumps[GetInstructionIndex(i)] = true;
}
}
foreach (ExceptionHandler eh in methodBody.ExceptionHandlers) {
if (eh.FilterStart != null) {
hasIncomingJumps[GetInstructionIndex(eh.FilterStart)] = true;
}
hasIncomingJumps[GetInstructionIndex(eh.HandlerStart)] = true;
}
}
#endregion
#region Step 2: create nodes
void CreateNodes()
{
// Step 2a: find basic blocks and create nodes for them
for (int i = 0; i < methodBody.Instructions.Count; i++) {
Instruction blockStart = methodBody.Instructions[i];
ExceptionHandler blockStartEH = FindInnermostExceptionHandler(blockStart.Offset);
// try and see how big we can make that block:
for (; i + 1 < methodBody.Instructions.Count; i++) {
Instruction inst = methodBody.Instructions[i];
if (IsBranch(inst.OpCode) || CanThrowException(inst.OpCode))
break;
if (hasIncomingJumps[i + 1])
break;
if (inst.Next != null) {
// ensure that blocks never contain instructions from different try blocks
ExceptionHandler instEH = FindInnermostExceptionHandler(inst.Next.Offset);
if (instEH != blockStartEH)
break;
}
}
nodes.Add(new ControlFlowNode(nodes.Count, blockStart, methodBody.Instructions[i]));
}
// Step 2b: Create special nodes for the exception handling constructs
foreach (ExceptionHandler handler in methodBody.ExceptionHandlers) {
if (handler.HandlerType == ExceptionHandlerType.Filter)
throw new NotSupportedException();
ControlFlowNode endFinallyOrFaultNode = null;
if (handler.HandlerType == ExceptionHandlerType.Finally || handler.HandlerType == ExceptionHandlerType.Fault) {
endFinallyOrFaultNode = new ControlFlowNode(nodes.Count, handler.HandlerEnd.Offset, ControlFlowNodeType.EndFinallyOrFault);
nodes.Add(endFinallyOrFaultNode);
}
nodes.Add(new ControlFlowNode(nodes.Count, handler, endFinallyOrFaultNode));
}
}
#endregion
#region Step 3: create edges for the normal flow of control (assuming no exceptions thrown)
void CreateRegularControlFlow()
{
CreateEdge(entryPoint, methodBody.Instructions[0], JumpType.Normal);
foreach (ControlFlowNode node in nodes) {
if (node.End != null) {
// create normal edges from one instruction to the next
if (!OpCodeInfo.IsUnconditionalBranch(node.End.OpCode))
CreateEdge(node, node.End.Next, JumpType.Normal);
// create edges for branch instructions
if (node.End.OpCode.OperandType == OperandType.InlineBrTarget || node.End.OpCode.OperandType == OperandType.ShortInlineBrTarget) {
if (node.End.OpCode == OpCodes.Leave || node.End.OpCode == OpCodes.Leave_S) {
var handlerBlock = FindInnermostHandlerBlock(node.End.Offset);
if (handlerBlock.NodeType == ControlFlowNodeType.FinallyOrFaultHandler)
CreateEdge(node, (Instruction)node.End.Operand, JumpType.LeaveTry);
else
CreateEdge(node, (Instruction)node.End.Operand, JumpType.Normal);
} else {
CreateEdge(node, (Instruction)node.End.Operand, JumpType.Normal);
}
} else if (node.End.OpCode.OperandType == OperandType.InlineSwitch) {
foreach (Instruction i in (Instruction[])node.End.Operand)
CreateEdge(node, i, JumpType.Normal);
}
// create edges for return instructions
if (node.End.OpCode.FlowControl == FlowControl.Return) {
switch (node.End.OpCode.Code) {
case Code.Ret:
CreateEdge(node, regularExit, JumpType.Normal);
break;
case Code.Endfinally:
ControlFlowNode handlerBlock = FindInnermostHandlerBlock(node.End.Offset);
if (handlerBlock.EndFinallyOrFaultNode == null)
throw new InvalidProgramException("Found endfinally in block " + handlerBlock);
CreateEdge(node, handlerBlock.EndFinallyOrFaultNode, JumpType.Normal);
break;
default:
throw new NotSupportedException(node.End.OpCode.ToString());
}
}
}
}
}
#endregion
#region Step 4: create edges for the exceptional control flow (from instructions that might throw, to the innermost containing exception handler)
void CreateExceptionalControlFlow()
{
foreach (ControlFlowNode node in nodes) {
if (node.End != null && CanThrowException(node.End.OpCode)) {
CreateEdge(node, FindInnermostExceptionHandlerNode(node.End.Offset), JumpType.JumpToExceptionHandler);
}
if (node.ExceptionHandler != null) {
if (node.EndFinallyOrFaultNode != null) {
// For Fault and Finally blocks, create edge from "EndFinally" to next exception handler.
// This represents the exception bubbling up after finally block was executed.
CreateEdge(node.EndFinallyOrFaultNode, FindParentExceptionHandlerNode(node), JumpType.JumpToExceptionHandler);
} else {
// For Catch blocks, create edge from "CatchHandler" block (at beginning) to next exception handler.
// This represents the exception bubbling up because it did not match the type of the catch block.
CreateEdge(node, FindParentExceptionHandlerNode(node), JumpType.JumpToExceptionHandler);
}
CreateEdge(node, node.ExceptionHandler.HandlerStart, JumpType.Normal);
}
}
}
ExceptionHandler FindInnermostExceptionHandler(int instructionOffsetInTryBlock)
{
foreach (ExceptionHandler h in methodBody.ExceptionHandlers) {
if (h.TryStart.Offset <= instructionOffsetInTryBlock && instructionOffsetInTryBlock < h.TryEnd.Offset) {
return h;
}
}
return null;
}
ControlFlowNode FindInnermostExceptionHandlerNode(int instructionOffsetInTryBlock)
{
ExceptionHandler h = FindInnermostExceptionHandler(instructionOffsetInTryBlock);
if (h != null)
return nodes.Single(n => n.ExceptionHandler == h && n.CopyFrom == null);
else
return exceptionalExit;
}
ControlFlowNode FindInnermostHandlerBlock(int instructionOffset)
{
foreach (ExceptionHandler h in methodBody.ExceptionHandlers) {
if (h.TryStart.Offset <= instructionOffset && instructionOffset < h.TryEnd.Offset
|| h.HandlerStart.Offset <= instructionOffset && instructionOffset < h.HandlerEnd.Offset)
{
return nodes.Single(n => n.ExceptionHandler == h && n.CopyFrom == null);
}
}
return exceptionalExit;
}
ControlFlowNode FindParentExceptionHandlerNode(ControlFlowNode exceptionHandler)
{
Debug.Assert(exceptionHandler.NodeType == ControlFlowNodeType.CatchHandler
|| exceptionHandler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
int offset = exceptionHandler.ExceptionHandler.TryStart.Offset;
for (int i = exceptionHandler.BlockIndex + 1; i < nodes.Count; i++) {
ExceptionHandler h = nodes[i].ExceptionHandler;
if (h != null && h.TryStart.Offset <= offset && offset < h.TryEnd.Offset)
return nodes[i];
}
return exceptionalExit;
}
#endregion
#region Step 5a: replace LeaveTry edges with EndFinally edges
// this is used only for copyFinallyBlocks==false; see Step 5b otherwise
void TransformLeaveEdges()
{
for (int i = nodes.Count - 1; i >= 0; i--) {
ControlFlowNode node = nodes[i];
if (node.End != null && node.Outgoing.Count == 1 && node.Outgoing[0].Type == JumpType.LeaveTry) {
Debug.Assert(node.End.OpCode == OpCodes.Leave || node.End.OpCode == OpCodes.Leave_S);
ControlFlowNode target = node.Outgoing[0].Target;
// remove the edge
target.Incoming.Remove(node.Outgoing[0]);
node.Outgoing.Clear();
ControlFlowNode handler = FindInnermostExceptionHandlerNode(node.End.Offset);
Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
CreateEdge(node, handler, JumpType.Normal);
CreateEdge(handler.EndFinallyOrFaultNode, target, JumpType.EndFinally);
}
}
}
#endregion
#region Step 5b: copy finally blocks into the LeaveTry edges
void CopyFinallyBlocksIntoLeaveEdges()
{
// We need to process try-finally blocks inside-out.
// We'll do that by going through all instructions in reverse order
for (int i = nodes.Count - 1; i >= 0; i--) {
ControlFlowNode node = nodes[i];
if (node.End != null && node.Outgoing.Count == 1 && node.Outgoing[0].Type == JumpType.LeaveTry) {
Debug.Assert(node.End.OpCode == OpCodes.Leave || node.End.OpCode == OpCodes.Leave_S);
ControlFlowNode target = node.Outgoing[0].Target;
// remove the edge
target.Incoming.Remove(node.Outgoing[0]);
node.Outgoing.Clear();
ControlFlowNode handler = FindInnermostExceptionHandlerNode(node.End.Offset);
Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
ControlFlowNode copy = CopyFinallySubGraph(handler, handler.EndFinallyOrFaultNode, target);
CreateEdge(node, copy, JumpType.Normal);
}
}
}
/// <summary>
/// Creates a copy of all nodes pointing to 'end' and replaces those references with references to 'newEnd'.
/// Nodes pointing to the copied node are copied recursively to update those references, too.
/// This recursion stops at 'start'. The modified version of start is returned.
/// </summary>
ControlFlowNode CopyFinallySubGraph(ControlFlowNode start, ControlFlowNode end, ControlFlowNode newEnd)
{
return new CopyFinallySubGraphLogic(this, start, end, newEnd).CopyFinallySubGraph();
}
class CopyFinallySubGraphLogic
{
readonly ControlFlowGraphBuilder builder;
readonly Dictionary<ControlFlowNode, ControlFlowNode> oldToNew = new Dictionary<ControlFlowNode, ControlFlowNode>();
readonly ControlFlowNode start;
readonly ControlFlowNode end;
readonly ControlFlowNode newEnd;
public CopyFinallySubGraphLogic(ControlFlowGraphBuilder builder, ControlFlowNode start, ControlFlowNode end, ControlFlowNode newEnd)
{
this.builder = builder;
this.start = start;
this.end = end;
this.newEnd = newEnd;
}
internal ControlFlowNode CopyFinallySubGraph()
{
foreach (ControlFlowNode n in end.Predecessors) {
CollectNodes(n);
}
foreach (var pair in oldToNew)
ReconstructEdges(pair.Key, pair.Value);
return GetNew(start);
}
void CollectNodes(ControlFlowNode node)
{
if (node == end || node == newEnd)
throw new InvalidOperationException("unexpected cycle involving finally construct");
if (!oldToNew.ContainsKey(node)) {
int newBlockIndex = builder.nodes.Count;
ControlFlowNode copy;
switch (node.NodeType) {
case ControlFlowNodeType.Normal:
copy = new ControlFlowNode(newBlockIndex, node.Start, node.End);
break;
case ControlFlowNodeType.FinallyOrFaultHandler:
copy = new ControlFlowNode(newBlockIndex, node.ExceptionHandler, node.EndFinallyOrFaultNode);
break;
default:
// other nodes shouldn't occur when copying finally blocks
throw new NotSupportedException(node.NodeType.ToString());
}
copy.CopyFrom = node;
builder.nodes.Add(copy);
oldToNew.Add(node, copy);
if (node != start) {
foreach (ControlFlowNode n in node.Predecessors) {
CollectNodes(n);
}
}
}
}
void ReconstructEdges(ControlFlowNode oldNode, ControlFlowNode newNode)
{
foreach (ControlFlowEdge oldEdge in oldNode.Outgoing) {
builder.CreateEdge(newNode, GetNew(oldEdge.Target), oldEdge.Type);
}
}
ControlFlowNode GetNew(ControlFlowNode oldNode)
{
if (oldNode == end)
return newEnd;
ControlFlowNode newNode;
if (oldToNew.TryGetValue(oldNode, out newNode))
return newNode;
return oldNode;
}
}
#endregion
#region CreateEdge methods
void CreateEdge(ControlFlowNode fromNode, Instruction toInstruction, JumpType type)
{
CreateEdge(fromNode, nodes.Single(n => n.Start == toInstruction), type);
}
void CreateEdge(ControlFlowNode fromNode, ControlFlowNode toNode, JumpType type)
{
ControlFlowEdge edge = new ControlFlowEdge(fromNode, toNode, type);
fromNode.Outgoing.Add(edge);
toNode.Incoming.Add(edge);
}
#endregion
#region OpCode info
static bool CanThrowException(OpCode opcode)
{
if (opcode.OpCodeType == OpCodeType.Prefix)
return false;
return OpCodeInfo.Get(opcode).CanThrow;
}
static bool IsBranch(OpCode opcode)
{
if (opcode.OpCodeType == OpCodeType.Prefix)
return false;
switch (opcode.FlowControl) {
case FlowControl.Cond_Branch:
case FlowControl.Branch:
case FlowControl.Throw:
case FlowControl.Return:
return true;
case FlowControl.Next:
case FlowControl.Call:
return false;
default:
throw new NotSupportedException(opcode.FlowControl.ToString());
}
}
#endregion
}
}

Просмотреть файл

@ -1,305 +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 ICSharpCode.Decompiler.Disassembler;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Type of the control flow node
/// </summary>
public enum ControlFlowNodeType
{
/// <summary>
/// A normal node represents a basic block.
/// </summary>
Normal,
/// <summary>
/// The entry point of the method.
/// </summary>
EntryPoint,
/// <summary>
/// The exit point of the method (every ret instruction branches to this node)
/// </summary>
RegularExit,
/// <summary>
/// This node represents leaving a method irregularly by throwing an exception.
/// </summary>
ExceptionalExit,
/// <summary>
/// This node is used as a header for exception handler blocks.
/// </summary>
CatchHandler,
/// <summary>
/// This node is used as a header for finally blocks and fault blocks.
/// Every leave instruction in the try block leads to the handler of the containing finally block;
/// and exceptional control flow also leads to this handler.
/// </summary>
FinallyOrFaultHandler,
/// <summary>
/// This node is used as footer for finally blocks and fault blocks.
/// Depending on the "copyFinallyBlocks" option used when creating the graph, it is connected with all leave targets using
/// EndFinally edges (when not copying); or with a specific leave target using a normal edge (when copying).
/// For fault blocks, an exception edge is used to represent the "re-throwing" of the exception.
/// </summary>
EndFinallyOrFault
}
/// <summary>
/// Represents a block in the control flow graph.
/// </summary>
public sealed class ControlFlowNode
{
/// <summary>
/// Index of this node in the ControlFlowGraph.Nodes collection.
/// </summary>
public readonly int BlockIndex;
/// <summary>
/// Gets the IL offset of this node.
/// </summary>
public readonly int Offset;
/// <summary>
/// Type of the node.
/// </summary>
public readonly ControlFlowNodeType NodeType;
/// <summary>
/// If this node is a FinallyOrFaultHandler node, this field points to the corresponding EndFinallyOrFault node.
/// Otherwise, this field is null.
/// </summary>
public readonly ControlFlowNode EndFinallyOrFaultNode;
/// <summary>
/// Visited flag, used in various algorithms.
/// Before using it in your algorithm, reset it to false by calling ControlFlowGraph.ResetVisited();
/// </summary>
public bool Visited;
/// <summary>
/// Gets whether this node is reachable. Requires that dominance is computed!
/// </summary>
public bool IsReachable {
get { return ImmediateDominator != null || NodeType == ControlFlowNodeType.EntryPoint; }
}
/// <summary>
/// Signalizes that this node is a copy of another node.
/// </summary>
public ControlFlowNode CopyFrom { get; internal set; }
/// <summary>
/// Gets the immediate dominator (the parent in the dominator tree).
/// Null if dominance has not been calculated; or if the node is unreachable.
/// </summary>
public ControlFlowNode ImmediateDominator { get; internal set; }
/// <summary>
/// List of children in the dominator tree.
/// </summary>
public readonly List<ControlFlowNode> DominatorTreeChildren = new List<ControlFlowNode>();
/// <summary>
/// The dominance frontier of this node.
/// This is the set of nodes for which this node dominates a predecessor, but which are not strictly dominated by this node.
/// </summary>
/// <remarks>
/// b.DominanceFrontier = { y in CFG; (exists p in predecessors(y): b dominates p) and not (b strictly dominates y)}
/// </remarks>
public HashSet<ControlFlowNode> DominanceFrontier;
/// <summary>
/// Start of code block represented by this node. Only set for nodetype == Normal.
/// </summary>
public readonly Instruction Start;
/// <summary>
/// End of the code block represented by this node. Only set for nodetype == Normal.
/// The end is exclusive, the end instruction itself does not belong to this block.
/// </summary>
public readonly Instruction End;
/// <summary>
/// Gets the exception handler associated with this node.
/// Only set for nodetype == CatchHandler or nodetype == FinallyOrFaultHandler.
/// </summary>
public readonly ExceptionHandler ExceptionHandler;
/// <summary>
/// List of incoming control flow edges.
/// </summary>
public readonly List<ControlFlowEdge> Incoming = new List<ControlFlowEdge>();
/// <summary>
/// List of outgoing control flow edges.
/// </summary>
public readonly List<ControlFlowEdge> Outgoing = new List<ControlFlowEdge>();
/// <summary>
/// Any user data
/// </summary>
public object UserData;
internal ControlFlowNode(int blockIndex, int offset, ControlFlowNodeType nodeType)
{
BlockIndex = blockIndex;
Offset = offset;
NodeType = nodeType;
}
internal ControlFlowNode(int blockIndex, Instruction start, Instruction end)
{
if (start == null)
throw new ArgumentNullException("start");
if (end == null)
throw new ArgumentNullException("end");
BlockIndex = blockIndex;
NodeType = ControlFlowNodeType.Normal;
Start = start;
End = end;
Offset = start.Offset;
}
internal ControlFlowNode(int blockIndex, ExceptionHandler exceptionHandler, ControlFlowNode endFinallyOrFaultNode)
{
BlockIndex = blockIndex;
NodeType = endFinallyOrFaultNode != null ? ControlFlowNodeType.FinallyOrFaultHandler : ControlFlowNodeType.CatchHandler;
ExceptionHandler = exceptionHandler;
EndFinallyOrFaultNode = endFinallyOrFaultNode;
Debug.Assert((exceptionHandler.HandlerType == ExceptionHandlerType.Finally || exceptionHandler.HandlerType == ExceptionHandlerType.Fault) == (endFinallyOrFaultNode != null));
Offset = exceptionHandler.HandlerStart.Offset;
}
/// <summary>
/// Gets all predecessors (=sources of incoming edges)
/// </summary>
public IEnumerable<ControlFlowNode> Predecessors {
get {
return Incoming.Select(e => e.Source);
}
}
/// <summary>
/// Gets all successors (=targets of outgoing edges)
/// </summary>
public IEnumerable<ControlFlowNode> Successors {
get {
return Outgoing.Select(e => e.Target);
}
}
/// <summary>
/// Gets all instructions in this node.
/// Returns an empty list for special nodes that don't have any instructions.
/// </summary>
public IEnumerable<Instruction> Instructions {
get {
Instruction inst = Start;
if (inst != null) {
yield return inst;
while (inst != End) {
inst = inst.Next;
yield return inst;
}
}
}
}
public void TraversePreOrder(Func<ControlFlowNode, IEnumerable<ControlFlowNode>> children, Action<ControlFlowNode> visitAction)
{
if (Visited)
return;
Visited = true;
visitAction(this);
foreach (ControlFlowNode t in children(this))
t.TraversePreOrder(children, visitAction);
}
public void TraversePostOrder(Func<ControlFlowNode, IEnumerable<ControlFlowNode>> children, Action<ControlFlowNode> visitAction)
{
if (Visited)
return;
Visited = true;
foreach (ControlFlowNode t in children(this))
t.TraversePostOrder(children, visitAction);
visitAction(this);
}
public override string ToString()
{
StringWriter writer = new StringWriter();
switch (NodeType) {
case ControlFlowNodeType.Normal:
writer.Write("Block #{0}", BlockIndex);
if (Start != null)
writer.Write(": IL_{0:x4}", Start.Offset);
if (End != null)
writer.Write(" to IL_{0:x4}", End.GetEndOffset());
break;
case ControlFlowNodeType.CatchHandler:
case ControlFlowNodeType.FinallyOrFaultHandler:
writer.Write("Block #{0}: {1}: ", BlockIndex, NodeType);
DisassemblerHelpers.WriteTo(ExceptionHandler, new PlainTextOutput(writer));
break;
default:
writer.Write("Block #{0}: {1}", BlockIndex, NodeType);
break;
}
// if (ImmediateDominator != null) {
// writer.WriteLine();
// writer.Write("ImmediateDominator: #{0}", ImmediateDominator.BlockIndex);
// }
if (DominanceFrontier != null && DominanceFrontier.Any()) {
writer.WriteLine();
writer.Write("DominanceFrontier: " + string.Join(",", DominanceFrontier.OrderBy(d => d.BlockIndex).Select(d => d.BlockIndex.ToString())));
}
foreach (Instruction inst in Instructions) {
writer.WriteLine();
DisassemblerHelpers.WriteTo(inst, new PlainTextOutput(writer));
}
if (UserData != null) {
writer.WriteLine();
writer.Write(UserData.ToString());
}
return writer.ToString();
}
/// <summary>
/// Gets whether <c>this</c> dominates <paramref name="node"/>.
/// </summary>
public bool Dominates(ControlFlowNode node)
{
// TODO: this can be made O(1) by numbering the dominator tree
ControlFlowNode tmp = node;
while (tmp != null) {
if (tmp == this)
return true;
tmp = tmp.ImmediateDominator;
}
return false;
}
}
}

Просмотреть файл

@ -1,241 +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 System.Threading;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Detects the structure of the control flow (exception blocks and loops).
/// </summary>
public class ControlStructureDetector
{
public static ControlStructure DetectStructure(ControlFlowGraph g, IEnumerable<ExceptionHandler> exceptionHandlers, CancellationToken cancellationToken)
{
ControlStructure root = new ControlStructure(new HashSet<ControlFlowNode>(g.Nodes), g.EntryPoint, ControlStructureType.Root);
// First build a structure tree out of the exception table
DetectExceptionHandling(root, g, exceptionHandlers);
// Then run the loop detection.
DetectLoops(g, root, cancellationToken);
return root;
}
#region Exception Handling
static void DetectExceptionHandling(ControlStructure current, ControlFlowGraph g, IEnumerable<ExceptionHandler> exceptionHandlers)
{
// We rely on the fact that the exception handlers are sorted so that the innermost come first.
// For each exception handler, we determine the nodes and substructures inside that handler, and move them into a new substructure.
// This is always possible because exception handlers are guaranteed (by the CLR spec) to be properly nested and non-overlapping;
// so they directly form the tree that we need.
foreach (ExceptionHandler eh in exceptionHandlers) {
var tryNodes = FindNodes(current, eh.TryStart, eh.TryEnd);
current.Nodes.ExceptWith(tryNodes);
ControlStructure tryBlock = new ControlStructure(
tryNodes,
g.Nodes.Single(n => n.Start == eh.TryStart),
ControlStructureType.Try);
tryBlock.ExceptionHandler = eh;
MoveControlStructures(current, tryBlock, eh.TryStart, eh.TryEnd);
current.Children.Add(tryBlock);
if (eh.FilterStart != null) {
throw new NotSupportedException();
}
var handlerNodes = FindNodes(current, eh.HandlerStart, eh.HandlerEnd);
var handlerNode = current.Nodes.Single(n => n.ExceptionHandler == eh);
handlerNodes.Add(handlerNode);
if (handlerNode.EndFinallyOrFaultNode != null)
handlerNodes.Add(handlerNode.EndFinallyOrFaultNode);
current.Nodes.ExceptWith(handlerNodes);
ControlStructure handlerBlock = new ControlStructure(
handlerNodes, handlerNode, ControlStructureType.Handler);
handlerBlock.ExceptionHandler = eh;
MoveControlStructures(current, handlerBlock, eh.HandlerStart, eh.HandlerEnd);
current.Children.Add(handlerBlock);
}
}
/// <summary>
/// Removes all nodes from start to end (exclusive) from this ControlStructure and moves them to the target structure.
/// </summary>
static HashSet<ControlFlowNode> FindNodes(ControlStructure current, Instruction startInst, Instruction endInst)
{
HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>();
int start = startInst.Offset;
int end = endInst.Offset;
foreach (var node in current.Nodes.ToArray()) {
if (node.Start != null && start <= node.Start.Offset && node.Start.Offset < end) {
result.Add(node);
}
}
return result;
}
static void MoveControlStructures(ControlStructure current, ControlStructure target, Instruction startInst, Instruction endInst)
{
for (int i = 0; i < current.Children.Count; i++) {
var child = current.Children[i];
if (startInst.Offset <= child.EntryPoint.Offset && child.EntryPoint.Offset < endInst.Offset) {
current.Children.RemoveAt(i--);
target.Children.Add(child);
target.AllNodes.UnionWith(child.AllNodes);
}
}
}
#endregion
#region Loop Detection
// Loop detection works like this:
// We find a top-level loop by looking for its entry point, which is characterized by a node dominating its own predecessor.
// Then we determine all other nodes that belong to such a loop (all nodes which lead to the entry point, and are dominated by it).
// Finally, we check whether our result conforms with potential existing exception structures, and create the substructure for the loop if successful.
// This algorithm is applied recursively for any substructures (both detected loops and exception blocks)
// But maybe we should get rid of this complex stuff and instead treat every backward jump as a loop?
// That should still work with the IL produced by compilers, and has the advantage that the detected loop bodies are consecutive IL regions.
static void DetectLoops(ControlFlowGraph g, ControlStructure current, CancellationToken cancellationToken)
{
if (!current.EntryPoint.IsReachable)
return;
g.ResetVisited();
cancellationToken.ThrowIfCancellationRequested();
FindLoops(current, current.EntryPoint);
foreach (ControlStructure loop in current.Children)
DetectLoops(g, loop, cancellationToken);
}
static void FindLoops(ControlStructure current, ControlFlowNode node)
{
if (node.Visited)
return;
node.Visited = true;
if (current.Nodes.Contains(node)
&& node.DominanceFrontier.Contains(node)
&& !(node == current.EntryPoint && current.Type == ControlStructureType.Loop))
{
HashSet<ControlFlowNode> loopContents = new HashSet<ControlFlowNode>();
FindLoopContents(current, loopContents, node, node);
List<ControlStructure> containedChildStructures = new List<ControlStructure>();
bool invalidNesting = false;
foreach (ControlStructure childStructure in current.Children) {
if (childStructure.AllNodes.IsSubsetOf(loopContents)) {
containedChildStructures.Add(childStructure);
} else if (childStructure.AllNodes.Intersect(loopContents).Any()) {
invalidNesting = true;
}
}
if (!invalidNesting) {
current.Nodes.ExceptWith(loopContents);
ControlStructure ctl = new ControlStructure(loopContents, node, ControlStructureType.Loop);
foreach (ControlStructure childStructure in containedChildStructures) {
ctl.Children.Add(childStructure);
current.Children.Remove(childStructure);
ctl.Nodes.ExceptWith(childStructure.AllNodes);
}
current.Children.Add(ctl);
}
}
foreach (var edge in node.Outgoing) {
FindLoops(current, edge.Target);
}
}
static void FindLoopContents(ControlStructure current, HashSet<ControlFlowNode> loopContents, ControlFlowNode loopHead, ControlFlowNode node)
{
if (current.AllNodes.Contains(node) && loopHead.Dominates(node) && loopContents.Add(node)) {
foreach (var edge in node.Incoming) {
FindLoopContents(current, loopContents, loopHead, edge.Source);
}
}
}
#endregion
}
public enum ControlStructureType
{
/// <summary>
/// The root block of the method
/// </summary>
Root,
/// <summary>
/// A nested control structure representing a loop.
/// </summary>
Loop,
/// <summary>
/// A nested control structure representing a try block.
/// </summary>
Try,
/// <summary>
/// A nested control structure representing a catch, finally, or fault block.
/// </summary>
Handler,
/// <summary>
/// A nested control structure representing an exception filter block.
/// </summary>
Filter
}
/// <summary>
/// Represents the structure detected by the <see cref="ControlStructureDetector"/>.
///
/// This is a tree of ControlStructure nodes. Each node contains a set of CFG nodes, and every CFG node is contained in exactly one ControlStructure node.
/// </summary>
public class ControlStructure
{
public readonly ControlStructureType Type;
public readonly List<ControlStructure> Children = new List<ControlStructure>();
/// <summary>
/// The nodes in this control structure.
/// </summary>
public readonly HashSet<ControlFlowNode> Nodes;
/// <summary>
/// The nodes in this control structure and in all child control structures.
/// </summary>
public readonly HashSet<ControlFlowNode> AllNodes;
/// <summary>
/// The entry point of this control structure.
/// </summary>
public readonly ControlFlowNode EntryPoint;
/// <summary>
/// The exception handler associated with this Try,Handler or Finally structure.
/// </summary>
public ExceptionHandler ExceptionHandler;
public ControlStructure(HashSet<ControlFlowNode> nodes, ControlFlowNode entryPoint, ControlStructureType type)
{
if (nodes == null)
throw new ArgumentNullException("nodes");
Nodes = nodes;
EntryPoint = entryPoint;
Type = type;
AllNodes = new HashSet<ControlFlowNode>(nodes);
}
}
}

Просмотреть файл

@ -1,312 +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.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Additional info about opcodes.
/// </summary>
internal sealed class OpCodeInfo
{
public static bool IsUnconditionalBranch(OpCode opcode)
{
if (opcode.OpCodeType == OpCodeType.Prefix)
return false;
switch (opcode.FlowControl) {
case FlowControl.Branch:
case FlowControl.Throw:
case FlowControl.Return:
return true;
case FlowControl.Next:
case FlowControl.Call:
case FlowControl.Cond_Branch:
return false;
default:
throw new NotSupportedException(opcode.FlowControl.ToString());
}
}
static readonly OpCodeInfo[] knownOpCodes = {
#region Base Instructions
new OpCodeInfo(OpCodes.Add) { CanThrow = false },
new OpCodeInfo(OpCodes.Add_Ovf) { CanThrow = true },
new OpCodeInfo(OpCodes.Add_Ovf_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.And) { CanThrow = false },
new OpCodeInfo(OpCodes.Arglist) { CanThrow = false },
new OpCodeInfo(OpCodes.Beq) { CanThrow = false },
new OpCodeInfo(OpCodes.Beq_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Bge) { CanThrow = false },
new OpCodeInfo(OpCodes.Bge_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Bge_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Bge_Un_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Bgt) { CanThrow = false },
new OpCodeInfo(OpCodes.Bgt_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Bgt_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Bgt_Un_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Ble) { CanThrow = false },
new OpCodeInfo(OpCodes.Ble_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Ble_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Ble_Un_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Blt) { CanThrow = false },
new OpCodeInfo(OpCodes.Blt_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Blt_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Blt_Un_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Bne_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Bne_Un_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Br) { CanThrow = false },
new OpCodeInfo(OpCodes.Br_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Break) { CanThrow = true },
new OpCodeInfo(OpCodes.Brfalse) { CanThrow = false },
new OpCodeInfo(OpCodes.Brfalse_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Brtrue) { CanThrow = false },
new OpCodeInfo(OpCodes.Brtrue_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Call) { CanThrow = true },
new OpCodeInfo(OpCodes.Calli) { CanThrow = true },
new OpCodeInfo(OpCodes.Ceq) { CanThrow = false },
new OpCodeInfo(OpCodes.Cgt) { CanThrow = false },
new OpCodeInfo(OpCodes.Cgt_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Ckfinite) { CanThrow = true },
new OpCodeInfo(OpCodes.Clt) { CanThrow = false },
new OpCodeInfo(OpCodes.Clt_Un) { CanThrow = false },
// conv.<to type>
new OpCodeInfo(OpCodes.Conv_I1) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_I2) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_I4) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_I8) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_R4) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_R8) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_U1) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_U2) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_U4) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_U8) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_I) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_U) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_R_Un) { CanThrow = false },
// conv.ovf.<to type>
new OpCodeInfo(OpCodes.Conv_Ovf_I1) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_I2) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_I4) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_I8) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_U1) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_U2) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_U4) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_U8) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_I) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_U) { CanThrow = true},
// conv.ovf.<to type>.un
new OpCodeInfo(OpCodes.Conv_Ovf_I1_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_I2_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_I4_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_I8_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_U1_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_U2_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_U4_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_U8_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_I_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_U_Un) { CanThrow = true },
//new OpCodeInfo(OpCodes.Cpblk) { CanThrow = true }, - no idea whether this might cause trouble for the type system, C# shouldn't use it so I'll disable it
new OpCodeInfo(OpCodes.Div) { CanThrow = true },
new OpCodeInfo(OpCodes.Div_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Dup) { CanThrow = true, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Endfilter) { CanThrow = false },
new OpCodeInfo(OpCodes.Endfinally) { CanThrow = false },
//new OpCodeInfo(OpCodes.Initblk) { CanThrow = true }, - no idea whether this might cause trouble for the type system, C# shouldn't use it so I'll disable it
//new OpCodeInfo(OpCodes.Jmp) { CanThrow = true } - We don't support non-local control transfers.
new OpCodeInfo(OpCodes.Ldarg) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldarg_0) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldarg_1) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldarg_2) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldarg_3) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldarg_S) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldarga) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldarga_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_M1) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_0) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_1) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_2) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_3) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_4) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_5) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_6) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_7) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_8) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I8) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_R4) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_R8) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldftn) { CanThrow = false },
// ldind.<type>
new OpCodeInfo(OpCodes.Ldind_I1) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_I2) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_I4) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_I8) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_U1) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_U2) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_U4) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_R4) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_R8) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_I) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_Ref) { CanThrow = true },
// the ldloc exceptions described in the spec can only occur on methods without .localsinit - but csc always sets that flag
new OpCodeInfo(OpCodes.Ldloc) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldloc_0) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldloc_1) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldloc_2) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldloc_3) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldloc_S) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldloca) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldloca_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldnull) { CanThrow = false },
new OpCodeInfo(OpCodes.Leave) { CanThrow = false },
new OpCodeInfo(OpCodes.Leave_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Localloc) { CanThrow = true },
new OpCodeInfo(OpCodes.Mul) { CanThrow = false },
new OpCodeInfo(OpCodes.Mul_Ovf) { CanThrow = true },
new OpCodeInfo(OpCodes.Mul_Ovf_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Neg) { CanThrow = false },
new OpCodeInfo(OpCodes.Nop) { CanThrow = false },
new OpCodeInfo(OpCodes.Not) { CanThrow = false },
new OpCodeInfo(OpCodes.Or) { CanThrow = false },
new OpCodeInfo(OpCodes.Pop) { CanThrow = false },
new OpCodeInfo(OpCodes.Rem) { CanThrow = true },
new OpCodeInfo(OpCodes.Rem_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Ret) { CanThrow = false },
new OpCodeInfo(OpCodes.Shl) { CanThrow = false },
new OpCodeInfo(OpCodes.Shr) { CanThrow = false },
new OpCodeInfo(OpCodes.Shr_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Starg) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Starg_S) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Stind_I1) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_I2) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_I4) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_I8) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_R4) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_R8) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_I) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_Ref) { CanThrow = true },
new OpCodeInfo(OpCodes.Stloc) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Stloc_0) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Stloc_1) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Stloc_2) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Stloc_3) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Stloc_S) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Sub) { CanThrow = false },
new OpCodeInfo(OpCodes.Sub_Ovf) { CanThrow = true },
new OpCodeInfo(OpCodes.Sub_Ovf_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Switch) { CanThrow = false },
new OpCodeInfo(OpCodes.Xor) { CanThrow = false },
#endregion
#region Object model instructions
// CanThrow is true by default - most OO instructions can throw, so we don't specify CanThrow all of the time
new OpCodeInfo(OpCodes.Box),
new OpCodeInfo(OpCodes.Callvirt),
new OpCodeInfo(OpCodes.Castclass),
new OpCodeInfo(OpCodes.Cpobj),
new OpCodeInfo(OpCodes.Initobj) { CanThrow = false },
new OpCodeInfo(OpCodes.Isinst) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldelem_Any),
// ldelem.<type>
new OpCodeInfo(OpCodes.Ldelem_I) ,
new OpCodeInfo(OpCodes.Ldelem_I1),
new OpCodeInfo(OpCodes.Ldelem_I2),
new OpCodeInfo(OpCodes.Ldelem_I4),
new OpCodeInfo(OpCodes.Ldelem_I8),
new OpCodeInfo(OpCodes.Ldelem_R4),
new OpCodeInfo(OpCodes.Ldelem_R8),
new OpCodeInfo(OpCodes.Ldelem_Ref),
new OpCodeInfo(OpCodes.Ldelem_U1),
new OpCodeInfo(OpCodes.Ldelem_U2),
new OpCodeInfo(OpCodes.Ldelem_U4),
new OpCodeInfo(OpCodes.Ldelema) ,
new OpCodeInfo(OpCodes.Ldfld) ,
new OpCodeInfo(OpCodes.Ldflda),
new OpCodeInfo(OpCodes.Ldlen) ,
new OpCodeInfo(OpCodes.Ldobj) ,
new OpCodeInfo(OpCodes.Ldsfld),
new OpCodeInfo(OpCodes.Ldsflda),
new OpCodeInfo(OpCodes.Ldstr) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldtoken) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldvirtftn),
new OpCodeInfo(OpCodes.Mkrefany),
new OpCodeInfo(OpCodes.Newarr),
new OpCodeInfo(OpCodes.Newobj),
new OpCodeInfo(OpCodes.Refanytype) { CanThrow = false },
new OpCodeInfo(OpCodes.Refanyval),
new OpCodeInfo(OpCodes.Rethrow),
new OpCodeInfo(OpCodes.Sizeof) { CanThrow = false },
new OpCodeInfo(OpCodes.Stelem_Any),
new OpCodeInfo(OpCodes.Stelem_I1),
new OpCodeInfo(OpCodes.Stelem_I2),
new OpCodeInfo(OpCodes.Stelem_I4),
new OpCodeInfo(OpCodes.Stelem_I8),
new OpCodeInfo(OpCodes.Stelem_R4),
new OpCodeInfo(OpCodes.Stelem_R8),
new OpCodeInfo(OpCodes.Stelem_Ref),
new OpCodeInfo(OpCodes.Stfld),
new OpCodeInfo(OpCodes.Stobj),
new OpCodeInfo(OpCodes.Stsfld),
new OpCodeInfo(OpCodes.Throw),
new OpCodeInfo(OpCodes.Unbox),
new OpCodeInfo(OpCodes.Unbox_Any),
#endregion
};
static readonly Dictionary<Code, OpCodeInfo> knownOpCodeDict = knownOpCodes.ToDictionary(info => info.OpCode.Code);
public static OpCodeInfo Get(OpCode opCode)
{
return Get(opCode.Code);
}
public static OpCodeInfo Get(Code code)
{
OpCodeInfo info;
if (knownOpCodeDict.TryGetValue(code, out info))
return info;
else
throw new NotSupportedException(code.ToString());
}
OpCode opcode;
OpCodeInfo(OpCode opcode)
{
this.opcode = opcode;
CanThrow = true;
}
public OpCode OpCode { get { return opcode; } }
/// <summary>
/// 'Move' kind of instructions have one input (may be stack or local variable) and copy that value to all outputs (again stack or local variable).
/// </summary>
public bool IsMoveInstruction { get; private set; }
/// <summary>
/// Specifies whether this opcode is capable of throwing exceptions.
/// </summary>
public bool CanThrow { get; private set; }
}
}

Просмотреть файл

@ -1,174 +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;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// This is a transformation working on SSA form.
/// It removes ldloca instructions and replaces them with SpecialOpCode.PrepareByOutCall or SpecialOpCode.PrepareByRefCall.
/// This then allows the variable that had its address taken to also be transformed into SSA.
/// </summary>
internal sealed class SimplifyByRefCalls
{
public static bool MakeByRefCallsSimple(SsaForm ssaForm)
{
SimplifyByRefCalls instance = new SimplifyByRefCalls(ssaForm);
foreach (SsaBlock block in ssaForm.Blocks) {
for (int i = 0; i < block.Instructions.Count; i++) {
SsaInstruction inst = block.Instructions[i];
if (inst.Instruction != null) {
switch (inst.Instruction.OpCode.Code) {
case Code.Call:
case Code.Callvirt:
instance.MakeByRefCallSimple(block, ref i, (IMethodSignature)inst.Instruction.Operand);
break;
case Code.Initobj:
instance.MakeInitObjCallSimple(block, ref i);
break;
case Code.Ldfld:
instance.MakeLoadFieldCallSimple(block, ref i);
break;
}
}
}
}
instance.RemoveRedundantInstructions();
if (instance.couldSimplifySomething)
ssaForm.ComputeVariableUsage();
return instance.couldSimplifySomething;
}
readonly SsaForm ssaForm;
bool couldSimplifySomething;
// the list of ldloca instructions we will remove
readonly List<SsaInstruction> redundantLoadAddressInstructions = new List<SsaInstruction>();
SimplifyByRefCalls(SsaForm ssaForm)
{
this.ssaForm = ssaForm;
}
void MakeByRefCallSimple(SsaBlock block, ref int instructionIndexInBlock, IMethodSignature targetMethod)
{
SsaInstruction inst = block.Instructions[instructionIndexInBlock];
for (int i = 0; i < inst.Operands.Length; i++) {
SsaVariable operand = inst.Operands[i];
if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) {
// address is used for this method call only
Instruction loadAddressInstruction = operand.Definition.Instruction;
// find target parameter type:
bool isOut;
if (i == 0 && targetMethod.HasThis) {
isOut = false;
} else {
ParameterDefinition parameter = targetMethod.Parameters[i - (targetMethod.HasThis ? 1 : 0)];
isOut = parameter.IsOut;
}
SsaVariable addressTakenOf = GetVariableFromLoadAddressInstruction(loadAddressInstruction);
// insert "Prepare" instruction on front
SpecialOpCode loadOpCode = isOut ? SpecialOpCode.PrepareByOutCall : SpecialOpCode.PrepareByRefCall;
block.Instructions.Insert(instructionIndexInBlock++, new SsaInstruction(
block, null, operand, new SsaVariable[] { addressTakenOf }, specialOpCode: loadOpCode));
// insert "WriteAfterByRefOrOutCall" instruction after call
block.Instructions.Insert(instructionIndexInBlock + 1, new SsaInstruction(
block, null, addressTakenOf, new SsaVariable[] { operand }, specialOpCode: SpecialOpCode.WriteAfterByRefOrOutCall));
couldSimplifySomething = true;
// remove the loadAddressInstruction later
// (later because it might be defined in the current block and we don't want instructionIndex to become invalid)
redundantLoadAddressInstructions.Add(operand.Definition);
}
}
}
SsaVariable GetVariableFromLoadAddressInstruction(Instruction loadAddressInstruction)
{
if (loadAddressInstruction.OpCode == OpCodes.Ldloca) {
return ssaForm.GetOriginalVariable((VariableReference)loadAddressInstruction.Operand);
} else {
Debug.Assert(loadAddressInstruction.OpCode == OpCodes.Ldarga);
return ssaForm.GetOriginalVariable((ParameterReference)loadAddressInstruction.Operand);
}
}
static bool IsLoadAddress(SsaInstruction inst)
{
return inst.Instruction != null && (inst.Instruction.OpCode == OpCodes.Ldloca || inst.Instruction.OpCode == OpCodes.Ldarga);
}
void MakeInitObjCallSimple(SsaBlock block, ref int instructionIndexInBlock)
{
SsaInstruction inst = block.Instructions[instructionIndexInBlock];
Debug.Assert(inst.Operands.Length == 1);
SsaVariable operand = inst.Operands[0];
if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) {
// replace instruction with special "InitObj" instruction
block.Instructions[instructionIndexInBlock] = new SsaInstruction(
inst.ParentBlock, null, GetVariableFromLoadAddressInstruction(operand.Definition.Instruction), null,
specialOpCode: SpecialOpCode.InitObj,
typeOperand: (TypeReference)inst.Instruction.Operand);
couldSimplifySomething = true;
// remove the loadAddressInstruction later
redundantLoadAddressInstructions.Add(operand.Definition);
}
}
void MakeLoadFieldCallSimple(SsaBlock block, ref int instructionIndexInBlock)
{
SsaInstruction inst = block.Instructions[instructionIndexInBlock];
Debug.Assert(inst.Operands.Length == 1);
SsaVariable operand = inst.Operands[0];
if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) {
// insert special "PrepareForFieldAccess" instruction in front
block.Instructions.Insert(instructionIndexInBlock++, new SsaInstruction(
inst.ParentBlock, null, operand,
new SsaVariable[] { GetVariableFromLoadAddressInstruction(operand.Definition.Instruction) },
specialOpCode: SpecialOpCode.PrepareForFieldAccess));
couldSimplifySomething = true;
// remove the loadAddressInstruction later
redundantLoadAddressInstructions.Add(operand.Definition);
}
}
void RemoveRedundantInstructions()
{
foreach (SsaInstruction inst in redundantLoadAddressInstructions) {
inst.ParentBlock.Instructions.Remove(inst);
}
}
}
}

Просмотреть файл

@ -1,60 +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.IO;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// A block in a control flow graph; with instructions represented by "SsaInstructions" (instructions use variables, no evaluation stack).
/// Usually these variables are in SSA form to make analysis easier.
/// </summary>
public sealed class SsaBlock
{
public readonly List<SsaBlock> Successors = new List<SsaBlock>();
public readonly List<SsaBlock> Predecessors = new List<SsaBlock>();
public readonly ControlFlowNodeType NodeType;
public readonly List<SsaInstruction> Instructions = new List<SsaInstruction>();
/// <summary>
/// The block index in the control flow graph.
/// This correspons to the node index in ControlFlowGraph.Nodes, so it can be used to retrieve the original CFG node and look
/// up additional information (e.g. dominance).
/// </summary>
public readonly int BlockIndex;
internal SsaBlock(ControlFlowNode node)
{
NodeType = node.NodeType;
BlockIndex = node.BlockIndex;
}
public override string ToString()
{
StringWriter writer = new StringWriter();
writer.Write("Block #{0} ({1})", BlockIndex, NodeType);
foreach (SsaInstruction inst in Instructions) {
writer.WriteLine();
inst.WriteTo(writer);
}
return writer.ToString();
}
}
}

Просмотреть файл

@ -1,162 +0,0 @@
// Copyright (c) 2010 Daniel Grunwald
//
// 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.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.Utils;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Represents a graph of SsaBlocks.
/// </summary>
public sealed class SsaForm
{
readonly SsaVariable[] parameters;
readonly SsaVariable[] locals;
public readonly ReadOnlyCollection<SsaVariable> OriginalVariables;
public readonly ReadOnlyCollection<SsaBlock> Blocks;
readonly bool methodHasThis;
public SsaBlock EntryPoint {
get { return Blocks[0]; }
}
public SsaBlock RegularExit {
get { return Blocks[1]; }
}
public SsaBlock ExceptionalExit {
get { return Blocks[2]; }
}
internal SsaForm(SsaBlock[] blocks, SsaVariable[] parameters, SsaVariable[] locals, SsaVariable[] stackLocations, bool methodHasThis)
{
this.parameters = parameters;
this.locals = locals;
Blocks = new ReadOnlyCollection<SsaBlock>(blocks);
OriginalVariables = new ReadOnlyCollection<SsaVariable>(parameters.Concat(locals).Concat(stackLocations).ToList());
this.methodHasThis = methodHasThis;
Debug.Assert(EntryPoint.NodeType == ControlFlowNodeType.EntryPoint);
Debug.Assert(RegularExit.NodeType == ControlFlowNodeType.RegularExit);
Debug.Assert(ExceptionalExit.NodeType == ControlFlowNodeType.ExceptionalExit);
for (int i = 0; i < OriginalVariables.Count; i++) {
OriginalVariables[i].OriginalVariableIndex = i;
}
}
public GraphVizGraph ExportBlockGraph(Func<SsaBlock, string> labelProvider = null)
{
if (labelProvider == null)
labelProvider = b => b.ToString();
GraphVizGraph graph = new GraphVizGraph();
foreach (SsaBlock block in Blocks) {
graph.AddNode(new GraphVizNode(block.BlockIndex) { label = labelProvider(block), shape = "box" });
}
foreach (SsaBlock block in Blocks) {
foreach (SsaBlock s in block.Successors) {
graph.AddEdge(new GraphVizEdge(block.BlockIndex, s.BlockIndex));
}
}
return graph;
}
public GraphVizGraph ExportVariableGraph(Func<SsaVariable, string> labelProvider = null)
{
if (labelProvider == null)
labelProvider = v => v.ToString();
GraphVizGraph graph = new GraphVizGraph();
foreach (SsaVariable v in AllVariables) {
graph.AddNode(new GraphVizNode(v.Name) { label = labelProvider(v) });
}
int instructionIndex = 0;
foreach (SsaBlock block in Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
if (inst.Operands.Length == 0 && inst.Target == null)
continue;
string id = "instruction" + (++instructionIndex);
graph.AddNode(new GraphVizNode(id) { label = inst.ToString(), shape = "box" });
foreach (SsaVariable op in inst.Operands)
graph.AddEdge(new GraphVizEdge(op.Name, id));
if (inst.Target != null)
graph.AddEdge(new GraphVizEdge(id, inst.Target.Name));
}
}
return graph;
}
public SsaVariable GetOriginalVariable(ParameterReference parameter)
{
if (methodHasThis)
return parameters[parameter.Index + 1];
else
return parameters[parameter.Index];
}
public SsaVariable GetOriginalVariable(VariableReference variable)
{
return locals[variable.Index];
}
#region ComputeVariableUsage
public void ComputeVariableUsage()
{
// clear data from previous runs
foreach (SsaBlock block in Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
foreach (SsaVariable v in inst.Operands) {
if (v.Usage != null)
v.Usage.Clear();
}
if (inst.Target != null && inst.Target.Usage != null)
inst.Target.Usage.Clear();
}
}
foreach (SsaBlock block in Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
foreach (SsaVariable v in inst.Operands) {
if (v.Usage == null)
v.Usage = new List<SsaInstruction>();
v.Usage.Add(inst);
}
if (inst.Target != null && inst.Target.Usage == null)
inst.Target.Usage = new List<SsaInstruction>();
}
}
}
#endregion
public IEnumerable<SsaVariable> AllVariables {
get {
return (
from block in Blocks
from instruction in block.Instructions
where instruction.Target != null
select instruction.Target
).Distinct();
}
}
}
}

Просмотреть файл

@ -1,257 +0,0 @@
// Copyright (c) 2010 Daniel Grunwald
//
// 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;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Constructs "SsaForm" graph for a CFG.
/// This class transforms the method from stack-based IL to a register-based IL language.
/// Then it calls into TransformToSsa to convert the resulting graph to static single assignment form.
/// </summary>
public sealed class SsaFormBuilder
{
public static SsaForm Build(MethodDefinition method)
{
if (method == null)
throw new ArgumentNullException("method");
var cfg = ControlFlowGraphBuilder.Build(method.Body);
cfg.ComputeDominance();
cfg.ComputeDominanceFrontier();
var ssa = BuildRegisterIL(method, cfg);
TransformToSsa.Transform(cfg, ssa);
return ssa;
}
public static SsaForm BuildRegisterIL(MethodDefinition method, ControlFlowGraph cfg)
{
if (method == null)
throw new ArgumentNullException("method");
if (cfg == null)
throw new ArgumentNullException("cfg");
return new SsaFormBuilder(method, cfg).Build();
}
readonly MethodDefinition method;
readonly ControlFlowGraph cfg;
readonly SsaBlock[] blocks; // array index = block index
readonly int[] stackSizeAtBlockStart; // array index = block index
readonly SsaVariable[] parameters; // array index = parameter number
readonly SsaVariable[] locals; // array index = local number
readonly SsaVariable[] stackLocations; // array index = position on the IL evaluation stack
SsaForm ssaForm;
SsaFormBuilder(MethodDefinition method, ControlFlowGraph cfg)
{
this.method = method;
this.cfg = cfg;
blocks = new SsaBlock[cfg.Nodes.Count];
stackSizeAtBlockStart = new int[cfg.Nodes.Count];
for (int i = 0; i < stackSizeAtBlockStart.Length; i++) {
stackSizeAtBlockStart[i] = -1;
}
stackSizeAtBlockStart[cfg.EntryPoint.BlockIndex] = 0;
parameters = new SsaVariable[method.Parameters.Count + (method.HasThis ? 1 : 0)];
if (method.HasThis)
parameters[0] = new SsaVariable(method.Body.ThisParameter);
for (int i = 0; i < method.Parameters.Count; i++)
parameters[i + (method.HasThis ? 1 : 0)] = new SsaVariable(method.Parameters[i]);
locals = new SsaVariable[method.Body.Variables.Count];
for (int i = 0; i < locals.Length; i++)
locals[i] = new SsaVariable(method.Body.Variables[i]);
stackLocations = new SsaVariable[method.Body.MaxStackSize];
for (int i = 0; i < stackLocations.Length; i++) {
stackLocations[i] = new SsaVariable(i);
}
}
internal SsaForm Build()
{
CreateGraphStructure();
ssaForm = new SsaForm(blocks, parameters, locals, stackLocations, method.HasThis);
CreateInstructions(cfg.EntryPoint.BlockIndex);
CreateSpecialInstructions();
return ssaForm;
}
void CreateGraphStructure()
{
for (int i = 0; i < blocks.Length; i++) {
blocks[i] = new SsaBlock(cfg.Nodes[i]);
}
for (int i = 0; i < blocks.Length; i++) {
foreach (ControlFlowNode node in cfg.Nodes[i].Successors) {
blocks[i].Successors.Add(blocks[node.BlockIndex]);
blocks[node.BlockIndex].Predecessors.Add(blocks[i]);
}
}
}
void CreateInstructions(int blockIndex)
{
ControlFlowNode cfgNode = cfg.Nodes[blockIndex];
SsaBlock block = blocks[blockIndex];
int stackSize = stackSizeAtBlockStart[blockIndex];
Debug.Assert(stackSize >= 0);
List<Instruction> prefixes = new List<Instruction>();
foreach (Instruction inst in cfgNode.Instructions) {
if (inst.OpCode.OpCodeType == OpCodeType.Prefix) {
prefixes.Add(inst);
continue;
}
int popCount = inst.GetPopDelta(method) ?? stackSize;
stackSize -= popCount;
if (stackSize < 0)
throw new InvalidProgramException("IL stack underflow");
int pushCount = inst.GetPushDelta();
if (stackSize + pushCount > stackLocations.Length)
throw new InvalidProgramException("IL stack overflow");
SsaVariable target;
SsaVariable[] operands;
DetermineOperands(stackSize, inst, popCount, pushCount, out target, out operands);
Instruction[] prefixArray = prefixes.Count > 0 ? prefixes.ToArray() : null;
prefixes.Clear();
// ignore NOP instructions
if (!(inst.OpCode == OpCodes.Nop || inst.OpCode == OpCodes.Pop)) {
block.Instructions.Add(new SsaInstruction(block, inst, target, operands, prefixArray));
}
stackSize += pushCount;
}
foreach (ControlFlowEdge edge in cfgNode.Outgoing) {
int newStackSize;
switch (edge.Type) {
case JumpType.Normal:
newStackSize = stackSize;
break;
case JumpType.EndFinally:
if (stackSize != 0)
throw new NotSupportedException("stacksize must be 0 in endfinally edge");
newStackSize = 0;
break;
case JumpType.JumpToExceptionHandler:
switch (edge.Target.NodeType) {
case ControlFlowNodeType.FinallyOrFaultHandler:
newStackSize = 0;
break;
case ControlFlowNodeType.ExceptionalExit:
case ControlFlowNodeType.CatchHandler:
newStackSize = 1;
break;
default:
throw new NotSupportedException("unsupported target node type: " + edge.Target.NodeType);
}
break;
default:
throw new NotSupportedException("unsupported jump type: " + edge.Type);
}
int nextStackSize = stackSizeAtBlockStart[edge.Target.BlockIndex];
if (nextStackSize == -1) {
stackSizeAtBlockStart[edge.Target.BlockIndex] = newStackSize;
CreateInstructions(edge.Target.BlockIndex);
} else if (nextStackSize != newStackSize) {
throw new InvalidProgramException("Stack size doesn't match");
}
}
}
void DetermineOperands(int stackSize, Instruction inst, int popCount, int pushCount, out SsaVariable target, out SsaVariable[] operands)
{
switch (inst.OpCode.Code) {
case Code.Ldarg:
operands = new SsaVariable[] { ssaForm.GetOriginalVariable((ParameterReference)inst.Operand) };
target = stackLocations[stackSize];
break;
case Code.Starg:
operands = new SsaVariable[] { stackLocations[stackSize] };
target = ssaForm.GetOriginalVariable((ParameterReference)inst.Operand);
break;
case Code.Ldloc:
operands = new SsaVariable[] { ssaForm.GetOriginalVariable((VariableReference)inst.Operand) };
target = stackLocations[stackSize];
break;
case Code.Stloc:
operands = new SsaVariable[] { stackLocations[stackSize] };
target = ssaForm.GetOriginalVariable((VariableReference)inst.Operand);
break;
case Code.Dup:
operands = new SsaVariable[] { stackLocations[stackSize] };
target = stackLocations[stackSize + 1];
break;
default:
operands = new SsaVariable[popCount];
for (int i = 0; i < popCount; i++) {
operands[i] = stackLocations[stackSize + i];
}
switch (pushCount) {
case 0:
target = null;
break;
case 1:
target = stackLocations[stackSize];
break;
default:
throw new NotSupportedException("unsupported pushCount=" + pushCount);
}
break;
}
}
void CreateSpecialInstructions()
{
// Everything needs an initial write for the SSA transformation to work correctly.
foreach (SsaVariable v in parameters) {
ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Parameter));
}
foreach (SsaVariable v in locals) {
ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Uninitialized));
}
foreach (SsaVariable v in stackLocations) {
ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Uninitialized));
}
foreach (SsaBlock b in blocks) {
if (b.NodeType == ControlFlowNodeType.CatchHandler) {
b.Instructions.Add(new SsaInstruction(b, null, stackLocations[0], null,
specialOpCode: SpecialOpCode.Exception,
typeOperand: cfg.Nodes[b.BlockIndex].ExceptionHandler.CatchType));
}
}
}
}
}

Просмотреть файл

@ -1,191 +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 ICSharpCode.Decompiler.Disassembler;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
public enum SpecialOpCode
{
/// <summary>
/// No special op code: SsaInstruction has a normal IL instruction
/// </summary>
None,
/// <summary>
/// Φ function: chooses the appropriate variable based on which CFG edge was used to enter this block
/// </summary>
Phi,
/// <summary>
/// Variable is read from before passing it by ref.
/// This instruction constructs a managed reference to the variable.
/// </summary>
PrepareByRefCall,
/// <summary>
/// This instruction constructs a managed reference to the variable.
/// The variable is not really read from.
/// </summary>
PrepareByOutCall,
/// <summary>
/// This instruction constructs a managed reference to the variable.
/// The reference is used for a field access on a value type.
/// </summary>
PrepareForFieldAccess,
/// <summary>
/// Variable is written to after passing it by ref or out.
/// </summary>
WriteAfterByRefOrOutCall,
/// <summary>
/// Variable is not initialized.
/// </summary>
Uninitialized,
/// <summary>
/// Value is passed in as parameter
/// </summary>
Parameter,
/// <summary>
/// Value is a caught exception.
/// TypeOperand is set to the exception type.
/// </summary>
Exception,
/// <summary>
/// Initialize a value type. Unlike the real initobj instruction, this one does not take an address
/// but assigns to the target variable.
/// TypeOperand is set to the type being created.
/// </summary>
InitObj
}
public sealed class SsaInstruction
{
public readonly SsaBlock ParentBlock;
public readonly SpecialOpCode SpecialOpCode;
/// <summary>
/// The original IL instruction.
/// May be null for "invented" instructions (SpecialOpCode != None).
/// </summary>
public readonly Instruction Instruction;
/// <summary>
/// Prefixes in front of the IL instruction.
/// </summary>
public readonly Instruction[] Prefixes;
/// <summary>
/// Gets the type operand. This is used only in combination with some special opcodes.
/// </summary>
public readonly TypeReference TypeOperand;
public SsaVariable Target;
public SsaVariable[] Operands;
static readonly SsaVariable[] emptyVariableArray = {};
static readonly Instruction[] emptyInstructionArray = {};
public SsaInstruction(SsaBlock parentBlock, Instruction instruction, SsaVariable target, SsaVariable[] operands,
Instruction[] prefixes = null, SpecialOpCode specialOpCode = SpecialOpCode.None,
TypeReference typeOperand = null)
{
ParentBlock = parentBlock;
Instruction = instruction;
Prefixes = prefixes ?? emptyInstructionArray;
Target = target;
Operands = operands ?? emptyVariableArray;
SpecialOpCode = specialOpCode;
TypeOperand = typeOperand;
Debug.Assert((typeOperand != null) == (specialOpCode == SpecialOpCode.Exception || specialOpCode == SpecialOpCode.InitObj));
}
/// <summary>
/// Gets whether this instruction is a simple assignment from one variable to another.
/// </summary>
public bool IsMoveInstruction {
get {
return Target != null && Operands.Length == 1 && Instruction != null && OpCodeInfo.Get(Instruction.OpCode).IsMoveInstruction;
}
}
public void ReplaceVariableInOperands(SsaVariable oldVar, SsaVariable newVar)
{
for (int i = 0; i < Operands.Length; i++) {
if (Operands[i] == oldVar)
Operands[i] = newVar;
}
}
public override string ToString()
{
StringWriter w = new StringWriter();
WriteTo(w);
return w.ToString();
}
public void WriteTo(TextWriter writer)
{
foreach (Instruction prefix in Prefixes) {
DisassemblerHelpers.WriteTo(prefix, new PlainTextOutput(writer));
writer.WriteLine();
}
if (Instruction != null && Instruction.Offset >= 0) {
writer.Write(CecilExtensions.OffsetToString(Instruction.Offset));
writer.Write(": ");
}
if (Target != null) {
writer.Write(Target.ToString());
writer.Write(" = ");
}
if (IsMoveInstruction) {
writer.Write(Operands[0].ToString());
if (Instruction != null) {
writer.Write(" (" + Instruction.OpCode.Name + ")");
}
} else {
if (Instruction == null) {
writer.Write(SpecialOpCode.ToString());
} else {
writer.Write(Instruction.OpCode.Name);
if(null != Instruction.Operand) {
writer.Write(' ');
DisassemblerHelpers.WriteOperand(new PlainTextOutput(writer), Instruction.Operand);
writer.Write(' ');
}
}
if (TypeOperand != null) {
writer.Write(' ');
writer.Write(TypeOperand.ToString());
writer.Write(' ');
}
if (Operands.Length > 0) {
writer.Write('(');
for (int i = 0; i < Operands.Length; i++) {
if (i > 0)
writer.Write(", ");
writer.Write(Operands[i].ToString());
}
writer.Write(')');
}
}
}
}
}

Просмотреть файл

@ -1,138 +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.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Contains some very simple optimizations that work on the SSA form.
/// </summary>
internal static class SsaOptimization
{
public static void Optimize(SsaForm ssaForm)
{
DirectlyStoreToVariables(ssaForm);
SimpleCopyPropagation(ssaForm);
RemoveDeadAssignments(ssaForm);
}
/// <summary>
/// When any instructions stores its result in a stack location that's used only once in a 'stloc' or 'starg' instruction,
/// we optimize this to directly store in the target location.
/// As optimization this is redundant (does the same as copy propagation), but it'll make us keep the variables named
/// after locals instead of keeping the temps as using only the simple copy propagation would do.
/// </summary>
public static void DirectlyStoreToVariables(SsaForm ssaForm)
{
foreach (SsaBlock block in ssaForm.Blocks) {
block.Instructions.RemoveAll(
inst => {
if (inst.Instruction != null && (inst.Instruction.OpCode == OpCodes.Stloc || inst.Instruction.OpCode == OpCodes.Starg)) {
SsaVariable target = inst.Target;
SsaVariable temp = inst.Operands[0];
if (target.IsSingleAssignment && temp.IsSingleAssignment && temp.Usage.Count == 1 && temp.IsStackLocation) {
temp.Definition.Target = target;
return true;
}
}
return false;
});
}
ssaForm.ComputeVariableUsage(); // update usage after we modified stuff
}
public static void SimpleCopyPropagation(SsaForm ssaForm, bool onlyForStackLocations = true)
{
foreach (SsaBlock block in ssaForm.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
if (inst.IsMoveInstruction && inst.Target.IsSingleAssignment && inst.Operands[0].IsSingleAssignment) {
if (inst.Target.IsStackLocation || !onlyForStackLocations) {
// replace all uses of 'target' with 'operands[0]'.
foreach (SsaInstruction useInstruction in inst.Target.Usage) {
useInstruction.ReplaceVariableInOperands(inst.Target, inst.Operands[0]);
}
}
}
}
}
ssaForm.ComputeVariableUsage(); // update usage after we modified stuff
}
public static void RemoveDeadAssignments(SsaForm ssaForm)
{
HashSet<SsaVariable> liveVariables = new HashSet<SsaVariable>();
// find variables that are used directly
foreach (SsaBlock block in ssaForm.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
if (!CanRemoveAsDeadCode(inst)) {
if (inst.Target != null)
liveVariables.Add(inst.Target);
foreach (SsaVariable op in inst.Operands) {
liveVariables.Add(op);
}
}
}
}
Queue<SsaVariable> queue = new Queue<SsaVariable>(liveVariables);
// find variables that are used indirectly
while (queue.Count > 0) {
SsaVariable v = queue.Dequeue();
if (v.IsSingleAssignment) {
foreach (SsaVariable op in v.Definition.Operands) {
if (liveVariables.Add(op))
queue.Enqueue(op);
}
}
}
// remove assignments to all unused variables
foreach (SsaBlock block in ssaForm.Blocks) {
block.Instructions.RemoveAll(
inst => {
if (inst.Target != null && !liveVariables.Contains(inst.Target)) {
Debug.Assert(inst.Target.IsSingleAssignment);
return true;
}
return false;
});
}
ssaForm.ComputeVariableUsage(); // update usage after we modified stuff
}
static bool CanRemoveAsDeadCode(SsaInstruction inst)
{
if (inst.Target != null && !inst.Target.IsSingleAssignment)
return false;
switch (inst.SpecialOpCode) {
case SpecialOpCode.Phi:
case SpecialOpCode.Exception:
case SpecialOpCode.Parameter:
case SpecialOpCode.Uninitialized:
return true;
case SpecialOpCode.None:
return inst.IsMoveInstruction;
default:
return false;
}
}
}
}

Просмотреть файл

@ -1,91 +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 Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Represents a variable used with the SsaInstruction register-based instructions.
/// Despite what the name suggests, the variable is not necessarily in single-assignment form - take a look at "bool IsSingleAssignment".
/// </summary>
public sealed class SsaVariable
{
public int OriginalVariableIndex;
public readonly string Name;
public readonly bool IsStackLocation;
public readonly ParameterDefinition Parameter;
public readonly VariableDefinition Variable;
public SsaVariable(ParameterDefinition p)
{
Name = string.IsNullOrEmpty(p.Name) ? "param" + p.Index : p.Name;
Parameter = p;
}
public SsaVariable(VariableDefinition v)
{
Name = string.IsNullOrEmpty(v.Name) ? "V_" + v.Index : v.Name;
Variable = v;
}
public SsaVariable(int stackLocation)
{
Name = "stack" + stackLocation;
IsStackLocation = true;
}
public SsaVariable(SsaVariable original, string newName)
{
Name = newName;
IsStackLocation = original.IsStackLocation;
OriginalVariableIndex = original.OriginalVariableIndex;
Parameter = original.Parameter;
Variable = original.Variable;
}
public override string ToString()
{
return Name;
}
/// <summary>
/// Gets whether this variable has only a single assignment.
/// This field is initialized in TransformToSsa step.
/// </summary>
/// <remarks>Not all variables can be transformed to single assignment form: variables that have their address taken
/// cannot be represented in SSA (although SimplifyByRefCalls will get rid of the address-taking instruction in almost all cases)</remarks>
public bool IsSingleAssignment;
/// <summary>
/// Gets the instruction defining the variable.
/// This field is initialized in TransformToSsa step. It is only set for variables with a single assignment.
/// </summary>
public SsaInstruction Definition;
/// <summary>
/// Gets the places where a variable is used.
/// If a single instruction reads a variable 2 times (e.g. adding to itself), then it must be included 2 times in this list!
/// </summary>
public List<SsaInstruction> Usage;
}
}

Просмотреть файл

@ -1,254 +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.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Convers a method to static single assignment form.
/// </summary>
internal sealed class TransformToSsa
{
public static void Transform(ControlFlowGraph cfg, SsaForm ssa, bool optimize = true)
{
TransformToSsa transform = new TransformToSsa(cfg, ssa);
transform.ConvertVariablesToSsa();
SsaOptimization.RemoveDeadAssignments(ssa); // required so that 'MakeByRefCallsSimple' can detect more cases
if (SimplifyByRefCalls.MakeByRefCallsSimple(ssa)) {
transform.ConvertVariablesToSsa();
}
if (optimize)
SsaOptimization.Optimize(ssa);
}
readonly ControlFlowGraph cfg;
readonly SsaForm ssaForm;
readonly List<SsaInstruction>[] writeToOriginalVariables; // array index -> SsaVariable OriginalVariableIndex
readonly bool[] addressTaken; // array index -> SsaVariable OriginalVariableIndex; value = whether ldloca instruction was used with variable
TransformToSsa(ControlFlowGraph cfg, SsaForm ssaForm)
{
this.cfg = cfg;
this.ssaForm = ssaForm;
writeToOriginalVariables = new List<SsaInstruction>[ssaForm.OriginalVariables.Count];
addressTaken = new bool[ssaForm.OriginalVariables.Count];
}
#region CollectInformationAboutOriginalVariableUse
void CollectInformationAboutOriginalVariableUse()
{
Debug.Assert(addressTaken.Length == writeToOriginalVariables.Length);
for (int i = 0; i < writeToOriginalVariables.Length; i++) {
Debug.Assert(ssaForm.OriginalVariables[i].OriginalVariableIndex == i);
addressTaken[i] = false;
// writeToOriginalVariables is only used when placing phi functions
// we don't need to do that anymore for variables that are already in SSA form
if (ssaForm.OriginalVariables[i].IsSingleAssignment)
writeToOriginalVariables[i] = null;
else
writeToOriginalVariables[i] = new List<SsaInstruction>();
}
foreach (SsaBlock block in ssaForm.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
if (inst.Target != null ) {
var list = writeToOriginalVariables[inst.Target.OriginalVariableIndex];
if (list != null)
list.Add(inst);
}
if (inst.Instruction != null) {
if (inst.Instruction.OpCode == OpCodes.Ldloca) {
addressTaken[ssaForm.GetOriginalVariable((VariableDefinition)inst.Instruction.Operand).OriginalVariableIndex] = true;
} else if (inst.Instruction.OpCode == OpCodes.Ldarga) {
addressTaken[ssaForm.GetOriginalVariable((ParameterDefinition)inst.Instruction.Operand).OriginalVariableIndex] = true;
}
}
}
}
}
#endregion
#region ConvertToSsa
void ConvertVariablesToSsa()
{
CollectInformationAboutOriginalVariableUse();
bool[] processVariable = new bool[ssaForm.OriginalVariables.Count];
foreach (SsaVariable variable in ssaForm.OriginalVariables) {
if (!variable.IsSingleAssignment && !addressTaken[variable.OriginalVariableIndex]) {
PlacePhiFunctions(variable);
processVariable[variable.OriginalVariableIndex] = true;
}
}
RenameVariables(processVariable);
foreach (SsaVariable variable in ssaForm.OriginalVariables) {
if (!addressTaken[variable.OriginalVariableIndex]) {
Debug.Assert(variable.IsSingleAssignment && variable.Definition != null);
}
}
ssaForm.ComputeVariableUsage();
}
#endregion
#region PlacePhiFunctions
void PlacePhiFunctions(SsaVariable variable)
{
cfg.ResetVisited();
HashSet<SsaBlock> blocksWithPhi = new HashSet<SsaBlock>();
Queue<ControlFlowNode> worklist = new Queue<ControlFlowNode>();
foreach (SsaInstruction writeInstruction in writeToOriginalVariables[variable.OriginalVariableIndex]) {
ControlFlowNode cfgNode = cfg.Nodes[writeInstruction.ParentBlock.BlockIndex];
if (!cfgNode.Visited) {
cfgNode.Visited = true;
worklist.Enqueue(cfgNode);
}
}
while (worklist.Count > 0) {
ControlFlowNode cfgNode = worklist.Dequeue();
foreach (ControlFlowNode dfNode in cfgNode.DominanceFrontier) {
// we don't need phi functions in the exit node
if (dfNode.NodeType == ControlFlowNodeType.RegularExit || dfNode.NodeType == ControlFlowNodeType.ExceptionalExit)
continue;
SsaBlock y = ssaForm.Blocks[dfNode.BlockIndex];
if (blocksWithPhi.Add(y)) {
// add a phi instruction in y
SsaVariable[] operands = Enumerable.Repeat(variable, dfNode.Incoming.Count).ToArray();
y.Instructions.Insert(0, new SsaInstruction(y, null, variable, operands, specialOpCode: SpecialOpCode.Phi));
if (!dfNode.Visited) {
dfNode.Visited = true;
worklist.Enqueue(dfNode);
}
}
}
}
}
#endregion
#region RenameVariable
int tempVariableCounter = 1;
void RenameVariables(bool[] processVariable)
{
VariableRenamer r = new VariableRenamer(this, processVariable);
r.Visit(ssaForm.EntryPoint);
}
sealed class VariableRenamer
{
readonly TransformToSsa transform;
readonly ReadOnlyCollection<SsaVariable> inputVariables;
internal readonly Stack<SsaVariable>[] versionStacks;
int[] versionCounters; // specifies for each input variable the next version number
// processVariable = specifies for each input variable whether we should rename it
public VariableRenamer(TransformToSsa transform, bool[] processVariable)
{
this.transform = transform;
inputVariables = transform.ssaForm.OriginalVariables;
Debug.Assert(inputVariables.Count == processVariable.Length);
versionCounters = new int[inputVariables.Count];
versionStacks = new Stack<SsaVariable>[inputVariables.Count];
for (int i = 0; i < versionStacks.Length; i++) {
if (processVariable[i]) {
Debug.Assert(inputVariables[i].IsSingleAssignment == false);
// only create version stacks for the variables that we need to process and that weren't already processed earlier
versionStacks[i] = new Stack<SsaVariable>();
versionStacks[i].Push(inputVariables[i]);
}
}
}
SsaVariable MakeNewVersion(int variableIndex)
{
int versionCounter = ++versionCounters[variableIndex];
SsaVariable x = inputVariables[variableIndex];
if (versionCounter == 1) {
return x;
} else {
if (x.IsStackLocation) {
return new SsaVariable(x, "temp" + (transform.tempVariableCounter++));
} else {
return new SsaVariable(x, x.Name + "_" + versionCounter);
}
}
}
internal void Visit(SsaBlock block)
{
// duplicate top of all stacks
foreach (var stack in versionStacks) {
if (stack != null)
stack.Push(stack.Peek());
}
foreach (SsaInstruction s in block.Instructions) {
// replace all uses of variables being processed with their current version.
if (s.SpecialOpCode != SpecialOpCode.Phi) {
for (int i = 0; i < s.Operands.Length; i++) {
var stack = versionStacks[s.Operands[i].OriginalVariableIndex];
if (stack != null)
s.Operands[i] = stack.Peek();
}
}
// if we're writing to a variable we should process:
if (s.Target != null) {
int targetIndex = s.Target.OriginalVariableIndex;
if (versionStacks[targetIndex] != null) {
s.Target = MakeNewVersion(targetIndex);
s.Target.IsSingleAssignment = true;
s.Target.Definition = s;
// we already pushed our entry for this SsaBlock at the beginning (where we duplicated all stacks),
// so now replace the top element
versionStacks[targetIndex].Pop();
versionStacks[targetIndex].Push(s.Target);
}
}
}
foreach (SsaBlock succ in block.Successors) {
int j = succ.Predecessors.IndexOf(block);
Debug.Assert(j >= 0);
foreach (SsaInstruction f in succ.Instructions) {
if (f.SpecialOpCode == SpecialOpCode.Phi) {
var stack = versionStacks[f.Target.OriginalVariableIndex];
if (stack != null) {
f.Operands[j] = stack.Peek();
}
}
}
}
foreach (ControlFlowNode child in transform.cfg.Nodes[block.BlockIndex].DominatorTreeChildren)
Visit(transform.ssaForm.Blocks[child.BlockIndex]);
// restore stacks:
foreach (var stack in versionStacks) {
if (stack != null)
stack.Pop();
}
}
}
#endregion
}
}

Просмотреть файл

@ -1,167 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<PropertyGroup>
<ProjectGuid>{984CC812-9470-4A13-AFF9-CC44068D666C}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<OutputType>Library</OutputType>
<RootNamespace>ICSharpCode.Decompiler</RootNamespace>
<AssemblyName>ICSharpCode.Decompiler</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<AppDesignerFolder>Properties</AppDesignerFolder>
<AllowUnsafeBlocks>False</AllowUnsafeBlocks>
<NoStdLib>False</NoStdLib>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<SignAssembly>false</SignAssembly>
<AssemblyOriginatorKeyFile>..\NRefactory\ICSharpCode.NRefactory.snk</AssemblyOriginatorKeyFile>
<DelaySign>False</DelaySign>
<AssemblyOriginatorKeyMode>File</AssemblyOriginatorKeyMode>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<RegisterForComInterop>False</RegisterForComInterop>
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
<BaseAddress>448462848</BaseAddress>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<OutputPath>bin\Debug\</OutputPath>
<DebugSymbols>true</DebugSymbols>
<DebugType>Full</DebugType>
<Optimize>False</Optimize>
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputPath>bin\Release\</OutputPath>
<DebugSymbols>true</DebugSymbols>
<DebugType>PdbOnly</DebugType>
<Optimize>True</Optimize>
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>0219;0414</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>0219;0414</NoWarn>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="ICSharpCode.NRefactory">
<HintPath>..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.NRefactory.Cecil">
<HintPath>..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.Cecil.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.NRefactory.CSharp">
<HintPath>..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.CSharp.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.NRefactory.Xml">
<HintPath>..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.Xml.dll</HintPath>
</Reference>
<Reference Include="Mono.Cecil">
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll</HintPath>
</Reference>
<Reference Include="Mono.Cecil.Mdb">
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Mdb.dll</HintPath>
</Reference>
<Reference Include="Mono.Cecil.Pdb">
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Pdb.dll</HintPath>
</Reference>
<Reference Include="Mono.Cecil.Rocks">
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.Rocks.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Ast\Annotations.cs" />
<Compile Include="Ast\AstBuilder.cs" />
<Compile Include="Ast\AstMethodBodyBuilder.cs" />
<Compile Include="Ast\CommentStatement.cs" />
<Compile Include="Ast\DecompilerContext.cs" />
<Compile Include="Ast\NameVariables.cs" />
<Compile Include="Ast\NRefactoryExtensions.cs" />
<Compile Include="Ast\TextTokenWriter.cs" />
<Compile Include="Ast\Transforms\AddCheckedBlocks.cs" />
<Compile Include="Ast\Transforms\CombineQueryExpressions.cs" />
<Compile Include="Ast\Transforms\ContextTrackingVisitor.cs" />
<Compile Include="Ast\Transforms\ConvertConstructorCallIntoInitializer.cs" />
<Compile Include="Ast\Transforms\CustomPatterns.cs" />
<Compile Include="Ast\Transforms\DecimalConstantTransform.cs" />
<Compile Include="Ast\Transforms\DeclareVariables.cs" />
<Compile Include="Ast\Transforms\DelegateConstruction.cs" />
<Compile Include="Ast\Transforms\ExpressionTreeConverter.cs" />
<Compile Include="Ast\Transforms\FlattenSwitchBlocks.cs" />
<Compile Include="Ast\Transforms\IntroduceExtensionMethods.cs" />
<Compile Include="Ast\Transforms\IntroduceQueryExpressions.cs" />
<Compile Include="Ast\Transforms\IntroduceUnsafeModifier.cs" />
<Compile Include="Ast\Transforms\IntroduceUsingDeclarations.cs" />
<Compile Include="Ast\Transforms\ReplaceMethodCallsWithOperators.cs" />
<Compile Include="Ast\Transforms\PushNegation.cs" />
<Compile Include="Ast\Transforms\TransformationPipeline.cs" />
<Compile Include="Ast\Transforms\PatternStatementTransform.cs" />
<Compile Include="Ast\TypesHierarchyHelpers.cs" />
<Compile Include="CecilExtensions.cs" />
<Compile Include="CodeMappings.cs" />
<Compile Include="DecompilerException.cs" />
<Compile Include="DecompilerSettings.cs" />
<Compile Include="Disassembler\DisassemblerHelpers.cs" />
<Compile Include="Disassembler\ILStructure.cs" />
<Compile Include="Disassembler\MethodBodyDisassembler.cs" />
<Compile Include="Disassembler\ReflectionDisassembler.cs" />
<Compile Include="FlowAnalysis\ControlFlowEdge.cs" />
<Compile Include="FlowAnalysis\ControlFlowGraph.cs" />
<Compile Include="FlowAnalysis\ControlFlowGraphBuilder.cs" />
<Compile Include="FlowAnalysis\ControlFlowNode.cs" />
<Compile Include="FlowAnalysis\ControlStructureDetector.cs" />
<Compile Include="FlowAnalysis\OpCodeInfo.cs" />
<Compile Include="FlowAnalysis\SimplifyByRefCalls.cs" />
<Compile Include="FlowAnalysis\SsaBlock.cs" />
<Compile Include="FlowAnalysis\SsaForm.cs" />
<Compile Include="FlowAnalysis\SsaFormBuilder.cs" />
<Compile Include="FlowAnalysis\SsaInstruction.cs" />
<Compile Include="FlowAnalysis\SsaOptimization.cs" />
<Compile Include="FlowAnalysis\SsaVariable.cs" />
<Compile Include="FlowAnalysis\TransformToSsa.cs" />
<Compile Include="ILAst\AsyncDecompiler.cs" />
<Compile Include="ILAst\LiftedOperators.cs" />
<Compile Include="ILAst\InitializerPeepholeTransforms.cs" />
<Compile Include="ILAst\DefaultDictionary.cs" />
<Compile Include="ILAst\GotoRemoval.cs" />
<Compile Include="ILAst\ILAstBuilder.cs" />
<Compile Include="ILAst\ILAstOptimizer.cs" />
<Compile Include="ILAst\ILAstTypes.cs" />
<Compile Include="ILAst\ILCodes.cs" />
<Compile Include="ILAst\ILInlining.cs" />
<Compile Include="ILAst\LoopsAndConditions.cs" />
<Compile Include="ILAst\PatternMatching.cs" />
<Compile Include="ILAst\PeepholeTransform.cs" />
<Compile Include="ILAst\SimpleControlFlow.cs" />
<Compile Include="ILAst\StateRange.cs" />
<Compile Include="ILAst\SymbolicExecution.cs" />
<Compile Include="ILAst\TypeAnalysis.cs" />
<Compile Include="ILAst\YieldReturnDecompiler.cs" />
<Compile Include="ITextOutput.cs" />
<Compile Include="PlainTextOutput.cs" />
<Compile Include="ReferenceResolvingException.cs" />
<Compile Include="TextOutputWriter.cs" />
<None Include="Properties\AssemblyInfo.template.cs" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
</Target>
</Project>

Просмотреть файл

@ -1,17 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Decompiler", "ICSharpCode.Decompiler.csproj", "{984CC812-9470-4A13-AFF9-CC44068D666C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{984CC812-9470-4A13-AFF9-CC44068D666C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

Просмотреть файл

@ -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>
internal 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);
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 @@
// 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>
internal 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)
{
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();
}
IEnumerator IEnumerable.GetEnumerator()
{
return dict.GetEnumerator();
}
}
}

Просмотреть файл

@ -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,839 +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)
{
Definitions = definitions;
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)
{
Definitions = definitions;
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_" + 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 (Code == ILCode.Stloc) || (Code == ILCode.Ldloca && Next != null && Next.Code == ILCode.Initobj);
}
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
// Label
sb.Append(Name);
sb.Append(':');
if (Label != null)
sb.Append('*');
// Name
sb.Append(' ');
if (Prefixes != null) {
foreach (var prefix in Prefixes) {
sb.Append(prefix.OpCode.Name);
sb.Append(' ');
}
}
sb.Append(Code.GetName());
if (Operand != null) {
sb.Append(' ');
if (Operand is Instruction) {
sb.Append("IL_" + ((Instruction)Operand).Offset.ToString("X2"));
} else if (Operand is Instruction[]) {
foreach(Instruction inst in (Instruction[])Operand) {
sb.Append("IL_" + inst.Offset.ToString("X2"));
sb.Append(" ");
}
} else if (Operand is ILLabel) {
sb.Append(((ILLabel)Operand).Name);
} else if (Operand is ILLabel[]) {
foreach(ILLabel label in (ILLabel[])Operand) {
sb.Append(label.Name);
sb.Append(" ");
}
} else {
sb.Append(Operand.ToString());
}
}
if (StackBefore != null) {
sb.Append(" StackBefore={");
bool first = true;
foreach (StackSlot slot in 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 (StoreTo != null && StoreTo.Count > 0) {
sb.Append(" StoreTo={");
bool first = true;
foreach (ILVariable stackVar in StoreTo) {
if (!first) sb.Append(",");
sb.Append(stackVar.Name);
first = false;
}
sb.Append("}");
}
if (VariablesBefore != null) {
sb.Append(" VarsBefore={");
bool first = true;
foreach (VariableSlot varSlot in 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, 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;
}
static int offsetIdx;
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++) {
var offset = byteCode.Offset != 0 ? byteCode.Offset : offsetIdx++;
ILVariable tmpVar = new ILVariable() { Name = string.Format("arg_{0:X2}_{1}", 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
var offset = byteCode.Offset != 0 ? byteCode.Offset : offsetIdx++;
ILVariable tmpVar = new ILVariable() { Name = string.Format("expr_{0:X2}", 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) {
Parameters.Add(new ILVariable { Type = p.ParameterType, Name = p.Name, OriginalParameter = p });
}
if (Parameters.Count > 0 && (methodDef.IsSetter || methodDef.IsAddOn || methodDef.IsRemoveOn)) {
// last parameter must be 'value', so rename it
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 : Parameters[p.Index];
break;
case ILCode.__Starg:
p = (ParameterDefinition)byteCode.Operand;
byteCode.Code = ILCode.Stloc;
byteCode.Operand = p.Index < 0 ? thisParameter : Parameters[p.Index];
break;
case ILCode.__Ldarga:
p = (ParameterDefinition)byteCode.Operand;
byteCode.Code = ILCode.Ldloca;
byteCode.Operand = p.Index < 0 ? thisParameter : Parameters[p.Index];
break;
}
}
if (thisParameter != null)
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 {
var offset = byteCode.Offset != 0 ? byteCode.Offset : offsetIdx++;
ILVariable tmpVar = new ILVariable() { Name = "expr_" + 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();
}
}
}

Просмотреть файл

@ -1,988 +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.FlowAnalysis;
using ICSharpCode.NRefactory.Utils;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.CSharp;
namespace ICSharpCode.Decompiler.ILAst
{
public enum ILAstOptimizationStep
{
RemoveRedundantCode,
ReduceBranchInstructionSet,
InlineVariables,
CopyPropagation,
YieldReturn,
AsyncAwait,
PropertyAccessInstructions,
SplitToMovableBlocks,
TypeInference,
HandlePointerArithmetic,
SimplifyShortCircuit,
SimplifyTernaryOperator,
SimplifyNullCoalescing,
JoinBasicBlocks,
SimplifyLogicNot,
SimplifyShiftOperators,
TypeConversionSimplifications,
SimplifyLdObjAndStObj,
SimplifyCustomShortCircuit,
SimplifyLiftedOperators,
TransformArrayInitializers,
TransformMultidimensionalArrayInitializers,
TransformObjectInitializers,
MakeAssignmentExpression,
IntroducePostIncrement,
InlineExpressionTreeParameterDeclarations,
InlineVariables2,
FindLoops,
FindConditions,
FlattenNestedMovableBlocks,
RemoveEndFinally,
RemoveRedundantCode2,
GotoRemoval,
DuplicateReturns,
GotoRemoval2,
ReduceIfNesting,
InlineVariables3,
CachedDelegateInitialization,
IntroduceFixedStatements,
RecombineVariables,
TypeInference2,
RemoveRedundantCode3,
None
}
public partial class ILAstOptimizer
{
int nextLabelIndex = 0;
DecompilerContext context;
TypeSystem typeSystem;
ILBlock method;
public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None)
{
this.context = context;
typeSystem = context.CurrentMethod.Module.TypeSystem;
this.method = method;
if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode) return;
RemoveRedundantCode(method);
if (abortBeforeStep == ILAstOptimizationStep.ReduceBranchInstructionSet) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
ReduceBranchInstructionSet(block);
}
// ReduceBranchInstructionSet runs before inlining because the non-aggressive inlining heuristic
// looks at which type of instruction consumes the inlined variable.
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables) return;
// Works better after simple goto removal because of the following debug pattern: stloc X; br Next; Next:; ldloc X
ILInlining inlining1 = new ILInlining(method);
inlining1.InlineAllVariables();
if (abortBeforeStep == ILAstOptimizationStep.CopyPropagation) return;
inlining1.CopyPropagation();
if (abortBeforeStep == ILAstOptimizationStep.YieldReturn) return;
YieldReturnDecompiler.Run(context, method);
AsyncDecompiler.RunStep1(context, method);
if (abortBeforeStep == ILAstOptimizationStep.AsyncAwait) return;
AsyncDecompiler.RunStep2(context, method);
if (abortBeforeStep == ILAstOptimizationStep.PropertyAccessInstructions) return;
IntroducePropertyAccessInstructions(method);
if (abortBeforeStep == ILAstOptimizationStep.SplitToMovableBlocks) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
SplitToBasicBlocks(block);
}
if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return;
// Types are needed for the ternary operator optimization
TypeAnalysis.Run(context, method);
if (abortBeforeStep == ILAstOptimizationStep.HandlePointerArithmetic) return;
HandlePointerArithmetic(method);
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
bool modified;
do {
modified = false;
if (abortBeforeStep == ILAstOptimizationStep.SimplifyShortCircuit) return;
modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyShortCircuit);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyTernaryOperator) return;
modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyTernaryOperator);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyNullCoalescing) return;
modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyNullCoalescing);
if (abortBeforeStep == ILAstOptimizationStep.JoinBasicBlocks) return;
modified |= block.RunOptimization(new SimpleControlFlow(context, method).JoinBasicBlocks);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyLogicNot) return;
modified |= block.RunOptimization(SimplifyLogicNot);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyShiftOperators) return;
modified |= block.RunOptimization(SimplifyShiftOperators);
if (abortBeforeStep == ILAstOptimizationStep.TypeConversionSimplifications) return;
modified |= block.RunOptimization(TypeConversionSimplifications);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyLdObjAndStObj) return;
modified |= block.RunOptimization(SimplifyLdObjAndStObj);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyCustomShortCircuit) return;
modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyCustomShortCircuit);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyLiftedOperators) return;
modified |= block.RunOptimization(SimplifyLiftedOperators);
if (abortBeforeStep == ILAstOptimizationStep.TransformArrayInitializers) return;
modified |= block.RunOptimization(TransformArrayInitializers);
if (abortBeforeStep == ILAstOptimizationStep.TransformMultidimensionalArrayInitializers) return;
modified |= block.RunOptimization(TransformMultidimensionalArrayInitializers);
if (abortBeforeStep == ILAstOptimizationStep.TransformObjectInitializers) return;
modified |= block.RunOptimization(TransformObjectInitializers);
if (abortBeforeStep == ILAstOptimizationStep.MakeAssignmentExpression) return;
if (context.Settings.MakeAssignmentExpressions) {
modified |= block.RunOptimization(MakeAssignmentExpression);
}
modified |= block.RunOptimization(MakeCompoundAssignments);
if (abortBeforeStep == ILAstOptimizationStep.IntroducePostIncrement) return;
if (context.Settings.IntroduceIncrementAndDecrement) {
modified |= block.RunOptimization(IntroducePostIncrement);
}
if (abortBeforeStep == ILAstOptimizationStep.InlineExpressionTreeParameterDeclarations) return;
if (context.Settings.ExpressionTrees) {
modified |= block.RunOptimization(InlineExpressionTreeParameterDeclarations);
}
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return;
modified |= new ILInlining(method).InlineAllInBlock(block);
new ILInlining(method).CopyPropagation();
} while(modified);
}
if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
new LoopsAndConditions(context).FindLoops(block);
}
if (abortBeforeStep == ILAstOptimizationStep.FindConditions) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
new LoopsAndConditions(context).FindConditions(block);
}
if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return;
FlattenBasicBlocks(method);
if (abortBeforeStep == ILAstOptimizationStep.RemoveEndFinally) return;
RemoveEndFinally(method);
if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode2) return;
RemoveRedundantCode(method);
if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) return;
new GotoRemoval().RemoveGotos(method);
if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) return;
DuplicateReturnStatements(method);
if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval2) return;
new GotoRemoval().RemoveGotos(method);
if (abortBeforeStep == ILAstOptimizationStep.ReduceIfNesting) return;
ReduceIfNesting(method);
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables3) return;
// The 2nd inlining pass is necessary because DuplicateReturns and the introduction of ternary operators
// open up additional inlining possibilities.
new ILInlining(method).InlineAllVariables();
if (abortBeforeStep == ILAstOptimizationStep.CachedDelegateInitialization) return;
if (context.Settings.AnonymousMethods) {
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = 0; i < block.Body.Count; i++) {
// TODO: Move before loops
CachedDelegateInitializationWithField(block, ref i);
CachedDelegateInitializationWithLocal(block, ref i);
}
}
}
if (abortBeforeStep == ILAstOptimizationStep.IntroduceFixedStatements) return;
// we need post-order traversal, not pre-order, for "fixed" to work correctly
foreach (ILBlock block in TreeTraversal.PostOrder<ILNode>(method, n => n.GetChildren()).OfType<ILBlock>()) {
for (int i = block.Body.Count - 1; i >= 0; i--) {
// TODO: Move before loops
if (i < block.Body.Count)
IntroduceFixedStatements(block.Body, i);
}
}
if (abortBeforeStep == ILAstOptimizationStep.RecombineVariables) return;
RecombineVariables(method);
if (abortBeforeStep == ILAstOptimizationStep.TypeInference2) return;
TypeAnalysis.Reset(method);
TypeAnalysis.Run(context, method);
if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode3) return;
GotoRemoval.RemoveRedundantCode(method);
// ReportUnassignedILRanges(method);
}
/// <summary>
/// Removes redundatant Br, Nop, Dup, Pop
/// Ignore arguments of 'leave'
/// </summary>
/// <param name="method"></param>
internal static void RemoveRedundantCode(ILBlock method)
{
Dictionary<ILLabel, int> labelRefCount = new Dictionary<ILLabel, int>();
foreach (ILLabel target in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) {
labelRefCount[target] = labelRefCount.GetOrDefault(target) + 1;
}
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
List<ILNode> body = block.Body;
List<ILNode> newBody = new List<ILNode>(body.Count);
for (int i = 0; i < body.Count; i++) {
ILLabel target;
ILExpression popExpr;
if (body[i].Match(ILCode.Br, out target) && i+1 < body.Count && body[i+1] == target) {
// Ignore the branch
if (labelRefCount[target] == 1)
i++; // Ignore the label as well
} else if (body[i].Match(ILCode.Nop)){
// Ignore nop
} else if (body[i].Match(ILCode.Pop, out popExpr)) {
ILVariable v;
if (!popExpr.Match(ILCode.Ldloc, out v))
throw new Exception("Pop should have just ldloc at this stage");
// Best effort to move the ILRange to previous statement
ILVariable prevVar;
ILExpression prevExpr;
if (i - 1 >= 0 && body[i - 1].Match(ILCode.Stloc, out prevVar, out prevExpr) && prevVar == v)
prevExpr.ILRanges.AddRange(((ILExpression)body[i]).ILRanges);
// Ignore pop
} else {
ILLabel label = body[i] as ILLabel;
if (label != null) {
if (labelRefCount.GetOrDefault(label) > 0)
newBody.Add(label);
} else {
newBody.Add(body[i]);
}
}
}
block.Body = newBody;
}
// Ignore arguments of 'leave'
foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.Code == ILCode.Leave)) {
if (expr.Arguments.Any(arg => !arg.Match(ILCode.Ldloc)))
throw new Exception("Leave should have just ldloc at this stage");
expr.Arguments.Clear();
}
// 'dup' removal
foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
for (int i = 0; i < expr.Arguments.Count; i++) {
ILExpression child;
if (expr.Arguments[i].Match(ILCode.Dup, out child)) {
child.ILRanges.AddRange(expr.Arguments[i].ILRanges);
expr.Arguments[i] = child;
}
}
}
}
/// <summary>
/// Reduces the branch codes to just br and brtrue.
/// Moves ILRanges to the branch argument
/// </summary>
void ReduceBranchInstructionSet(ILBlock block)
{
for (int i = 0; i < block.Body.Count; i++) {
ILExpression expr = block.Body[i] as ILExpression;
if (expr != null && expr.Prefixes == null) {
ILCode op;
switch(expr.Code) {
case ILCode.Switch:
case ILCode.Brtrue:
expr.Arguments.Single().ILRanges.AddRange(expr.ILRanges);
expr.ILRanges.Clear();
continue;
case ILCode.__Brfalse: op = ILCode.LogicNot; break;
case ILCode.__Beq: op = ILCode.Ceq; break;
case ILCode.__Bne_Un: op = ILCode.Cne; break;
case ILCode.__Bgt: op = ILCode.Cgt; break;
case ILCode.__Bgt_Un: op = ILCode.Cgt_Un; break;
case ILCode.__Ble: op = ILCode.Cle; break;
case ILCode.__Ble_Un: op = ILCode.Cle_Un; break;
case ILCode.__Blt: op = ILCode.Clt; break;
case ILCode.__Blt_Un: op = ILCode.Clt_Un; break;
case ILCode.__Bge: op = ILCode.Cge; break;
case ILCode.__Bge_Un: op = ILCode.Cge_Un; break;
default:
continue;
}
var newExpr = new ILExpression(op, null, expr.Arguments);
block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, newExpr);
newExpr.ILRanges = expr.ILRanges;
}
}
}
/// <summary>
/// Converts call and callvirt instructions that read/write properties into CallGetter/CallSetter instructions.
///
/// CallGetter/CallSetter is used to allow the ILAst to represent "while ((SomeProperty = value) != null)".
///
/// Also simplifies 'newobj(SomeDelegate, target, ldvirtftn(F, target))' to 'newobj(SomeDelegate, target, ldvirtftn(F))'
/// </summary>
void IntroducePropertyAccessInstructions(ILNode node)
{
ILExpression parentExpr = node as ILExpression;
if (parentExpr != null) {
for (int i = 0; i < parentExpr.Arguments.Count; i++) {
ILExpression expr = parentExpr.Arguments[i];
IntroducePropertyAccessInstructions(expr);
IntroducePropertyAccessInstructions(expr, parentExpr, i);
}
} else {
foreach (ILNode child in node.GetChildren()) {
IntroducePropertyAccessInstructions(child);
ILExpression expr = child as ILExpression;
if (expr != null) {
IntroducePropertyAccessInstructions(expr, null, -1);
}
}
}
}
void IntroducePropertyAccessInstructions(ILExpression expr, ILExpression parentExpr, int posInParent)
{
if (expr.Code == ILCode.Call || expr.Code == ILCode.Callvirt) {
MethodReference cecilMethod = (MethodReference)expr.Operand;
if (cecilMethod.DeclaringType is ArrayType) {
switch (cecilMethod.Name) {
case "Get":
expr.Code = ILCode.CallGetter;
break;
case "Set":
expr.Code = ILCode.CallSetter;
break;
case "Address":
ByReferenceType brt = cecilMethod.ReturnType as ByReferenceType;
if (brt != null) {
MethodReference getMethod = new MethodReference("Get", brt.ElementType, cecilMethod.DeclaringType);
foreach (var p in cecilMethod.Parameters)
getMethod.Parameters.Add(p);
getMethod.HasThis = cecilMethod.HasThis;
expr.Operand = getMethod;
}
expr.Code = ILCode.CallGetter;
if (parentExpr != null) {
parentExpr.Arguments[posInParent] = new ILExpression(ILCode.AddressOf, null, expr);
}
break;
}
} else {
MethodDefinition cecilMethodDef = cecilMethod.Resolve();
if (cecilMethodDef != null) {
if (cecilMethodDef.IsGetter)
expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallGetter : ILCode.CallvirtGetter;
else if (cecilMethodDef.IsSetter)
expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallSetter : ILCode.CallvirtSetter;
}
}
} else if (expr.Code == ILCode.Newobj && expr.Arguments.Count == 2) {
// Might be 'newobj(SomeDelegate, target, ldvirtftn(F, target))'.
ILVariable target;
if (expr.Arguments[0].Match(ILCode.Ldloc, out target)
&& expr.Arguments[1].Code == ILCode.Ldvirtftn
&& expr.Arguments[1].Arguments.Count == 1
&& expr.Arguments[1].Arguments[0].MatchLdloc(target))
{
// Remove the 'target' argument from the ldvirtftn instruction.
// It's not needed in the translation to C#, and needs to be eliminated so that the target expression
// can be inlined.
expr.Arguments[1].Arguments.Clear();
}
}
}
/// <summary>
/// Group input into a set of blocks that can be later arbitraliby schufled.
/// The method adds necessary branches to make control flow between blocks
/// explicit and thus order independent.
/// </summary>
void SplitToBasicBlocks(ILBlock block)
{
List<ILNode> basicBlocks = new List<ILNode>();
ILLabel entryLabel = block.Body.FirstOrDefault() as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++) };
ILBasicBlock basicBlock = new ILBasicBlock();
basicBlocks.Add(basicBlock);
basicBlock.Body.Add(entryLabel);
block.EntryGoto = new ILExpression(ILCode.Br, entryLabel);
if (block.Body.Count > 0) {
if (block.Body[0] != entryLabel)
basicBlock.Body.Add(block.Body[0]);
for (int i = 1; i < block.Body.Count; i++) {
ILNode lastNode = block.Body[i - 1];
ILNode currNode = block.Body[i];
// Start a new basic block if necessary
if (currNode is ILLabel ||
currNode is ILTryCatchBlock || // Counts as label
lastNode.IsConditionalControlFlow() ||
lastNode.IsUnconditionalControlFlow())
{
// Try to reuse the label
ILLabel label = currNode as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++).ToString() };
// Terminate the last block
if (!lastNode.IsUnconditionalControlFlow()) {
// Explicit branch from one block to other
basicBlock.Body.Add(new ILExpression(ILCode.Br, label));
}
// Start the new block
basicBlock = new ILBasicBlock();
basicBlocks.Add(basicBlock);
basicBlock.Body.Add(label);
// Add the node to the basic block
if (currNode != label)
basicBlock.Body.Add(currNode);
} else {
basicBlock.Body.Add(currNode);
}
}
}
block.Body = basicBlocks;
return;
}
void DuplicateReturnStatements(ILBlock method)
{
Dictionary<ILLabel, ILNode> nextSibling = new Dictionary<ILLabel, ILNode>();
// Build navigation data
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = 0; i < block.Body.Count - 1; i++) {
ILLabel curr = block.Body[i] as ILLabel;
if (curr != null) {
nextSibling[curr] = block.Body[i + 1];
}
}
}
// Duplicate returns
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = 0; i < block.Body.Count; i++) {
ILLabel targetLabel;
if (block.Body[i].Match(ILCode.Br, out targetLabel) || block.Body[i].Match(ILCode.Leave, out targetLabel)) {
// Skip extra labels
while(nextSibling.ContainsKey(targetLabel) && nextSibling[targetLabel] is ILLabel) {
targetLabel = (ILLabel)nextSibling[targetLabel];
}
// Inline return statement
ILNode target;
List<ILExpression> retArgs;
if (nextSibling.TryGetValue(targetLabel, out target)) {
if (target.Match(ILCode.Ret, out retArgs)) {
ILVariable locVar;
object constValue;
if (retArgs.Count == 0) {
block.Body[i] = new ILExpression(ILCode.Ret, null);
} else if (retArgs.Single().Match(ILCode.Ldloc, out locVar)) {
block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldloc, locVar));
} else if (retArgs.Single().Match(ILCode.Ldc_I4, out constValue)) {
block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldc_I4, constValue));
}
}
} else {
if (method.Body.Count > 0 && method.Body.Last() == targetLabel) {
// It exits the main method - so it is same as return;
block.Body[i] = new ILExpression(ILCode.Ret, null);
}
}
}
}
}
}
/// <summary>
/// Flattens all nested basic blocks, except the the top level 'node' argument
/// </summary>
void FlattenBasicBlocks(ILNode node)
{
ILBlock block = node as ILBlock;
if (block != null) {
List<ILNode> flatBody = new List<ILNode>();
foreach (ILNode child in block.GetChildren()) {
FlattenBasicBlocks(child);
ILBasicBlock childAsBB = child as ILBasicBlock;
if (childAsBB != null) {
if (!(childAsBB.Body.FirstOrDefault() is ILLabel))
throw new Exception("Basic block has to start with a label. \n" + childAsBB.ToString());
if (childAsBB.Body.LastOrDefault() is ILExpression && !childAsBB.Body.LastOrDefault().IsUnconditionalControlFlow())
throw new Exception("Basci block has to end with unconditional control flow. \n" + childAsBB.ToString());
flatBody.AddRange(childAsBB.GetChildren());
} else {
flatBody.Add(child);
}
}
block.EntryGoto = null;
block.Body = flatBody;
} else if (node is ILExpression) {
// Optimization - no need to check expressions
} else if (node != null) {
// Recursively find all ILBlocks
foreach(ILNode child in node.GetChildren()) {
FlattenBasicBlocks(child);
}
}
}
/// <summary>
/// Replace endfinally with jump to the end of the finally block
/// </summary>
void RemoveEndFinally(ILBlock method)
{
// Go thought the list in reverse so that we do the nested blocks first
foreach(var tryCatch in method.GetSelfAndChildrenRecursive<ILTryCatchBlock>(tc => tc.FinallyBlock != null).Reverse()) {
ILLabel label = new ILLabel() { Name = "EndFinally_" + nextLabelIndex++ };
tryCatch.FinallyBlock.Body.Add(label);
foreach(var block in tryCatch.FinallyBlock.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = 0; i < block.Body.Count; i++) {
if (block.Body[i].Match(ILCode.Endfinally)) {
block.Body[i] = new ILExpression(ILCode.Br, label).WithILRanges(((ILExpression)block.Body[i]).ILRanges);
}
}
}
}
}
/// <summary>
/// Reduce the nesting of conditions.
/// It should be done on flat data that already had most gotos removed
/// </summary>
void ReduceIfNesting(ILNode node)
{
ILBlock block = node as ILBlock;
if (block != null) {
for (int i = 0; i < block.Body.Count; i++) {
ILCondition cond = block.Body[i] as ILCondition;
if (cond != null) {
bool trueExits = cond.TrueBlock.Body.LastOrDefault().IsUnconditionalControlFlow();
bool falseExits = cond.FalseBlock.Body.LastOrDefault().IsUnconditionalControlFlow();
if (trueExits) {
// Move the false block after the condition
block.Body.InsertRange(i + 1, cond.FalseBlock.GetChildren());
cond.FalseBlock = new ILBlock();
} else if (falseExits) {
// Move the true block after the condition
block.Body.InsertRange(i + 1, cond.TrueBlock.GetChildren());
cond.TrueBlock = new ILBlock();
}
// Eliminate empty true block
if (!cond.TrueBlock.GetChildren().Any() && cond.FalseBlock.GetChildren().Any()) {
// Swap bodies
ILBlock tmp = cond.TrueBlock;
cond.TrueBlock = cond.FalseBlock;
cond.FalseBlock = tmp;
cond.Condition = new ILExpression(ILCode.LogicNot, null, cond.Condition);
}
}
}
}
// We are changing the number of blocks so we use plain old recursion to get all blocks
foreach(ILNode child in node.GetChildren()) {
if (child != null && !(child is ILExpression))
ReduceIfNesting(child);
}
}
void RecombineVariables(ILBlock method)
{
// Recombine variables that were split when the ILAst was created
// This ensures that a single IL variable is a single C# variable (gets assigned only one name)
// The DeclareVariables transformation might then split up the C# variable again if it is used indendently in two separate scopes.
Dictionary<VariableDefinition, ILVariable> dict = new Dictionary<VariableDefinition, ILVariable>();
ReplaceVariables(
method,
delegate(ILVariable v) {
if (v.OriginalVariable == null)
return v;
ILVariable combinedVariable;
if (!dict.TryGetValue(v.OriginalVariable, out combinedVariable)) {
dict.Add(v.OriginalVariable, v);
combinedVariable = v;
}
return combinedVariable;
});
}
static void HandlePointerArithmetic(ILNode method)
{
foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
List<ILExpression> args = expr.Arguments;
switch (expr.Code) {
case ILCode.Localloc:
args[0] = DivideBySize(args[0], ((PointerType)expr.InferredType).ElementType);
break;
case ILCode.Add:
case ILCode.Add_Ovf:
case ILCode.Add_Ovf_Un:
if (expr.InferredType is PointerType) {
if (args[0].ExpectedType is PointerType)
args[1] = DivideBySize(args[1], ((PointerType)expr.InferredType).ElementType);
else if (args[1].ExpectedType is PointerType)
args[0] = DivideBySize(args[0], ((PointerType)expr.InferredType).ElementType);
}
break;
case ILCode.Sub:
case ILCode.Sub_Ovf:
case ILCode.Sub_Ovf_Un:
if (expr.InferredType is PointerType) {
if (args[0].ExpectedType is PointerType)
args[1] = DivideBySize(args[1], ((PointerType)expr.InferredType).ElementType);
}
break;
}
}
}
static ILExpression UnwrapIntPtrCast(ILExpression expr)
{
if (expr.Code != ILCode.Conv_I && expr.Code != ILCode.Conv_U)
return expr;
ILExpression arg = expr.Arguments[0];
switch (arg.InferredType.MetadataType) {
case MetadataType.Byte:
case MetadataType.SByte:
case MetadataType.UInt16:
case MetadataType.Int16:
case MetadataType.UInt32:
case MetadataType.Int32:
case MetadataType.UInt64:
case MetadataType.Int64:
return arg;
}
return expr;
}
static ILExpression DivideBySize(ILExpression expr, TypeReference type)
{
expr = UnwrapIntPtrCast(expr);
ILExpression sizeOfExpression;
switch (TypeAnalysis.GetInformationAmount(type)) {
case 1:
case 8:
sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 1);
break;
case 16:
sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 2);
break;
case 32:
sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 4);
break;
case 64:
sizeOfExpression = new ILExpression(ILCode.Ldc_I4, 8);
break;
default:
sizeOfExpression = new ILExpression(ILCode.Sizeof, type);
break;
}
if (expr.Code == ILCode.Mul || expr.Code == ILCode.Mul_Ovf || expr.Code == ILCode.Mul_Ovf_Un) {
ILExpression mulArg = expr.Arguments[1];
if (mulArg.Code == sizeOfExpression.Code && sizeOfExpression.Operand.Equals(mulArg.Operand))
return UnwrapIntPtrCast(expr.Arguments[0]);
}
if (expr.Code == sizeOfExpression.Code) {
if (sizeOfExpression.Operand.Equals(expr.Operand))
return new ILExpression(ILCode.Ldc_I4, 1);
if (expr.Code == ILCode.Ldc_I4) {
int offsetInBytes = (int)expr.Operand;
int elementSize = (int)sizeOfExpression.Operand;
int offsetInElements = offsetInBytes / elementSize;
// ensure integer division
if (offsetInElements * elementSize == offsetInBytes) {
expr.Operand = offsetInElements;
return expr;
}
}
}
return new ILExpression(ILCode.Div_Un, null, expr, sizeOfExpression);
}
public static void ReplaceVariables(ILNode node, Func<ILVariable, ILVariable> variableMapping)
{
ILExpression expr = node as ILExpression;
if (expr != null) {
ILVariable v = expr.Operand as ILVariable;
if (v != null)
expr.Operand = variableMapping(v);
foreach (ILExpression child in expr.Arguments)
ReplaceVariables(child, variableMapping);
} else {
var catchBlock = node as ILTryCatchBlock.CatchBlock;
if (catchBlock != null && catchBlock.ExceptionVariable != null) {
catchBlock.ExceptionVariable = variableMapping(catchBlock.ExceptionVariable);
}
foreach (ILNode child in node.GetChildren())
ReplaceVariables(child, variableMapping);
}
}
void ReportUnassignedILRanges(ILBlock method)
{
var unassigned = ILRange.Invert(method.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.ILRanges), context.CurrentMethod.Body.CodeSize).ToList();
if (unassigned.Count > 0)
Debug.WriteLine(string.Format("Unassigned ILRanges for {0}.{1}: {2}", context.CurrentMethod.DeclaringType.Name, context.CurrentMethod.Name, string.Join(", ", unassigned.Select(r => r.ToString()))));
}
}
public static class ILAstOptimizerExtensionMethods
{
/// <summary>
/// Perform one pass of a given optimization on this block.
/// This block must consist of only basicblocks.
/// </summary>
public static bool RunOptimization(this ILBlock block, Func<List<ILNode>, ILBasicBlock, int, bool> optimization)
{
bool modified = false;
List<ILNode> body = block.Body;
for (int i = body.Count - 1; i >= 0; i--) {
if (i < body.Count && optimization(body, (ILBasicBlock)body[i], i)) {
modified = true;
}
}
return modified;
}
public static bool RunOptimization(this ILBlock block, Func<List<ILNode>, ILExpression, int, bool> optimization)
{
bool modified = false;
foreach (ILBasicBlock bb in block.Body) {
for (int i = bb.Body.Count - 1; i >= 0; i--) {
ILExpression expr = bb.Body.ElementAtOrDefault(i) as ILExpression;
if (expr != null && optimization(bb.Body, expr, i)) {
modified = true;
}
}
}
return modified;
}
public static bool IsConditionalControlFlow(this ILNode node)
{
ILExpression expr = node as ILExpression;
return expr != null && expr.Code.IsConditionalControlFlow();
}
public static bool IsUnconditionalControlFlow(this ILNode node)
{
ILExpression expr = node as ILExpression;
return expr != null && expr.Code.IsUnconditionalControlFlow();
}
/// <summary>
/// The expression has no effect on the program and can be removed
/// if its return value is not needed.
/// </summary>
public static bool HasNoSideEffects(this ILExpression expr)
{
// Remember that if expression can throw an exception, it is a side effect
switch(expr.Code) {
case ILCode.Ldloc:
case ILCode.Ldloca:
case ILCode.Ldstr:
case ILCode.Ldnull:
case ILCode.Ldc_I4:
case ILCode.Ldc_I8:
case ILCode.Ldc_R4:
case ILCode.Ldc_R8:
case ILCode.Ldc_Decimal:
return true;
default:
return false;
}
}
public static bool IsStoreToArray(this ILCode code)
{
switch (code) {
case ILCode.Stelem_Any:
case ILCode.Stelem_I:
case ILCode.Stelem_I1:
case ILCode.Stelem_I2:
case ILCode.Stelem_I4:
case ILCode.Stelem_I8:
case ILCode.Stelem_R4:
case ILCode.Stelem_R8:
case ILCode.Stelem_Ref:
return true;
default:
return false;
}
}
public static bool IsLoadFromArray(this ILCode code)
{
switch (code) {
case ILCode.Ldelem_Any:
case ILCode.Ldelem_I:
case ILCode.Ldelem_I1:
case ILCode.Ldelem_I2:
case ILCode.Ldelem_I4:
case ILCode.Ldelem_I8:
case ILCode.Ldelem_U1:
case ILCode.Ldelem_U2:
case ILCode.Ldelem_U4:
case ILCode.Ldelem_R4:
case ILCode.Ldelem_R8:
case ILCode.Ldelem_Ref:
return true;
default:
return false;
}
}
/// <summary>
/// Can the expression be used as a statement in C#?
/// </summary>
public static bool CanBeExpressionStatement(this ILExpression expr)
{
switch(expr.Code) {
case ILCode.Call:
case ILCode.Callvirt:
// property getters can't be expression statements, but all other method calls can be
MethodReference mr = (MethodReference)expr.Operand;
return !mr.Name.StartsWith("get_", StringComparison.Ordinal);
case ILCode.CallSetter:
case ILCode.CallvirtSetter:
case ILCode.Newobj:
case ILCode.Newarr:
case ILCode.Stloc:
case ILCode.Stobj:
case ILCode.Stsfld:
case ILCode.Stfld:
case ILCode.Stind_Ref:
case ILCode.Stelem_Any:
case ILCode.Stelem_I:
case ILCode.Stelem_I1:
case ILCode.Stelem_I2:
case ILCode.Stelem_I4:
case ILCode.Stelem_I8:
case ILCode.Stelem_R4:
case ILCode.Stelem_R8:
case ILCode.Stelem_Ref:
return true;
default:
return false;
}
}
public static ILExpression WithILRanges(this ILExpression expr, IEnumerable<ILRange> ilranges)
{
expr.ILRanges.AddRange(ilranges);
return expr;
}
public static void RemoveTail(this List<ILNode> body, params ILCode[] codes)
{
for (int i = 0; i < codes.Length; i++) {
if (((ILExpression)body[body.Count - codes.Length + i]).Code != codes[i])
throw new Exception("Tailing code does not match expected.");
}
body.RemoveRange(body.Count - codes.Length, codes.Length);
}
public static V GetOrDefault<K,V>(this Dictionary<K, V> dict, K key)
{
V ret;
dict.TryGetValue(key, out ret);
return ret;
}
public static void RemoveOrThrow<T>(this ICollection<T> collection, T item)
{
if (!collection.Remove(item))
throw new Exception("The item was not found in the collection");
}
public static void RemoveOrThrow<K,V>(this Dictionary<K,V> collection, K key)
{
if (!collection.Remove(key))
throw new Exception("The key was not found in the dictionary");
}
public static bool ContainsReferenceTo(this ILExpression expr, ILVariable v)
{
if (expr.Operand == v)
return true;
foreach (var arg in expr.Arguments) {
if (ContainsReferenceTo(arg, v))
return true;
}
return false;
}
}
}

Просмотреть файл

@ -1,601 +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 Mono.CSharp;
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 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)
{
Body = new List<ILNode>(body);
}
public ILBlock(List<ILNode> body)
{
Body = body;
}
public override IEnumerable<ILNode> GetChildren()
{
if (EntryGoto != null)
yield return EntryGoto;
foreach(ILNode child in Body) {
yield return child;
}
}
public override void WriteTo(ITextOutput output)
{
foreach(ILNode child in 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 Body;
}
public override void WriteTo(ITextOutput output)
{
foreach(ILNode child in 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 (TryBlock != null)
yield return TryBlock;
foreach (var catchBlock in CatchBlocks) {
yield return catchBlock;
}
if (FaultBlock != null)
yield return FaultBlock;
if (FinallyBlock != null)
yield return 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)
{
From = @from;
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)
{
Code = code;
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");
Code = code;
Operand = operand;
Arguments = new List<ILExpression>(args);
ILRanges = new List<ILRange>(1);
}
public ILExpression(ILCode code, object operand, params ILExpression[] args)
{
if (operand is ILExpression)
throw new ArgumentException("operand");
Code = code;
Operand = operand;
Arguments = new List<ILExpression>(args);
ILRanges = new List<ILRange>(1);
}
public void AddPrefix(ILExpressionPrefix prefix)
{
ILExpressionPrefix[] arr = Prefixes;
if (arr == null)
arr = new ILExpressionPrefix[1];
else
Array.Resize(ref arr, arr.Length + 1);
arr[arr.Length - 1] = prefix;
Prefixes = arr;
}
public ILExpressionPrefix GetPrefix(ILCode code)
{
var prefixes = 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 Operand is ILLabel || Operand is ILLabel[];
}
public IEnumerable<ILLabel> GetBranchTargets()
{
if (Operand is ILLabel) {
return new ILLabel[] { (ILLabel)Operand };
} else if (Operand is ILLabel[]) {
return (ILLabel[])Operand;
} else {
return new ILLabel[] { };
}
}
public override void WriteTo(ITextOutput output)
{
if (Operand is ILVariable && ((ILVariable)Operand).IsGenerated) {
if (Code == ILCode.Stloc && 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 (InferredType != null) {
output.Write(':');
InferredType.WriteTo(output, ILNameSyntax.ShortTypeName);
if (ExpectedType != null && ExpectedType.FullName != InferredType.FullName) {
output.Write("[exp:");
ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write(']');
}
}
return;
}
}
if (Prefixes != null) {
foreach (var prefix in Prefixes) {
output.Write(prefix.Code.GetName());
output.Write(". ");
}
}
output.Write(Code.GetName());
if (InferredType != null) {
output.Write(':');
InferredType.WriteTo(output, ILNameSyntax.ShortTypeName);
if (ExpectedType != null && ExpectedType.FullName != InferredType.FullName) {
output.Write("[exp:");
ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write(']');
}
} else if (ExpectedType != null) {
output.Write("[exp:");
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 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 (Condition != null)
yield return Condition;
if (BodyBlock != null)
yield return BodyBlock;
}
public override void WriteTo(ITextOutput output)
{
output.WriteLine("");
output.Write("loop (");
if (Condition != null)
Condition.WriteTo(output);
output.WriteLine(") {");
output.Indent();
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 (Condition != null)
yield return Condition;
if (TrueBlock != null)
yield return TrueBlock;
if (FalseBlock != null)
yield return 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 (Values != null) {
foreach (int i in 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 (Condition != null)
yield return Condition;
foreach (ILBlock caseBlock in CaseBlocks) {
yield return caseBlock;
}
}
public override void WriteTo(ITextOutput output)
{
output.Write("switch (");
Condition.WriteTo(output);
output.WriteLine(") {");
output.Indent();
foreach (CaseBlock caseBlock in 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 Initializers)
yield return initializer;
if (BodyBlock != null)
yield return BodyBlock;
}
public override void WriteTo(ITextOutput output)
{
output.Write("fixed (");
for (int i = 0; i < Initializers.Count; i++) {
if (i > 0)
output.Write(", ");
Initializers[i].WriteTo(output);
}
output.WriteLine(") {");
output.Indent();
BodyBlock.WriteTo(output);
output.Unindent();
output.WriteLine("}");
}
}
}

Просмотреть файл

@ -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,524 +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>
/// Performs inlining transformations.
/// </summary>
public class ILInlining
{
readonly ILBlock method;
internal Dictionary<ILVariable, int> numStloc = new Dictionary<ILVariable, int>();
internal Dictionary<ILVariable, int> numLdloc = new Dictionary<ILVariable, int>();
internal Dictionary<ILVariable, int> numLdloca = new Dictionary<ILVariable, int>();
public ILInlining(ILBlock method)
{
this.method = method;
AnalyzeMethod();
}
void AnalyzeMethod()
{
numStloc.Clear();
numLdloc.Clear();
numLdloca.Clear();
// Analyse the whole method
AnalyzeNode(method);
}
/// <summary>
/// For each variable reference, adds <paramref name="direction"/> to the num* dicts.
/// Direction will be 1 for analysis, and -1 when removing a node from analysis.
/// </summary>
void AnalyzeNode(ILNode node, int direction = 1)
{
ILExpression expr = node as ILExpression;
if (expr != null) {
ILVariable locVar = expr.Operand as ILVariable;
if (locVar != null) {
if (expr.Code == ILCode.Stloc) {
numStloc[locVar] = numStloc.GetOrDefault(locVar) + direction;
} else if (expr.Code == ILCode.Ldloc) {
numLdloc[locVar] = numLdloc.GetOrDefault(locVar) + direction;
} else if (expr.Code == ILCode.Ldloca) {
numLdloca[locVar] = numLdloca.GetOrDefault(locVar) + direction;
} else {
throw new NotSupportedException(expr.Code.ToString());
}
}
foreach (ILExpression child in expr.Arguments)
AnalyzeNode(child, direction);
} else {
var catchBlock = node as ILTryCatchBlock.CatchBlock;
if (catchBlock != null && catchBlock.ExceptionVariable != null) {
numStloc[catchBlock.ExceptionVariable] = numStloc.GetOrDefault(catchBlock.ExceptionVariable) + direction;
}
foreach (ILNode child in node.GetChildren())
AnalyzeNode(child, direction);
}
}
public bool InlineAllVariables()
{
bool modified = false;
ILInlining i = new ILInlining(method);
foreach (ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>())
modified |= i.InlineAllInBlock(block);
return modified;
}
public bool InlineAllInBlock(ILBlock block)
{
bool modified = false;
List<ILNode> body = block.Body;
if (block is ILTryCatchBlock.CatchBlock && body.Count > 1) {
ILVariable v = ((ILTryCatchBlock.CatchBlock)block).ExceptionVariable;
if (v != null && v.IsGenerated) {
if (numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 1 && numLdloc.GetOrDefault(v) == 1) {
ILVariable v2;
ILExpression ldException;
if (body[0].Match(ILCode.Stloc, out v2, out ldException) && ldException.MatchLdloc(v)) {
body.RemoveAt(0);
((ILTryCatchBlock.CatchBlock)block).ExceptionVariable = v2;
modified = true;
}
}
}
}
for(int i = 0; i < body.Count - 1;) {
ILVariable locVar;
ILExpression expr;
if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(block.Body, i, aggressive: false)) {
modified = true;
i = Math.Max(0, i - 1); // Go back one step
} else {
i++;
}
}
foreach(ILBasicBlock bb in body.OfType<ILBasicBlock>()) {
modified |= InlineAllInBasicBlock(bb);
}
return modified;
}
public bool InlineAllInBasicBlock(ILBasicBlock bb)
{
bool modified = false;
List<ILNode> body = bb.Body;
for(int i = 0; i < body.Count;) {
ILVariable locVar;
ILExpression expr;
if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(bb.Body, i, aggressive: false)) {
modified = true;
i = Math.Max(0, i - 1); // Go back one step
} else {
i++;
}
}
return modified;
}
/// <summary>
/// Inlines instructions before pos into block.Body[pos].
/// </summary>
/// <returns>The number of instructions that were inlined.</returns>
public int InlineInto(List<ILNode> body, int pos, bool aggressive)
{
if (pos >= body.Count)
return 0;
int count = 0;
while (--pos >= 0) {
ILExpression expr = body[pos] as ILExpression;
if (expr == null || expr.Code != ILCode.Stloc)
break;
if (InlineOneIfPossible(body, pos, aggressive))
count++;
else
break;
}
return count;
}
/// <summary>
/// Aggressively inlines the stloc instruction at block.Body[pos] into the next instruction, if possible.
/// If inlining was possible; we will continue to inline (non-aggressively) into the the combined instruction.
/// </summary>
/// <remarks>
/// After the operation, pos will point to the new combined instruction.
/// </remarks>
public bool InlineIfPossible(List<ILNode> body, ref int pos)
{
if (InlineOneIfPossible(body, pos, true)) {
pos -= InlineInto(body, pos, false);
return true;
}
return false;
}
/// <summary>
/// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible.
/// </summary>
public bool InlineOneIfPossible(List<ILNode> body, int pos, bool aggressive)
{
ILVariable v;
ILExpression inlinedExpression;
if (body[pos].Match(ILCode.Stloc, out v, out inlinedExpression) && !v.IsPinned) {
if (InlineIfPossible(v, inlinedExpression, body.ElementAtOrDefault(pos+1), aggressive)) {
// Assign the ranges of the stloc instruction:
inlinedExpression.ILRanges.AddRange(((ILExpression)body[pos]).ILRanges);
// Remove the stloc instruction:
body.RemoveAt(pos);
return true;
} else if (numLdloc.GetOrDefault(v) == 0 && numLdloca.GetOrDefault(v) == 0) {
// The variable is never loaded
if (inlinedExpression.HasNoSideEffects()) {
// Remove completely
AnalyzeNode(body[pos], -1);
body.RemoveAt(pos);
return true;
} else if (inlinedExpression.CanBeExpressionStatement() && v.IsGenerated) {
// Assign the ranges of the stloc instruction:
inlinedExpression.ILRanges.AddRange(((ILExpression)body[pos]).ILRanges);
// Remove the stloc, but keep the inner expression
body[pos] = inlinedExpression;
return true;
}
}
}
return false;
}
/// <summary>
/// Inlines 'expr' into 'next', if possible.
/// </summary>
bool InlineIfPossible(ILVariable v, ILExpression inlinedExpression, ILNode next, bool aggressive)
{
// ensure the variable is accessed only a single time
if (numStloc.GetOrDefault(v) != 1)
return false;
int ldloc = numLdloc.GetOrDefault(v);
if (ldloc > 1 || ldloc + numLdloca.GetOrDefault(v) != 1)
return false;
if (next is ILCondition)
next = ((ILCondition)next).Condition;
else if (next is ILWhileLoop)
next = ((ILWhileLoop)next).Condition;
ILExpression parent;
int pos;
if (FindLoadInNext(next as ILExpression, v, inlinedExpression, out parent, out pos) == true) {
if (ldloc == 0) {
if (!IsGeneratedValueTypeTemporary((ILExpression)next, parent, pos, v, inlinedExpression))
return false;
} else {
if (!aggressive && !v.IsGenerated && !NonAggressiveInlineInto((ILExpression)next, parent, inlinedExpression))
return false;
}
// Assign the ranges of the ldloc instruction:
inlinedExpression.ILRanges.AddRange(parent.Arguments[pos].ILRanges);
if (ldloc == 0) {
// it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof' so that the types
// comes out correctly
parent.Arguments[pos] = new ILExpression(ILCode.AddressOf, null, inlinedExpression);
} else {
parent.Arguments[pos] = inlinedExpression;
}
return true;
}
return false;
}
/// <summary>
/// Is this a temporary variable generated by the C# compiler for instance method calls on value type values
/// </summary>
/// <param name="next">The next top-level expression</param>
/// <param name="parent">The direct parent of the load within 'next'</param>
/// <param name="pos">Index of the load within 'parent'</param>
/// <param name="v">The variable being inlined.</param>
/// <param name="inlinedExpression">The expression being inlined</param>
bool IsGeneratedValueTypeTemporary(ILExpression next, ILExpression parent, int pos, ILVariable v, ILExpression inlinedExpression)
{
if (pos == 0 && v.Type != null && v.Type.IsValueType) {
// Inlining a value type variable is allowed only if the resulting code will maintain the semantics
// that the method is operating on a copy.
// Thus, we have to disallow inlining of other locals, fields, array elements, dereferenced pointers
switch (inlinedExpression.Code) {
case ILCode.Ldloc:
case ILCode.Stloc:
case ILCode.CompoundAssignment:
case ILCode.Ldelem_Any:
case ILCode.Ldelem_I:
case ILCode.Ldelem_I1:
case ILCode.Ldelem_I2:
case ILCode.Ldelem_I4:
case ILCode.Ldelem_I8:
case ILCode.Ldelem_R4:
case ILCode.Ldelem_R8:
case ILCode.Ldelem_Ref:
case ILCode.Ldelem_U1:
case ILCode.Ldelem_U2:
case ILCode.Ldelem_U4:
case ILCode.Ldobj:
case ILCode.Ldind_Ref:
return false;
case ILCode.Ldfld:
case ILCode.Stfld:
case ILCode.Ldsfld:
case ILCode.Stsfld:
// allow inlining field access only if it's a readonly field
FieldDefinition f = ((FieldReference)inlinedExpression.Operand).Resolve();
if (!(f != null && f.IsInitOnly))
return false;
break;
case ILCode.Call:
case ILCode.CallGetter:
// inlining runs both before and after IntroducePropertyAccessInstructions,
// so we have to handle both 'call' and 'callgetter'
MethodReference mr = (MethodReference)inlinedExpression.Operand;
// ensure that it's not an multi-dimensional array getter
if (mr.DeclaringType is ArrayType)
return false;
goto case ILCode.Callvirt;
case ILCode.Callvirt:
case ILCode.CallvirtGetter:
// don't inline foreach loop variables:
mr = (MethodReference)inlinedExpression.Operand;
if (mr.Name == "get_Current" && mr.HasThis)
return false;
break;
case ILCode.Castclass:
case ILCode.Unbox_Any:
// These are valid, but might occur as part of a foreach loop variable.
ILExpression arg = inlinedExpression.Arguments[0];
if (arg.Code == ILCode.CallGetter || arg.Code == ILCode.CallvirtGetter || arg.Code == ILCode.Call || arg.Code == ILCode.Callvirt) {
mr = (MethodReference)arg.Operand;
if (mr.Name == "get_Current" && mr.HasThis)
return false; // looks like a foreach loop variable, so don't inline it
}
break;
}
// inline the compiler-generated variable that are used when accessing a member on a value type:
switch (parent.Code) {
case ILCode.Call:
case ILCode.CallGetter:
case ILCode.CallSetter:
case ILCode.Callvirt:
case ILCode.CallvirtGetter:
case ILCode.CallvirtSetter:
MethodReference mr = (MethodReference)parent.Operand;
return mr.HasThis;
case ILCode.Stfld:
case ILCode.Ldfld:
case ILCode.Ldflda:
case ILCode.Await:
return true;
}
}
return false;
}
/// <summary>
/// Determines whether a variable should be inlined in non-aggressive mode, even though it is not a generated variable.
/// </summary>
/// <param name="next">The next top-level expression</param>
/// <param name="parent">The direct parent of the load within 'next'</param>
/// <param name="inlinedExpression">The expression being inlined</param>
bool NonAggressiveInlineInto(ILExpression next, ILExpression parent, ILExpression inlinedExpression)
{
if (inlinedExpression.Code == ILCode.DefaultValue)
return true;
switch (next.Code) {
case ILCode.Ret:
case ILCode.Brtrue:
return parent == next;
case ILCode.Switch:
return parent == next || (parent.Code == ILCode.Sub && parent == next.Arguments[0]);
default:
return false;
}
}
/// <summary>
/// Gets whether 'expressionBeingMoved' can be inlined into 'expr'.
/// </summary>
public bool CanInlineInto(ILExpression expr, ILVariable v, ILExpression expressionBeingMoved)
{
ILExpression parent;
int pos;
return FindLoadInNext(expr, v, expressionBeingMoved, out parent, out pos) == true;
}
/// <summary>
/// Finds the position to inline to.
/// </summary>
/// <returns>true = found; false = cannot continue search; null = not found</returns>
bool? FindLoadInNext(ILExpression expr, ILVariable v, ILExpression expressionBeingMoved, out ILExpression parent, out int pos)
{
parent = null;
pos = 0;
if (expr == null)
return false;
for (int i = 0; i < expr.Arguments.Count; i++) {
// Stop when seeing an opcode that does not guarantee that its operands will be evaluated.
// Inlining in that case might result in the inlined expresion not being evaluted.
if (i == 1 && (expr.Code == ILCode.LogicAnd || expr.Code == ILCode.LogicOr || expr.Code == ILCode.TernaryOp || expr.Code == ILCode.NullCoalescing))
return false;
ILExpression arg = expr.Arguments[i];
if ((arg.Code == ILCode.Ldloc || arg.Code == ILCode.Ldloca) && arg.Operand == v) {
parent = expr;
pos = i;
return true;
}
bool? r = FindLoadInNext(arg, v, expressionBeingMoved, out parent, out pos);
if (r != null)
return r;
}
if (IsSafeForInlineOver(expr, expressionBeingMoved))
return null; // continue searching
else
return false; // abort, inlining not possible
}
/// <summary>
/// Determines whether it is safe to move 'expressionBeingMoved' past 'expr'
/// </summary>
bool IsSafeForInlineOver(ILExpression expr, ILExpression expressionBeingMoved)
{
switch (expr.Code) {
case ILCode.Ldloc:
ILVariable loadedVar = (ILVariable)expr.Operand;
if (numLdloca.GetOrDefault(loadedVar) != 0) {
// abort, inlining is not possible
return false;
}
foreach (ILExpression potentialStore in expressionBeingMoved.GetSelfAndChildrenRecursive<ILExpression>()) {
if (potentialStore.Code == ILCode.Stloc && potentialStore.Operand == loadedVar)
return false;
}
// the expression is loading a non-forbidden variable
return true;
case ILCode.Ldloca:
case ILCode.Ldflda:
case ILCode.Ldsflda:
case ILCode.Ldelema:
case ILCode.AddressOf:
case ILCode.ValueOf:
case ILCode.NullableOf:
// address-loading instructions are safe if their arguments are safe
foreach (ILExpression arg in expr.Arguments) {
if (!IsSafeForInlineOver(arg, expressionBeingMoved))
return false;
}
return true;
default:
// instructions with no side-effects are safe (except for Ldloc and Ldloca which are handled separately)
return expr.HasNoSideEffects();
}
}
/// <summary>
/// Runs a very simple form of copy propagation.
/// Copy propagation is used in two cases:
/// 1) assignments from arguments to local variables
/// If the target variable is assigned to only once (so always is that argument) and the argument is never changed (no ldarga/starg),
/// then we can replace the variable with the argument.
/// 2) assignments of address-loading instructions to local variables
/// </summary>
public void CopyPropagation()
{
foreach (ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = 0; i < block.Body.Count; i++) {
ILVariable v;
ILExpression copiedExpr;
if (block.Body[i].Match(ILCode.Stloc, out v, out copiedExpr)
&& !v.IsParameter && numStloc.GetOrDefault(v) == 1 && numLdloca.GetOrDefault(v) == 0
&& CanPerformCopyPropagation(copiedExpr, v))
{
// un-inline the arguments of the ldArg instruction
ILVariable[] uninlinedArgs = new ILVariable[copiedExpr.Arguments.Count];
for (int j = 0; j < uninlinedArgs.Length; j++) {
uninlinedArgs[j] = new ILVariable { IsGenerated = true, Name = v.Name + "_cp_" + j };
block.Body.Insert(i++, new ILExpression(ILCode.Stloc, uninlinedArgs[j], copiedExpr.Arguments[j]));
}
// perform copy propagation:
foreach (var expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
if (expr.Code == ILCode.Ldloc && expr.Operand == v) {
expr.Code = copiedExpr.Code;
expr.Operand = copiedExpr.Operand;
for (int j = 0; j < uninlinedArgs.Length; j++) {
expr.Arguments.Add(new ILExpression(ILCode.Ldloc, uninlinedArgs[j]));
}
}
}
block.Body.RemoveAt(i);
if (uninlinedArgs.Length > 0) {
// if we un-inlined stuff; we need to update the usage counters
AnalyzeMethod();
}
InlineInto(block.Body, i, aggressive: false); // maybe inlining gets possible after the removal of block.Body[i]
i -= uninlinedArgs.Length + 1;
}
}
}
}
bool CanPerformCopyPropagation(ILExpression expr, ILVariable copyVariable)
{
switch (expr.Code) {
case ILCode.Ldloca:
case ILCode.Ldelema:
case ILCode.Ldflda:
case ILCode.Ldsflda:
// All address-loading instructions always return the same value for a given operand/argument combination,
// so they can be safely copied.
return true;
case ILCode.Ldloc:
ILVariable v = (ILVariable)expr.Operand;
if (v.IsParameter) {
// Parameters can be copied only if they aren't assigned to (directly or indirectly via ldarga)
return numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 0;
} else {
// Variables are be copied only if both they and the target copy variable are generated,
// and if the variable has only a single assignment
return v.IsGenerated && copyVariable.IsGenerated && numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 1;
}
default:
return false;
}
}
}
}

Просмотреть файл

@ -1,535 +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;
}
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");
}
}
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>
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 @@
// 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)
{
Arguments = arguments;
}
public virtual bool Match(PatternMatcher pm, ILExpression e)
{
if (e.Arguments.Count != Arguments.Length || e.Prefixes != null) return false;
for (int i = 0; i < Arguments.Length; i++)
if (!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 == code && base.Match(pm, e);
}
public override ILExpression BuildNew(PatternMatcher pm)
{
var args = new ILExpression[Arguments.Length];
for (int i = 0; i < args.Length; i++) args[i] = 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 != code) return false;
var m = (MethodReference)e.Operand;
return m.Name == 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 != code) return false;
var v = e.Operand as ILVariable;
return v != null && (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 = 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) && 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()
{
A = null;
B = null;
Operator = null;
SimpleOperand = null;
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++) {
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) {
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 @@
// 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 @@
// 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;
}
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -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;
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 : 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,312 +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
{
internal struct Interval
{
public readonly int Start, End;
public Interval(int start, int end)
{
Debug.Assert(start <= end || (start == 0 && end == -1));
Start = start;
End = end;
}
public override string ToString()
{
return string.Format("({0} to {1})", Start, End);
}
}
internal class StateRange
{
readonly List<Interval> data = new List<Interval>();
public StateRange()
{
}
public StateRange(int start, int end)
{
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);
}
}
internal enum StateRangeAnalysisMode
{
IteratorMoveNext,
IteratorDispose,
AsyncMoveNext
}
internal 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--;
} else {
// ensure that the first element at body[pos] is a label:
ILLabel newLabel = new ILLabel();
newLabel.Name = "YieldReturnEntryPoint";
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;
}
}
}
}
}
internal class LabelRangeMapping : List<KeyValuePair<ILLabel, StateRange>> {}
}

Просмотреть файл

@ -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>
internal class SymbolicAnalysisFailedException : Exception {}
internal 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
}
internal struct SymbolicValue
{
public readonly int Constant;
public readonly SymbolicValueType Type;
public SymbolicValue(SymbolicValueType type, int constant = 0)
{
Type = type;
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}]", Type, Constant);
}
}
internal 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();
}
}
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -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
{
internal 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)
{
NewBodyPos = newBodyPos;
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
}
}

Просмотреть файл

@ -1,62 +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.IO;
using ICSharpCode.NRefactory;
using Mono.Cecil;
namespace ICSharpCode.Decompiler
{
public interface ITextOutput
{
TextLocation Location { get; }
void Indent();
void Unindent();
void Write(char ch);
void Write(string text);
void WriteLine();
void WriteDefinition(string text, object definition, bool isLocal = true);
void WriteReference(string text, object reference, bool isLocal = false);
void AddDebugSymbols(MethodDebugSymbols methodDebugSymbols);
void MarkFoldStart(string collapsedText = "...", bool defaultCollapsed = false);
void MarkFoldEnd();
}
public static class TextOutputExtensions
{
public static void Write(this ITextOutput output, string format, params object[] args)
{
output.Write(string.Format(format, args));
}
public static void WriteLine(this ITextOutput output, string text)
{
output.Write(text);
output.WriteLine();
}
public static void WriteLine(this ITextOutput output, string format, params object[] args)
{
output.WriteLine(string.Format(format, args));
}
}
}

Просмотреть файл

@ -1,122 +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.IO;
using ICSharpCode.NRefactory;
namespace ICSharpCode.Decompiler
{
public sealed class PlainTextOutput : ITextOutput
{
readonly TextWriter writer;
int indent;
bool needsIndent;
int line = 1;
int column = 1;
public PlainTextOutput(TextWriter writer)
{
if (writer == null)
throw new ArgumentNullException("writer");
this.writer = writer;
}
public PlainTextOutput()
{
writer = new StringWriter();
}
public TextLocation Location {
get {
return new TextLocation(line, column + (needsIndent ? indent : 0));
}
}
public override string ToString()
{
return writer.ToString();
}
public void Indent()
{
indent++;
}
public void Unindent()
{
indent--;
}
void WriteIndent()
{
if (needsIndent) {
needsIndent = false;
for (int i = 0; i < indent; i++) {
writer.Write('\t');
}
column += indent;
}
}
public void Write(char ch)
{
WriteIndent();
writer.Write(ch);
column++;
}
public void Write(string text)
{
WriteIndent();
writer.Write(text);
column += text.Length;
}
public void WriteLine()
{
writer.WriteLine();
needsIndent = true;
line++;
column = 1;
}
public void WriteDefinition(string text, object definition, bool isLocal)
{
Write(text);
}
public void WriteReference(string text, object reference, bool isLocal)
{
Write(text);
}
void ITextOutput.MarkFoldStart(string collapsedText, bool defaultCollapsed)
{
}
void ITextOutput.MarkFoldEnd()
{
}
void ITextOutput.AddDebugSymbols(MethodDebugSymbols methodDebugSymbols)
{
}
}
}

Просмотреть файл

@ -1,27 +0,0 @@
#region Using directives
using System;
using System.Resources;
using System.Reflection;
using System.Runtime.InteropServices;
#endregion
[assembly: AssemblyTitle("ICSharpCode.Decompiler")]
[assembly: AssemblyDescription("IL decompiler engine")]
[assembly: AssemblyCompany("ic#code")]
[assembly: AssemblyProduct("ILSpy")]
[assembly: AssemblyCopyright("Copyright 2011-2014 AlphaSierraPapa for the SharpDevelop Team")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// This sets the default COM visibility of types in the assembly to invisible.
// If you need to expose a type to COM, use [ComVisible(true)] on that type.
[assembly: ComVisible(false)]
[assembly: AssemblyVersion("$INSERTVERSION$")]
[assembly: AssemblyInformationalVersion("$INSERTVERSION$$INSERTBRANCHPOSTFIX$$INSERTVERSIONNAMEPOSTFIX$-$INSERTSHORTCOMMITHASH$")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly",
Justification = "AssemblyInformationalVersion does not need to be a parsable version")]

Просмотреть файл

@ -1,68 +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 System.Text;
namespace ICSharpCode.Decompiler
{
/// <summary>
/// Represents an error while resolving a reference to a type or a member.
/// </summary>
[Serializable]
public class ReferenceResolvingException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="T:ResolveException"/> class
/// </summary>
public ReferenceResolvingException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="T:ResolveException"/> class
/// </summary>
/// <param name="message">A <see cref="T:System.String"/> that describes the error. The content of message is intended to be understood by humans. The caller of this constructor is required to ensure that this string has been localized for the current system culture.</param>
public ReferenceResolvingException(string message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="T:ResolveException"/> class
/// </summary>
/// <param name="message">A <see cref="T:System.String"/> that describes the error. The content of message is intended to be understood by humans. The caller of this constructor is required to ensure that this string has been localized for the current system culture.</param>
/// <param name="inner">The exception that is the cause of the current exception. If the innerException parameter is not a null reference, the current exception is raised in a catch block that handles the inner exception.</param>
public ReferenceResolvingException(string message, Exception inner)
: base(message, inner)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="T:ResolveException"/> class
/// </summary>
/// <param name="info">The object that holds the serialized object data.</param>
/// <param name="context">The contextual information about the source or destination.</param>
protected ReferenceResolvingException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
: base(info, context)
{
}
}
}

Просмотреть файл

@ -1,155 +0,0 @@
// Copyright (c) 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.
#pragma warning disable 1998
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
public class Async
{
public async void SimpleVoidMethod()
{
Console.WriteLine("Before");
await Task.Delay(TimeSpan.FromSeconds(1.0));
Console.WriteLine("After");
}
public async void VoidMethodWithoutAwait()
{
Console.WriteLine("No Await");
}
public async void AwaitYield()
{
await Task.Yield();
}
public async void AwaitDefaultYieldAwaitable()
{
await default(YieldAwaitable);
}
public async Task SimpleVoidTaskMethod()
{
Console.WriteLine("Before");
await Task.Delay(TimeSpan.FromSeconds(1.0));
Console.WriteLine("After");
}
public async Task TaskMethodWithoutAwait()
{
Console.WriteLine("No Await");
}
public async Task<bool> SimpleBoolTaskMethod()
{
Console.WriteLine("Before");
await Task.Delay(TimeSpan.FromSeconds(1.0));
Console.WriteLine("After");
return true;
}
public async void TwoAwaitsWithDifferentAwaiterTypes()
{
Console.WriteLine("Before");
if (await this.SimpleBoolTaskMethod())
{
await Task.Delay(TimeSpan.FromSeconds(1.0));
}
Console.WriteLine("After");
}
public async void StreamCopyTo(Stream destination, int bufferSize)
{
byte[] array = new byte[bufferSize];
int count;
while ((count = await destination.ReadAsync(array, 0, array.Length)) != 0)
{
await destination.WriteAsync(array, 0, count);
}
}
public async void StreamCopyToWithConfigureAwait(Stream destination, int bufferSize)
{
byte[] array = new byte[bufferSize];
int count;
while ((count = await destination.ReadAsync(array, 0, array.Length).ConfigureAwait(false)) != 0)
{
await destination.WriteAsync(array, 0, count).ConfigureAwait(false);
}
}
public async void AwaitInLoopCondition()
{
while (await this.SimpleBoolTaskMethod())
{
Console.WriteLine("Body");
}
}
public async Task<int> AwaitInForEach(IEnumerable<Task<int>> elements)
{
int num = 0;
foreach (Task<int> current in elements)
{
num += await current;
}
return num;
}
public async Task TaskMethodWithoutAwaitButWithExceptionHandling()
{
try
{
using (new StringWriter())
{
Console.WriteLine("No Await");
}
}
catch (Exception)
{
Console.WriteLine("Crash");
}
}
public async Task<int> NestedAwait(Task<Task<int>> task)
{
return await(await task);
}
public async Task AwaitWithStack(Task<int> task)
{
Console.WriteLine("A", 1, await task);
}
public async Task AwaitWithStack2(Task<int> task)
{
if (await this.SimpleBoolTaskMethod())
{
Console.WriteLine("A", 1, await task);
}
else
{
int num = 1;
Console.WriteLine("A", 1, num);
}
}
}

Просмотреть файл

@ -1,59 +0,0 @@
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
.ver 4:0:0:0
}
.assembly BooleanConsumedAsInteger
{
.hash algorithm 0x00008004
.ver 1:0:0:0
}
.module BooleanConsumedAsInteger.exe
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000003 // ILONLY 32BITREQUIRED
.class private auto ansi beforefieldinit BooleanConsumedAsInteger.Program extends [mscorlib]System.Object
{
.method public hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 8
ret
}
.method public hidebysig static int32 ReturnBoolAsInt() cil managed
{
ldnull
ldnull
call bool [mscorlib] System.Object::Equals(object, object)
ret
}
.method public hidebysig static int32 BitwiseOperationOnBool() cil managed
{
ldnull
ldnull
call bool [mscorlib] System.Object::Equals(object, object)
ldc.i4 255
and
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Program::.ctor
} // end of class StackTests.Program
// =============================================================

Просмотреть файл

@ -1,52 +0,0 @@
// Copyright (c) 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;
public class CallOverloadedMethod
{
public void OverloadedMethod(object a)
{
}
public void OverloadedMethod(int? a)
{
}
public void OverloadedMethod(string a)
{
}
public void Call()
{
this.OverloadedMethod("(string)");
this.OverloadedMethod((object)"(object)");
this.OverloadedMethod(5);
this.OverloadedMethod((object)5);
this.OverloadedMethod(5L);
this.OverloadedMethod((object)null);
this.OverloadedMethod((string)null);
this.OverloadedMethod((int?)null);
}
public void CallMethodUsingInterface(List<int> list)
{
((ICollection<int>)list).Clear();
}
}

Просмотреть файл

@ -1,117 +0,0 @@
// Copyright (c) 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;
public class CheckedUnchecked
{
public int Operators(int a, int b)
{
int num = checked(a + b);
int num2 = a + b;
int num3 = checked(a - b);
int num4 = a - b;
int num5 = checked(a * b);
int num6 = a * b;
int num7 = a / b;
int num8 = a % b;
// The division operators / and % only exist in one form (checked vs. unchecked doesn't matter for them)
return num * num2 * num3 * num4 * num5 * num6 * num7 * num8;
}
public int Cast(int a)
{
short num = checked((short)a);
short num2 = (short)a;
byte b = checked((byte)a);
byte b2 = (byte)a;
return num * num2 * b * b2;
}
public void ForWithCheckedIteratorAndUncheckedBody(int n)
{
checked
{
for (int i = n + 1; i < n + 1; i++)
{
n = unchecked(i * i);
}
}
}
public void ForWithCheckedInitializerAndUncheckedIterator(int n)
{
checked
{
int i = n;
for (i -= 10; i < n; i = unchecked(i + 1))
{
n--;
}
}
}
public void ObjectCreationInitializerChecked()
{
this.TestHelp(new
{
x = 0,
l = 0
}, n => checked(new
{
x = n.x + 1,
l = n.l + 1
}));
}
public void ObjectCreationWithOneFieldChecked()
{
this.TestHelp(new
{
x = 0,
l = 0
}, n => new
{
x = checked(n.x + 1),
l = n.l + 1
});
}
public void ArrayInitializerChecked()
{
this.TestHelp<int[]>(new int[]
{
1,
2
}, (int[] n) => checked(new int[]
{
n[0] + 1,
n[1] + 1
}));
}
public T TestHelp<T>(T t, Func<T, T> f)
{
return f(t);
}
public void CheckedInArrayCreationArgument(int a, int b)
{
Console.WriteLine(new int[checked(a + b)]);
}
}

Просмотреть файл

@ -1,133 +0,0 @@
// Copyright (c) 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 System.Text;
using System.IO;
namespace ICSharpCode.Decompiler.Tests
{
static class CodeSampleFileParser
{
public static IEnumerable<string> ListSections(string s)
{
var query = from line in ToLines(s)
let sectionName = ReadSectionName(line)
where sectionName != null
select sectionName;
return query;
}
public static string GetSection(string sectionName, string s)
{
var lines = ToLines(s);
bool sectionFound = false;
var sectionText = new StringBuilder();
Action<string> parser = null;
Action<string> commonSectionReader = line =>
{
if (IsCommonSectionEnd(line))
parser = null;
else
sectionText.AppendLine(line);
};
Action<string> namedSectionReader = line =>
{
string name = ReadSectionName(line);
if (name == null)
sectionText.AppendLine(line);
else if (name != sectionName)
parser = null;
};
Action<string> defaultReader = line =>
{
if (IsCommonSectionStart(line))
parser = commonSectionReader;
else if (ReadSectionName(line) == sectionName)
{
parser = namedSectionReader;
sectionFound = true;
}
};
foreach(var line in lines)
{
(parser ?? defaultReader)(line);
}
if (sectionFound)
return sectionText.ToString();
else
return "";
}
public static bool IsCommentOrBlank(string s)
{
if(String.IsNullOrWhiteSpace(s))
return true;
s = s.Trim();
return s.StartsWith("//") || s.StartsWith("#"); // Also ignore #pragmas for warning suppression
}
public static string ConcatLines(IEnumerable<string> lines)
{
var buffer = new StringBuilder();
foreach (var line in lines)
{
buffer.AppendLine(line);
}
return buffer.ToString();
}
static string ReadSectionName(string line)
{
line = line.TrimStart();
if (line.StartsWith("//$$"))
return line.Substring(4).Trim();
else
return null;
}
static bool IsCommonSectionStart(string line)
{
return line.Trim() == "//$CS";
}
static bool IsCommonSectionEnd(string line)
{
return line.Trim() == "//$CE";
}
static IEnumerable<string> ToLines(string s)
{
var reader = new StringReader(s);
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
}

Просмотреть файл

@ -1,97 +0,0 @@
// Copyright (c) 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;
public static class ControlFlow
{
public static void EmptyIf(string input, List<string> value, Dictionary<int, string> _headers)
{
if (value.Contains("test"))
{
}
_headers.Add(2, "result");
}
public static void NormalIf(string input, List<string> value, Dictionary<int, string> _headers)
{
if (value.Contains("test"))
{
_headers.Add(1, "result");
}
else
{
_headers.Add(1, "else");
}
_headers.Add(2, "end");
}
public static void NormalIf2(string input, List<string> value, Dictionary<int, string> _headers)
{
if (value.Contains("test"))
{
_headers.Add(1, "result");
}
_headers.Add(2, "end");
}
public static void NormalIf3(string input, List<string> value, Dictionary<int, string> _headers)
{
if (value.Contains("test"))
{
_headers.Add(1, "result");
}
else
{
_headers.Add(1, "else");
}
}
public static void Test(string input, List<string> value, Dictionary<int, string> _headers)
{
foreach (string current in value)
{
_headers.Add(0, current);
}
if (value.Contains("test"))
{
_headers.Add(1, "result");
}
else
{
_headers.Add(1, "else");
}
}
public static void CascadingIfElse(bool condition, string input, int index)
{
if (condition)
{
Console.WriteLine("condition");
}
else if (input == null)
{
Console.WriteLine("condition2");
}
else if (index > 1)
{
Console.WriteLine("condition3");
}
}
}

Просмотреть файл

@ -1,41 +0,0 @@
using System;
namespace aa
{
public static class CustomAtributes
{
[Flags]
public enum EnumWithFlag
{
All = 15,
None = 0,
Item1 = 1,
Item2 = 2,
Item3 = 4,
Item4 = 8
}
[AttributeUsage(AttributeTargets.All)]
public class MyAttribute : Attribute
{
public MyAttribute(CustomAtributes.EnumWithFlag en)
{
}
}
[CustomAtributes.MyAttribute(CustomAtributes.EnumWithFlag.Item1 | CustomAtributes.EnumWithFlag.Item2)]
private static int field;
[CustomAtributes.MyAttribute(CustomAtributes.EnumWithFlag.All)]
public static string Property
{
get
{
return "aa";
}
}
[Obsolete("some message")]
public static void ObsoletedMethod()
{
Console.WriteLine("{0} $$$ {1}", AttributeTargets.Interface, AttributeTargets.Property | AttributeTargets.Field);
AttributeTargets attributeTargets = AttributeTargets.Property | AttributeTargets.Field;
Console.WriteLine("{0} $$$ {1}", AttributeTargets.Interface, attributeTargets);
}
}
}

Просмотреть файл

@ -1,30 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
namespace ICSharpCode.Decompiler.Tests.CustomAttributes
{
[TestFixture]
public class CustomAttributeTests : DecompilerTestBase
{
[Test]
public void CustomAttributeSamples()
{
ValidateFileRoundtrip(@"CustomAttributes\S_CustomAttributeSamples.cs");
}
[Test]
public void CustomAttributesMultiTest()
{
ValidateFileRoundtrip(@"CustomAttributes\S_CustomAttributes.cs");
}
[Test]
public void AssemblyCustomAttributesMultiTest()
{
ValidateFileRoundtrip(@"CustomAttributes\S_AssemblyCustomAttribute.cs");
}
}
}

Просмотреть файл

@ -1,21 +0,0 @@
// Copyright (c) 2014 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;
[assembly: CLSCompliant(false)]

Просмотреть файл

@ -1,508 +0,0 @@
// Copyright (c) 2014 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.
//$CS
using System;
//$CE
//$$ TargetModule (ignored)
//[module: CLSCompliantAttribute(false)]
//$$ ParameterlessAttributeUsage
namespace ParameterLessAttributeUsage
{
[Flags]
public enum EnumWithFlagsAttribute
{
None = 0
}
}
//$$ AttributeWithEnumArgument
namespace AttributeWithEnumArgument
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
}
//$$ AttributeWithEnumExpressionArgument
namespace AttributeWithEnumExpressionArgument
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Interface)]
public class MyAttributeAttribute : Attribute
{
}
}
//$$ AttributeWithStringExpressionArgument
namespace AttributeWithStringExpressionArgument
{
[Obsolete("message")]
public class ObsoletedClass
{
}
}
//$$ AttributeWithTypeArgument
namespace AttributeWithTypeArgument
{
[AttributeUsage(AttributeTargets.All)]
public class MyTypeAttribute : Attribute
{
public MyTypeAttribute(Type t)
{
}
}
[MyType(typeof(Attribute))]
public class SomeClass
{
}
}
//$$ AppliedToEvent
namespace AppliedToEvent
{
[AttributeUsage(AttributeTargets.Event)]
public class MyAttributeAttribute : Attribute
{
}
public class TestClass
{
[MyAttribute]
public event EventHandler MyEvent;
}
}
//$$ AppliedToEventMethods
namespace AppliedToEventMethods
{
[AttributeUsage(AttributeTargets.Method)]
public class MyAttributeAttribute : Attribute
{
}
public class TestClass
{
[method: MyAttribute]
public event EventHandler MyEvent;
}
}
//$$ AppliedToField
namespace AppliedToField
{
[AttributeUsage(AttributeTargets.Field)]
public class MyAttributeAttribute : Attribute
{
}
public class TestClass
{
[MyAttribute]
public int Field;
}
}
//$$ AppliedToProperty
namespace AppliedToProperty
{
public class TestClass
{
[Obsolete("reason")]
public int Property
{
get
{
return 0;
}
}
}
}
//$$ AppliedToPropertyGet
namespace AppliedToPropertyGet
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class TestClass
{
public int Property
{
[MyAttribute]
get
{
return 0;
}
}
}
}
//$$ AppliedToPropertySet
namespace AppliedToPropertySet
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class TestClass
{
public int Property
{
get
{
return 3;
}
[MyAttribute]
set
{
}
}
}
}
//$$ AppliedToIndexer
namespace AppliedToIndexer
{
public class TestClass
{
[Obsolete("reason")]
public int this[int i]
{
get
{
return 0;
}
}
}
}
//$$ AppliedToDelegate
[Obsolete("reason")]
public delegate int AppliedToDelegate();
//$$ AppliedToMethod
namespace AppliedToMethod
{
[AttributeUsage(AttributeTargets.Method)]
public class MyAttributeAttribute : Attribute
{
}
public class TestClass
{
[MyAttribute]
public void Method()
{
}
}
}
//$$ AppliedToInterface
[Obsolete("reason")]
public interface AppliedToInterface
{
}
//$$ AppliedToStruct
[Obsolete("reason")]
public struct AppliedToStruct
{
public int Field;
}
//$$ AppliedToParameter
namespace AppliedToParameter
{
[AttributeUsage(AttributeTargets.Parameter)]
public class MyAttributeAttribute : Attribute
{
}
public class MyClass
{
public void Method([MyAttribute] int val)
{
}
}
}
//$$ NamedInitializerProperty
namespace NamedInitializerProperty
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class MyAttributeAttribute : Attribute
{
}
}
//$$ NamedInitializerPropertyString
namespace NamedInitializerPropertyString
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
public string Prop
{
get
{
return "";
}
set
{
}
}
}
[MyAttribute(Prop = "value")]
public class MyClass
{
}
}
//$$ NamedInitializerPropertyType
namespace NamedInitializerPropertyType
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
public Type Prop
{
get
{
return null;
}
set
{
}
}
}
[MyAttribute(Prop = typeof(Enum))]
public class MyClass
{
}
}
//$$ NamedInitializerPropertyEnum
namespace NamedInitializerPropertyEnum
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
public AttributeTargets Prop
{
get
{
return AttributeTargets.All;
}
set
{
}
}
}
[MyAttribute(Prop = (AttributeTargets.Class | AttributeTargets.Method))]
public class MyClass
{
}
}
//$$ NamedInitializerFieldEnum
namespace NamedInitializerFieldEnum
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
public AttributeTargets Field;
}
[MyAttribute(Field = (AttributeTargets.Class | AttributeTargets.Method))]
public class MyClass
{
}
}
//$$ TargetReturn
namespace TargetReturn
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class MyClass
{
[return: MyAttribute]
public int MyMethod()
{
return 5;
}
}
}
//$$ TargetPropertyGetReturn
namespace TargetPropertyGetReturn
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class MyClass
{
public int Prop
{
[return: MyAttribute]
get
{
return 3;
}
}
}
}
//$$ TargetPropertySetParam
namespace TargetPropertySetParam
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class MyClass
{
public int Prop
{
[param: MyAttribute]
set
{
}
}
}
}
//$$ TargetPropertySetReturn
namespace TargetPropertySetReturn
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class MyClass
{
public int Prop
{
get
{
return 3;
}
[return: MyAttribute]
set
{
}
}
}
}
//$$ TargetPropertyIndexGetReturn
namespace TargetPropertyIndexGetReturn
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class MyClass
{
public int this[string s]
{
[return: MyAttribute]
get
{
return 3;
}
}
}
}
//$$ TargetPropertyIndexParamOnlySet
namespace TargetPropertyIndexParamOnlySet
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class MyClass
{
public int this[[MyAttribute] string s]
{
set
{
}
}
}
}
//$$ TargetPropertyIndexParamOnlyGet
namespace TargetPropertyIndexParamOnlyGet
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class MyClass
{
public int this[[MyAttribute] string s]
{
get
{
return 3;
}
}
}
}
//$$ TargetPropertyIndexSetReturn
namespace TargetPropertyIndexSetReturn
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class MyClass
{
public string this[int index]
{
get
{
return "";
}
[return: MyAttribute]
set
{
}
}
}
}
//$$ TargetPropertyIndexSetMultiParam
namespace TargetPropertyIndexSetMultiParam
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
public int Field;
}
public class MyClass
{
public string this[[MyAttribute(Field = 2)] int index1, [MyAttribute(Field = 3)] int index2]
{
get
{
return "";
}
[param: MyAttribute]
set
{
}
}
}
}
//$$ ClassAttributeOnTypeParameter
namespace ClassAttributeOnTypeParameter
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class MyClass<[MyAttribute] T>
{
}
}
//$$ AttributeOnReturnTypeOfDelegate
namespace AttributeOnReturnTypeOfDelegate
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
[return: MyAttribute]
public delegate void Test();
}

Просмотреть файл

@ -1,79 +0,0 @@
// Copyright (c) 2014 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;
namespace aa
{
public static class CustomAttributes
{
[Flags]
public enum EnumWithFlag
{
All = 15,
None = 0,
Item1 = 1,
Item2 = 2,
Item3 = 4,
Item4 = 8
}
[AttributeUsage(AttributeTargets.All)]
public class MyAttribute : Attribute
{
public MyAttribute(object val)
{
}
}
[CustomAttributes.MyAttribute(CustomAttributes.EnumWithFlag.Item1 | CustomAttributes.EnumWithFlag.Item2)]
private static int field;
[CustomAttributes.MyAttribute(CustomAttributes.EnumWithFlag.All)]
public static string Property
{
get
{
return "aa";
}
}
[Obsolete("some message")]
public static void ObsoletedMethod()
{
//Console.WriteLine("{0} $$$ {1}", AttributeTargets.Interface, (AttributeTargets)(AttributeTargets.Property | AttributeTargets.Field));
Console.WriteLine("{0} $$$ {1}", AttributeTargets.Interface, AttributeTargets.Property | AttributeTargets.Field);
AttributeTargets attributeTargets = AttributeTargets.Property | AttributeTargets.Field;
Console.WriteLine("{0} $$$ {1}", AttributeTargets.Interface, attributeTargets);
}
// No Boxing
[CustomAttributes.MyAttribute(new StringComparison[]
{
StringComparison.Ordinal,
StringComparison.CurrentCulture
})]
public static void ArrayAsAttribute1()
{
}
// Boxing of each array element
[CustomAttributes.MyAttribute(new object[]
{
StringComparison.Ordinal,
StringComparison.CurrentCulture
})]
public static void ArrayAsAttribute2()
{
}
}
}

Просмотреть файл

@ -1,88 +0,0 @@
// Copyright (c) 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;
public static class CustomShortCircuitOperators
{
// TODO: Restore base class after https://roslyn.codeplex.com/workitem/358 is fixed.
private class C
{
public static bool operator true(CustomShortCircuitOperators.C x)
{
return true;
}
public static bool operator false(CustomShortCircuitOperators.C x)
{
return false;
}
public static CustomShortCircuitOperators.C operator &(CustomShortCircuitOperators.C x, CustomShortCircuitOperators.C y)
{
return null;
}
public static CustomShortCircuitOperators.C operator |(CustomShortCircuitOperators.C x, CustomShortCircuitOperators.C y)
{
return null;
}
public static bool operator !(CustomShortCircuitOperators.C x)
{
return false;
}
private static void Main()
{
CustomShortCircuitOperators.C c = new CustomShortCircuitOperators.C();
CustomShortCircuitOperators.C c2 = new CustomShortCircuitOperators.C();
CustomShortCircuitOperators.C c3 = c && c2;
CustomShortCircuitOperators.C c4 = c || c2;
Console.WriteLine(c3.ToString());
Console.WriteLine(c4.ToString());
}
private static void Test2()
{
CustomShortCircuitOperators.C c = new CustomShortCircuitOperators.C();
if (c && c)
{
Console.WriteLine(c.ToString());
}
if (!(c && c))
{
Console.WriteLine(c.ToString());
}
}
private static void Test3()
{
CustomShortCircuitOperators.C c = new CustomShortCircuitOperators.C();
if (c)
{
Console.WriteLine(c.ToString());
}
if (!c)
{
Console.WriteLine(c.ToString());
}
}
}
}

Просмотреть файл

@ -1,89 +0,0 @@
// Copyright (c) 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.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using ICSharpCode.Decompiler.Ast;
using ICSharpCode.Decompiler.Tests.Helpers;
using Microsoft.CSharp;
using Mono.Cecil;
using NUnit.Framework;
namespace ICSharpCode.Decompiler.Tests
{
public abstract class DecompilerTestBase
{
protected static void ValidateFileRoundtrip(string samplesFileName)
{
var fullPath = Path.Combine(@"..\..\Tests", samplesFileName);
AssertRoundtripCode(fullPath);
}
static string RemoveIgnorableLines(IEnumerable<string> lines)
{
return CodeSampleFileParser.ConcatLines(lines.Where(l => !CodeSampleFileParser.IsCommentOrBlank(l)));
}
protected static void AssertRoundtripCode(string fileName, bool optimize = false, bool useDebug = false, int compilerVersion = 4)
{
var code = RemoveIgnorableLines(File.ReadLines(fileName));
AssemblyDefinition assembly = CompileLegacy(code, optimize, useDebug, compilerVersion);
AstBuilder decompiler = new AstBuilder(new DecompilerContext(assembly.MainModule));
decompiler.AddAssembly(assembly);
new Helpers.RemoveCompilerAttribute().Run(decompiler.SyntaxTree);
StringWriter output = new StringWriter();
decompiler.GenerateCode(new PlainTextOutput(output));
CodeAssert.AreEqual(code, output.ToString());
}
protected static AssemblyDefinition CompileLegacy(string code, bool optimize, bool useDebug, int compilerVersion)
{
CSharpCodeProvider provider = new CSharpCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v" + new Version(compilerVersion, 0) } });
CompilerParameters options = new CompilerParameters();
options.CompilerOptions = "/unsafe /o" + (optimize ? "+" : "-") + (useDebug ? " /debug" : "");
if (compilerVersion >= 4)
options.ReferencedAssemblies.Add("System.Core.dll");
CompilerResults results = provider.CompileAssemblyFromSource(options, code);
try
{
if (results.Errors.Count > 0)
{
StringBuilder b = new StringBuilder("Compiler error:");
foreach (var error in results.Errors)
{
b.AppendLine(error.ToString());
}
throw new Exception(b.ToString());
}
return AssemblyDefinition.ReadAssembly(results.PathToAssembly);
}
finally
{
File.Delete(results.PathToAssembly);
results.TempFiles.Delete();
}
}
}
}

Просмотреть файл

@ -1,205 +0,0 @@
// Copyright (c) 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;
public static class DelegateConstruction
{
class InstanceTests
{
public Action CaptureOfThis()
{
return delegate {
CaptureOfThis();
};
}
public Action CaptureOfThisAndParameter(int a)
{
return delegate {
CaptureOfThisAndParameter(a);
};
}
public Action CaptureOfThisAndParameterInForEach(int a)
{
foreach (int item in Enumerable.Empty<int>()) {
if (item > 0) {
return delegate {
CaptureOfThisAndParameter(item + a);
};
}
}
return null;
}
public Action CaptureOfThisAndParameterInForEachWithItemCopy(int a)
{
foreach (int item in Enumerable.Empty<int>()) {
int copyOfItem = item;
if (item > 0) {
return delegate {
CaptureOfThisAndParameter(item + a + copyOfItem);
};
}
}
return null;
}
public void LambdaInForLoop()
{
for (int i = 0; i < 100000; i++) {
Bar(() => Foo());
}
}
public int Foo()
{
return 0;
}
public void Bar(Func<int> f)
{
}
}
public static void Test(this string a)
{
}
public static Action<string> ExtensionMethodUnbound()
{
return new Action<string>(DelegateConstruction.Test);
}
public static Action ExtensionMethodBound()
{
return new Action("abc".Test);
}
public static Action ExtensionMethodBoundOnNull()
{
return new Action(((string)null).Test);
}
public static object StaticMethod()
{
return new Func<Action>(DelegateConstruction.ExtensionMethodBound);
}
public static object InstanceMethod()
{
return new Func<string>("hello".ToUpper);
}
public static object InstanceMethodOnNull()
{
return new Func<string>(((string)null).ToUpper);
}
public static List<Action<int>> AnonymousMethodStoreWithinLoop()
{
List<Action<int>> list = new List<Action<int>>();
for (int i = 0; i < 10; i++)
{
int counter;
list.Add(delegate(int x)
{
counter = x;
}
);
}
return list;
}
public static List<Action<int>> AnonymousMethodStoreOutsideLoop()
{
List<Action<int>> list = new List<Action<int>>();
int counter;
for (int i = 0; i < 10; i++)
{
list.Add(delegate(int x)
{
counter = x;
}
);
}
return list;
}
public static Action StaticAnonymousMethodNoClosure()
{
return delegate
{
Console.WriteLine();
};
}
public static void NameConflict()
{
// i is captured variable,
// j is parameter in anonymous method
// k is local in anonymous method,
// l is local in main method
// Ensure that the decompiler doesn't introduce name conflicts
List<Action<int>> list = new List<Action<int>>();
for (int l = 0; l < 10; l++) {
int i;
for (i = 0; i < 10; i++) {
list.Add(
delegate (int j) {
for (int k = 0; k < i; k += j) {
Console.WriteLine();
}
});
}
}
}
public static void NameConflict2(int j)
{
List<Action<int>> list = new List<Action<int>>();
for (int k = 0; k < 10; k++) {
list.Add(
delegate(int i) {
Console.WriteLine(i);
});
}
}
public static Action<int> NameConflict3(int i)
{
return delegate(int j) {
for (int k = 0; k < j; k++) {
Console.WriteLine(k);
}
};
}
public static Func<int, Func<int, int>> CurriedAddition(int a)
{
return b => c => a + b + c;
}
public static Func<int, Func<int, Func<int, int>>> CurriedAddition2(int a)
{
return b => c => d => a + b + c + d;
}
}

Просмотреть файл

@ -1,28 +0,0 @@
// Copyright (c) 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;
public class DoubleConstants
{
public const double Zero = 0.0;
public const double MinusZero = -0.0;
public const double NaN = double.NaN;
public const double PositiveInfinity = double.PositiveInfinity;
public const double NegativeInfinity = double.NegativeInfinity;
}

Просмотреть файл

@ -1,128 +0,0 @@
// Copyright (c) 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.Threading;
public class ExceptionHandling
{
public void MethodEndingWithEndFinally()
{
try
{
throw null;
}
finally
{
Console.WriteLine();
}
}
public void MethodEndingWithRethrow()
{
try
{
throw null;
}
catch
{
throw;
}
}
public void TryCatchFinally()
{
try
{
Console.WriteLine("Try");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
Console.WriteLine("Finally");
}
}
public void TryCatchMultipleHandlers()
{
try
{
Console.WriteLine("Try");
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.Message);
}
catch (Exception ex2)
{
Console.WriteLine(ex2.Message);
}
catch
{
Console.WriteLine("other");
}
}
public void NoUsingStatementBecauseTheVariableIsAssignedTo()
{
CancellationTokenSource cancellationTokenSource = null;
try
{
cancellationTokenSource = new CancellationTokenSource();
}
finally
{
if (cancellationTokenSource != null)
{
cancellationTokenSource.Dispose();
}
}
}
public void UsingStatementThatChangesTheVariable()
{
CancellationTokenSource cancellationTokenSource = null;
using (cancellationTokenSource)
{
cancellationTokenSource = new CancellationTokenSource();
}
}
public void TwoCatchBlocksWithSameVariable()
{
try
{
Console.WriteLine("Try1");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
try
{
Console.WriteLine("Try2");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}

Просмотреть файл

@ -1,370 +0,0 @@
// Copyright (c) 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 System.Linq.Expressions;
using System.Xml;
public class ExpressionTrees
{
class GenericClass<X>
{
public static X StaticField;
public X InstanceField;
public static X StaticProperty { get; set; }
public X InstanceProperty { get; set; }
public static bool GenericMethod<Y>()
{
return false;
}
}
int field;
static object ToCode<R>(object x, Expression<Func<R>> expr)
{
return expr;
}
static object ToCode<T, R>(object x, Expression<Func<T, R>> expr)
{
return expr;
}
static object X()
{
return null;
}
public void Parameter(bool a)
{
ToCode(X(), () => a);
}
public void LocalVariable()
{
bool a = true;
ToCode(X(), () => a);
}
public void LambdaParameter()
{
ToCode(X(), (bool a) => a);
}
public void AddOperator(int x)
{
ToCode(X(), () => 1 + x + 2);
}
public void AnonymousClasses()
{
ToCode(X(), () => new { X = 3, A = "a" });
}
public void ArrayIndex()
{
ToCode(X(), () => new[] { 3, 4, 5 }[0 + (int)(DateTime.Now.Ticks % 3)]);
}
public void ArrayLengthAndDoubles()
{
ToCode(X(), () => new[] { 1.0, 2.01, 3.5 }.Concat(new[] { 1.0, 2.0 }).ToArray().Length);
}
public void AsOperator()
{
ToCode(X(), () => new object() as string);
}
public void ComplexGenericName()
{
ToCode(X(), () => ((Func<int, bool>)(x => x > 0))(0));
}
public void DefaultValue()
{
ToCode(X(), () => new TimeSpan(1, 2, 3) == default(TimeSpan));
}
public void EnumConstant()
{
ToCode(X(), () => new object().Equals(MidpointRounding.ToEven));
}
public void IndexerAccess()
{
var dict = Enumerable.Range(1, 20).ToDictionary(n => n.ToString());
ToCode(X(), () => dict["3"] == 3);
}
public void IsOperator()
{
ToCode(X(), () => new object() is string);
}
public void ListInitializer()
{
ToCode(X(), () => new Dictionary<int, int> { { 1, 1 }, { 2, 2 }, { 3, 4 } }.Count == 3);
}
public void ListInitializer2()
{
ToCode(X(), () => new List<int>(50) { 1, 2, 3 }.Count == 3);
}
public void ListInitializer3()
{
ToCode(X(), () => new List<int> { 1, 2, 3 }.Count == 3);
}
public void LiteralCharAndProperty()
{
ToCode(X(), () => new string(' ', 3).Length == 1);
}
public void CharNoCast()
{
ToCode(X(), () => "abc"[1] == 'b');
}
public void StringsImplicitCast()
{
int i = 1;
string x = "X";
ToCode(X(), () => (("a\n\\b" ?? x) + x).Length == 2 ? false : true && (1m + -i > 0 || false));
}
public void NotImplicitCast()
{
byte z = 42;
ToCode(X(), () => ~z == 0);
}
public void MembersBuiltin()
{
ToCode(X(), () => 1.23m.ToString());
ToCode(X(), () => AttributeTargets.All.HasFlag((Enum)AttributeTargets.Assembly));
ToCode(X(), () => "abc".Length == 3);
ToCode(X(), () => 'a'.CompareTo('b') < 0);
}
public void MembersDefault()
{
ToCode(X(), () => default(DateTime).Ticks == 0);
ToCode(X(), () => default(int[]).Length == 0);
ToCode(X(), () => default(Type).IsLayoutSequential);
ToCode(X(), () => default(List<int>).Count);
ToCode(X(), () => default(int[]).Clone() == null);
ToCode(X(), () => default(Type).IsInstanceOfType(new object()));
ToCode(X(), () => default(List<int>).AsReadOnly());
}
public void DoAssert()
{
field = 37;
ToCode(X(), () => field != C());
ToCode(X(), () => !ReferenceEquals(this, new ExpressionTrees()));
ToCode(X(), () => MyEquals(this) && !MyEquals(default(ExpressionTrees)));
}
int C()
{
return field + 5;
}
bool MyEquals(ExpressionTrees other)
{
return other != null && field == other.field;
}
public void MethodGroupAsExtensionMethod()
{
ToCode(X(), () => (Func<bool>)new[] { 2000, 2004, 2008, 2012 }.Any);
}
public void MethodGroupConstant()
{
ToCode(X(), () => Array.TrueForAll(new[] { 2000, 2004, 2008, 2012 }, DateTime.IsLeapYear));
HashSet<int> set = new HashSet<int>();
ToCode(X(), () => new[] { 2000, 2004, 2008, 2012 }.All(set.Add));
Func<Func<object, object, bool>, bool> sink = f => f(null, null);
ToCode(X(), () => sink(int.Equals));
}
public void MultipleCasts()
{
ToCode(X(), () => 1 == (int)(object)1);
}
public void MultipleDots()
{
ToCode(X(), () => 3.ToString().ToString().Length > 0);
}
public void NestedLambda()
{
Func<Func<int>, int> call = f => f();
//no params
ToCode(X(), () => call(() => 42));
//one param
ToCode(X(), () => new[] { 37, 42 }.Select(x => x * 2));
//two params
ToCode(X(), () => new[] { 37, 42 }.Select((x, i) => x * 2));
}
public void CurriedLambda()
{
ToCode<int, Func<int, Func<int, int>>>(X(), a => b => c => a + b + c);
}
bool Fizz(Func<int, bool> a)
{
return a(42);
}
bool Buzz(Func<int, bool> a)
{
return a(42);
}
bool Fizz(Func<string, bool> a)
{
return a("42");
}
public void NestedLambda2()
{
ToCode(X(), () => Fizz(x => x == "a"));
ToCode(X(), () => Fizz(x => x == 37));
ToCode(X(), () => Fizz((int x) => true));
ToCode(X(), () => Buzz(x => true));
}
public void NewArrayAndExtensionMethod()
{
ToCode(X(), () => new[] { 1.0, 2.01, 3.5 }.SequenceEqual(new[] { 1.0, 2.01, 3.5 }));
}
public void NewMultiDimArray()
{
ToCode(X(), () => new int[3, 4].Length == 1);
}
public void NewObject()
{
ToCode(X(), () => new object() != new object());
}
public void NotOperator()
{
bool x = true;
int y = 3;
byte z = 42;
ToCode(X(), () => ~(int)z == 0);
ToCode(X(), () => ~y == 0);
ToCode(X(), () => !x);
}
public void ObjectInitializers()
{
XmlReaderSettings s = new XmlReaderSettings {
CloseInput = false,
CheckCharacters = false
};
ToCode(X(), () => new XmlReaderSettings { CloseInput = s.CloseInput, CheckCharacters = s.CheckCharacters }.Equals(s));
}
public void Quoted()
{
ToCode(X(), () => (Expression<Func<int, string, string>>)((n, s) => s + n.ToString()) != null);
}
public void Quoted2()
{
ToCode(X(), () => ToCode(X(), () => true).Equals(null));
}
public void QuotedWithAnonymous()
{
ToCode(X(), () => new[] { new { X = "a", Y = "b" } }.Select(o => o.X + o.Y).Single());
}
public void StaticCall()
{
ToCode(X(), () => Equals(3, 0));
}
public void ThisCall()
{
ToCode(X(), () => !Equals(3));
}
public void ThisExplicit()
{
ToCode(X(), () => object.Equals(this, 3));
}
public void TypedConstant()
{
ToCode(X(), () => new[] { typeof(int), typeof(string) });
}
public void StaticCallImplicitCast()
{
ToCode(X(), () => Equals(3, 0));
}
public void StaticMembers()
{
ToCode(X(), () => (DateTime.Now > DateTime.Now + TimeSpan.FromMilliseconds(10.001)).ToString() == "False");
}
public void Strings()
{
int i = 1;
string x = "X";
ToCode(X(), () => (("a\n\\b" ?? x) + x).Length == 2 ? false : true && (1m + (decimal)-i > 0m || false));
}
public void StringAccessor()
{
ToCode(X(), () => (int)"abc"[1] == 98);
}
public void GenericClassInstance()
{
ToCode(X(), () => new GenericClass<int>().InstanceField + new GenericClass<double>().InstanceProperty);
}
public void GenericClassStatic()
{
ToCode(X(), () => GenericClass<int>.StaticField + GenericClass<double>.StaticProperty);
}
public void InvokeGenericMethod()
{
ToCode(X(), () => GenericClass<int>.GenericMethod<double>());
}
}

Просмотреть файл

@ -1,165 +0,0 @@
// Copyright (c) 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;
public static class Generics
{
public class MyArray<T>
{
public class NestedClass<Y>
{
public T Item1;
public Y Item2;
}
public enum NestedEnum
{
A,
B
}
private T[] arr;
public MyArray(int capacity)
{
this.arr = new T[capacity];
}
public void Size(int capacity)
{
Array.Resize<T>(ref this.arr, capacity);
}
public void Grow(int capacity)
{
if (capacity >= this.arr.Length)
{
this.Size(capacity);
}
}
}
public interface IInterface
{
void Method1<T>() where T : class;
void Method2<T>() where T : class;
}
public abstract class Base : Generics.IInterface
{
// constraints must be repeated on implicit interface implementation
public abstract void Method1<T>() where T : class;
// constraints must not be specified on explicit interface implementation
void Generics.IInterface.Method2<T>()
{
}
}
public class Derived : Generics.Base
{
// constraints are inherited automatically and must not be specified
public override void Method1<T>()
{
}
}
private const Generics.MyArray<string>.NestedEnum enumVal = Generics.MyArray<string>.NestedEnum.A;
private static Type type1 = typeof(List<>);
private static Type type2 = typeof(Generics.MyArray<>);
private static Type type3 = typeof(List<>.Enumerator);
private static Type type4 = typeof(Generics.MyArray<>.NestedClass<>);
private static Type type5 = typeof(List<int>[]);
private static Type type6 = typeof(Generics.MyArray<>.NestedEnum);
public static void MethodWithConstraint<T, S>() where T : class, S where S : ICloneable, new()
{
}
public static void MethodWithStructConstraint<T>() where T : struct
{
}
private static void MultidimensionalArray<T>(T[,] array)
{
array[0, 0] = array[0, 1];
}
public static Dictionary<string, string>.KeyCollection.Enumerator GetEnumerator(Dictionary<string, string> d, Generics.MyArray<string>.NestedClass<int> nc)
{
// Tests references to inner classes in generic classes
return d.Keys.GetEnumerator();
}
public static bool IsString<T>(T input)
{
return input is string;
}
public static string AsString<T>(T input)
{
return input as string;
}
public static string CastToString<T>(T input)
{
return (string)((object)input);
}
public static T CastFromString<T>(string input)
{
return (T)((object)input);
}
public static bool IsInt<T>(T input)
{
return input is int;
}
public static int CastToInt<T>(T input)
{
return (int)((object)input);
}
public static T CastFromInt<T>(int input)
{
return (T)((object)input);
}
public static bool IsNullableInt<T>(T input)
{
return input is int?;
}
public static int? AsNullableInt<T>(T input)
{
return input as int?;
}
public static int? CastToNullableInt<T>(T input)
{
return (int?)((object)input);
}
public static T CastFromNullableInt<T>(int? input)
{
return (T)((object)input);
}
}

Просмотреть файл

@ -1,110 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using DiffLib;
using NUnit.Framework;
namespace ICSharpCode.Decompiler.Tests.Helpers
{
public class CodeAssert
{
public static void AreEqual(string input1, string input2)
{
var diff = new StringWriter();
if (!Compare(input1, input2, diff)) {
Assert.Fail(diff.ToString());
}
}
static bool Compare(string input1, string input2, StringWriter diff)
{
var differ = new AlignedDiff<string>(
NormalizeAndSplitCode(input1),
NormalizeAndSplitCode(input2),
new CodeLineEqualityComparer(),
new StringSimilarityComparer(),
new StringAlignmentFilter());
bool result = true, ignoreChange;
int line1 = 0, line2 = 0;
foreach (var change in differ.Generate()) {
switch (change.Change) {
case ChangeType.Same:
diff.Write("{0,4} {1,4} ", ++line1, ++line2);
diff.Write(" ");
diff.WriteLine(change.Element1);
break;
case ChangeType.Added:
diff.Write(" {1,4} ", line1, ++line2);
result &= ignoreChange = ShouldIgnoreChange(change.Element2);
diff.Write(ignoreChange ? " " : " + ");
diff.WriteLine(change.Element2);
break;
case ChangeType.Deleted:
diff.Write("{0,4} ", ++line1, line2);
result &= ignoreChange = ShouldIgnoreChange(change.Element1);
diff.Write(ignoreChange ? " " : " - ");
diff.WriteLine(change.Element1);
break;
case ChangeType.Changed:
diff.Write("{0,4} ", ++line1, line2);
result = false;
diff.Write("(-) ");
diff.WriteLine(change.Element1);
diff.Write(" {1,4} ", line1, ++line2);
diff.Write("(+) ");
diff.WriteLine(change.Element2);
break;
}
}
return result;
}
class CodeLineEqualityComparer : IEqualityComparer<string>
{
private IEqualityComparer<string> baseComparer = EqualityComparer<string>.Default;
public bool Equals(string x, string y)
{
return baseComparer.Equals(
NormalizeLine(x),
NormalizeLine(y)
);
}
public int GetHashCode(string obj)
{
return baseComparer.GetHashCode(NormalizeLine(obj));
}
}
private static string NormalizeLine(string line)
{
line = line.Trim();
var index = line.IndexOf("//");
if (index >= 0) {
return line.Substring(0, index);
} else if (line.StartsWith("#")) {
return string.Empty;
} else {
return line;
}
}
private static bool ShouldIgnoreChange(string line)
{
// for the result, we should ignore blank lines and added comments
return NormalizeLine(line) == string.Empty;
}
private static IEnumerable<string> NormalizeAndSplitCode(string input)
{
return input.Split(new[] { "\r\n", "\n\r", "\n", "\r" }, StringSplitOptions.RemoveEmptyEntries);
}
}
}

Просмотреть файл

@ -1,38 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ICSharpCode.Decompiler.Ast.Transforms;
using ICSharpCode.NRefactory.CSharp;
namespace ICSharpCode.Decompiler.Tests.Helpers
{
class RemoveCompilerAttribute : DepthFirstAstVisitor<object, object>, IAstTransform
{
public override object VisitAttribute(NRefactory.CSharp.Attribute attribute, object data)
{
var section = (AttributeSection)attribute.Parent;
SimpleType type = attribute.Type as SimpleType;
if (section.AttributeTarget == "assembly" &&
(type.Identifier == "CompilationRelaxations" || type.Identifier == "RuntimeCompatibility" || type.Identifier == "SecurityPermission" || type.Identifier == "AssemblyVersion" || type.Identifier == "Debuggable"))
{
attribute.Remove();
if (section.Attributes.Count == 0)
section.Remove();
}
if (section.AttributeTarget == "module" && type.Identifier == "UnverifiableCode")
{
attribute.Remove();
if (section.Attributes.Count == 0)
section.Remove();
}
return null;
}
public void Run(AstNode node)
{
node.AcceptVisitor(this, null);
}
}
}

Просмотреть файл

@ -1,137 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<PropertyGroup>
<ProjectGuid>{FEC0DA52-C4A6-4710-BE36-B484A20C5E22}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<OutputType>Library</OutputType>
<RootNamespace>ICSharpCode.Decompiler.Tests</RootNamespace>
<AssemblyName>ICSharpCode.Decompiler.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<AppDesignerFolder>Properties</AppDesignerFolder>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<NoStdLib>False</NoStdLib>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
<NoWarn>67,169,1058,728,1720,649</NoWarn>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'x86' ">
<PlatformTarget>x86</PlatformTarget>
<RegisterForComInterop>False</RegisterForComInterop>
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
<BaseAddress>4194304</BaseAddress>
<FileAlignment>4096</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<RegisterForComInterop>False</RegisterForComInterop>
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
<BaseAddress>4194304</BaseAddress>
<FileAlignment>4096</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<OutputPath>..\bin\Debug\</OutputPath>
<DebugSymbols>true</DebugSymbols>
<DebugType>Full</DebugType>
<Optimize>False</Optimize>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputPath>..\bin\Release\</OutputPath>
<DebugSymbols>false</DebugSymbols>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Reference Include="DiffLib">
<HintPath>..\..\packages\DiffLib.1.0.0.55\lib\net35-Client\DiffLib.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.NRefactory">
<HintPath>..\..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.NRefactory.CSharp">
<HintPath>..\..\packages\ICSharpCode.NRefactory.5.5.1\lib\Net40\ICSharpCode.NRefactory.CSharp.dll</HintPath>
</Reference>
<Reference Include="nunit.framework">
<SpecificVersion>False</SpecificVersion>
<HintPath>.\nunit.framework.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Async.cs" />
<Compile Include="CallOverloadedMethod.cs" />
<Compile Include="CheckedUnchecked.cs" />
<Compile Include="ControlFlow.cs" />
<Compile Include="DoubleConstants.cs" />
<Compile Include="ExpressionTrees.cs" />
<None Include="IL\SequenceOfNestedIfs.Output.cs" />
<Compile Include="IL\ILTests.cs" />
<Compile Include="LiftedOperators.cs" />
<Compile Include="CustomShortCircuitOperators.cs" />
<Compile Include="Helpers\CodeAssert.cs" />
<Compile Include="IncrementDecrement.cs" />
<Compile Include="Lock.cs" />
<Compile Include="PInvoke.cs" />
<Compile Include="QueryExpressions.cs" />
<Compile Include="Switch.cs" />
<Compile Include="TypeAnalysisTests.cs" />
<Compile Include="UndocumentedExpressions.cs" />
<Compile Include="UnsafeCode.cs" />
<Compile Include="Types\S_TypeDeclarations.cs" />
<Compile Include="YieldReturn.cs" />
<None Include="IL\SequenceOfNestedIfs.dll" />
<None Include="IL\SequenceOfNestedIfs.il" />
<None Include="IL\StackTests.exe" />
<None Include="IL\StackTests.il" />
<None Include="packages.config" />
<None Include="Types\S_EnumSamples.cs" />
<None Include="CustomAttributes\S_AssemblyCustomAttribute.cs" />
<Compile Include="Helpers\RemoveCompilerAttribute.cs" />
<Compile Include="Types\S_TypeMemberDeclarations.cs" />
<Compile Include="Types\EnumTests.cs" />
<Compile Include="Types\TypeTests.cs" />
<Compile Include="DelegateConstruction.cs" />
<None Include="CustomAttributes\S_CustomAttributes.cs" />
<Compile Include="Loops.cs" />
<Compile Include="PropertiesAndEvents.cs" />
<None Include="CustomAttributes\S_CustomAttributeSamples.cs" />
<Compile Include="CodeSampleFileParser.cs" />
<Compile Include="CustomAttributes\CustomAttributeTests.cs" />
<Compile Include="DecompilerTestBase.cs" />
<Compile Include="InitializerTests.cs" />
<Compile Include="ExceptionHandling.cs" />
<Compile Include="Generics.cs" />
<Compile Include="MultidimensionalArray.cs" />
<Compile Include="TestRunner.cs" />
<Compile Include="ValueTypes.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Mono.Cecil\Mono.Cecil.csproj">
<Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project>
<Name>Mono.Cecil</Name>
</ProjectReference>
<ProjectReference Include="..\ICSharpCode.Decompiler.csproj">
<Project>{984CC812-9470-4A13-AFF9-CC44068D666C}</Project>
<Name>ICSharpCode.Decompiler</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<ItemGroup>
<None Include="BooleanConsumedAsInteger.il" />
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
</Project>

Просмотреть файл

@ -1,51 +0,0 @@
// Copyright (c) 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.IO;
using ICSharpCode.Decompiler.Ast;
using ICSharpCode.Decompiler.Tests.Helpers;
using Mono.Cecil;
using NUnit.Framework;
namespace ICSharpCode.Decompiler.Tests
{
[TestFixture]
public class ILTests
{
const string path = "../../Tests/IL";
[Test]
public void SequenceOfNestedIfs()
{
Run("SequenceOfNestedIfs.dll", "SequenceOfNestedIfs.Output.cs");
}
void Run(string compiledFile, string expectedOutputFile)
{
string expectedOutput = File.ReadAllText(Path.Combine(path, expectedOutputFile));
var assembly = AssemblyDefinition.ReadAssembly(Path.Combine(path, compiledFile));
AstBuilder decompiler = new AstBuilder(new DecompilerContext(assembly.MainModule));
decompiler.AddAssembly(assembly);
new Helpers.RemoveCompilerAttribute().Run(decompiler.SyntaxTree);
StringWriter output = new StringWriter();
decompiler.GenerateCode(new PlainTextOutput(output));
CodeAssert.AreEqual(expectedOutput, output.ToString());
}
}
}

Просмотреть файл

@ -1,53 +0,0 @@
using System;
[Serializable]
public class Material
{
public static implicit operator bool(Material m)
{
return m == null;
}
}
[Serializable]
public class SequenceOfNestedIfs
{
public bool _clear;
public Material _material;
public override bool CheckShader()
{
return false;
}
public override void CreateMaterials()
{
if (!this._clear)
{
if (!this.CheckShader())
{
return;
}
this._material = new Material();
}
if (!this._material)
{
if (!this.CheckShader())
{
return;
}
this._material = new Material();
}
if (!this._material)
{
if (!this.CheckShader())
{
return;
}
this._material = new Material();
}
if (!this._material)
{
if (this.CheckShader())
{
this._material = new Material();
}
}
}
}

Просмотреть файл

@ -1,140 +0,0 @@
// Metadata version: v2.0.50727
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 2:0:0:0
}
.assembly SequenceOfNestedIfs
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx
63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows.
.hash algorithm 0x00008004
.ver 0:0:0:0
}
.module SequenceOfNestedIfs
// MVID: {DCEC8A87-5679-4EBE-89A3-51274D8B5446}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY
// Image base: 0x01D60000
// =============== CLASS MEMBERS DECLARATION ===================
.class public auto ansi serializable beforefieldinit Material
extends [mscorlib]System.Object
{
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Material::.ctor
.method public hidebysig specialname static
bool op_Implicit(class Material m) cil managed
{
// Code size 11 (0xb)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldnull
IL_0008: ceq
IL_000a: ret
} // end of method Material::op_Implicit
} // end of class Material
.class public auto ansi serializable beforefieldinit SequenceOfNestedIfs
extends [mscorlib]System.Object
{
.field public bool _clear
.field public class Material _material
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method SequenceOfNestedIfs::.ctor
.method public hidebysig virtual instance bool
CheckShader() cil managed
{
// Code size 2 (0x2)
.maxstack 8
IL_0000: ldc.i4.0
IL_0001: ret
} // end of method SequenceOfNestedIfs::CheckShader
.method public hidebysig virtual instance void
CreateMaterials() cil managed
{
// Code size 168 (0xa8)
.maxstack 13
IL_0000: ldarg.0
IL_0001: ldfld bool SequenceOfNestedIfs::_clear
IL_0006: brtrue IL_0026
IL_000b: ldarg.0
IL_000c: callvirt instance bool SequenceOfNestedIfs::CheckShader()
IL_0011: brtrue IL_001b
IL_0016: br IL_00a7
IL_001b: ldarg.0
IL_001c: newobj instance void Material::.ctor()
IL_0021: stfld class Material SequenceOfNestedIfs::_material
IL_0026: ldarg.0
IL_0027: ldfld class Material SequenceOfNestedIfs::_material
IL_002c: call bool Material::op_Implicit(class Material)
IL_0031: brtrue IL_0051
IL_0036: ldarg.0
IL_0037: callvirt instance bool SequenceOfNestedIfs::CheckShader()
IL_003c: brtrue IL_0046
IL_0041: br IL_00a7
IL_0046: ldarg.0
IL_0047: newobj instance void Material::.ctor()
IL_004c: stfld class Material SequenceOfNestedIfs::_material
IL_0051: ldarg.0
IL_0052: ldfld class Material SequenceOfNestedIfs::_material
IL_0057: call bool Material::op_Implicit(class Material)
IL_005c: brtrue IL_007c
IL_0061: ldarg.0
IL_0062: callvirt instance bool SequenceOfNestedIfs::CheckShader()
IL_0067: brtrue IL_0071
IL_006c: br IL_00a7
IL_0071: ldarg.0
IL_0072: newobj instance void Material::.ctor()
IL_0077: stfld class Material SequenceOfNestedIfs::_material
IL_007c: ldarg.0
IL_007d: ldfld class Material SequenceOfNestedIfs::_material
IL_0082: call bool Material::op_Implicit(class Material)
IL_0087: brtrue IL_00a7
IL_008c: ldarg.0
IL_008d: callvirt instance bool SequenceOfNestedIfs::CheckShader()
IL_0092: brtrue IL_009c
IL_0097: br IL_00a7
IL_009c: ldarg.0
IL_009d: newobj instance void Material::.ctor()
IL_00a2: stfld class Material SequenceOfNestedIfs::_material
IL_00a7: ret
} // end of method SequenceOfNestedIfs::CreateMaterials
} // end of class SequenceOfNestedIfs

Просмотреть файл

@ -1,132 +0,0 @@
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
.ver 4:0:0:0
}
.assembly StackTests
{
.hash algorithm 0x00008004
.ver 1:0:4059:39717
}
.module StackTests.exe
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000003 // ILONLY 32BITREQUIRED
.class private auto ansi beforefieldinit StackTests.Program extends [mscorlib]System.Object
{
.method public hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 8
ldc.i4.0
call string StackTests.Program::Test1(bool cond)
call void [mscorlib]System.Console::WriteLine(string) // false
ldc.i4.1
call string StackTests.Program::Test1(bool cond)
call void [mscorlib]System.Console::WriteLine(string) // true
ldc.i4.0
ldc.i4.0
ldc.i4.0
call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2)
call void [mscorlib]System.Console::WriteLine(int32) // 11
ldc.i4.0
ldc.i4.1
ldc.i4.0
call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2)
call void [mscorlib]System.Console::WriteLine(int32) // 21
ldc.i4.1
ldc.i4.1
ldc.i4.1
call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2)
call void [mscorlib]System.Console::WriteLine(int32) // 32
ldc.i4.2
ldc.i4.1
ldc.i4.0
call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2)
call void [mscorlib]System.Console::WriteLine(int32) // 23
ret
}
.method public hidebysig static string Test1(bool cond) cil managed
{
ldarg.0
brtrue TRUE
FALSE:
ldstr "false"
br EXIT
TRUE:
ldstr "true"
EXIT:
ret
}
.method public hidebysig static int32 Test2(int32 switch1, int32 br1, int32 br2) cil managed
{
ldarg.0
switch (ENTRY1, ENTRY2, ENTRY3)
ldc.i4.0
ret
ENTRY1:
ldc.i4.1
br BRANCH1
ENTRY2:
ldc.i4.2
br BRANCH1
ENTRY3:
ldc.i4.3
br BRANCH2
BRANCH1:
ldarg.1
brtrue BRANCH2
EXIT1:
ldc.i4 10
add
ret
BRANCH2:
ldarg.2
brtrue.s EXIT3
EXIT2:
ldc.i4 20
add
ret
EXIT3:
ldc.i4 30
add
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Program::.ctor
} // end of class StackTests.Program
// =============================================================

Просмотреть файл

@ -1,254 +0,0 @@
// Copyright (c) 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;
public class IncrementDecrement
{
[Flags]
private enum MyEnum
{
None = 0,
One = 1,
Two = 2,
Four = 4
}
public class MutableClass
{
public int Field;
public int Property
{
get;
set;
}
public uint this[string name]
{
get
{
return 0u;
}
set
{
}
}
}
private IncrementDecrement.MyEnum enumField;
public static int StaticField;
public static int StaticProperty
{
get;
set;
}
private IncrementDecrement.MutableClass M()
{
return new IncrementDecrement.MutableClass();
}
private int[,] Array()
{
return null;
}
private unsafe int* GetPointer()
{
return null;
}
public int PreIncrementInAddition(int i, int j)
{
return i + ++j;
}
public int PreIncrementArrayElement(int[] array, int pos)
{
return --array[pos];
}
public int PreIncrementInstanceField()
{
return ++this.M().Field;
}
public int PreIncrementInstanceField2(IncrementDecrement.MutableClass m)
{
return ++m.Field;
}
public int PreIncrementInstanceProperty()
{
return ++this.M().Property;
}
public int PreIncrementStaticField()
{
return ++IncrementDecrement.StaticField;
}
public int PreIncrementStaticProperty()
{
return ++IncrementDecrement.StaticProperty;
}
// public uint PreIncrementIndexer(string name)
// {
// return ++this.M()[name];
// }
public int PreIncrementByRef(ref int i)
{
return ++i;
}
public unsafe int PreIncrementByPointer()
{
return ++(*this.GetPointer());
}
public int PreIncrement2DArray()
{
return ++this.Array()[1, 2];
}
public int CompoundAssignInstanceField()
{
return this.M().Field *= 10;
}
public int CompoundAssignInstanceProperty()
{
return this.M().Property *= 10;
}
public int CompoundAssignStaticField()
{
return IncrementDecrement.StaticField ^= 100;
}
public int CompoundAssignStaticProperty()
{
return IncrementDecrement.StaticProperty &= 10;
}
public int CompoundAssignArrayElement1(int[] array, int pos)
{
return array[pos] *= 10;
}
public int CompoundAssignArrayElement2(int[] array)
{
return array[Environment.TickCount] *= 10;
}
// public uint CompoundAssignIndexer(string name)
// {
// return this.M()[name] -= 2;
// }
public int CompoundAssignIncrement2DArray()
{
return this.Array()[1, 2] %= 10;
}
public int CompoundAssignByRef(ref int i)
{
return i <<= 2;
}
public unsafe double CompoundAssignByPointer(double* ptr)
{
return *ptr /= 1.5;
}
public void CompoundAssignEnum()
{
this.enumField |= IncrementDecrement.MyEnum.Two;
this.enumField &= ~IncrementDecrement.MyEnum.Four;
}
public int PostIncrementInAddition(int i, int j)
{
return i++ + j;
}
public void PostIncrementInlineLocalVariable(Func<int, int> f)
{
int num = 0;
f(num++);
}
public int PostIncrementArrayElement(int[] array, int pos)
{
return array[pos]--;
}
public int PostIncrementStaticField()
{
return IncrementDecrement.StaticField++;
}
public int PostIncrementStaticProperty()
{
return IncrementDecrement.StaticProperty++;
}
public int PostIncrementInstanceField(IncrementDecrement.MutableClass m)
{
return m.Field++;
}
// public uint PostIncrementIndexer(string name)
// {
// return this.M()[name]++;
// }
// public unsafe int PostIncrementOfPointer(int* ptr)
// {
// return *(ptr++);
// }
public int PostIncrementInstanceField()
{
return this.M().Field--;
}
public int PostIncrementInstanceProperty()
{
return this.M().Property--;
}
public int PostIncrement2DArray()
{
return this.Array()[IncrementDecrement.StaticField, IncrementDecrement.StaticProperty]++;
}
public int PostIncrementByRef(ref int i)
{
return i++;
}
public unsafe int PostIncrementByPointer()
{
return (*this.GetPointer())++;
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше