diff --git a/ICSharpCode.Decompiler/Ast/Annotations.cs b/ICSharpCode.Decompiler/Ast/Annotations.cs
index 257f889c0..7b22dd013 100644
--- a/ICSharpCode.Decompiler/Ast/Annotations.cs
+++ b/ICSharpCode.Decompiler/Ast/Annotations.cs
@@ -1,4 +1,8 @@
using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using ICSharpCode.Decompiler.ILAst;
+using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast
@@ -12,4 +16,35 @@ namespace ICSharpCode.Decompiler.Ast
this.InferredType = inferredType;
}
}
+
+ public class LdTokenAnnotation {}
+
+ ///
+ /// Annotation that is applied to the body expression of an Expression.Lambda() call.
+ ///
+ public class ParameterDeclarationAnnotation
+ {
+ public readonly List Parameters = new List();
+
+ public ParameterDeclarationAnnotation(ILExpression expr)
+ {
+ Debug.Assert(expr.Code == ILCode.ExpressionTreeParameterDeclarations);
+ for (int i = 0; i < expr.Arguments.Count - 1; i++) {
+ ILExpression p = expr.Arguments[i];
+ // p looks like this:
+ // stloc(v, call(Expression::Parameter, call(Type::GetTypeFromHandle, ldtoken(...)), ldstr(...)))
+ ILVariable v = (ILVariable)p.Operand;
+ TypeReference typeRef = (TypeReference)p.Arguments[0].Arguments[0].Arguments[0].Operand;
+ string name = (string)p.Arguments[0].Arguments[1].Operand;
+ Parameters.Add(new ParameterDeclaration(AstBuilder.ConvertType(typeRef), name).WithAnnotation(v));
+ }
+ }
+ }
+
+ ///
+ /// Annotation that is applied to a LambdaExpression that was produced by an expression tree.
+ ///
+ public class ExpressionTreeLambdaAnnotation
+ {
+ }
}
diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
index f40d5d154..178b7311d 100644
--- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
+++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
@@ -370,9 +370,6 @@ namespace ICSharpCode.Decompiler.Ast
// change "new (int[,])[10] to new int[10][,]"
ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers);
ace.Initializer = new ArrayInitializerExpression();
- var first = ace.AdditionalArraySpecifiers.First();
- first.Remove();
- ace.Arguments.AddRange(Enumerable.Repeat(0, first.Dimensions).Select(i => new EmptyExpression()));
}
var newArgs = new List();
foreach (var arrayDimension in arrayType.Dimensions.Skip(1).Reverse())
@@ -461,9 +458,9 @@ namespace ICSharpCode.Decompiler.Ast
goto case ILCode.Cle;
}
case ILCode.Cle: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2);
- case ILCode.Cge_Un:
+ case ILCode.Cge_Un:
case ILCode.Cge: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2);
- case ILCode.Clt_Un:
+ case ILCode.Clt_Un:
case ILCode.Clt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2);
#endregion
#region Logical
@@ -661,7 +658,20 @@ namespace ICSharpCode.Decompiler.Ast
if (operand is Cecil.TypeReference) {
return AstBuilder.CreateTypeOfExpression((TypeReference)operand).Member("TypeHandle");
} else {
- return InlineAssembly(byteCode, args);
+ var referencedEntity = new IdentifierExpression(FormatByteCodeOperand(byteCode.Operand)).WithAnnotation(byteCode.Operand);
+ string loadName;
+ string handleName;
+ if (operand is Cecil.FieldReference) {
+ loadName = "fieldof";
+ handleName = "FieldHandle";
+ } else if (operand is Cecil.MethodReference) {
+ loadName = "methodof";
+ handleName = "MethodHandle";
+ } else {
+ loadName = "ldtoken";
+ handleName = "Handle";
+ }
+ return new IdentifierExpression(loadName).Invoke(referencedEntity).WithAnnotation(new LdTokenAnnotation()).Member(handleName);
}
case ILCode.Leave: return new GotoStatement() { Label = ((ILLabel)operand).Name };
case ILCode.Localloc:
@@ -718,22 +728,7 @@ namespace ICSharpCode.Decompiler.Ast
MethodDefinition ctor = ((MethodReference)operand).Resolve();
if (methodDef != null) {
AnonymousTypeCreateExpression atce = new AnonymousTypeCreateExpression();
- bool allNamesCanBeInferred = true;
- for (int i = 0; i < args.Count; i++) {
- string inferredName;
- if (args[i] is IdentifierExpression)
- inferredName = ((IdentifierExpression)args[i]).Identifier;
- else if (args[i] is MemberReferenceExpression)
- inferredName = ((MemberReferenceExpression)args[i]).MemberName;
- else
- inferredName = null;
-
- if (inferredName != ctor.Parameters[i].Name) {
- allNamesCanBeInferred = false;
- break;
- }
- }
- if (allNamesCanBeInferred) {
+ if (CanInferAnonymousTypePropertyNamesFromArguments(args, ctor.Parameters)) {
atce.Initializers.AddRange(args);
} else {
for (int i = 0; i < args.Count; i++) {
@@ -829,13 +824,34 @@ namespace ICSharpCode.Decompiler.Ast
return arg1.WithAnnotation(PushNegation.LiftedOperatorAnnotation);
case ILCode.AddressOf:
return MakeRef(arg1);
+ case ILCode.ExpressionTreeParameterDeclarations:
+ args[args.Count - 1].AddAnnotation(new ParameterDeclarationAnnotation(byteCode));
+ return args[args.Count - 1];
case ILCode.NullableOf:
- case ILCode.ValueOf: return arg1;
+ case ILCode.ValueOf: return arg1;
default:
throw new Exception("Unknown OpCode: " + byteCode.Code);
}
}
+ internal static bool CanInferAnonymousTypePropertyNamesFromArguments(IList args, IList parameters)
+ {
+ for (int i = 0; i < args.Count; i++) {
+ string inferredName;
+ if (args[i] is IdentifierExpression)
+ inferredName = ((IdentifierExpression)args[i]).Identifier;
+ else if (args[i] is MemberReferenceExpression)
+ inferredName = ((MemberReferenceExpression)args[i]).MemberName;
+ else
+ inferredName = null;
+
+ if (inferredName != parameters[i].Name) {
+ return false;
+ }
+ }
+ return true;
+ }
+
static readonly AstNode objectInitializerPattern = new AssignmentExpression(
new MemberReferenceExpression {
Target = new InitializedObjectExpression()
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/CustomPatterns.cs b/ICSharpCode.Decompiler/Ast/Transforms/CustomPatterns.cs
new file mode 100644
index 000000000..9c1d80197
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/CustomPatterns.cs
@@ -0,0 +1,109 @@
+// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Linq;
+using System.Reflection;
+using ICSharpCode.NRefactory.CSharp;
+using ICSharpCode.NRefactory.PatternMatching;
+using Mono.Cecil;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ sealed class TypePattern : Pattern
+ {
+ readonly string ns;
+ readonly string name;
+
+ public TypePattern(Type type)
+ {
+ this.ns = type.Namespace;
+ this.name = type.Name;
+ }
+
+ public override bool DoMatch(INode other, Match match)
+ {
+ ComposedType ct = other as ComposedType;
+ AstType o;
+ if (ct != null && !ct.HasNullableSpecifier && ct.PointerRank == 0 && !ct.ArraySpecifiers.Any()) {
+ // Special case: ILSpy sometimes produces a ComposedType but then removed all array specifiers
+ // from it. In that case, we need to look at the base type for the annotations.
+ o = ct.BaseType;
+ } else {
+ o = other as AstType;
+ if (o == null)
+ return false;
+ }
+ TypeReference tr = o.Annotation();
+ return tr != null && tr.Namespace == ns && tr.Name == name;
+ }
+
+ public override string ToString()
+ {
+ return name;
+ }
+ }
+
+ sealed class LdTokenPattern : Pattern
+ {
+ AnyNode childNode;
+
+ public LdTokenPattern(string groupName)
+ {
+ this.childNode = new AnyNode(groupName);
+ }
+
+ public override bool DoMatch(INode other, Match match)
+ {
+ InvocationExpression ie = other as InvocationExpression;
+ if (ie != null && ie.Annotation() != null && ie.Arguments.Count == 1) {
+ return childNode.DoMatch(ie.Arguments.Single(), match);
+ }
+ return false;
+ }
+
+ public override string ToString()
+ {
+ return "ldtoken(...)";
+ }
+ }
+
+ ///
+ /// typeof-Pattern that applies on the expanded form of typeof (prior to ReplaceMethodCallsWithOperators)
+ ///
+ sealed class TypeOfPattern : Pattern
+ {
+ INode childNode;
+
+ public TypeOfPattern(string groupName)
+ {
+ childNode = new TypePattern(typeof(Type)).ToType().Invoke(
+ "GetTypeFromHandle", new TypeOfExpression(new AnyNode(groupName)).Member("TypeHandle"));
+ }
+
+ public override bool DoMatch(INode other, Match match)
+ {
+ return childNode.DoMatch(other, match);
+ }
+
+ public override string ToString()
+ {
+ return "typeof(...)";
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
index 17cca7650..9a0c7edb9 100644
--- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
+++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
@@ -209,6 +209,18 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return true;
}
+ public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data)
+ {
+ if (context.Settings.ExpressionTrees && ExpressionTreeConverter.CouldBeExpressionTree(invocationExpression)) {
+ Expression converted = ExpressionTreeConverter.TryConvert(context, invocationExpression);
+ if (converted != null) {
+ invocationExpression.ReplaceWith(converted);
+ return converted.AcceptVisitor(this, data);
+ }
+ }
+ return base.VisitInvocationExpression(invocationExpression, data);
+ }
+
#region Track current variables
public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data)
{
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs b/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs
new file mode 100644
index 000000000..adedbf744
--- /dev/null
+++ b/ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs
@@ -0,0 +1,420 @@
+// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection;
+using ICSharpCode.Decompiler.ILAst;
+using ICSharpCode.NRefactory.CSharp;
+using ICSharpCode.NRefactory.PatternMatching;
+using Mono.Cecil;
+
+namespace ICSharpCode.Decompiler.Ast.Transforms
+{
+ public class ExpressionTreeConverter
+ {
+ #region static TryConvert method
+ public static bool CouldBeExpressionTree(InvocationExpression expr)
+ {
+ if (expr != null && expr.Arguments.Count == 2) {
+ MethodReference mr = expr.Annotation();
+ return mr != null && mr.Name == "Lambda" && mr.DeclaringType.FullName == "System.Linq.Expressions.Expression";
+ }
+ return false;
+ }
+
+ public static Expression TryConvert(DecompilerContext context, Expression expr)
+ {
+ Expression converted = new ExpressionTreeConverter(context).Convert(expr);
+ if (converted != null) {
+ converted.AddAnnotation(new ExpressionTreeLambdaAnnotation());
+ }
+ return converted;
+ }
+ #endregion
+
+ readonly DecompilerContext context;
+ Stack activeLambdas = new Stack();
+
+ private ExpressionTreeConverter(DecompilerContext context)
+ {
+ this.context = context;
+ }
+
+ #region Main Convert method
+ Expression Convert(Expression expr)
+ {
+ InvocationExpression invocation = expr as InvocationExpression;
+ if (invocation != null) {
+ MethodReference mr = invocation.Annotation();
+ if (mr != null && mr.DeclaringType.FullName == "System.Linq.Expressions.Expression") {
+ switch (mr.Name) {
+ case "Add":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.Add, false);
+ case "AddChecked":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.Add, true);
+ case "AddAssign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Add, false);
+ case "AddAssignChecked":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Add, true);
+ case "And":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.BitwiseAnd);
+ case "AndAlso":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.ConditionalAnd);
+ case "AndAssign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.BitwiseAnd);
+ case "ArrayAccess":
+ case "ArrayIndex":
+ case "ArrayLength":
+ return NotImplemented(invocation);
+ case "Assign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Assign);
+ case "Call":
+ return NotImplemented(invocation);
+ case "Coalesce":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.NullCoalescing);
+ case "Condition":
+ return NotImplemented(invocation);
+ case "Constant":
+ if (invocation.Arguments.Count >= 1)
+ return invocation.Arguments.First().Clone();
+ else
+ return NotSupported(expr);
+ case "Convert":
+ case "ConvertChecked":
+ case "Default":
+ return NotImplemented(invocation);
+ case "Divide":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.Divide);
+ case "DivideAssign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Divide);
+ case "Equal":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.Equality);
+ case "ExclusiveOr":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.ExclusiveOr);
+ case "ExclusiveOrAssign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.ExclusiveOr);
+ case "Field":
+ return ConvertField(invocation);
+ case "GreaterThan":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.GreaterThan);
+ case "GreaterThanOrEqual":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.GreaterThanOrEqual);
+ case "Lambda":
+ return ConvertLambda(invocation);
+ case "LeftShift":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.ShiftLeft);
+ case "LeftShiftAssign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.ShiftLeft);
+ case "LessThan":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.LessThan);
+ case "LessThanOrEqual":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.LessThanOrEqual);
+ case "Modulo":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.Modulus);
+ case "ModuloAssign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Modulus);
+ case "Multiply":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.Multiply, false);
+ case "MultiplyChecked":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.Multiply, true);
+ case "MultiplyAssign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Multiply, false);
+ case "MultiplyAssignChecked":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Multiply, true);
+ case "Negate":
+ case "NegateChecked":
+ return NotImplemented(invocation);
+ case "New":
+ return ConvertNewObject(invocation);
+ case "Not":
+ return NotImplemented(invocation);
+ case "NotEqual":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.InEquality);
+ case "OnesComplement":
+ return NotImplemented(invocation);
+ case "Or":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.BitwiseOr);
+ case "OrAssign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.BitwiseOr);
+ case "OrElse":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.ConditionalOr);
+ case "Quote":
+ return NotImplemented(invocation);
+ case "RightShift":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.ShiftRight);
+ case "RightShiftAssign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.ShiftRight);
+ case "Subtract":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.Subtract, false);
+ case "SubtractChecked":
+ return ConvertBinaryOperator(invocation, BinaryOperatorType.Subtract, true);
+ case "SubtractAssign":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Subtract, false);
+ case "SubtractAssignChecked":
+ return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Subtract, true);
+ case "TypeAs":
+ return ConvertTypeAs(invocation);
+ case "TypeIs":
+ return ConvertTypeIs(invocation);
+ }
+ }
+ }
+ IdentifierExpression ident = expr as IdentifierExpression;
+ if (ident != null) {
+ ILVariable v = ident.Annotation();
+ if (v != null) {
+ foreach (LambdaExpression lambda in activeLambdas) {
+ foreach (ParameterDeclaration p in lambda.Parameters) {
+ if (p.Annotation() == v)
+ return new IdentifierExpression(p.Name).WithAnnotation(v);
+ }
+ }
+ }
+ }
+ return NotSupported(expr);
+ }
+
+ Expression NotSupported(Expression expr)
+ {
+ Debug.WriteLine("Expression Tree Conversion Failed: '" + expr + "' is not supported");
+ return null;
+ }
+
+ Expression NotImplemented(Expression expr)
+ {
+ return new IdentifierExpression("NotImplemented").Invoke(expr.Clone());
+ }
+ #endregion
+
+ #region Convert Lambda
+ static readonly Expression emptyArrayPattern = new ArrayCreateExpression {
+ Type = new AnyNode(),
+ Arguments = { new PrimitiveExpression(0) }
+ };
+
+ Expression ConvertLambda(InvocationExpression invocation)
+ {
+ if (invocation.Arguments.Count != 2)
+ return NotSupported(invocation);
+ LambdaExpression lambda = new LambdaExpression();
+ Expression body = invocation.Arguments.First();
+ ArrayCreateExpression parameterArray = invocation.Arguments.Last() as ArrayCreateExpression;
+ if (parameterArray == null)
+ return NotSupported(invocation);
+
+ var annotation = body.Annotation();
+ if (annotation != null) {
+ lambda.Parameters.AddRange(annotation.Parameters);
+ } else {
+ // No parameter declaration annotation found.
+ if (!emptyArrayPattern.IsMatch(parameterArray))
+ return null;
+ }
+
+ activeLambdas.Push(lambda);
+ Expression convertedBody = Convert(body);
+ activeLambdas.Pop();
+ if (convertedBody == null)
+ return null;
+ lambda.Body = convertedBody;
+ return lambda;
+ }
+ #endregion
+
+ #region Convert Field
+ static readonly Expression getFieldFromHandlePattern =
+ new TypePattern(typeof(FieldInfo)).ToType().Invoke("GetFieldFromHandle", new LdTokenPattern("field").ToExpression().Member("FieldHandle"));
+
+ Expression ConvertField(InvocationExpression invocation)
+ {
+ if (invocation.Arguments.Count != 2)
+ return NotSupported(invocation);
+
+ Expression fieldInfoExpr = invocation.Arguments.ElementAt(1);
+ Match m = getFieldFromHandlePattern.Match(fieldInfoExpr);
+ if (!m.Success)
+ return NotSupported(invocation);
+
+ FieldReference fr = m.Get("field").Single().Annotation();
+ if (fr == null)
+ return null;
+
+ Expression target = Convert(invocation.Arguments.ElementAt(0));
+ if (target == null)
+ return null;
+
+ return target.Member(fr.Name).WithAnnotation(fr);
+ }
+ #endregion
+
+ #region Convert Binary Operator
+ Expression ConvertBinaryOperator(InvocationExpression invocation, BinaryOperatorType op, bool? isChecked = null)
+ {
+ if (invocation.Arguments.Count != 2)
+ return NotSupported(invocation);
+
+ Expression left = Convert(invocation.Arguments.ElementAt(0));
+ if (left == null)
+ return null;
+ Expression right = Convert(invocation.Arguments.ElementAt(1));
+ if (right == null)
+ return null;
+
+ BinaryOperatorExpression boe = new BinaryOperatorExpression(left, op, right);
+ if (isChecked != null)
+ boe.AddAnnotation(isChecked.Value ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation);
+ return boe;
+ }
+
+ Expression ConvertAssignmentOperator(InvocationExpression invocation, AssignmentOperatorType op, bool? isChecked = null)
+ {
+ return NotImplemented(invocation);
+ }
+ #endregion
+
+ #region Convert New Object
+ static readonly Expression newObjectCtorPattern = new TypePattern(typeof(MethodBase)).ToType().Invoke
+ (
+ "GetMethodFromHandle",
+ new LdTokenPattern("ctor").ToExpression().Member("MethodHandle"),
+ new OptionalNode(new TypeOfExpression(new AnyNode("declaringType")).Member("TypeHandle"))
+ ).CastTo(new TypePattern(typeof(ConstructorInfo)));
+
+ Expression ConvertNewObject(InvocationExpression invocation)
+ {
+ if (invocation.Arguments.Count < 1 || invocation.Arguments.Count > 3)
+ return NotSupported(invocation);
+
+ Match m = newObjectCtorPattern.Match(invocation.Arguments.First());
+ if (!m.Success)
+ return NotSupported(invocation);
+
+ MethodReference ctor = m.Get("ctor").Single().Annotation();
+ if (ctor == null)
+ return null;
+
+ TypeReference declaringType;
+ if (m.Has("declaringType")) {
+ declaringType = m.Get("declaringType").Single().Annotation();
+ } else {
+ declaringType = ctor.DeclaringType;
+ }
+ if (declaringType == null)
+ return null;
+
+ ObjectCreateExpression oce = new ObjectCreateExpression(AstBuilder.ConvertType(declaringType));
+ if (invocation.Arguments.Count >= 2) {
+ IList arguments = ConvertExpressionsArray(invocation.Arguments.ElementAtOrDefault(1));
+ if (arguments == null)
+ return null;
+ oce.Arguments.AddRange(arguments);
+ }
+ if (invocation.Arguments.Count >= 3 && declaringType.IsAnonymousType()) {
+ MethodDefinition resolvedCtor = ctor.Resolve();
+ if (resolvedCtor == null || resolvedCtor.Parameters.Count != oce.Arguments.Count)
+ return null;
+ AnonymousTypeCreateExpression atce = new AnonymousTypeCreateExpression();
+ var arguments = oce.Arguments.ToArray();
+ if (AstMethodBodyBuilder.CanInferAnonymousTypePropertyNamesFromArguments(arguments, resolvedCtor.Parameters)) {
+ oce.Arguments.MoveTo(atce.Initializers);
+ } else {
+ for (int i = 0; i < resolvedCtor.Parameters.Count; i++) {
+ atce.Initializers.Add(
+ new NamedExpression {
+ Identifier = resolvedCtor.Parameters[i].Name,
+ Expression = arguments[i].Detach()
+ });
+ }
+ }
+ return atce;
+ }
+
+ return oce;
+ }
+ #endregion
+
+ #region ConvertExpressionsArray
+ static readonly Pattern expressionArrayPattern = new Choice {
+ new ArrayCreateExpression {
+ Type = new TypePattern(typeof(System.Linq.Expressions.Expression)),
+ Arguments = { new PrimitiveExpression(0) }
+ },
+ new ArrayCreateExpression {
+ Type = new TypePattern(typeof(System.Linq.Expressions.Expression)),
+ AdditionalArraySpecifiers = { new ArraySpecifier() },
+ Initializer = new ArrayInitializerExpression {
+ Elements = { new Repeat(new AnyNode("elements")) }
+ }
+ }
+ };
+
+ IList ConvertExpressionsArray(Expression arrayExpression)
+ {
+ Match m = expressionArrayPattern.Match(arrayExpression);
+ if (m.Success) {
+ List result = new List();
+ foreach (Expression expr in m.Get("elements")) {
+ Expression converted = Convert(expr);
+ if (converted == null)
+ return null;
+ result.Add(converted);
+ }
+ return result;
+ }
+ return null;
+ }
+ #endregion
+
+ #region Convert TypeAs/TypeIs
+ static readonly TypeOfPattern typeOfPattern = new TypeOfPattern("type");
+
+ Expression ConvertTypeAs(InvocationExpression invocation)
+ {
+ if (invocation.Arguments.Count != 2)
+ return null;
+ Match m = typeOfPattern.Match(invocation.Arguments.ElementAt(1));
+ if (m.Success) {
+ Expression converted = Convert(invocation.Arguments.First());
+ if (converted != null)
+ return new AsExpression(converted, m.Get("type").Single().Clone());
+ }
+ return null;
+ }
+
+ Expression ConvertTypeIs(InvocationExpression invocation)
+ {
+ if (invocation.Arguments.Count != 2)
+ return null;
+ Match m = typeOfPattern.Match(invocation.Arguments.ElementAt(1));
+ if (m.Success) {
+ Expression converted = Convert(invocation.Arguments.First());
+ if (converted != null) {
+ return new IsExpression {
+ Expression = converted,
+ Type = m.Get("type").Single().Clone()
+ };
+ }
+ }
+ return null;
+ }
+ #endregion
+ }
+}
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
index 8a19eca2a..10d09ba82 100644
--- a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
+++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
@@ -227,7 +227,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
Name = variableName,
Initializer = m1.Get("initializer").Single().Detach()
}.CopyAnnotationsFrom(node.Expression)
- .WithAnnotation(m1.Get("variable").Single().Annotation())
+ .WithAnnotation(m1.Get("variable").Single().Annotation())
}
}.CopyAnnotationsFrom(node);
} else {
@@ -985,28 +985,5 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return null;
}
#endregion
-
- #region Pattern Matching Helpers
- sealed class TypePattern : Pattern
- {
- readonly string ns;
- readonly string name;
-
- public TypePattern(Type type)
- {
- this.ns = type.Namespace;
- this.name = type.Name;
- }
-
- public override bool DoMatch(INode other, Match match)
- {
- AstNode o = other as AstNode;
- if (o == null)
- return false;
- TypeReference tr = o.Annotation();
- return tr != null && tr.Namespace == ns && tr.Name == name;
- }
- }
- #endregion
}
}
diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs
index ddd0b038f..05a598520 100644
--- a/ICSharpCode.Decompiler/DecompilerSettings.cs
+++ b/ICSharpCode.Decompiler/DecompilerSettings.cs
@@ -41,6 +41,21 @@ namespace ICSharpCode.Decompiler
}
}
+ bool expressionTrees = true;
+
+ ///
+ /// Decompile expression trees.
+ ///
+ public bool ExpressionTrees {
+ get { return expressionTrees; }
+ set {
+ if (expressionTrees != value) {
+ expressionTrees = value;
+ OnPropertyChanged("ExpressionTrees");
+ }
+ }
+ }
+
bool yieldReturn = true;
///
diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
index a717d07f0..8a497a8b3 100644
--- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
+++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
@@ -61,9 +61,11 @@
+
+
diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
index 464df9521..710ad85ce 100644
--- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
+++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
@@ -53,6 +53,7 @@ namespace ICSharpCode.Decompiler.ILAst
TransformObjectInitializers,
MakeAssignmentExpression,
IntroducePostIncrement,
+ InlineExpressionTreeParameterDeclarations,
InlineVariables2,
FindLoops,
FindConditions,
@@ -171,6 +172,11 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.IntroducePostIncrement) return;
modified |= block.RunOptimization(IntroducePostIncrement);
+ if (abortBeforeStep == ILAstOptimizationStep.InlineExpressionTreeParameterDeclarations) return;
+ if (context.Settings.ExpressionTrees) {
+ modified |= block.RunOptimization(InlineExpressionTreeParameterDeclarations);
+ }
+
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return;
modified |= new ILInlining(method).InlineAllInBlock(block);
new ILInlining(method).CopyPropagation();
diff --git a/ICSharpCode.Decompiler/ILAst/ILCodes.cs b/ICSharpCode.Decompiler/ILAst/ILCodes.cs
index 475420e46..cbf548698 100644
--- a/ICSharpCode.Decompiler/ILAst/ILCodes.cs
+++ b/ICSharpCode.Decompiler/ILAst/ILCodes.cs
@@ -328,6 +328,12 @@ namespace ICSharpCode.Decompiler.ILAst
/// becomes "NullableOf(add(valueof(...), ...))"
///
NullableOf,
+ ///
+ /// Declares parameters that are used in an expression tree.
+ /// The last child of this node is the call constructing the expression tree, all other children are the
+ /// assignments to the ParameterExpression variables.
+ ///
+ ExpressionTreeParameterDeclarations
}
public static class ILCodeUtil
diff --git a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
index d32a4926c..858ba9a52 100644
--- a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
+++ b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
@@ -953,17 +953,17 @@ namespace ICSharpCode.Decompiler.ILAst
var a = expr.Arguments[0];
ILCode c;
switch (a.Code) {
- case ILCode.Ceq: c = ILCode.Cne; break;
- case ILCode.Cne: c = ILCode.Ceq; break;
- case ILCode.Cgt: c = ILCode.Cle; break;
- case ILCode.Cgt_Un: c = ILCode.Cle_Un; break;
- case ILCode.Cge: c = ILCode.Clt; break;
- case ILCode.Cge_Un: c = ILCode.Clt_Un; break;
- case ILCode.Clt: c = ILCode.Cge; break;
- case ILCode.Clt_Un: c = ILCode.Cge_Un; break;
- case ILCode.Cle: c = ILCode.Cgt; break;
- case ILCode.Cle_Un: c = ILCode.Cgt_Un; break;
- default: return false;
+ case ILCode.Ceq: c = ILCode.Cne; break;
+ case ILCode.Cne: c = ILCode.Ceq; break;
+ case ILCode.Cgt: c = ILCode.Cle; break;
+ case ILCode.Cgt_Un: c = ILCode.Cle_Un; break;
+ case ILCode.Cge: c = ILCode.Clt; break;
+ case ILCode.Cge_Un: c = ILCode.Clt_Un; break;
+ case ILCode.Clt: c = ILCode.Cge; break;
+ case ILCode.Clt_Un: c = ILCode.Cge_Un; break;
+ case ILCode.Cle: c = ILCode.Cgt; break;
+ case ILCode.Cle_Un: c = ILCode.Cgt_Un; break;
+ default: return false;
}
a.Code = c;
a.ILRanges.AddRange(expr.ILRanges);
@@ -982,20 +982,20 @@ namespace ICSharpCode.Decompiler.ILAst
static void SimplifyShiftOperators(ILExpression expr, ref bool modified)
{
- for (int i = 0; i < expr.Arguments.Count; i++)
+ for (int i = 0; i < expr.Arguments.Count; i++)
SimplifyShiftOperators(expr.Arguments[i], ref modified);
- if (expr.Code != ILCode.Shl && expr.Code != ILCode.Shr && expr.Code != ILCode.Shr_Un)
+ if (expr.Code != ILCode.Shl && expr.Code != ILCode.Shr && expr.Code != ILCode.Shr_Un)
return;
var a = expr.Arguments[1];
- if (a.Code != ILCode.And || a.Arguments[1].Code != ILCode.Ldc_I4 || expr.InferredType == null)
+ if (a.Code != ILCode.And || a.Arguments[1].Code != ILCode.Ldc_I4 || expr.InferredType == null)
return;
int mask;
switch (expr.InferredType.MetadataType) {
case MetadataType.Int32:
- case MetadataType.UInt32: mask = 31; break;
+ case MetadataType.UInt32: mask = 31; break;
case MetadataType.Int64:
- case MetadataType.UInt64: mask = 63; break;
- default: return;
+ case MetadataType.UInt64: mask = 63; break;
+ default: return;
}
if ((int)a.Arguments[1].Operand != mask) return;
var res = a.Arguments[0];
@@ -1005,5 +1005,81 @@ namespace ICSharpCode.Decompiler.ILAst
modified = true;
}
#endregion
+
+ #region InlineExpressionTreeParameterDeclarations
+ bool InlineExpressionTreeParameterDeclarations(List body, ILExpression expr, int pos)
+ {
+ // When there is a Expression.Lambda() call, and the parameters are declared in the
+ // IL statement immediately prior to the one containing the Lambda() call,
+ // using this code for the3 declaration:
+ // stloc(v, call(Expression::Parameter, call(Type::GetTypeFromHandle, ldtoken(...)), ldstr(...)))
+ // and the variables v are assigned only once (in that statements), and read only in a Expression::Lambda
+ // call that immediately follows the assignment statements, then we will inline those assignments
+ // into the Lambda call using ILCode.ExpressionTreeParameterDeclarations.
+
+ // This is sufficient to allow inlining over the expression tree construction. The remaining translation
+ // of expression trees into C# will be performed by a C# AST transformer.
+
+ for (int i = expr.Arguments.Count - 1; i >= 0; i--) {
+ if (InlineExpressionTreeParameterDeclarations(body, expr.Arguments[i], pos))
+ return true;
+ }
+
+ MethodReference mr;
+ ILExpression lambdaBodyExpr, parameterArray;
+ if (!(expr.Match(ILCode.Call, out mr, out lambdaBodyExpr, out parameterArray) && mr.Name == "Lambda"))
+ return false;
+ if (!(parameterArray.Code == ILCode.InitArray && mr.DeclaringType.FullName == "System.Linq.Expressions.Expression"))
+ return false;
+ int firstParameterPos = pos - parameterArray.Arguments.Count;
+ if (firstParameterPos < 0)
+ return false;
+
+ ILExpression[] parameterInitExpressions = new ILExpression[parameterArray.Arguments.Count + 1];
+ for (int i = 0; i < parameterArray.Arguments.Count; i++) {
+ parameterInitExpressions[i] = body[firstParameterPos + i] as ILExpression;
+ if (!MatchParameterVariableAssignment(parameterInitExpressions[i]))
+ return false;
+ ILVariable v = (ILVariable)parameterInitExpressions[i].Operand;
+ if (!parameterArray.Arguments[i].MatchLdloc(v))
+ return false;
+ // TODO: validate that the variable is only used here and within 'body'
+ }
+
+ parameterInitExpressions[parameterInitExpressions.Length - 1] = lambdaBodyExpr;
+ Debug.Assert(expr.Arguments[0] == lambdaBodyExpr);
+ expr.Arguments[0] = new ILExpression(ILCode.ExpressionTreeParameterDeclarations, null, parameterInitExpressions);
+
+ body.RemoveRange(firstParameterPos, parameterArray.Arguments.Count);
+
+ return true;
+ }
+
+ bool MatchParameterVariableAssignment(ILExpression expr)
+ {
+ // stloc(v, call(Expression::Parameter, call(Type::GetTypeFromHandle, ldtoken(...)), ldstr(...)))
+ ILVariable v;
+ ILExpression init;
+ if (!expr.Match(ILCode.Stloc, out v, out init))
+ return false;
+ if (v.IsGenerated || v.IsParameter || v.IsPinned)
+ return false;
+ if (v.Type == null || v.Type.FullName != "System.Linq.Expressions.ParameterExpression")
+ return false;
+ MethodReference parameterMethod;
+ ILExpression typeArg, nameArg;
+ if (!init.Match(ILCode.Call, out parameterMethod, out typeArg, out nameArg))
+ return false;
+ if (!(parameterMethod.Name == "Parameter" && parameterMethod.DeclaringType.FullName == "System.Linq.Expressions.Expression"))
+ return false;
+ MethodReference getTypeFromHandle;
+ ILExpression typeToken;
+ if (!typeArg.Match(ILCode.Call, out getTypeFromHandle, out typeToken))
+ return false;
+ if (!(getTypeFromHandle.Name == "GetTypeFromHandle" && getTypeFromHandle.DeclaringType.FullName == "System.Type"))
+ return false;
+ return typeToken.Code == ILCode.Ldtoken && nameArg.Code == ILCode.Ldstr;
+ }
+ #endregion
}
}
diff --git a/ICSharpCode.Decompiler/Tests/ExpressionTrees.cs b/ICSharpCode.Decompiler/Tests/ExpressionTrees.cs
new file mode 100644
index 000000000..e52eb4d0a
--- /dev/null
+++ b/ICSharpCode.Decompiler/Tests/ExpressionTrees.cs
@@ -0,0 +1,345 @@
+// 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;
+
+namespace ICSharpCode.Decompiler.Tests
+{
+ public class ExpressionTrees
+ {
+ int field;
+
+ static object ToCode(object x, Expression> expr)
+ {
+ return expr;
+ }
+
+ static object ToCode(object x, Expression> 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)(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 { { 1, 1 }, { 2, 2 }, { 3, 4 } }.Count == 3);
+ }
+
+ public void ListInitializer2()
+ {
+ ToCode(X(), () => new List(50) { 1, 2, 3 }.Count == 3);
+ }
+
+ public void ListInitializer3()
+ {
+ ToCode(X(), () => new List { 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).Count);
+ ToCode(X(), () => default(int[]).Clone() == null);
+ ToCode(X(), () => default(Type).IsInstanceOfType(new object()));
+ ToCode(X(), () => default(List).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)new[] { 2000, 2004, 2008, 2012 }.Any);
+ }
+
+ public void MethodGroupConstant()
+ {
+ ToCode(X(), () => Array.TrueForAll(new[] { 2000, 2004, 2008, 2012 }, DateTime.IsLeapYear));
+
+ HashSet set = new HashSet();
+ ToCode(X(), () => new[] { 2000, 2004, 2008, 2012 }.All(set.Add));
+
+ Func, 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, 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>>(X(), a => b => c => a + b + c);
+ }
+
+ bool Fizz(Func a)
+ {
+ return a(42);
+ }
+
+ bool Buzz(Func a)
+ {
+ return a(42);
+ }
+
+ bool Fizz(Func 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>)((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);
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
index 9b3457dd4..0244a2d23 100644
--- a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
+++ b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -14,7 +14,7 @@
4
false
False
- 67,169,1058,728
+ 67,169,1058,728,1720
x86
@@ -64,6 +64,7 @@
+
diff --git a/ICSharpCode.Decompiler/Tests/InitializerTests.cs b/ICSharpCode.Decompiler/Tests/InitializerTests.cs
index 8240a6aef..2f17c2976 100644
--- a/ICSharpCode.Decompiler/Tests/InitializerTests.cs
+++ b/ICSharpCode.Decompiler/Tests/InitializerTests.cs
@@ -534,7 +534,7 @@ public class InitializerTests
public int[,] MultidimensionalInit()
{
- return new int[, ]
+ return new int[,]
{
{
@@ -655,7 +655,7 @@ public class InitializerTests
{
return new int[][,]
{
- new int[, ]
+ new int[,]
{
{
@@ -687,7 +687,7 @@ public class InitializerTests
}
},
- new int[, ]
+ new int[,]
{
{
@@ -719,7 +719,7 @@ public class InitializerTests
}
},
- new int[, ]
+ new int[,]
{
{
@@ -750,7 +750,7 @@ public class InitializerTests
0
}
},
- new int[, ]
+ new int[,]
{
{
@@ -789,7 +789,7 @@ public class InitializerTests
{
return new int[][,,]
{
- new int[, , ]
+ new int[,,]
{
{
{
@@ -827,7 +827,7 @@ public class InitializerTests
}
},
- new int[, , ]
+ new int[,,]
{
{
{
diff --git a/ICSharpCode.Decompiler/Tests/TestRunner.cs b/ICSharpCode.Decompiler/Tests/TestRunner.cs
index 4d278e0e9..a4feebca0 100644
--- a/ICSharpCode.Decompiler/Tests/TestRunner.cs
+++ b/ICSharpCode.Decompiler/Tests/TestRunner.cs
@@ -52,6 +52,12 @@ namespace ICSharpCode.Decompiler.Tests
TestFile(@"..\..\Tests\DelegateConstruction.cs");
}
+ [Test, Ignore("Not yet implemented")]
+ public void ExpressionTrees()
+ {
+ TestFile(@"..\..\Tests\ExpressionTrees.cs");
+ }
+
[Test]
public void ExceptionHandling()
{
diff --git a/ILSpy/AboutPage.cs b/ILSpy/AboutPage.cs
index 91237ddf8..336e8aa49 100644
--- a/ILSpy/AboutPage.cs
+++ b/ILSpy/AboutPage.cs
@@ -52,6 +52,7 @@ namespace ICSharpCode.ILSpy
}
static readonly Uri UpdateUrl = new Uri("http://www.ilspy.net/updates.xml");
+ const string band = "beta";
static AvailableVersionInfo latestAvailableVersion;
@@ -193,7 +194,7 @@ namespace ICSharpCode.ILSpy
try {
XDocument doc = XDocument.Load(new MemoryStream(e.Result));
var bands = doc.Root.Elements("band");
- var currentBand = bands.FirstOrDefault(b => (string)b.Attribute("id") == "stable") ?? bands.First();
+ var currentBand = bands.FirstOrDefault(b => (string)b.Attribute("id") == band) ?? bands.First();
Version version = new Version((string)currentBand.Element("latestVersion"));
string url = (string)currentBand.Element("downloadUrl");
if (!(url.StartsWith("http://", StringComparison.Ordinal) || url.StartsWith("https://", StringComparison.Ordinal)))