Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Ward <matt.ward@xamarin.com>2015-11-19 12:43:19 +0300
committerMatt Ward <matt.ward@xamarin.com>2015-11-19 12:43:19 +0300
commit3464fad275c48d7c4ffb3602dfcc66afa467b76a (patch)
tree10db040d0a2cf55165ac8d43dd24b60ca297a887 /main/contrib
parentd94562154c646871e5af85aeb9eca1ece86776f9 (diff)
parent203ed0aabb6a60c7bb18f3b7b490588ab09cf38e (diff)
Merge branch 'master' into aspnet-project-wizard
Conflicts: main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj main/tests/UnitTests/UnitTests.csproj
Diffstat (limited to 'main/contrib')
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Ast/Annotations.cs4
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Ast/AstBuilder.cs30
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs104
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Ast/TextTokenWriter.cs28
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs31
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs14
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs2
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs6
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs23
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs92
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs40
-rw-r--r--main/contrib/ICSharpCode.Decompiler/CecilExtensions.cs47
-rw-r--r--main/contrib/ICSharpCode.Decompiler/DecompilerException.cs2
-rw-r--r--main/contrib/ICSharpCode.Decompiler/DecompilerSettings.cs16
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs8
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs4
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs22
-rw-r--r--main/contrib/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj12
-rw-r--r--main/contrib/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs99
-rw-r--r--main/contrib/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs8
-rw-r--r--main/contrib/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs221
-rw-r--r--main/contrib/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs2
-rw-r--r--main/contrib/ICSharpCode.Decompiler/ILAst/ILInlining.cs20
-rw-r--r--main/contrib/ICSharpCode.Decompiler/ILAst/PatternMatching.cs6
-rw-r--r--main/contrib/ICSharpCode.Decompiler/ILAst/StateRange.cs14
-rw-r--r--main/contrib/ICSharpCode.Decompiler/ILAst/SymbolicExecution.cs15
-rw-r--r--main/contrib/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs15
-rw-r--r--main/contrib/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs30
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Properties/AssemblyInfo.template.cs27
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/Async.cs155
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/CodeSampleFileParser.cs3
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/ControlFlow.cs97
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs88
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs32
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs16
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/DoubleConstants.cs28
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/ExpressionTrees.cs370
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/Helpers/CodeAssert.cs110
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj7
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/IL/ILTests.cs51
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.Output.cs53
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.dllbin0 -> 2560 bytes
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.il140
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.exebin0 -> 2048 bytes
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.il132
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/LiftedOperators.cs830
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/Lock.cs38
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/PInvoke.cs96
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/QueryExpressions.cs188
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/TestRunner.cs63
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/TypeAnalysisTests.cs153
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeDeclarations.cs17
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs2
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/UndocumentedExpressions.cs41
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/UnsafeCode.cs42
-rw-r--r--main/contrib/ICSharpCode.Decompiler/Tests/ValueTypes.cs17
56 files changed, 3427 insertions, 284 deletions
diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Annotations.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Annotations.cs
index 7b22dd0136..1c957292ec 100644
--- a/main/contrib/ICSharpCode.Decompiler/Ast/Annotations.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Ast/Annotations.cs
@@ -10,10 +10,12 @@ namespace ICSharpCode.Decompiler.Ast
public class TypeInformation
{
public readonly TypeReference InferredType;
+ public readonly TypeReference ExpectedType;
- public TypeInformation(TypeReference inferredType)
+ public TypeInformation(TypeReference inferredType, TypeReference expectedType)
{
this.InferredType = inferredType;
+ this.ExpectedType = expectedType;
}
}
diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/main/contrib/ICSharpCode.Decompiler/Ast/AstBuilder.cs
index b488cf4840..82d6a71554 100644
--- a/main/contrib/ICSharpCode.Decompiler/Ast/AstBuilder.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Ast/AstBuilder.cs
@@ -176,14 +176,19 @@ namespace ICSharpCode.Decompiler.Ast
RunTransformations();
syntaxTree.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true });
- var outputFormatter = new TextTokenWriter(output) { FoldBraces = context.Settings.FoldBraces };
+ var outputFormatter = new TextTokenWriter(output, context) { FoldBraces = context.Settings.FoldBraces };
var formattingPolicy = context.Settings.CSharpFormattingOptions;
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(outputFormatter, formattingPolicy));
}
public void AddAssembly(AssemblyDefinition assemblyDefinition, bool onlyAssemblyLevel = false)
{
- if (assemblyDefinition.Name.Version != null) {
+ AddAssembly(assemblyDefinition.MainModule, onlyAssemblyLevel);
+ }
+
+ public void AddAssembly(ModuleDefinition moduleDefinition, bool onlyAssemblyLevel = false)
+ {
+ if (moduleDefinition.Assembly != null && moduleDefinition.Assembly.Name.Version != null) {
syntaxTree.AddChild(
new AttributeSection {
AttributeTarget = "assembly",
@@ -192,22 +197,24 @@ namespace ICSharpCode.Decompiler.Ast
Type = new SimpleType("AssemblyVersion")
.WithAnnotation(new TypeReference(
"System.Reflection", "AssemblyVersionAttribute",
- assemblyDefinition.MainModule, assemblyDefinition.MainModule.TypeSystem.Corlib)),
+ moduleDefinition, moduleDefinition.TypeSystem.Corlib)),
Arguments = {
- new PrimitiveExpression(assemblyDefinition.Name.Version.ToString())
+ new PrimitiveExpression(moduleDefinition.Assembly.Name.Version.ToString())
}
}
}
}, EntityDeclaration.AttributeRole);
}
- ConvertCustomAttributes(syntaxTree, assemblyDefinition, "assembly");
- ConvertSecurityAttributes(syntaxTree, assemblyDefinition, "assembly");
- ConvertCustomAttributes(syntaxTree, assemblyDefinition.MainModule, "module");
- AddTypeForwarderAttributes(syntaxTree, assemblyDefinition.MainModule, "assembly");
+ if (moduleDefinition.Assembly != null) {
+ ConvertCustomAttributes(syntaxTree, moduleDefinition.Assembly, "assembly");
+ ConvertSecurityAttributes(syntaxTree, moduleDefinition.Assembly, "assembly");
+ }
+ ConvertCustomAttributes(syntaxTree, moduleDefinition, "module");
+ AddTypeForwarderAttributes(syntaxTree, moduleDefinition, "assembly");
if (!onlyAssemblyLevel) {
- foreach (TypeDefinition typeDef in assemblyDefinition.MainModule.Types) {
+ foreach (TypeDefinition typeDef in moduleDefinition.Types) {
// Skip the <Module> class
if (typeDef.Name == "<Module>") continue;
// Skip any hidden types
@@ -330,19 +337,22 @@ namespace ICSharpCode.Decompiler.Ast
if (typeDef.IsEnum) {
long expectedEnumMemberValue = 0;
bool forcePrintingInitializers = IsFlagsEnum(typeDef);
+ TypeCode baseType = TypeCode.Int32;
foreach (FieldDefinition field in typeDef.Fields) {
if (!field.IsStatic) {
// the value__ field
if (field.FieldType != typeDef.Module.TypeSystem.Int32) {
astType.AddChild(ConvertType(field.FieldType), Roles.BaseType);
+ baseType = TypeAnalysis.GetTypeCode(field.FieldType);
}
} else {
EnumMemberDeclaration enumMember = new EnumMemberDeclaration();
+ ConvertCustomAttributes(enumMember, field);
enumMember.AddAnnotation(field);
enumMember.Name = CleanName(field.Name);
long memberValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false);
if (forcePrintingInitializers || memberValue != expectedEnumMemberValue) {
- enumMember.AddChild(new PrimitiveExpression(field.Constant), EnumMemberDeclaration.InitializerRole);
+ enumMember.AddChild(new PrimitiveExpression(CSharpPrimitiveCast.Cast(baseType, field.Constant, false)), EnumMemberDeclaration.InitializerRole);
}
expectedEnumMemberValue = memberValue + 1;
astType.AddChild(enumMember, Roles.TypeMemberRole);
diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/main/contrib/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
index fae41f6860..e98e8dbc71 100644
--- a/main/contrib/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
@@ -52,8 +52,8 @@ namespace ICSharpCode.Decompiler.Ast
/// These are used to update the parameter names when the decompiler generates names for the parameters.</param>
/// <returns>Block for the method body</returns>
public static BlockStatement CreateMethodBody(MethodDefinition methodDef,
- DecompilerContext context,
- IEnumerable<ParameterDeclaration> parameters = null)
+ DecompilerContext context,
+ IEnumerable<ParameterDeclaration> parameters = null)
{
MethodDefinition oldCurrentMethod = context.CurrentMethod;
Debug.Assert(oldCurrentMethod == null || oldCurrentMethod == methodDef);
@@ -103,8 +103,8 @@ namespace ICSharpCode.Decompiler.Ast
if (parameters != null) {
foreach (var pair in (from p in parameters
- join v in astBuilder.Parameters on p.Annotation<ParameterDefinition>() equals v.OriginalParameter
- select new { p, v.Name }))
+ join v in astBuilder.Parameters on p.Annotation<ParameterDefinition>() equals v.OriginalParameter
+ select new { p, v.Name }))
{
pair.p.Name = pair.Name;
}
@@ -204,7 +204,7 @@ namespace ICSharpCode.Decompiler.Ast
tryCatchStmt.TryBlock = TransformBlock(tryCatchNode.TryBlock);
foreach (var catchClause in tryCatchNode.CatchBlocks) {
if (catchClause.ExceptionVariable == null
- && (catchClause.ExceptionType == null || catchClause.ExceptionType.MetadataType == MetadataType.Object))
+ && (catchClause.ExceptionType == null || catchClause.ExceptionType.MetadataType == MetadataType.Object))
{
tryCatchStmt.CatchClauses.Add(new Ast.CatchClause { Body = TransformBlock(catchClause) });
} else {
@@ -262,7 +262,7 @@ namespace ICSharpCode.Decompiler.Ast
result = node;
if (result != null)
- result = result.WithAnnotation(new TypeInformation(expr.InferredType));
+ result = result.WithAnnotation(new TypeInformation(expr.InferredType, expr.ExpectedType));
if (result != null)
return result.WithAnnotation(ilRanges);
@@ -291,16 +291,10 @@ namespace ICSharpCode.Decompiler.Ast
{
BinaryOperatorExpression boe;
if (byteCode.InferredType is PointerType) {
- if (byteCode.Arguments[0].ExpectedType is PointerType) {
- arg2 = DivideBySize(arg2, ((PointerType)byteCode.InferredType).ElementType);
- boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
- boe.AddAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation);
- } else if (byteCode.Arguments[1].ExpectedType is PointerType) {
- arg1 = DivideBySize(arg1, ((PointerType)byteCode.InferredType).ElementType);
- boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
+ boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
+ if (byteCode.Arguments[0].ExpectedType is PointerType ||
+ byteCode.Arguments[1].ExpectedType is PointerType) {
boe.AddAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation);
- } else {
- boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
}
} else {
boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2);
@@ -314,12 +308,9 @@ namespace ICSharpCode.Decompiler.Ast
{
BinaryOperatorExpression boe;
if (byteCode.InferredType is PointerType) {
+ boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2);
if (byteCode.Arguments[0].ExpectedType is PointerType) {
- arg2 = DivideBySize(arg2, ((PointerType)byteCode.InferredType).ElementType);
- boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2);
boe.WithAnnotation(IntroduceUnsafeModifier.PointerArithmeticAnnotation);
- } else {
- boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2);
}
} else {
boe = new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2);
@@ -460,12 +451,30 @@ namespace ICSharpCode.Decompiler.Ast
// can also mean Inequality, when used with object references
TypeReference arg1Type = byteCode.Arguments[0].InferredType;
if (arg1Type != null && !arg1Type.IsValueType) goto case ILCode.Cne;
+
+ // when comparing signed integral values using Cgt_Un with 0
+ // the Ast should actually contain InEquality since "(uint)a > 0u" is identical to "a != 0"
+ if (arg1Type.IsSignedIntegralType())
+ {
+ var p = arg2 as Ast.PrimitiveExpression;
+ if (p != null && p.Value.IsZero()) goto case ILCode.Cne;
+ }
+
goto case ILCode.Cgt;
}
case ILCode.Cle_Un: {
// can also mean Equality, when used with object references
TypeReference arg1Type = byteCode.Arguments[0].InferredType;
if (arg1Type != null && !arg1Type.IsValueType) goto case ILCode.Ceq;
+
+ // when comparing signed integral values using Cle_Un with 0
+ // the Ast should actually contain Equality since "(uint)a <= 0u" is identical to "a == 0"
+ if (arg1Type.IsSignedIntegralType())
+ {
+ var p = arg2 as Ast.PrimitiveExpression;
+ if (p != null && p.Value.IsZero()) goto case ILCode.Ceq;
+ }
+
goto case ILCode.Cle;
}
case ILCode.Cle: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2);
@@ -706,7 +715,7 @@ namespace ICSharpCode.Decompiler.Ast
}
return new StackAllocExpression {
Type = AstBuilder.ConvertType(type),
- CountExpression = DivideBySize(arg1, type)
+ CountExpression = arg1
};
}
case ILCode.Mkrefany:
@@ -849,7 +858,7 @@ namespace ICSharpCode.Decompiler.Ast
args[args.Count - 1].AddAnnotation(new ParameterDeclarationAnnotation(byteCode));
return args[args.Count - 1];
case ILCode.Await:
- return new UnaryOperatorExpression(UnaryOperatorType.Await, arg1);
+ return new UnaryOperatorExpression(UnaryOperatorType.Await, UnpackDirectionExpression(arg1));
case ILCode.NullableOf:
case ILCode.ValueOf:
return arg1;
@@ -902,45 +911,6 @@ namespace ICSharpCode.Decompiler.Ast
}
}
- /// <summary>
- /// Divides expr by the size of 'type'.
- /// </summary>
- Expression DivideBySize(Expression expr, TypeReference type)
- {
- CastExpression cast = expr as CastExpression;
- if (cast != null && cast.Type is PrimitiveType && ((PrimitiveType)cast.Type).Keyword == "int")
- expr = cast.Expression.Detach();
-
- Expression sizeOfExpression;
- switch (TypeAnalysis.GetInformationAmount(type)) {
- case 1:
- case 8:
- sizeOfExpression = new PrimitiveExpression(1);
- break;
- case 16:
- sizeOfExpression = new PrimitiveExpression(2);
- break;
- case 32:
- sizeOfExpression = new PrimitiveExpression(4);
- break;
- case 64:
- sizeOfExpression = new PrimitiveExpression(8);
- break;
- default:
- sizeOfExpression = new SizeOfExpression { Type = AstBuilder.ConvertType(type) };
- break;
- }
-
- BinaryOperatorExpression boe = expr as BinaryOperatorExpression;
- if (boe != null && boe.Operator == BinaryOperatorType.Multiply && sizeOfExpression.IsMatch(boe.Right))
- return boe.Left.Detach();
-
- if (sizeOfExpression.IsMatch(expr))
- return new PrimitiveExpression(1);
-
- return new BinaryOperatorExpression(expr, BinaryOperatorType.Divide, sizeOfExpression);
- }
-
Expression MakeDefaultValue(TypeReference type)
{
TypeDefinition typeDef = type.Resolve();
@@ -975,10 +945,7 @@ namespace ICSharpCode.Decompiler.Ast
// Unpack any DirectionExpression that is used as target for the call
// (calling methods on value types implicitly passes the first argument by reference)
- if (target is DirectionExpression) {
- target = ((DirectionExpression)target).Expression;
- target.Remove(); // detach from DirectionExpression
- }
+ target = UnpackDirectionExpression(target);
if (cecilMethodDef != null) {
// convert null.ToLower() to ((string)null).ToLower()
@@ -1078,6 +1045,15 @@ namespace ICSharpCode.Decompiler.Ast
return target.Invoke(cecilMethod.Name, ConvertTypeArguments(cecilMethod), methodArgs).WithAnnotation(cecilMethod);
}
+ static Expression UnpackDirectionExpression(Expression target)
+ {
+ if (target is DirectionExpression) {
+ return ((DirectionExpression)target).Expression.Detach();
+ } else {
+ return target;
+ }
+ }
+
static void AdjustArgumentsForMethodCall(MethodReference cecilMethod, List<Expression> methodArgs)
{
// Convert 'ref' into 'out' where necessary
diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/TextTokenWriter.cs b/main/contrib/ICSharpCode.Decompiler/Ast/TextTokenWriter.cs
index b77284cb24..4f52b89d0d 100644
--- a/main/contrib/ICSharpCode.Decompiler/Ast/TextTokenWriter.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Ast/TextTokenWriter.cs
@@ -30,6 +30,7 @@ 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;
@@ -40,15 +41,22 @@ namespace ICSharpCode.Decompiler.Ast
public bool FoldBraces = false;
- public TextTokenWriter(ITextOutput output)
+ 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)
{
+ if (identifier.IsVerbatim || CSharpOutputVisitor.IsKeyword(identifier.Name, identifier)) {
+ output.Write('@');
+ }
+
var definition = GetCurrentDefinition();
if (definition != null) {
output.WriteDefinition(identifier.Name, definition, false);
@@ -89,6 +97,24 @@ namespace ICSharpCode.Decompiler.Ast
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;
}
diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs
index 3b38762651..ed1155ba15 100644
--- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs
@@ -144,6 +144,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
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) {
@@ -268,6 +277,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
}
}
+
+ 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) {
@@ -285,6 +303,19 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
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)
{
diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
index 9c5aa97f6e..0741466f95 100644
--- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
@@ -180,8 +180,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
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);
@@ -189,11 +189,19 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
Expression returnExpr = ((ReturnStatement)body.Statements.Single()).Expression;
returnExpr.Remove();
lambda.Body = returnExpr;
- objectCreateExpression.ReplaceWith(lambda);
+ replacement = lambda;
} else {
ame.Body = body;
- objectCreateExpression.ReplaceWith(ame);
+ 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;
}
diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs
index 32f7cb0dbe..ffd6f5ac76 100644
--- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs
@@ -804,7 +804,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return new IndexerExpression(targetConverted, indexConverted);
}
IList<Expression> indexesConverted = ConvertExpressionsArray(index);
- if (indexConverted != null) {
+ if (indexesConverted != null) {
return new IndexerExpression(targetConverted, indexesConverted);
}
return null;
diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs
index 43548e38d4..a9e72564f3 100644
--- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs
@@ -35,7 +35,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
protected override bool VisitChildren(AstNode node, object data)
{
bool result = false;
- for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
+ AstNode next;
+ for (AstNode child = node.FirstChild; child != null; child = next) {
+ // Store next to allow the loop to continue
+ // if the visitor removes/replaces child.
+ next = child.NextSibling;
result |= child.AcceptVisitor(this, data);
}
if (result && node is EntityDeclaration && !(node is Accessor)) {
diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs
index 5c3784fa4c..4182eec8f2 100644
--- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs
@@ -38,24 +38,23 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
public void Run(AstNode compilationUnit)
{
- if (!context.Settings.UsingDeclarations)
- return;
-
// 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
- // 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] };
+ 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);
}
- compilationUnit.InsertChildAfter(null, new UsingDeclaration { Import = nsType }, SyntaxTree.MemberRole);
}
if (!context.Settings.FullyQualifyAmbiguousTypeNames)
diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
index 22d1afe627..bff2cc6b95 100644
--- a/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
@@ -99,6 +99,9 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
if (result != null)
return result;
}
+ AstNode simplifiedIfElse = SimplifyCascadingIfElseStatements(ifElseStatement);
+ if (simplifiedIfElse != null)
+ return simplifiedIfElse;
return base.VisitIfElseStatement(ifElseStatement, data);
}
@@ -614,6 +617,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
static readonly AstNode lockTryCatchPattern = new TryCatchStatement {
TryBlock = new BlockStatement {
+ new OptionalNode(new VariableDeclarationStatement()).ToStatement(),
new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke(
"Enter", new AnyNode("enter"),
new DirectionExpression {
@@ -626,21 +630,57 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
new IfElseStatement {
Condition = new Backreference("flag"),
TrueStatement = new BlockStatement {
- new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke("Exit", new NamedNode("exit", new IdentifierExpression(Pattern.AnyString)))
+ new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke("Exit", new AnyNode("exit"))
}
}
}};
+
+ static readonly AstNode oldMonitorCallPattern = new ExpressionStatement(
+ new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke("Enter", new AnyNode("enter"))
+ );
+
+ static readonly AstNode oldLockTryCatchPattern = new TryCatchStatement
+ {
+ TryBlock = new BlockStatement {
+ new Repeat(new AnyNode()).ToStatement()
+ },
+ FinallyBlock = new BlockStatement {
+ new TypePattern(typeof(System.Threading.Monitor)).ToType().Invoke("Exit", new AnyNode("exit"))
+ }
+ };
+
+ bool AnalyzeLockV2(ExpressionStatement node, out Expression enter, out Expression exit)
+ {
+ enter = null;
+ exit = null;
+ Match m1 = oldMonitorCallPattern.Match(node);
+ if (!m1.Success) return false;
+ Match m2 = oldLockTryCatchPattern.Match(node.NextSibling);
+ if (!m2.Success) return false;
+ enter = m1.Get<Expression>("enter").Single();
+ exit = m2.Get<Expression>("exit").Single();
+ return true;
+ }
+
+ bool AnalyzeLockV4(ExpressionStatement node, out Expression enter, out Expression exit)
+ {
+ enter = null;
+ exit = null;
+ Match m1 = lockFlagInitPattern.Match(node);
+ if (!m1.Success) return false;
+ Match m2 = lockTryCatchPattern.Match(node.NextSibling);
+ if (!m2.Success) return false;
+ enter = m2.Get<Expression>("enter").Single();
+ exit = m2.Get<Expression>("exit").Single();
+ return m1.Get<IdentifierExpression>("variable").Single().Identifier == m2.Get<IdentifierExpression>("flag").Single().Identifier;
+ }
public LockStatement TransformLock(ExpressionStatement node)
{
- Match m1 = lockFlagInitPattern.Match(node);
- if (!m1.Success) return null;
- AstNode tryCatch = node.NextSibling;
- Match m2 = lockTryCatchPattern.Match(tryCatch);
- if (!m2.Success) return null;
- if (m1.Get<IdentifierExpression>("variable").Single().Identifier == m2.Get<IdentifierExpression>("flag").Single().Identifier) {
- Expression enter = m2.Get<Expression>("enter").Single();
- IdentifierExpression exit = m2.Get<IdentifierExpression>("exit").Single();
+ Expression enter, exit;
+ bool isV2 = AnalyzeLockV2(node, out enter, out exit);
+ if (isV2 || AnalyzeLockV4(node, out enter, out exit)) {
+ AstNode tryCatch = node.NextSibling;
if (!exit.IsMatch(enter)) {
// If exit and enter are not the same, then enter must be "exit = ..."
AssignmentExpression assign = enter as AssignmentExpression;
@@ -656,7 +696,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
LockStatement l = new LockStatement();
l.Expression = enter.Detach();
l.EmbeddedStatement = ((TryCatchStatement)tryCatch).TryBlock.Detach();
- ((BlockStatement)l.EmbeddedStatement).Statements.First().Remove(); // Remove 'Enter()' call
+ if (!isV2) // Remove 'Enter()' call
+ ((BlockStatement)l.EmbeddedStatement).Statements.First().Remove();
tryCatch.ReplaceWith(l);
node.Remove(); // remove flag variable
return l;
@@ -1047,5 +1088,36 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return null;
}
#endregion
+
+ #region Simplify cascading if-else-if statements
+ static readonly IfElseStatement cascadingIfElsePattern = new IfElseStatement
+ {
+ Condition = new AnyNode(),
+ TrueStatement = new AnyNode(),
+ FalseStatement = new BlockStatement {
+ Statements = {
+ new NamedNode(
+ "nestedIfStatement",
+ new IfElseStatement {
+ Condition = new AnyNode(),
+ TrueStatement = new AnyNode(),
+ FalseStatement = new OptionalNode(new AnyNode())
+ }
+ )
+ }
+ }
+ };
+
+ AstNode SimplifyCascadingIfElseStatements(IfElseStatement node)
+ {
+ Match m = cascadingIfElsePattern.Match(node);
+ if (m.Success) {
+ IfElseStatement elseIf = m.Get<IfElseStatement>("nestedIfStatement").Single();
+ node.FalseStatement = elseIf.Detach();
+ }
+
+ return null;
+ }
+ #endregion
}
}
diff --git a/main/contrib/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs b/main/contrib/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs
index 8de45cca56..f0f5ece532 100644
--- a/main/contrib/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs
@@ -32,9 +32,11 @@ namespace ICSharpCode.Decompiler.Ast
if (resolveTypeArguments)
return BaseTypes(derivedType).Any(t => t.Item == baseType);
else {
- var comparableBaseType = baseType.ResolveOrThrow();
+ var comparableBaseType = baseType.Resolve();
+ if (comparableBaseType == null)
+ return false;
while (derivedType.BaseType != null) {
- var resolvedBaseType = derivedType.BaseType.ResolveOrThrow();
+ var resolvedBaseType = derivedType.BaseType.Resolve();
if (resolvedBaseType == null)
return false;
if (comparableBaseType == resolvedBaseType)
@@ -185,24 +187,32 @@ namespace ICSharpCode.Decompiler.Ast
if (derivedType == null)
throw new ArgumentNullException("derivedType");
- var visibility = IsVisibleFromDerived(baseMember);
- if (visibility.HasValue)
- return visibility.Value;
+ MethodAttributes attrs = GetAccessAttributes(baseMember) & MethodAttributes.MemberAccessMask;
+ if (attrs == MethodAttributes.Private)
+ return false;
if (baseMember.DeclaringType.Module == derivedType.Module)
return true;
- // TODO: Check also InternalsVisibleToAttribute.
- return false;
- }
- private static bool? IsVisibleFromDerived(IMemberDefinition member)
- {
- MethodAttributes attrs = GetAccessAttributes(member) & MethodAttributes.MemberAccessMask;
- if (attrs == MethodAttributes.Private)
+ 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;
- if (attrs == MethodAttributes.Assembly || attrs == MethodAttributes.FamANDAssem)
- return null;
- return true;
+ }
+
+ return true;
}
private static MethodAttributes GetAccessAttributes(IMemberDefinition member)
diff --git a/main/contrib/ICSharpCode.Decompiler/CecilExtensions.cs b/main/contrib/ICSharpCode.Decompiler/CecilExtensions.cs
index cc047e09cd..0c7cb1e409 100644
--- a/main/contrib/ICSharpCode.Decompiler/CecilExtensions.cs
+++ b/main/contrib/ICSharpCode.Decompiler/CecilExtensions.cs
@@ -126,6 +126,41 @@ namespace ICSharpCode.Decompiler
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>
@@ -190,6 +225,7 @@ namespace ICSharpCode.Decompiler
return null;
}
+ [Obsolete("throwing exceptions is considered a bug")]
public static TypeDefinition ResolveOrThrow(this TypeReference typeReference)
{
var resolved = typeReference.Resolve();
@@ -323,5 +359,16 @@ namespace ICSharpCode.Decompiler
}
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;
+ }
}
}
diff --git a/main/contrib/ICSharpCode.Decompiler/DecompilerException.cs b/main/contrib/ICSharpCode.Decompiler/DecompilerException.cs
index d62b516da4..5f5f022fc5 100644
--- a/main/contrib/ICSharpCode.Decompiler/DecompilerException.cs
+++ b/main/contrib/ICSharpCode.Decompiler/DecompilerException.cs
@@ -23,7 +23,7 @@ using Mono.Cecil;
namespace ICSharpCode.Decompiler
{
/// <summary>
- /// Desctiption of DecompilerException.
+ /// Description of DecompilerException.
/// </summary>
public class DecompilerException : Exception, ISerializable
{
diff --git a/main/contrib/ICSharpCode.Decompiler/DecompilerSettings.cs b/main/contrib/ICSharpCode.Decompiler/DecompilerSettings.cs
index c3790203da..a354291ed2 100644
--- a/main/contrib/ICSharpCode.Decompiler/DecompilerSettings.cs
+++ b/main/contrib/ICSharpCode.Decompiler/DecompilerSettings.cs
@@ -295,6 +295,21 @@ namespace ICSharpCode.Decompiler
}
}
+ 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>
@@ -318,6 +333,7 @@ namespace ICSharpCode.Decompiler
if (csharpFormattingOptions == null) {
csharpFormattingOptions = FormattingOptionsFactory.CreateAllman();
csharpFormattingOptions.IndentSwitchBody = false;
+ csharpFormattingOptions.ArrayInitializerWrapping = Wrapping.WrapAlways;
}
return csharpFormattingOptions;
}
diff --git a/main/contrib/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs b/main/contrib/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
index 4c2080045e..ea6e54dbea 100644
--- a/main/contrib/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
@@ -359,6 +359,10 @@ namespace ICSharpCode.Decompiler.Disassembler
} 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);
@@ -375,6 +379,10 @@ namespace ICSharpCode.Decompiler.Disassembler
} 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);
diff --git a/main/contrib/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs b/main/contrib/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs
index a45a718207..ef6d32e69d 100644
--- a/main/contrib/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs
@@ -54,8 +54,8 @@ namespace ICSharpCode.Decompiler.Disassembler
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.EntryPoint == method)
- output.WriteLine (".entrypoint");
+ if (method.DeclaringType.Module.Assembly != null && method.DeclaringType.Module.Assembly.EntryPoint == method)
+ output.WriteLine (".entrypoint");
if (method.Body.HasVariables) {
output.Write(".locals ");
diff --git a/main/contrib/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/main/contrib/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
index 1a7d1a4f71..fcdb2b926e 100644
--- a/main/contrib/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
@@ -33,7 +33,7 @@ namespace ICSharpCode.Decompiler.Disassembler
/// </summary>
public sealed class ReflectionDisassembler
{
- ITextOutput output;
+ readonly ITextOutput output;
CancellationToken cancellationToken;
bool isInType; // whether we are currently disassembling a whole type (-> defaultCollapsed for foldings)
MethodBodyDisassembler methodBodyDisassembler;
@@ -213,8 +213,9 @@ namespace ICSharpCode.Decompiler.Disassembler
output.WriteLine();
}
}
+ WriteParameterAttributes(0, method.MethodReturnType, method.MethodReturnType);
foreach (var p in method.Parameters) {
- WriteParameterAttributes(p);
+ WriteParameterAttributes(p.Index + 1, p, p);
}
WriteSecurityDeclarations(method);
@@ -613,22 +614,17 @@ namespace ICSharpCode.Decompiler.Disassembler
}
}
- bool HasParameterAttributes(ParameterDefinition p)
+ void WriteParameterAttributes(int index, IConstantProvider cp, ICustomAttributeProvider cap)
{
- return p.HasConstant || p.HasCustomAttributes;
- }
-
- void WriteParameterAttributes(ParameterDefinition p)
- {
- if (!HasParameterAttributes(p))
+ if (!cp.HasConstant && !cap.HasCustomAttributes)
return;
- output.Write(".param [{0}]", p.Index + 1);
- if (p.HasConstant) {
+ output.Write(".param [{0}]", index);
+ if (cp.HasConstant) {
output.Write(" = ");
- WriteConstant(p.Constant);
+ WriteConstant(cp.Constant);
}
output.WriteLine();
- WriteAttributes(p.CustomAttributes);
+ WriteAttributes(cap.CustomAttributes);
}
void WriteConstant(object constant)
diff --git a/main/contrib/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/main/contrib/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
index b46b95935f..08ad03daf0 100644
--- a/main/contrib/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
+++ b/main/contrib/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
@@ -56,6 +56,7 @@
<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" />
@@ -97,6 +98,8 @@
<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" />
@@ -109,6 +112,8 @@
<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" />
@@ -116,11 +121,6 @@
<Compile Include="ReferenceResolvingException.cs" />
<Compile Include="TextOutputWriter.cs" />
<None Include="Properties\AssemblyInfo.template.cs" />
- <Compile Include="ILAst\LiftedOperators.cs" />
- <Compile Include="ILAst\StateRange.cs" />
- <Compile Include="ILAst\SymbolicExecution.cs" />
- <Compile Include="ILAst\AsyncDecompiler.cs" />
- <Compile Include="Ast\TextTokenWriter.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\external\cecil\Mono.Cecil.csproj">
@@ -147,4 +147,4 @@
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
</ItemGroup>
-</Project>
+</Project> \ No newline at end of file
diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs
index 4535c0f82a..c8a7f9b76a 100644
--- a/main/contrib/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs
+++ b/main/contrib/ICSharpCode.Decompiler/ILAst/AsyncDecompiler.cs
@@ -58,6 +58,7 @@ namespace ICSharpCode.Decompiler.ILAst
FieldDefinition builderField;
FieldDefinition stateField;
Dictionary<FieldDefinition, ILVariable> fieldToParameterMap = new Dictionary<FieldDefinition, ILVariable>();
+ ILVariable cachedStateVar;
// These fields are set by AnalyzeMoveNext()
int finalState = -2;
@@ -155,7 +156,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression loadStateMachineForBuilderExpr;
if (!loadBuilderExpr.Match(ILCode.Ldfld, out builderFieldRef, out loadStateMachineForBuilderExpr))
return false;
- if (!loadStateMachineForBuilderExpr.MatchLdloca(stateMachineVar))
+ if (!(loadStateMachineForBuilderExpr.MatchLdloca(stateMachineVar) || loadStateMachineForBuilderExpr.MatchLdloc(stateMachineVar)))
return false;
builderField = builderFieldRef.ResolveWithinSameModule();
if (builderField == null)
@@ -235,36 +236,50 @@ namespace ICSharpCode.Decompiler.ILAst
{
ILBlock ilMethod = CreateILAst(moveNextMethod);
- if (ilMethod.Body.Count != 6)
+ 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[0] as ILTryCatchBlock;
+ 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[1] as ILLabel;
+ setResultAndExitLabel = ilMethod.Body[startIndex + 1] as ILLabel;
if (setResultAndExitLabel == null)
throw new SymbolicAnalysisFailedException();
- if (!MatchStateAssignment(ilMethod.Body[2], out finalState))
+ 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[3].Match(ILCode.Call, out setResultMethod, out builderExpr, out resultExpr))
+ if (!ilMethod.Body[startIndex + 3].Match(ILCode.Call, out setResultMethod, out builderExpr, out resultExpr))
throw new SymbolicAnalysisFailedException();
} else {
- if (!ilMethod.Body[3].Match(ILCode.Call, out setResultMethod, out builderExpr))
+ 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[4] as ILLabel;
+ exitLabel = ilMethod.Body[startIndex + 4] as ILLabel;
if (exitLabel == null)
throw new SymbolicAnalysisFailedException();
}
@@ -318,7 +333,7 @@ namespace ICSharpCode.Decompiler.ILAst
bool MatchStateAssignment(ILNode stfld, out int stateID)
{
- // stfld(StateMachine::<>1__state, ldloc(this), ldc.i4(-2))
+ // stfld(StateMachine::<>1__state, ldloc(this), ldc.i4(stateId))
stateID = 0;
FieldReference fieldRef;
ILExpression target, val;
@@ -329,6 +344,31 @@ namespace ICSharpCode.Decompiler.ILAst
}
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
@@ -345,7 +385,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (body.Count == 0)
throw new SymbolicAnalysisFailedException();
}
- StateRangeAnalysis rangeAnalysis = new StateRangeAnalysis(body[0], StateRangeAnalysisMode.AsyncMoveNext, stateField);
+ 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);
@@ -405,7 +445,7 @@ namespace ICSharpCode.Decompiler.ILAst
var tryBody = tryCatchBlock.TryBlock.Body;
if (tryBody.Count == 0)
throw new SymbolicAnalysisFailedException();
- StateRangeAnalysis rangeAnalysis = new StateRangeAnalysis(tryBody[0], StateRangeAnalysisMode.AsyncMoveNext, stateField);
+ StateRangeAnalysis rangeAnalysis = new StateRangeAnalysis(tryBody[0], StateRangeAnalysisMode.AsyncMoveNext, stateField, cachedStateVar);
int tryBodyLength = tryBody.Count;
int posInTryBody = rangeAnalysis.AssignStateRanges(tryBody, tryBodyLength);
rangeAnalysis.EnsureLabelAtPos(tryBody, ref posInTryBody, ref tryBodyLength);
@@ -435,18 +475,16 @@ namespace ICSharpCode.Decompiler.ILAst
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.Count > 0 && newBody[0].Match(ILCode.Brtrue, out endFinallyLabel, out ceqExpr)) {
- ILExpression loadDoFinallyBodies, loadZero;
- object unused;
- if (ceqExpr.Match(ILCode.Ceq, out unused, out loadDoFinallyBodies, out loadZero)) {
- int num;
- if (loadDoFinallyBodies.MatchLdloc(doFinallyBodies) && loadZero.Match(ILCode.Ldc_I4, out num) && num == 0) {
+ 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 (ceqExpr.Match(ILCode.LogicNot, out loadDoFinallyBodies)) {
- if (loadDoFinallyBodies.MatchLdloc(doFinallyBodies)) {
+ } else if (condition.Code == ILCode.Clt && condition.Arguments[0].MatchLdloc(cachedStateVar) && condition.Arguments[1].MatchLdcI4(0)) {
newBody.RemoveAt(0);
}
}
@@ -454,6 +492,17 @@ namespace ICSharpCode.Decompiler.ILAst
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.
@@ -475,7 +524,8 @@ namespace ICSharpCode.Decompiler.ILAst
newBody.RemoveAt(newBody.Count - 1); // remove AwaitUnsafeOnCompleted call
if (callAwaitUnsafeOnCompleted == null || callAwaitUnsafeOnCompleted.Code != ILCode.Call)
throw new SymbolicAnalysisFailedException();
- if (((MethodReference)callAwaitUnsafeOnCompleted.Operand).Name != "AwaitUnsafeOnCompleted")
+ string methodName = ((MethodReference)callAwaitUnsafeOnCompleted.Operand).Name;
+ if (methodName != "AwaitUnsafeOnCompleted" && methodName != "AwaitOnCompleted")
throw new SymbolicAnalysisFailedException();
if (callAwaitUnsafeOnCompleted.Arguments.Count != 3)
throw new SymbolicAnalysisFailedException();
@@ -493,9 +543,10 @@ namespace ICSharpCode.Decompiler.ILAst
throw new SymbolicAnalysisFailedException();
// stfld(StateMachine::<>1__state, ldloc(this), ldc.i4(0))
- if (!MatchStateAssignment(newBody.LastOrDefault(), out targetStateID))
- throw new SymbolicAnalysisFailedException();
- newBody.RemoveAt(newBody.Count - 1); // remove awaiter field assignment
+ 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
diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs
index da8c6c6beb..e30398e042 100644
--- a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs
+++ b/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs
@@ -413,11 +413,11 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
- // Occasionally the compilers or obfuscators generate unreachable code (which might be intentonally invalid)
- // I belive it is safe to just remove it
+ // 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);
- // Genertate temporary variables to replace stack
+ // Generate temporary variables to replace stack
foreach(ByteCode byteCode in body) {
int argIdx = 0;
int popCount = byteCode.PopCount ?? byteCode.StackBefore.Length;
@@ -652,7 +652,7 @@ namespace ICSharpCode.Decompiler.ILAst
// 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).OrderBy(eh => eh.TryStart.Offset).ToList();
+ 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
diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
index 5de51b9b1a..ecf1e9fc5f 100644
--- a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
+++ b/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
@@ -20,6 +20,7 @@ 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;
@@ -37,6 +38,7 @@ namespace ICSharpCode.Decompiler.ILAst
PropertyAccessInstructions,
SplitToMovableBlocks,
TypeInference,
+ HandlePointerArithmetic,
SimplifyShortCircuit,
SimplifyTernaryOperator,
SimplifyNullCoalescing,
@@ -123,6 +125,9 @@ namespace ICSharpCode.Decompiler.ILAst
// 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 {
@@ -168,11 +173,15 @@ namespace ICSharpCode.Decompiler.ILAst
modified |= block.RunOptimization(TransformObjectInitializers);
if (abortBeforeStep == ILAstOptimizationStep.MakeAssignmentExpression) return;
- modified |= block.RunOptimization(MakeAssignmentExpression);
+ if (context.Settings.MakeAssignmentExpressions) {
+ modified |= block.RunOptimization(MakeAssignmentExpression);
+ }
modified |= block.RunOptimization(MakeCompoundAssignments);
if (abortBeforeStep == ILAstOptimizationStep.IntroducePostIncrement) return;
- modified |= block.RunOptimization(IntroducePostIncrement);
+ if (context.Settings.IntroduceIncrementAndDecrement) {
+ modified |= block.RunOptimization(IntroducePostIncrement);
+ }
if (abortBeforeStep == ILAstOptimizationStep.InlineExpressionTreeParameterDeclarations) return;
if (context.Settings.ExpressionTrees) {
@@ -223,11 +232,13 @@ namespace ICSharpCode.Decompiler.ILAst
new ILInlining(method).InlineAllVariables();
if (abortBeforeStep == ILAstOptimizationStep.CachedDelegateInitialization) return;
- 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 (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);
+ }
}
}
@@ -337,15 +348,15 @@ namespace ICSharpCode.Decompiler.ILAst
expr.ILRanges.Clear();
continue;
case ILCode.__Brfalse: op = ILCode.LogicNot; break;
- case ILCode.__Beq: op = ILCode.Ceq; 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: op = ILCode.Cgt; break;
case ILCode.__Bgt_Un: op = ILCode.Cgt_Un; break;
- case ILCode.__Ble: op = ILCode.Cle; 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: op = ILCode.Clt; break;
case ILCode.__Blt_Un: op = ILCode.Clt_Un; break;
- case ILCode.__Bge: op = ILCode.Cge; break;
+ case ILCode.__Bge: op = ILCode.Cge; break;
case ILCode.__Bge_Un: op = ILCode.Cge_Un; break;
default:
continue;
@@ -424,9 +435,9 @@ namespace ICSharpCode.Decompiler.ILAst
// 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))
+ && 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
@@ -461,9 +472,9 @@ namespace ICSharpCode.Decompiler.ILAst
// Start a new basic block if necessary
if (currNode is ILLabel ||
- currNode is ILTryCatchBlock || // Counts as label
- lastNode.IsConditionalControlFlow() ||
- lastNode.IsUnconditionalControlFlow())
+ 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() };
@@ -656,6 +667,180 @@ namespace ICSharpCode.Decompiler.ILAst
return combinedVariable;
});
}
+
+ void HandlePointerArithmetic(ILNode method)
+ {
+ foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
+ List<ILExpression> args = expr.Arguments;
+ switch (expr.Code) {
+ case ILCode.Localloc:
+ {
+ PointerType type = expr.InferredType as PointerType;
+ if (type != null) {
+ ILExpression arg0 = args[0];
+ ILExpression expr2 = expr;
+ DivideOrMultiplyBySize(ref expr2, ref arg0, type.ElementType, true);
+ // expr shouldn't change
+ if (expr2 != expr)
+ throw new InvalidOperationException();
+ args[0] = arg0;
+ }
+ break;
+ }
+ case ILCode.Add:
+ case ILCode.Add_Ovf:
+ case ILCode.Add_Ovf_Un:
+ {
+ ILExpression arg0 = args[0];
+ ILExpression arg1 = args[1];
+ if (expr.InferredType is PointerType) {
+ if (arg0.ExpectedType is PointerType) {
+ DivideOrMultiplyBySize(ref arg0, ref arg1, ((PointerType)expr.InferredType).ElementType, true);
+ } else if (arg1.ExpectedType is PointerType)
+ DivideOrMultiplyBySize(ref arg1, ref arg0, ((PointerType)expr.InferredType).ElementType, true);
+ }
+ args[0] = arg0;
+ args[1] = arg1;
+ break;
+ }
+ case ILCode.Sub:
+ case ILCode.Sub_Ovf:
+ case ILCode.Sub_Ovf_Un:
+ {
+ ILExpression arg0 = args[0];
+ ILExpression arg1 = args[1];
+ if (expr.InferredType is PointerType) {
+ if (arg0.ExpectedType is PointerType && !(arg1.InferredType is PointerType))
+ DivideOrMultiplyBySize(ref arg0, ref arg1, ((PointerType)expr.InferredType).ElementType, true);
+ }
+ args[0] = arg0;
+ args[1] = arg1;
+ break;
+ }
+ case ILCode.Conv_I8:
+ {
+ ILExpression arg0 = args[0];
+ // conv.i8(div:intptr(p0 - p1))
+ if (arg0.Code == ILCode.Div && arg0.InferredType.FullName == "System.IntPtr")
+ {
+ ILExpression dividend = arg0.Arguments[0];
+ if (dividend.InferredType.FullName == "System.IntPtr" &&
+ (dividend.Code == ILCode.Sub || dividend.Code == ILCode.Sub_Ovf || dividend.Code == ILCode.Sub_Ovf_Un))
+ {
+ PointerType pointerType0 = dividend.Arguments[0].InferredType as PointerType;
+ PointerType pointerType1 = dividend.Arguments[1].InferredType as PointerType;
+
+ if (pointerType0 != null && pointerType1 != null) {
+ if (pointerType0.ElementType.FullName == "System.Void" ||
+ pointerType0.ElementType.FullName != pointerType1.ElementType.FullName) {
+ pointerType0 = pointerType1 = new PointerType(typeSystem.Byte);
+ dividend.Arguments[0] = Cast(dividend.Arguments[0], pointerType0);
+ dividend.Arguments[1] = Cast(dividend.Arguments[1], pointerType1);
+ }
+
+ DivideOrMultiplyBySize(ref dividend, ref arg0, pointerType0.ElementType, false);
+ // dividend shouldn't change
+ if (args[0].Arguments[0] != dividend)
+ throw new InvalidOperationException();
+ }
+ }
+ }
+ args[0] = arg0;
+ 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 Cast(ILExpression expr, TypeReference type)
+ {
+ return new ILExpression(ILCode.Castclass, type, expr)
+ {
+ InferredType = type,
+ ExpectedType = type
+ };
+ }
+
+ void DivideOrMultiplyBySize(ref ILExpression pointerExpr, ref ILExpression adjustmentExpr, TypeReference elementType, bool divide)
+ {
+ adjustmentExpr = UnwrapIntPtrCast(adjustmentExpr);
+
+ ILExpression sizeOfExpression;
+ switch (TypeAnalysis.GetInformationAmount(elementType)) {
+ case 0: // System.Void
+ pointerExpr = Cast(pointerExpr, new PointerType(typeSystem.Byte));
+ goto case 1;
+ 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, elementType);
+ break;
+ }
+
+ if (divide && (adjustmentExpr.Code == ILCode.Mul || adjustmentExpr.Code == ILCode.Mul_Ovf || adjustmentExpr.Code == ILCode.Mul_Ovf_Un) ||
+ !divide && (adjustmentExpr.Code == ILCode.Div || adjustmentExpr.Code == ILCode.Div_Un)) {
+ ILExpression mulArg = adjustmentExpr.Arguments[1];
+ if (mulArg.Code == sizeOfExpression.Code && sizeOfExpression.Operand.Equals(mulArg.Operand)) {
+ adjustmentExpr = UnwrapIntPtrCast(adjustmentExpr.Arguments[0]);
+ return;
+ }
+ }
+
+ if (adjustmentExpr.Code == sizeOfExpression.Code) {
+ if (sizeOfExpression.Operand.Equals(adjustmentExpr.Operand)) {
+ adjustmentExpr = new ILExpression(ILCode.Ldc_I4, 1);
+ return;
+ }
+
+ if (adjustmentExpr.Code == ILCode.Ldc_I4) {
+ int offsetInBytes = (int)adjustmentExpr.Operand;
+ int elementSize = (int)sizeOfExpression.Operand;
+
+ if (offsetInBytes % elementSize != 0) {
+ pointerExpr = Cast(pointerExpr, new PointerType(typeSystem.Byte));
+ return;
+ }
+
+ adjustmentExpr.Operand = offsetInBytes / elementSize;
+ return;
+ }
+ }
+
+ if (!(sizeOfExpression.Code == ILCode.Ldc_I4 && (int)sizeOfExpression.Operand == 1))
+ adjustmentExpr = new ILExpression(divide ? ILCode.Div_Un : ILCode.Mul, null, adjustmentExpr, sizeOfExpression);
+ }
public static void ReplaceVariables(ILNode node, Func<ILVariable, ILVariable> variableMapping)
{
diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
index 7fd74a60e8..179b40a673 100644
--- a/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
+++ b/main/contrib/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
@@ -21,8 +21,10 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
+using System.Text;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Disassembler;
+using ICSharpCode.NRefactory.Utils;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Cecil = Mono.Cecil;
diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/ILInlining.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/ILInlining.cs
index 880592b103..11140ef083 100644
--- a/main/contrib/ICSharpCode.Decompiler/ILAst/ILInlining.cs
+++ b/main/contrib/ICSharpCode.Decompiler/ILAst/ILInlining.cs
@@ -50,32 +50,36 @@ namespace ICSharpCode.Decompiler.ILAst
AnalyzeNode(method);
}
- void AnalyzeNode(ILNode node)
+ /// <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) + 1;
+ numStloc[locVar] = numStloc.GetOrDefault(locVar) + direction;
} else if (expr.Code == ILCode.Ldloc) {
- numLdloc[locVar] = numLdloc.GetOrDefault(locVar) + 1;
+ numLdloc[locVar] = numLdloc.GetOrDefault(locVar) + direction;
} else if (expr.Code == ILCode.Ldloca) {
- numLdloca[locVar] = numLdloca.GetOrDefault(locVar) + 1;
+ numLdloca[locVar] = numLdloca.GetOrDefault(locVar) + direction;
} else {
throw new NotSupportedException(expr.Code.ToString());
}
}
foreach (ILExpression child in expr.Arguments)
- AnalyzeNode(child);
+ AnalyzeNode(child, direction);
} else {
var catchBlock = node as ILTryCatchBlock.CatchBlock;
if (catchBlock != null && catchBlock.ExceptionVariable != null) {
- numStloc[catchBlock.ExceptionVariable] = numStloc.GetOrDefault(catchBlock.ExceptionVariable) + 1;
+ numStloc[catchBlock.ExceptionVariable] = numStloc.GetOrDefault(catchBlock.ExceptionVariable) + direction;
}
foreach (ILNode child in node.GetChildren())
- AnalyzeNode(child);
+ AnalyzeNode(child, direction);
}
}
@@ -194,6 +198,7 @@ namespace ICSharpCode.Decompiler.ILAst
// 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) {
@@ -334,6 +339,7 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Stfld:
case ILCode.Ldfld:
case ILCode.Ldflda:
+ case ILCode.Await:
return true;
}
}
diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/PatternMatching.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/PatternMatching.cs
index 31fcf62b15..441088b903 100644
--- a/main/contrib/ICSharpCode.Decompiler/ILAst/PatternMatching.cs
+++ b/main/contrib/ICSharpCode.Decompiler/ILAst/PatternMatching.cs
@@ -167,5 +167,11 @@ namespace ICSharpCode.Decompiler.ILAst
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;
+ }
}
}
diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/StateRange.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/StateRange.cs
index 4a55e1d0b6..3a3dfc2f34 100644
--- a/main/contrib/ICSharpCode.Decompiler/ILAst/StateRange.cs
+++ b/main/contrib/ICSharpCode.Decompiler/ILAst/StateRange.cs
@@ -137,23 +137,25 @@ namespace ICSharpCode.Decompiler.ILAst
internal DefaultDictionary<ILNode, StateRange> ranges;
SymbolicEvaluationContext evalContext;
- internal Dictionary<MethodDefinition, Interval> finallyMethodToStateInterval; // used only for IteratorDispose
+ 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)
+ public StateRangeAnalysis(ILNode entryPoint, StateRangeAnalysisMode mode, FieldDefinition stateField, ILVariable cachedStateVar = null)
{
this.mode = mode;
this.stateField = stateField;
if (mode == StateRangeAnalysisMode.IteratorDispose) {
- finallyMethodToStateInterval = new Dictionary<MethodDefinition, Interval>();
+ 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)
@@ -213,7 +215,7 @@ namespace ICSharpCode.Decompiler.ILAst
break;
case ILCode.Brtrue:
{
- SymbolicValue val = evalContext.Eval(expr.Arguments[0]);
+ 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]];
@@ -249,9 +251,9 @@ namespace ICSharpCode.Decompiler.ILAst
// 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 || finallyMethodToStateInterval.ContainsKey(mdef))
+ if (mdef == null || finallyMethodToStateRange.ContainsKey(mdef))
throw new SymbolicAnalysisFailedException();
- finallyMethodToStateInterval.Add(mdef, nodeRange.ToEnclosingInterval());
+ finallyMethodToStateRange.Add(mdef, nodeRange);
break;
} else {
goto default;
diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/SymbolicExecution.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/SymbolicExecution.cs
index 98255f2f2d..5fb40acf92 100644
--- a/main/contrib/ICSharpCode.Decompiler/ILAst/SymbolicExecution.cs
+++ b/main/contrib/ICSharpCode.Decompiler/ILAst/SymbolicExecution.cs
@@ -67,7 +67,16 @@ namespace ICSharpCode.Decompiler.ILAst
this.Type = type;
this.Constant = constant;
}
-
+
+ public SymbolicValue AsBool()
+ {
+ if (Type == SymbolicValueType.State) {
+ // convert state integer to bool:
+ // if (state + c) = if (state + c != 0) = if (state != -c)
+ return new SymbolicValue(SymbolicValueType.StateInEquals, unchecked(-Constant));
+ }
+ return this;
+ }
public override string ToString()
{
return string.Format("[SymbolicValue {0}: {1}]", this.Type, this.Constant);
@@ -133,7 +142,7 @@ namespace ICSharpCode.Decompiler.ILAst
// 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]);
+ 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)
@@ -141,7 +150,7 @@ namespace ICSharpCode.Decompiler.ILAst
else
return Failed();
default:
- return Failed();
+ return Failed();
}
}
}
diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
index b050353ab4..6ed1f4dc05 100644
--- a/main/contrib/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
+++ b/main/contrib/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
@@ -450,7 +450,7 @@ namespace ICSharpCode.Decompiler.ILAst
return (TypeReference)expr.Operand;
case ILCode.Localloc:
if (forceInferChildren) {
- InferTypeForExpression(expr.Arguments[0], typeSystem.Int32);
+ InferTypeForExpression(expr.Arguments[0], null);
}
if (expectedType is PointerType)
return expectedType;
@@ -539,6 +539,8 @@ namespace ICSharpCode.Decompiler.ILAst
if (forceInferChildren)
InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
TypeReference type = NumericPromotion(InferTypeForExpression(expr.Arguments[0], null));
+ if (type == null)
+ return null;
TypeReference expectedInputType = null;
switch (type.MetadataType) {
case MetadataType.Int32:
@@ -827,7 +829,7 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Await:
{
TypeReference taskType = InferTypeForExpression(expr.Arguments[0], null);
- if (taskType.Name == "Task`1" && taskType.IsGenericInstance && taskType.Namespace == "System.Threading.Tasks") {
+ if (taskType != null && taskType.Name == "Task`1" && taskType.IsGenericInstance && taskType.Namespace == "System.Threading.Tasks") {
return ((GenericInstanceType)taskType).GenericArguments[0];
}
return null;
@@ -1011,7 +1013,7 @@ namespace ICSharpCode.Decompiler.ILAst
TypeReference leftPreferred = DoInferTypeForExpression(left, expectedType);
if (leftPreferred is PointerType) {
left.InferredType = left.ExpectedType = leftPreferred;
- InferTypeForExpression(right, typeSystem.IntPtr);
+ InferTypeForExpression(right, null);
return leftPreferred;
}
if (IsEnum(leftPreferred)) {
@@ -1022,7 +1024,7 @@ namespace ICSharpCode.Decompiler.ILAst
}
TypeReference rightPreferred = DoInferTypeForExpression(right, expectedType);
if (rightPreferred is PointerType) {
- InferTypeForExpression(left, typeSystem.IntPtr);
+ InferTypeForExpression(left, null);
right.InferredType = right.ExpectedType = rightPreferred;
return rightPreferred;
}
@@ -1042,7 +1044,10 @@ namespace ICSharpCode.Decompiler.ILAst
TypeReference leftPreferred = DoInferTypeForExpression(left, expectedType);
if (leftPreferred is PointerType) {
left.InferredType = left.ExpectedType = leftPreferred;
- InferTypeForExpression(right, typeSystem.IntPtr);
+ TypeReference rightPreferred = InferTypeForExpression(right, null);
+ // subtracting two pointers is not a pointer
+ if (rightPreferred is PointerType)
+ return typeSystem.IntPtr;
return leftPreferred;
}
if (IsEnum(leftPreferred)) {
diff --git a/main/contrib/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs b/main/contrib/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs
index b4b6183bf8..2da165f610 100644
--- a/main/contrib/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs
+++ b/main/contrib/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs
@@ -312,7 +312,7 @@ namespace ICSharpCode.Decompiler.ILAst
// 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, Interval> finallyMethodToStateInterval;
+ Dictionary<MethodDefinition, StateRange> finallyMethodToStateRange;
void ConstructExceptionTable()
{
@@ -321,11 +321,11 @@ namespace ICSharpCode.Decompiler.ILAst
var rangeAnalysis = new StateRangeAnalysis(ilMethod.Body[0], StateRangeAnalysisMode.IteratorDispose, stateField);
rangeAnalysis.AssignStateRanges(ilMethod.Body, ilMethod.Body.Count);
- finallyMethodToStateInterval = rangeAnalysis.finallyMethodToStateInterval;
+ finallyMethodToStateRange = rangeAnalysis.finallyMethodToStateRange;
// Now look at the finally blocks:
foreach (var tryFinally in ilMethod.GetSelfAndChildrenRecursive<ILTryCatchBlock>()) {
- Interval interval = rangeAnalysis.ranges[tryFinally.TryBlock.Body[0]].ToEnclosingInterval();
+ var range = rangeAnalysis.ranges[tryFinally.TryBlock.Body[0]];
var finallyBody = tryFinally.FinallyBlock.Body;
if (finallyBody.Count != 2)
throw new SymbolicAnalysisFailedException();
@@ -338,9 +338,9 @@ namespace ICSharpCode.Decompiler.ILAst
throw new SymbolicAnalysisFailedException();
MethodDefinition mdef = GetMethodDefinition(call.Operand as MethodReference);
- if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef))
+ if (mdef == null || finallyMethodToStateRange.ContainsKey(mdef))
throw new SymbolicAnalysisFailedException();
- finallyMethodToStateInterval.Add(mdef, interval);
+ finallyMethodToStateRange.Add(mdef, range);
}
rangeAnalysis = null;
}
@@ -430,10 +430,9 @@ namespace ICSharpCode.Decompiler.ILAst
bodyLength--; // don't conside the stloc instruction to be part of the body
}
- // verify that the last element in the body is a label pointing to the 'ret(false)'
+ // The last element in the body usually is a label pointing to the 'ret(false)'
returnFalseLabel = body.ElementAtOrDefault(bodyLength - 1) as ILLabel;
- if (returnFalseLabel == null)
- throw new SymbolicAnalysisFailedException();
+ // Note: in Roslyn-compiled code, returnFalseLabel may be null.
var rangeAnalysis = new StateRangeAnalysis(body[0], StateRangeAnalysisMode.IteratorMoveNext, stateField);
int pos = rangeAnalysis.AssignStateRanges(body, bodyLength);
@@ -485,7 +484,7 @@ namespace ICSharpCode.Decompiler.ILAst
throw new SymbolicAnalysisFailedException();
int val = (int)expr.Arguments[0].Operand;
if (val == 0) {
- newBody.Add(MakeGoTo(returnFalseLabel));
+ newBody.Add(new ILExpression(ILCode.YieldBreak, null));
} else if (val == 1) {
newBody.Add(MakeGoTo(labels, currentState));
} else {
@@ -497,7 +496,7 @@ namespace ICSharpCode.Decompiler.ILAst
// handle direct return (e.g. in release builds)
int val = (int)expr.Arguments[0].Operand;
if (val == 0) {
- newBody.Add(MakeGoTo(returnFalseLabel));
+ newBody.Add(new ILExpression(ILCode.YieldBreak, null));
} else if (val == 1) {
newBody.Add(MakeGoTo(labels, currentState));
} else {
@@ -507,21 +506,21 @@ namespace ICSharpCode.Decompiler.ILAst
MethodDefinition method = GetMethodDefinition(expr.Operand as MethodReference);
if (method == null)
throw new SymbolicAnalysisFailedException();
- Interval interval;
+ 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(MakeGoTo(returnFalseLabel));
- } else if (finallyMethodToStateInterval.TryGetValue(method, out interval)) {
+ newBody.Add(new ILExpression(ILCode.YieldBreak, null));
+ } else if (finallyMethodToStateRange.TryGetValue(method, out stateRange)) {
// Call to Finally-method
- int index = stateChanges.FindIndex(ss => ss.NewState >= interval.Start && ss.NewState <= interval.End);
+ int index = stateChanges.FindIndex(ss => stateRange.Contains(ss.NewState));
if (index < 0)
throw new SymbolicAnalysisFailedException();
ILLabel label = new ILLabel();
- label.Name = "JumpOutOfTryFinally" + interval.Start + "_" + interval.End;
+ label.Name = "JumpOutOfTryFinally" + stateChanges[index].NewState;
newBody.Add(new ILExpression(ILCode.Leave, label));
SetState stateChange = stateChanges[index];
@@ -544,6 +543,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression MakeGoTo(ILLabel targetLabel)
{
+ Debug.Assert(targetLabel != null);
if (targetLabel == returnFalseLabel)
return new ILExpression(ILCode.YieldBreak, null);
else
diff --git a/main/contrib/ICSharpCode.Decompiler/Properties/AssemblyInfo.template.cs b/main/contrib/ICSharpCode.Decompiler/Properties/AssemblyInfo.template.cs
new file mode 100644
index 0000000000..3dba481ae1
--- /dev/null
+++ b/main/contrib/ICSharpCode.Decompiler/Properties/AssemblyInfo.template.cs
@@ -0,0 +1,27 @@
+#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")]
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Async.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Async.cs
new file mode 100644
index 0000000000..7629dd5e62
--- /dev/null
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/Async.cs
@@ -0,0 +1,155 @@
+// 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);
+ }
+ }
+}
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/CodeSampleFileParser.cs b/main/contrib/ICSharpCode.Decompiler/Tests/CodeSampleFileParser.cs
index 552d092ee5..5cb49aad73 100644
--- a/main/contrib/ICSharpCode.Decompiler/Tests/CodeSampleFileParser.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/CodeSampleFileParser.cs
@@ -87,7 +87,8 @@ namespace ICSharpCode.Decompiler.Tests
{
if(String.IsNullOrWhiteSpace(s))
return true;
- return s.Trim().StartsWith("//");
+ s = s.Trim();
+ return s.StartsWith("//") || s.StartsWith("#"); // Also ignore #pragmas for warning suppression
}
public static string ConcatLines(IEnumerable<string> lines)
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/ControlFlow.cs b/main/contrib/ICSharpCode.Decompiler/Tests/ControlFlow.cs
new file mode 100644
index 0000000000..d83f04a2b1
--- /dev/null
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/ControlFlow.cs
@@ -0,0 +1,97 @@
+// 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");
+ }
+ }
+}
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs b/main/contrib/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs
new file mode 100644
index 0000000000..5bd0ec516b
--- /dev/null
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs
@@ -0,0 +1,88 @@
+// 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());
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs b/main/contrib/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs
index 8205042a97..659bb2be68 100644
--- a/main/contrib/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs
@@ -35,10 +35,8 @@ namespace ICSharpCode.Decompiler.Tests
{
protected static void ValidateFileRoundtrip(string samplesFileName)
{
- var lines = File.ReadAllLines(Path.Combine(@"..\..\Tests", samplesFileName));
- var testCode = RemoveIgnorableLines(lines);
- var decompiledTestCode = RoundtripCode(testCode);
- CodeAssert.AreEqual(testCode, decompiledTestCode);
+ var fullPath = Path.Combine(@"..\..\Tests", samplesFileName);
+ AssertRoundtripCode(fullPath);
}
static string RemoveIgnorableLines(IEnumerable<string> lines)
@@ -46,29 +44,27 @@ namespace ICSharpCode.Decompiler.Tests
return CodeSampleFileParser.ConcatLines(lines.Where(l => !CodeSampleFileParser.IsCommentOrBlank(l)));
}
- /// <summary>
- /// Compiles and decompiles a source code.
- /// </summary>
- /// <param name="code">The source code to copile.</param>
- /// <returns>The decompilation result of compiled source code.</returns>
- static string RoundtripCode(string code)
+ protected static void AssertRoundtripCode(string fileName, bool optimize = false, bool useDebug = false, int compilerVersion = 4)
{
- DecompilerSettings settings = new DecompilerSettings();
- settings.FullyQualifyAmbiguousTypeNames = false;
- AssemblyDefinition assembly = Compile(code);
- AstBuilder decompiler = new AstBuilder(new DecompilerContext(assembly.MainModule) { Settings = settings });
+ 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));
- return output.ToString();
+ CodeAssert.AreEqual(code, output.ToString());
}
- static AssemblyDefinition Compile(string code)
+ protected static AssemblyDefinition CompileLegacy(string code, bool optimize, bool useDebug, int compilerVersion)
{
- CSharpCodeProvider provider = new CSharpCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v4.0" } });
+ CSharpCodeProvider provider = new CSharpCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v" + new Version(compilerVersion, 0) } });
CompilerParameters options = new CompilerParameters();
- options.ReferencedAssemblies.Add("System.Core.dll");
+ options.CompilerOptions = "/unsafe /o" + (optimize ? "+" : "-") + (useDebug ? " /debug" : "");
+ if (compilerVersion >= 4)
+ options.ReferencedAssemblies.Add("System.Core.dll");
CompilerResults results = provider.CompileAssemblyFromSource(options, code);
try
{
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs b/main/contrib/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs
index 5d5071f676..70c00fc655 100644
--- a/main/contrib/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs
@@ -62,6 +62,22 @@ public static class DelegateConstruction
}
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)
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/DoubleConstants.cs b/main/contrib/ICSharpCode.Decompiler/Tests/DoubleConstants.cs
new file mode 100644
index 0000000000..2bc20a7730
--- /dev/null
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/DoubleConstants.cs
@@ -0,0 +1,28 @@
+// 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;
+}
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/ExpressionTrees.cs b/main/contrib/ICSharpCode.Decompiler/Tests/ExpressionTrees.cs
new file mode 100644
index 0000000000..9d325e9e9d
--- /dev/null
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/ExpressionTrees.cs
@@ -0,0 +1,370 @@
+// 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>());
+ }
+}
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Helpers/CodeAssert.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Helpers/CodeAssert.cs
new file mode 100644
index 0000000000..d06e2db29d
--- /dev/null
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/Helpers/CodeAssert.cs
@@ -0,0 +1,110 @@
+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);
+ }
+ }
+}
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/main/contrib/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
index 69598ac73f..8bb4934e78 100644
--- a/main/contrib/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -67,6 +67,7 @@
<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" />
@@ -74,6 +75,7 @@
<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" />
@@ -126,11 +128,12 @@
<Name>ICSharpCode.Decompiler</Name>
</ProjectReference>
</ItemGroup>
+ <ItemGroup />
<ItemGroup>
- <Folder Include="IL" />
+ <None Include="BooleanConsumedAsInteger.il" />
</ItemGroup>
<ItemGroup>
- <None Include="BooleanConsumedAsInteger.il" />
+ <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
</Project> \ No newline at end of file
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/IL/ILTests.cs b/main/contrib/ICSharpCode.Decompiler/Tests/IL/ILTests.cs
new file mode 100644
index 0000000000..f06f3108c5
--- /dev/null
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/IL/ILTests.cs
@@ -0,0 +1,51 @@
+// 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());
+ }
+ }
+}
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.Output.cs b/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.Output.cs
new file mode 100644
index 0000000000..754d7dafdc
--- /dev/null
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.Output.cs
@@ -0,0 +1,53 @@
+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();
+ }
+ }
+ }
+}
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.dll b/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.dll
new file mode 100644
index 0000000000..f517d4546d
--- /dev/null
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.dll
Binary files differ
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.il b/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.il
new file mode 100644
index 0000000000..9c9b749ce4
--- /dev/null
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/IL/SequenceOfNestedIfs.il
@@ -0,0 +1,140 @@
+// 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 \ No newline at end of file
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.exe b/main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.exe
new file mode 100644
index 0000000000..db6194ad68
--- /dev/null
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.exe
Binary files differ
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.il b/main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.il
new file mode 100644
index 0000000000..51cee37549
--- /dev/null
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/IL/StackTests.il
@@ -0,0 +1,132 @@
+.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
+
+
+// =============================================================
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/LiftedOperators.cs b/main/contrib/ICSharpCode.Decompiler/Tests/LiftedOperators.cs
new file mode 100644
index 0000000000..426276ab0a
--- /dev/null
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/LiftedOperators.cs
@@ -0,0 +1,830 @@
+// 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.Runtime.InteropServices;
+
+public static class LiftedOperators
+{
+ // C# uses 4 different patterns of IL for lifted operators: bool, other primitive types, decimal, other structs.
+ // Different patterns are used depending on whether both of the operands are nullable or only the left/right operand is nullable.
+ // Negation must not be pushed through such comparisons because it would change the semantics.
+ // A comparison used in a condition differs somewhat from a comparison used as a simple value.
+
+ public static void BoolBasic(bool? a, bool? b)
+ {
+ if (a == b)
+ {
+ Console.WriteLine();
+ }
+ if (a != b)
+ {
+ Console.WriteLine();
+ }
+
+ if (!(a == b))
+ {
+ Console.WriteLine();
+ }
+ if (!(a != b))
+ {
+ Console.WriteLine();
+ }
+ }
+
+ public static void BoolComplex(bool? a, Func<bool> x)
+ {
+ if (a == x())
+ {
+ Console.WriteLine();
+ }
+ if (a != x())
+ {
+ Console.WriteLine();
+ }
+
+ if (x() == a)
+ {
+ Console.WriteLine();
+ }
+ if (x() != a)
+ {
+ Console.WriteLine();
+ }
+
+ if (!(a == x()))
+ {
+ Console.WriteLine();
+ }
+ if (!(a != x()))
+ {
+ Console.WriteLine();
+ }
+ if (!(x() == a))
+ {
+ Console.WriteLine();
+ }
+ if (!(x() != a))
+ {
+ Console.WriteLine();
+ }
+ }
+
+ public static void BoolConst(bool? a)
+ {
+ if (a == true)
+ {
+ Console.WriteLine();
+ }
+ if (a != true)
+ {
+ Console.WriteLine();
+ }
+ if (a == false)
+ {
+ Console.WriteLine();
+ }
+ if (a != false)
+ {
+ Console.WriteLine();
+ }
+ if (a ?? true)
+ {
+ Console.WriteLine();
+ }
+ if (a ?? false)
+ {
+ Console.WriteLine();
+ }
+ }
+
+ public static void BoolValueBasic(bool? a, bool? b)
+ {
+ Console.WriteLine(a == b);
+ Console.WriteLine(a != b);
+
+ Console.WriteLine(!(a == b));
+ Console.WriteLine(!(a != b));
+
+ Console.WriteLine(a & b);
+ Console.WriteLine(a | b);
+ Console.WriteLine(a ^ b);
+ Console.WriteLine(a ?? b);
+ Console.WriteLine(!a);
+ a &= b;
+ a |= b;
+ a ^= b;
+ }
+
+ public static void BoolValueComplex(bool? a, Func<bool> x)
+ {
+ Console.WriteLine(a == x());
+ Console.WriteLine(a != x());
+
+ Console.WriteLine(x() == a);
+ Console.WriteLine(x() != a);
+
+ Console.WriteLine(!(a == x()));
+ Console.WriteLine(!(a != x()));
+
+ Console.WriteLine(a & x());
+ Console.WriteLine(a | x());
+ Console.WriteLine(a ^ x());
+ Console.WriteLine(a ?? x());
+ a &= x();
+ a |= x();
+ a ^= x();
+
+ Console.WriteLine(x() ^ a);
+ (new bool?[0])[0] ^= x();
+ }
+
+ public static void BoolValueConst(bool? a)
+ {
+ Console.WriteLine(a == true);
+ Console.WriteLine(a != true);
+ Console.WriteLine(a == false);
+ Console.WriteLine(a != false);
+ Console.WriteLine(a ?? true);
+ Console.WriteLine(a ?? false);
+ }
+
+ public static void IntBasic(int? a, int? b)
+ {
+ if (a == b)
+ {
+ Console.WriteLine();
+ }
+ if (a != b)
+ {
+ Console.WriteLine();
+ }
+ if (a > b)
+ {
+ Console.WriteLine();
+ }
+ if (a < b)
+ {
+ Console.WriteLine();
+ }
+ if (a >= b)
+ {
+ Console.WriteLine();
+ }
+ if (a <= b)
+ {
+ Console.WriteLine();
+ }
+
+ if (!(a == b))
+ {
+ Console.WriteLine();
+ }
+ if (!(a != b))
+ {
+ Console.WriteLine();
+ }
+ if (!(a > b))
+ {
+ Console.WriteLine();
+ }
+ }
+
+ public static void IntComplex(int? a, Func<int> x)
+ {
+ if (a == x())
+ {
+ Console.WriteLine();
+ }
+ if (a != x())
+ {
+ Console.WriteLine();
+ }
+ if (a > x())
+ {
+ Console.WriteLine();
+ }
+
+ if (x() == a)
+ {
+ Console.WriteLine();
+ }
+ if (x() != a)
+ {
+ Console.WriteLine();
+ }
+ if (x() > a)
+ {
+ Console.WriteLine();
+ }
+
+ if (!(a == x()))
+ {
+ Console.WriteLine();
+ }
+ if (!(a != x()))
+ {
+ Console.WriteLine();
+ }
+ if (!(a > x()))
+ {
+ Console.WriteLine();
+ }
+ }
+
+ public static void IntConst(int? a)
+ {
+ if (a == 2)
+ {
+ Console.WriteLine();
+ }
+ if (a != 2)
+ {
+ Console.WriteLine();
+ }
+ if (a > 2)
+ {
+ Console.WriteLine();
+ }
+
+ if (2 == a)
+ {
+ Console.WriteLine();
+ }
+ if (2 != a)
+ {
+ Console.WriteLine();
+ }
+ if (2 > a)
+ {
+ Console.WriteLine();
+ }
+ }
+
+ public static void IntValueBasic(int? a, int? b)
+ {
+ Console.WriteLine(a == b);
+ Console.WriteLine(a != b);
+ Console.WriteLine(a > b);
+
+ Console.WriteLine(!(a == b));
+ Console.WriteLine(!(a != b));
+ Console.WriteLine(!(a > b));
+
+ Console.WriteLine(a + b);
+ Console.WriteLine(a - b);
+ Console.WriteLine(a * b);
+ Console.WriteLine(a / b);
+ Console.WriteLine(a % b);
+ Console.WriteLine(a & b);
+ Console.WriteLine(a | b);
+ Console.WriteLine(a ^ b);
+ Console.WriteLine(a << b);
+ Console.WriteLine(a >> b);
+ Console.WriteLine(a ?? b);
+ Console.WriteLine(-a);
+ Console.WriteLine(~a);
+ // TODO:
+ //Console.WriteLine(a++);
+ //Console.WriteLine(a--);
+ Console.WriteLine(++a);
+ Console.WriteLine(--a);
+ a += b;
+ a -= b;
+ a *= b;
+ a /= b;
+ a %= b;
+ a &= b;
+ a |= b;
+ a ^= b;
+ a <<= b;
+ a >>= b;
+ }
+
+ public static void IntValueComplex(int? a, Func<int> x)
+ {
+ Console.WriteLine(a == x());
+ Console.WriteLine(a != x());
+ Console.WriteLine(a > x());
+
+ Console.WriteLine(x() == a);
+ Console.WriteLine(x() != a);
+ Console.WriteLine(x() > a);
+
+ Console.WriteLine(a + x());
+ Console.WriteLine(a - x());
+ Console.WriteLine(a * x());
+ Console.WriteLine(a / x());
+ Console.WriteLine(a % x());
+ Console.WriteLine(a & x());
+ Console.WriteLine(a | x());
+ Console.WriteLine(a ^ x());
+ Console.WriteLine(a << x());
+ Console.WriteLine(a >> x());
+ Console.WriteLine(a ?? x());
+ a += x();
+ a -= x();
+ a *= x();
+ a /= x();
+ a %= x();
+ a &= x();
+ a |= x();
+ a ^= x();
+ a <<= x();
+ a >>= x();
+
+ Console.WriteLine(x() + a);
+ (new int?[0])[0] += x();
+ }
+
+ public static void IntValueConst(int? a)
+ {
+ Console.WriteLine(a == 2);
+ Console.WriteLine(a != 2);
+ Console.WriteLine(a > 2);
+
+ Console.WriteLine(2 == a);
+ Console.WriteLine(2 != a);
+ Console.WriteLine(2 > a);
+
+ Console.WriteLine(a + 2);
+ Console.WriteLine(a - 2);
+ Console.WriteLine(a * 2);
+ Console.WriteLine(a / 2);
+ Console.WriteLine(a % 2);
+ Console.WriteLine(a & 2);
+ Console.WriteLine(a | 2);
+ Console.WriteLine(a ^ 2);
+ Console.WriteLine(a << 2);
+ Console.WriteLine(a >> 2);
+ Console.WriteLine(a ?? 2);
+ a += 2;
+ a -= 2;
+ a *= 2;
+ a /= 2;
+ a %= 2;
+ a &= 2;
+ a |= 2;
+ a ^= 2;
+ a <<= 2;
+ a >>= 2;
+
+ Console.WriteLine(2 + a);
+ }
+
+ public static void NumberBasic(decimal? a, decimal? b)
+ {
+ if (a == b)
+ {
+ Console.WriteLine();
+ }
+ if (a != b)
+ {
+ Console.WriteLine();
+ }
+ if (a > b)
+ {
+ Console.WriteLine();
+ }
+ if (a < b)
+ {
+ Console.WriteLine();
+ }
+ if (a >= b)
+ {
+ Console.WriteLine();
+ }
+ if (a <= b)
+ {
+ Console.WriteLine();
+ }
+
+ if (!(a == b))
+ {
+ Console.WriteLine();
+ }
+ if (!(a != b))
+ {
+ Console.WriteLine();
+ }
+ if (!(a > b))
+ {
+ Console.WriteLine();
+ }
+ }
+
+ public static void NumberComplex(decimal? a, Func<decimal> x)
+ {
+ if (a == x())
+ {
+ Console.WriteLine();
+ }
+ if (a != x())
+ {
+ Console.WriteLine();
+ }
+ if (a > x())
+ {
+ Console.WriteLine();
+ }
+
+ if (x() == a)
+ {
+ Console.WriteLine();
+ }
+ if (x() != a)
+ {
+ Console.WriteLine();
+ }
+ if (x() > a)
+ {
+ Console.WriteLine();
+ }
+ }
+
+ public static void NumberConst(decimal? a)
+ {
+ if (a == 2m)
+ {
+ Console.WriteLine();
+ }
+ if (a != 2m)
+ {
+ Console.WriteLine();
+ }
+ if (a > 2m)
+ {
+ Console.WriteLine();
+ }
+
+ if (2m == a)
+ {
+ Console.WriteLine();
+ }
+ if (2m != a)
+ {
+ Console.WriteLine();
+ }
+ if (2m > a)
+ {
+ Console.WriteLine();
+ }
+ }
+
+ public static void NumberValueBasic(decimal? a, decimal? b)
+ {
+ Console.WriteLine(a == b);
+ Console.WriteLine(a != b);
+ Console.WriteLine(a > b);
+
+ Console.WriteLine(!(a == b));
+ Console.WriteLine(!(a != b));
+ Console.WriteLine(!(a > b));
+
+ Console.WriteLine(a + b);
+ Console.WriteLine(a - b);
+ Console.WriteLine(a * b);
+ Console.WriteLine(a / b);
+ Console.WriteLine(a % b);
+ Console.WriteLine(a ?? b);
+ Console.WriteLine(-a);
+ // TODO:
+ //Console.WriteLine(a++);
+ //Console.WriteLine(a--);
+ //Console.WriteLine(++a);
+ //Console.WriteLine(--a);
+ a += b;
+ a -= b;
+ a *= b;
+ a /= b;
+ a %= b;
+ }
+
+ public static void NumberValueComplex(decimal? a, Func<decimal> x)
+ {
+ Console.WriteLine(a == x());
+ Console.WriteLine(a != x());
+ Console.WriteLine(a > x());
+
+ Console.WriteLine(x() == a);
+ Console.WriteLine(x() != a);
+ Console.WriteLine(x() > a);
+
+ Console.WriteLine(a + x());
+ Console.WriteLine(a - x());
+ Console.WriteLine(a * x());
+ Console.WriteLine(a / x());
+ Console.WriteLine(a % x());
+ Console.WriteLine(a ?? x());
+ a += x();
+ a -= x();
+ a *= x();
+ a /= x();
+ a %= x();
+
+ Console.WriteLine(x() + a);
+ (new decimal?[0])[0] += x();
+ }
+
+ public static void NumberValueConst(decimal? a)
+ {
+ Console.WriteLine(a == 2m);
+ Console.WriteLine(a != 2m);
+ Console.WriteLine(a > 2m);
+
+ Console.WriteLine(2m == a);
+ Console.WriteLine(2m != a);
+ Console.WriteLine(2m > a);
+
+ Console.WriteLine(a + 2m);
+ Console.WriteLine(a - 2m);
+ Console.WriteLine(a * 2m);
+ Console.WriteLine(a / 2m);
+ Console.WriteLine(a % 2m);
+ Console.WriteLine(a ?? 2m);
+ a += 2m;
+ a -= 2m;
+ a *= 2m;
+ a /= 2m;
+ a %= 2m;
+
+ Console.WriteLine(2m + a);
+ }
+
+ public static void StructBasic(TS? a, TS? b)
+ {
+ if (a == b)
+ {
+ Console.WriteLine();
+ }
+ if (a != b)
+ {
+ Console.WriteLine();
+ }
+ if (a > b)
+ {
+ Console.WriteLine();
+ }
+ if (a < b)
+ {
+ Console.WriteLine();
+ }
+ if (a >= b)
+ {
+ Console.WriteLine();
+ }
+ if (a <= b)
+ {
+ Console.WriteLine();
+ }
+
+ if (!(a == b))
+ {
+ Console.WriteLine();
+ }
+ if (!(a != b))
+ {
+ Console.WriteLine();
+ }
+ if (!(a > b))
+ {
+ Console.WriteLine();
+ }
+ }
+
+ public static void StructComplex(TS? a, Func<TS> x)
+ {
+ if (a == x())
+ {
+ Console.WriteLine();
+ }
+ if (a != x())
+ {
+ Console.WriteLine();
+ }
+ if (a > x())
+ {
+ Console.WriteLine();
+ }
+
+ if (x() == a)
+ {
+ Console.WriteLine();
+ }
+ if (x() != a)
+ {
+ Console.WriteLine();
+ }
+ if (x() > a)
+ {
+ Console.WriteLine();
+ }
+ }
+
+ public static void StructValueBasic(TS? a, TS? b, int? i)
+ {
+ Console.WriteLine(a == b);
+ Console.WriteLine(a != b);
+ Console.WriteLine(a > b);
+
+ Console.WriteLine(!(a == b));
+ Console.WriteLine(!(a != b));
+ Console.WriteLine(!(a > b));
+
+ Console.WriteLine(a + b);
+ Console.WriteLine(a - b);
+ Console.WriteLine(a * b);
+ Console.WriteLine(a / b);
+ Console.WriteLine(a % b);
+ Console.WriteLine(a & b);
+ Console.WriteLine(a | b);
+ Console.WriteLine(a ^ b);
+ Console.WriteLine(a << i);
+ Console.WriteLine(a >> i);
+ Console.WriteLine(a ?? b);
+ Console.WriteLine(+a);
+ Console.WriteLine(-a);
+ Console.WriteLine(!a);
+ Console.WriteLine(~a);
+ // TODO:
+ //Console.WriteLine(a++);
+ //Console.WriteLine(a--);
+ //Console.WriteLine(++a);
+ //Console.WriteLine(--a);
+ //Console.WriteLine((int?)a);
+ a += b;
+ a -= b;
+ a *= b;
+ a /= b;
+ a %= b;
+ a &= b;
+ a |= b;
+ a ^= b;
+ a <<= i;
+ a >>= i;
+ }
+
+ public static void StructValueComplex(TS? a, Func<TS> x, Func<int> i)
+ {
+ Console.WriteLine(a == x());
+ Console.WriteLine(a != x());
+ Console.WriteLine(a > x());
+
+ Console.WriteLine(x() == a);
+ Console.WriteLine(x() != a);
+ Console.WriteLine(x() > a);
+
+ Console.WriteLine(a + x());
+ Console.WriteLine(a - x());
+ Console.WriteLine(a * x());
+ Console.WriteLine(a / x());
+ Console.WriteLine(a % x());
+ Console.WriteLine(a & x());
+ Console.WriteLine(a | x());
+ Console.WriteLine(a ^ x());
+ Console.WriteLine(a << i());
+ Console.WriteLine(a >> i());
+ Console.WriteLine(a ?? x());
+ a += x();
+ a -= x();
+ a *= x();
+ a /= x();
+ a %= x();
+ a &= x();
+ a |= x();
+ a ^= x();
+ a <<= i();
+ a >>= i();
+
+ Console.WriteLine(x() + a);
+ (new TS?[0])[0] += x();
+ }
+}
+
+// dummy structure for testing custom operators
+[StructLayout(LayoutKind.Sequential, Size = 1)]
+public struct TS
+{
+ // unary
+ public static TS operator +(TS a)
+ {
+ throw null;
+ }
+ public static TS operator -(TS a)
+ {
+ throw null;
+ }
+ public static TS operator !(TS a)
+ {
+ throw null;
+ }
+ public static TS operator ~(TS a)
+ {
+ throw null;
+ }
+ public static TS operator ++(TS a)
+ {
+ throw null;
+ }
+ public static TS operator --(TS a)
+ {
+ throw null;
+ }
+
+ public static explicit operator int(TS a)
+ {
+ throw null;
+ }
+
+ // binary
+ public static TS operator +(TS a, TS b)
+ {
+ throw null;
+ }
+ public static TS operator -(TS a, TS b)
+ {
+ throw null;
+ }
+ public static TS operator *(TS a, TS b)
+ {
+ throw null;
+ }
+ public static TS operator /(TS a, TS b)
+ {
+ throw null;
+ }
+ public static TS operator %(TS a, TS b)
+ {
+ throw null;
+ }
+ public static TS operator &(TS a, TS b)
+ {
+ throw null;
+ }
+ public static TS operator |(TS a, TS b)
+ {
+ throw null;
+ }
+ public static TS operator ^(TS a, TS b)
+ {
+ throw null;
+ }
+ public static TS operator <<(TS a, int b)
+ {
+ throw null;
+ }
+ public static TS operator >>(TS a, int b)
+ {
+ throw null;
+ }
+
+ // comparisons
+ public static bool operator ==(TS a, TS b)
+ {
+ throw null;
+ }
+ public static bool operator !=(TS a, TS b)
+ {
+ throw null;
+ }
+ public static bool operator <(TS a, TS b)
+ {
+ throw null;
+ }
+ public static bool operator <=(TS a, TS b)
+ {
+ throw null;
+ }
+ public static bool operator >(TS a, TS b)
+ {
+ throw null;
+ }
+ public static bool operator >=(TS a, TS b)
+ {
+ throw null;
+ }
+
+ public override bool Equals(object obj)
+ {
+ throw null;
+ }
+ public override int GetHashCode()
+ {
+ throw null;
+ }
+}
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Lock.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Lock.cs
new file mode 100644
index 0000000000..da5a59c74f
--- /dev/null
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/Lock.cs
@@ -0,0 +1,38 @@
+// 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 Lock
+{
+ public void LockThis()
+ {
+ lock (this)
+ {
+ Console.WriteLine();
+ }
+ }
+
+ public void LockOnType()
+ {
+ lock (typeof(Lock))
+ {
+ Console.WriteLine();
+ }
+ }
+}
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/PInvoke.cs b/main/contrib/ICSharpCode.Decompiler/Tests/PInvoke.cs
new file mode 100644
index 0000000000..0c828ac696
--- /dev/null
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/PInvoke.cs
@@ -0,0 +1,96 @@
+// 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.InteropServices;
+
+// P/Invoke and marshalling attribute tests
+public class PInvoke
+{
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 2)]
+ public struct MarshalAsTest
+ {
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
+ public uint[] FixedArray;
+
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.Bool)]
+ public int[] FixedBoolArray;
+
+ [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
+ public string[] SafeBStrArray;
+
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
+ public string FixedString;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ public struct Rect
+ {
+ [FieldOffset(0)]
+ public int left;
+ [FieldOffset(4)]
+ public int top;
+ [FieldOffset(8)]
+ public int right;
+ [FieldOffset(12)]
+ public int bottom;
+ }
+
+ public static decimal MarshalAttributesOnPropertyAccessors
+ {
+ [return: MarshalAs(UnmanagedType.Currency)]
+ get
+ {
+ return 0m;
+ }
+ [param: MarshalAs(UnmanagedType.Currency)]
+ set
+ {
+ }
+ }
+
+ [DllImport("xyz.dll", CharSet = CharSet.Auto)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool Method([MarshalAs(UnmanagedType.LPStr)] string input);
+
+ [DllImport("xyz.dll")]
+ private static extern void New1(int ElemCnt, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] ar);
+
+ [DllImport("xyz.dll")]
+ private static extern void New2([MarshalAs(UnmanagedType.LPArray, SizeConst = 128)] int[] ar);
+
+ [DllImport("xyz.dll")]
+ private static extern void New3([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Bool, SizeConst = 64, SizeParamIndex = 1)] int[] ar);
+
+ public void CustomMarshal1([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "MyCompany.MyMarshaler")] object o)
+ {
+ }
+
+ public void CustomMarshal2([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "MyCompany.MyMarshaler", MarshalCookie = "Cookie")] object o)
+ {
+ }
+
+ [DllImport("ws2_32.dll", SetLastError = true)]
+ internal static extern IntPtr ioctlsocket([In] IntPtr socketHandle, [In] int cmd, [In] [Out] ref int argp);
+
+ public void CallMethodWithInOutParameter()
+ {
+ int num = 0;
+ PInvoke.ioctlsocket(IntPtr.Zero, 0, ref num);
+ }
+}
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/QueryExpressions.cs b/main/contrib/ICSharpCode.Decompiler/Tests/QueryExpressions.cs
new file mode 100644
index 0000000000..d8b6e06234
--- /dev/null
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/QueryExpressions.cs
@@ -0,0 +1,188 @@
+// 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 class QueryExpressions
+{
+ public class Customer
+ {
+ public int CustomerID;
+ public IEnumerable<QueryExpressions.Order> Orders;
+ public string Name;
+ public string Country;
+ public string City;
+ }
+
+ public class Order
+ {
+ public int OrderID;
+ public DateTime OrderDate;
+ public QueryExpressions.Customer Customer;
+ public int CustomerID;
+ public decimal Total;
+ public IEnumerable<QueryExpressions.OrderDetail> Details;
+ }
+
+ public class OrderDetail
+ {
+ public decimal UnitPrice;
+ public int Quantity;
+ }
+
+ public IEnumerable<QueryExpressions.Customer> customers;
+ public IEnumerable<QueryExpressions.Order> orders;
+
+ public object MultipleWhere()
+ {
+ return from c in this.customers
+ where c.Orders.Count<QueryExpressions.Order>() > 10
+ where c.Country == "DE"
+ select c;
+ }
+
+ public object SelectManyFollowedBySelect()
+ {
+ return from c in this.customers
+ from o in c.Orders
+ select new
+ {
+ c.Name,
+ o.OrderID,
+ o.Total
+ };
+ }
+
+ public object SelectManyFollowedByOrderBy()
+ {
+ return from c in this.customers
+ from o in c.Orders
+ orderby o.Total descending
+ select new
+ {
+ c.Name,
+ o.OrderID,
+ o.Total
+ };
+ }
+
+ public object MultipleSelectManyFollowedBySelect()
+ {
+ return from c in this.customers
+ from o in c.Orders
+ from d in o.Details
+ select new
+ {
+ c.Name,
+ o.OrderID,
+ d.Quantity
+ };
+ }
+
+ public object MultipleSelectManyFollowedByLet()
+ {
+ return from c in this.customers
+ from o in c.Orders
+ from d in o.Details
+ let x = d.Quantity * d.UnitPrice
+ select new
+ {
+ c.Name,
+ o.OrderID,
+ x
+ };
+ }
+
+ public object FromLetWhereSelect()
+ {
+ return from o in this.orders
+ let t = o.Details.Sum((QueryExpressions.OrderDetail d) => d.UnitPrice * d.Quantity)
+ where t >= 1000m
+ select new
+ {
+ OrderID = o.OrderID,
+ Total = t
+ };
+ }
+
+ public object MultipleLet()
+ {
+ return from a in this.customers
+ let b = a.Country
+ let c = a.Name
+ select b + c;
+ }
+
+ public object Join()
+ {
+ return from c in this.customers
+ join o in this.orders on c.CustomerID equals o.CustomerID
+ select new
+ {
+ c.Name,
+ o.OrderDate,
+ o.Total
+ };
+ }
+
+ public object JoinInto()
+ {
+ return from c in this.customers
+ join o in this.orders on c.CustomerID equals o.CustomerID into co
+ let n = co.Count<QueryExpressions.Order>()
+ where n >= 10
+ select new
+ {
+ Name = c.Name,
+ OrderCount = n
+ };
+ }
+
+ public object OrderBy()
+ {
+ return from o in this.orders
+ orderby o.Customer.Name, o.Total descending
+ select o;
+ }
+
+ public object GroupBy()
+ {
+ return from c in this.customers
+ group c.Name by c.Country;
+ }
+
+ public object ExplicitType()
+ {
+ return from QueryExpressions.Customer c in this.customers
+ where c.City == "London"
+ select c;
+ }
+
+ public object QueryContinuation()
+ {
+ return from c in this.customers
+ group c by c.Country into g
+ select new
+ {
+ Country = g.Key,
+ CustCount = g.Count<QueryExpressions.Customer>()
+ };
+ }
+}
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/TestRunner.cs b/main/contrib/ICSharpCode.Decompiler/Tests/TestRunner.cs
index 009a3c6c17..215725b0a6 100644
--- a/main/contrib/ICSharpCode.Decompiler/Tests/TestRunner.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/TestRunner.cs
@@ -32,7 +32,7 @@ using NUnit.Framework;
namespace ICSharpCode.Decompiler.Tests
{
[TestFixture]
- public class TestRunner
+ public class TestRunner : DecompilerTestBase
{
[Test]
public void Async()
@@ -67,7 +67,8 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void ExceptionHandling()
{
- TestFile(@"..\..\Tests\ExceptionHandling.cs", optimize: false);
+ AssertRoundtripCode(@"..\..\Tests\ExceptionHandling.cs", optimize: false);
+ AssertRoundtripCode(@"..\..\Tests\ExceptionHandling.cs", optimize: false);
}
[Test]
@@ -85,7 +86,14 @@ namespace ICSharpCode.Decompiler.Tests
[Test]
public void ControlFlowWithDebug()
{
- TestFile(@"..\..\Tests\ControlFlow.cs", optimize: false, useDebug: true);
+ AssertRoundtripCode(@"..\..\Tests\ControlFlow.cs", optimize: false, useDebug: true);
+ AssertRoundtripCode(@"..\..\Tests\ControlFlow.cs", optimize: false, useDebug: true);
+ }
+
+ [Test]
+ public void DoubleConstants()
+ {
+ TestFile(@"..\..\Tests\DoubleConstants.cs");
}
[Test]
@@ -107,6 +115,13 @@ namespace ICSharpCode.Decompiler.Tests
}
[Test]
+ public void Lock()
+ {
+ //TestFile(@"..\..\Tests\Lock.cs", compilerVersion: 2);
+ TestFile(@"..\..\Tests\Lock.cs", compilerVersion: 4);
+ }
+
+ [Test]
public void Loops()
{
TestFile(@"..\..\Tests\Loops.cs");
@@ -172,44 +187,12 @@ namespace ICSharpCode.Decompiler.Tests
TestFile(@"..\..\Tests\TypeAnalysisTests.cs");
}
- static void TestFile(string fileName, bool useDebug = false)
- {
- TestFile(fileName, false, useDebug);
- TestFile(fileName, true, useDebug);
- }
-
- static void TestFile(string fileName, bool optimize, bool useDebug = false)
+ static void TestFile(string fileName, bool useDebug = false, int compilerVersion = 4)
{
- string code = File.ReadAllText(fileName);
- AssemblyDefinition assembly = Compile(code, optimize, useDebug);
- 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());
- }
-
- static AssemblyDefinition Compile(string code, bool optimize, bool useDebug)
- {
- CSharpCodeProvider provider = new CSharpCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v4.0" } });
- CompilerParameters options = new CompilerParameters();
- options.CompilerOptions = "/unsafe /o" + (optimize ? "+" : "-") + (useDebug ? " /debug": "");
- 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();
- }
+ AssertRoundtripCode(fileName, optimize: false, useDebug: useDebug, compilerVersion: compilerVersion);
+ AssertRoundtripCode(fileName, optimize: true, useDebug: useDebug, compilerVersion: compilerVersion);
+ AssertRoundtripCode(fileName, optimize: false, useDebug: useDebug, compilerVersion: compilerVersion);
+ AssertRoundtripCode(fileName, optimize: true, useDebug: useDebug, compilerVersion: compilerVersion);
}
}
}
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/TypeAnalysisTests.cs b/main/contrib/ICSharpCode.Decompiler/Tests/TypeAnalysisTests.cs
new file mode 100644
index 0000000000..760bb862d3
--- /dev/null
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/TypeAnalysisTests.cs
@@ -0,0 +1,153 @@
+// 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 TypeAnalysisTests
+{
+ public byte SubtractFrom256(byte b)
+ {
+ return (byte)(256 - (int)b);
+ }
+
+ #region Shift
+ public int LShiftInteger(int num1, int num2)
+ {
+ return num1 << num2;
+ }
+
+ public uint LShiftUnsignedInteger(uint num1, uint num2)
+ {
+ return num1 << (int)num2;
+ }
+
+ public long LShiftLong(long num1, long num2)
+ {
+ return num1 << (int)num2;
+ }
+
+ public ulong LShiftUnsignedLong(ulong num1, ulong num2)
+ {
+ return num1 << (int)num2;
+ }
+
+ public int RShiftInteger(int num1, int num2)
+ {
+ return num1 >> num2;
+ }
+
+ public uint RShiftUnsignedInteger(uint num1, int num2)
+ {
+ return num1 >> num2;
+ }
+
+ public long RShiftLong(long num1, long num2)
+ {
+ return num1 >> (int)num2;
+ }
+
+ public ulong RShiftUnsignedLong(ulong num1, ulong num2)
+ {
+ return num1 >> (int)num2;
+ }
+
+ public int ShiftByte(byte num)
+ {
+ return (int)num << 8;
+ }
+
+ public int RShiftByte(byte num)
+ {
+ return num >> 8;
+ }
+
+ public uint RShiftByteWithZeroExtension(byte num)
+ {
+ return (uint)num >> 8;
+ }
+
+ public int RShiftByteAsSByte(byte num)
+ {
+ return (sbyte)num >> 8;
+ }
+
+ public int RShiftSByte(sbyte num)
+ {
+ return num >> 8;
+ }
+
+ public uint RShiftSByteWithZeroExtension(sbyte num)
+ {
+ return (uint)num >> 8;
+ }
+
+ public int RShiftSByteAsByte(sbyte num)
+ {
+ return (byte)num >> 8;
+ }
+ #endregion
+
+ public int GetHashCode(long num)
+ {
+ return (int)num ^ (int)(num >> 32);
+ }
+
+ public void TernaryOp(Random a, Random b, bool c)
+ {
+ if ((c ? a : b) == null)
+ {
+ Console.WriteLine();
+ }
+ }
+
+ public void OperatorIs(object o)
+ {
+ Console.WriteLine(o is Random);
+ Console.WriteLine(!(o is Random));
+ }
+
+ public byte[] CreateArrayWithInt(int length)
+ {
+ return new byte[length];
+ }
+
+ public byte[] CreateArrayWithLong(long length)
+ {
+ return new byte[length];
+ }
+
+ public byte[] CreateArrayWithUInt(uint length)
+ {
+ return new byte[length];
+ }
+
+ public byte[] CreateArrayWithULong(ulong length)
+ {
+ return new byte[length];
+ }
+
+ public StringComparison EnumDiffNumber(StringComparison data)
+ {
+ return data - 1;
+ }
+
+ public int EnumDiff(StringComparison a, StringComparison b)
+ {
+ return Math.Abs(a - b);
+ }
+}
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeDeclarations.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeDeclarations.cs
new file mode 100644
index 0000000000..99bf4279e3
--- /dev/null
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeDeclarations.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace ClassMultiInterface
+{
+ public interface IA
+ {
+ }
+ public interface IA2 : IA
+ {
+ }
+ public interface IB
+ {
+ }
+ public class C : IA2, IB
+ {
+ }
+}
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs b/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs
index 72db7aa465..4a85b9f88a 100644
--- a/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs
@@ -679,7 +679,9 @@ namespace HideMembers3
}
public class J2 : J
{
+#pragma warning disable 0108 // Deliberate bad code for test case
public int get_P;
+#pragma warning restore 0108
}
}
//$$ HideMembers4
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/UndocumentedExpressions.cs b/main/contrib/ICSharpCode.Decompiler/Tests/UndocumentedExpressions.cs
new file mode 100644
index 0000000000..80f5b0371e
--- /dev/null
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/UndocumentedExpressions.cs
@@ -0,0 +1,41 @@
+// 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 UndocumentedExpressions
+{
+ public static int GetArgCount(__arglist)
+ {
+ ArgIterator argIterator = new ArgIterator(__arglist);
+ return argIterator.GetRemainingCount();
+ }
+
+ public static void MakeTypedRef(object o)
+ {
+ TypedReference tr = __makeref(o);
+ UndocumentedExpressions.AcceptTypedRef(tr);
+ }
+
+ private static void AcceptTypedRef(TypedReference tr)
+ {
+ Console.WriteLine("Value is: " + __refvalue(tr, object).ToString());
+ Console.WriteLine("Type is: " + __reftype(tr).Name);
+ __refvalue(tr, object) = 1;
+ }
+}
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/UnsafeCode.cs b/main/contrib/ICSharpCode.Decompiler/Tests/UnsafeCode.cs
index 66fb29529b..cce6d525c7 100644
--- a/main/contrib/ICSharpCode.Decompiler/Tests/UnsafeCode.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/UnsafeCode.cs
@@ -122,7 +122,47 @@ public class UnsafeCode
}
return this.PointerReferenceExpression((double*)ptr);
}
-
+
+ public unsafe int* PointerArithmetic(int* p)
+ {
+ return p + 2;
+ }
+
+ public unsafe byte* PointerArithmetic2(long* p, int y, int x)
+ {
+ return (byte*)((short*)p + (y * x));
+ }
+
+ public unsafe long* PointerArithmetic3(long* p)
+ {
+ return (long*)((byte*)p + 3);
+ }
+
+ public unsafe long* PointerArithmetic4(void* p)
+ {
+ return (long*)((byte*)p + 3);
+ }
+
+ public unsafe int PointerArithmetic5(void* p, byte* q, int i)
+ {
+ return (int)(q[i] + *(byte*)p);
+ }
+
+ public unsafe int PointerSubtraction(long* p, long* q)
+ {
+ return (int)((long)(p - q));
+ }
+
+ public unsafe int PointerSubtraction2(long* p, short* q)
+ {
+ return (int)((long)((byte*)p - (byte*)q));
+ }
+
+ public unsafe int PointerSubtraction3(void* p, void* q)
+ {
+ return (int)((long)((byte*)p - (byte*)q));
+ }
+
unsafe ~UnsafeCode()
{
this.PassPointerAsRefParameter(this.NullPointer);
diff --git a/main/contrib/ICSharpCode.Decompiler/Tests/ValueTypes.cs b/main/contrib/ICSharpCode.Decompiler/Tests/ValueTypes.cs
index b3aa644786..1493cff4c9 100644
--- a/main/contrib/ICSharpCode.Decompiler/Tests/ValueTypes.cs
+++ b/main/contrib/ICSharpCode.Decompiler/Tests/ValueTypes.cs
@@ -168,4 +168,21 @@ public static class ValueTypes
Console.WriteLine("true");
}
}
+
+ public static void CompareNotEqual0IsReallyNotEqual(IComparable<int> a)
+ {
+ if (a.CompareTo(0) != 0)
+ {
+ Console.WriteLine("true");
+ }
+ }
+
+ public static void CompareEqual0IsReallyEqual(IComparable<int> a)
+ {
+ if (a.CompareTo(0) == 0)
+ {
+ Console.WriteLine("true");
+ }
+ }
+
}