Browse Source

Merge branch 'master' of git://github.com/icsharpcode/ILSpy

pull/285/head
Eusebiu Marcu 14 years ago
parent
commit
cabe2fdcc6
  1. 35
      ICSharpCode.Decompiler/Ast/Annotations.cs
  2. 64
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  3. 109
      ICSharpCode.Decompiler/Ast/Transforms/CustomPatterns.cs
  4. 12
      ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
  5. 695
      ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs
  6. 25
      ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
  7. 82
      ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs
  8. 15
      ICSharpCode.Decompiler/DecompilerSettings.cs
  9. 2
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  10. 6
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  11. 6
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  12. 110
      ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  13. 370
      ICSharpCode.Decompiler/Tests/ExpressionTrees.cs
  14. 3
      ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  15. 14
      ICSharpCode.Decompiler/Tests/InitializerTests.cs
  16. 6
      ICSharpCode.Decompiler/Tests/TestRunner.cs
  17. 3
      ILSpy/AboutPage.cs

35
ICSharpCode.Decompiler/Ast/Annotations.cs

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

64
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -370,9 +370,6 @@ namespace ICSharpCode.Decompiler.Ast
// change "new (int[,])[10] to new int[10][,]" // change "new (int[,])[10] to new int[10][,]"
ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers); ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers);
ace.Initializer = new ArrayInitializerExpression(); 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<Expression>(); var newArgs = new List<Expression>();
foreach (var arrayDimension in arrayType.Dimensions.Skip(1).Reverse()) foreach (var arrayDimension in arrayType.Dimensions.Skip(1).Reverse())
@ -461,9 +458,9 @@ namespace ICSharpCode.Decompiler.Ast
goto case ILCode.Cle; goto case ILCode.Cle;
} }
case ILCode.Cle: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); 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.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); case ILCode.Clt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2);
#endregion #endregion
#region Logical #region Logical
@ -661,7 +658,20 @@ namespace ICSharpCode.Decompiler.Ast
if (operand is Cecil.TypeReference) { if (operand is Cecil.TypeReference) {
return AstBuilder.CreateTypeOfExpression((TypeReference)operand).Member("TypeHandle"); return AstBuilder.CreateTypeOfExpression((TypeReference)operand).Member("TypeHandle");
} else { } 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.Leave: return new GotoStatement() { Label = ((ILLabel)operand).Name };
case ILCode.Localloc: case ILCode.Localloc:
@ -718,22 +728,7 @@ namespace ICSharpCode.Decompiler.Ast
MethodDefinition ctor = ((MethodReference)operand).Resolve(); MethodDefinition ctor = ((MethodReference)operand).Resolve();
if (methodDef != null) { if (methodDef != null) {
AnonymousTypeCreateExpression atce = new AnonymousTypeCreateExpression(); AnonymousTypeCreateExpression atce = new AnonymousTypeCreateExpression();
bool allNamesCanBeInferred = true; if (CanInferAnonymousTypePropertyNamesFromArguments(args, ctor.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 != ctor.Parameters[i].Name) {
allNamesCanBeInferred = false;
break;
}
}
if (allNamesCanBeInferred) {
atce.Initializers.AddRange(args); atce.Initializers.AddRange(args);
} else { } else {
for (int i = 0; i < args.Count; i++) { for (int i = 0; i < args.Count; i++) {
@ -829,13 +824,34 @@ namespace ICSharpCode.Decompiler.Ast
return arg1.WithAnnotation(PushNegation.LiftedOperatorAnnotation); return arg1.WithAnnotation(PushNegation.LiftedOperatorAnnotation);
case ILCode.AddressOf: case ILCode.AddressOf:
return MakeRef(arg1); return MakeRef(arg1);
case ILCode.ExpressionTreeParameterDeclarations:
args[args.Count - 1].AddAnnotation(new ParameterDeclarationAnnotation(byteCode));
return args[args.Count - 1];
case ILCode.NullableOf: case ILCode.NullableOf:
case ILCode.ValueOf: return arg1; case ILCode.ValueOf: return arg1;
default: default:
throw new Exception("Unknown OpCode: " + byteCode.Code); throw new Exception("Unknown OpCode: " + byteCode.Code);
} }
} }
internal static bool CanInferAnonymousTypePropertyNamesFromArguments(IList<Expression> args, IList<ParameterDefinition> 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( static readonly AstNode objectInitializerPattern = new AssignmentExpression(
new MemberReferenceExpression { new MemberReferenceExpression {
Target = new InitializedObjectExpression() Target = new InitializedObjectExpression()
@ -1047,7 +1063,7 @@ namespace ICSharpCode.Decompiler.Ast
} }
} }
static PropertyDefinition GetIndexer(MethodDefinition cecilMethodDef) internal static PropertyDefinition GetIndexer(MethodDefinition cecilMethodDef)
{ {
TypeDefinition typeDef = cecilMethodDef.DeclaringType; TypeDefinition typeDef = cecilMethodDef.DeclaringType;
string indexerName = null; string indexerName = null;

109
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<TypeReference>();
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<LdTokenAnnotation>() != null && ie.Arguments.Count == 1) {
return childNode.DoMatch(ie.Arguments.Single(), match);
}
return false;
}
public override string ToString()
{
return "ldtoken(...)";
}
}
/// <summary>
/// typeof-Pattern that applies on the expanded form of typeof (prior to ReplaceMethodCallsWithOperators)
/// </summary>
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(...)";
}
}
}

12
ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs

@ -209,6 +209,18 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return true; 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 #region Track current variables
public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data) public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data)
{ {

695
ICSharpCode.Decompiler/Ast/Transforms/ExpressionTreeConverter.cs

@ -0,0 +1,695 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
public class ExpressionTreeConverter
{
#region static TryConvert method
public static bool CouldBeExpressionTree(InvocationExpression expr)
{
if (expr != null && expr.Arguments.Count == 2) {
MethodReference mr = expr.Annotation<MethodReference>();
return mr != null && mr.Name == "Lambda" && mr.DeclaringType.FullName == "System.Linq.Expressions.Expression";
}
return false;
}
public static Expression TryConvert(DecompilerContext context, Expression expr)
{
Expression converted = new ExpressionTreeConverter(context).Convert(expr);
if (converted != null) {
converted.AddAnnotation(new ExpressionTreeLambdaAnnotation());
}
return converted;
}
#endregion
readonly DecompilerContext context;
Stack<LambdaExpression> activeLambdas = new Stack<LambdaExpression>();
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<MethodReference>();
if (mr != null && mr.DeclaringType.FullName == "System.Linq.Expressions.Expression") {
switch (mr.Name) {
case "Add":
return ConvertBinaryOperator(invocation, BinaryOperatorType.Add, false);
case "AddChecked":
return ConvertBinaryOperator(invocation, BinaryOperatorType.Add, true);
case "AddAssign":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Add, false);
case "AddAssignChecked":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Add, true);
case "And":
return ConvertBinaryOperator(invocation, BinaryOperatorType.BitwiseAnd);
case "AndAlso":
return ConvertBinaryOperator(invocation, BinaryOperatorType.ConditionalAnd);
case "AndAssign":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.BitwiseAnd);
case "ArrayAccess":
return NotImplemented(invocation);
case "ArrayIndex":
return ConvertArrayIndex(invocation);
case "ArrayLength":
return ConvertArrayLength(invocation);
case "Assign":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Assign);
case "Call":
return ConvertCall(invocation);
case "Coalesce":
return ConvertBinaryOperator(invocation, BinaryOperatorType.NullCoalescing);
case "Condition":
return NotImplemented(invocation);
case "Constant":
if (invocation.Arguments.Count >= 1)
return invocation.Arguments.First().Clone();
else
return NotSupported(expr);
case "Convert":
return ConvertCast(invocation, false);
case "ConvertChecked":
return ConvertCast(invocation, true);
case "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 "Invoke":
return ConvertInvoke(invocation);
case "Lambda":
return ConvertLambda(invocation);
case "LeftShift":
return ConvertBinaryOperator(invocation, BinaryOperatorType.ShiftLeft);
case "LeftShiftAssign":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.ShiftLeft);
case "LessThan":
return ConvertBinaryOperator(invocation, BinaryOperatorType.LessThan);
case "LessThanOrEqual":
return ConvertBinaryOperator(invocation, BinaryOperatorType.LessThanOrEqual);
case "Modulo":
return ConvertBinaryOperator(invocation, BinaryOperatorType.Modulus);
case "ModuloAssign":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Modulus);
case "Multiply":
return ConvertBinaryOperator(invocation, BinaryOperatorType.Multiply, false);
case "MultiplyChecked":
return ConvertBinaryOperator(invocation, BinaryOperatorType.Multiply, true);
case "MultiplyAssign":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Multiply, false);
case "MultiplyAssignChecked":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Multiply, true);
case "Negate":
return ConvertUnaryOperator(invocation, UnaryOperatorType.Minus, false);
case "NegateChecked":
return ConvertUnaryOperator(invocation, UnaryOperatorType.Minus, true);
case "New":
return ConvertNewObject(invocation);
case "NewArrayInit":
return ConvertNewArrayInit(invocation);
case "Not":
return ConvertUnaryOperator(invocation, UnaryOperatorType.Not);
case "NotEqual":
return ConvertBinaryOperator(invocation, BinaryOperatorType.InEquality);
case "OnesComplement":
return ConvertUnaryOperator(invocation, UnaryOperatorType.BitNot);
case "Or":
return ConvertBinaryOperator(invocation, BinaryOperatorType.BitwiseOr);
case "OrAssign":
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.BitwiseOr);
case "OrElse":
return ConvertBinaryOperator(invocation, BinaryOperatorType.ConditionalOr);
case "Property":
return ConvertProperty(invocation);
case "Quote":
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<ILVariable>();
if (v != null) {
foreach (LambdaExpression lambda in activeLambdas) {
foreach (ParameterDeclaration p in lambda.Parameters) {
if (p.Annotation<ILVariable>() == v)
return new IdentifierExpression(p.Name).WithAnnotation(v);
}
}
}
}
return NotSupported(expr);
}
Expression NotSupported(Expression expr)
{
Debug.WriteLine("Expression Tree Conversion Failed: '" + expr + "' is not supported");
return null;
}
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<ParameterDeclarationAnnotation>();
if (annotation != null) {
lambda.Parameters.AddRange(annotation.Parameters);
} else {
// No parameter declaration annotation found.
if (!emptyArrayPattern.IsMatch(parameterArray))
return null;
}
activeLambdas.Push(lambda);
Expression convertedBody = Convert(body);
activeLambdas.Pop();
if (convertedBody == null)
return null;
lambda.Body = convertedBody;
return lambda;
}
#endregion
#region Convert Field
static readonly Expression getFieldFromHandlePattern =
new TypePattern(typeof(FieldInfo)).ToType().Invoke(
"GetFieldFromHandle",
new LdTokenPattern("field").ToExpression().Member("FieldHandle"),
new OptionalNode(new TypeOfExpression(new AnyNode("declaringType")).Member("TypeHandle"))
);
Expression ConvertField(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 2)
return NotSupported(invocation);
Expression fieldInfoExpr = invocation.Arguments.ElementAt(1);
Match m = getFieldFromHandlePattern.Match(fieldInfoExpr);
if (!m.Success)
return NotSupported(invocation);
FieldReference fr = m.Get<AstNode>("field").Single().Annotation<FieldReference>();
if (fr == null)
return null;
Expression target = invocation.Arguments.ElementAt(0);
Expression convertedTarget;
if (target is NullReferenceExpression) {
if (m.Has("declaringType"))
convertedTarget = new TypeReferenceExpression(m.Get<AstType>("declaringType").Single().Clone());
else
convertedTarget = new TypeReferenceExpression(AstBuilder.ConvertType(fr.DeclaringType));
} else {
convertedTarget = Convert(target);
if (convertedTarget == null)
return null;
}
return convertedTarget.Member(fr.Name).WithAnnotation(fr);
}
#endregion
#region Convert Property
static readonly Expression getMethodFromHandlePattern =
new TypePattern(typeof(MethodBase)).ToType().Invoke(
"GetMethodFromHandle",
new LdTokenPattern("method").ToExpression().Member("MethodHandle"),
new OptionalNode(new TypeOfExpression(new AnyNode("declaringType")).Member("TypeHandle"))
).CastTo(new TypePattern(typeof(MethodInfo)));
Expression ConvertProperty(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 2)
return NotSupported(invocation);
Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(1));
if (!m.Success)
return NotSupported(invocation);
MethodReference mr = m.Get<AstNode>("method").Single().Annotation<MethodReference>();
if (mr == null)
return null;
Expression target = invocation.Arguments.ElementAt(0);
Expression convertedTarget;
if (target is NullReferenceExpression) {
if (m.Has("declaringType"))
convertedTarget = new TypeReferenceExpression(m.Get<AstType>("declaringType").Single().Clone());
else
convertedTarget = new TypeReferenceExpression(AstBuilder.ConvertType(mr.DeclaringType));
} else {
convertedTarget = Convert(target);
if (convertedTarget == null)
return null;
}
string name = mr.Name;
if (name.StartsWith("get_", StringComparison.Ordinal) || name.StartsWith("set_", StringComparison.Ordinal))
name = name.Substring(4);
return convertedTarget.Member(name).WithAnnotation(mr);
}
#endregion
#region Convert Call
Expression ConvertCall(InvocationExpression invocation)
{
if (invocation.Arguments.Count < 2)
return NotSupported(invocation);
Expression target;
int firstArgumentPosition;
Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(0));
if (m.Success) {
target = null;
firstArgumentPosition = 1;
} else {
m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(1));
if (!m.Success)
return NotSupported(invocation);
target = invocation.Arguments.ElementAt(0);
firstArgumentPosition = 2;
}
MethodReference mr = m.Get<AstNode>("method").Single().Annotation<MethodReference>();
if (mr == null)
return null;
Expression convertedTarget;
if (target == null || target is NullReferenceExpression) {
// static method
if (m.Has("declaringType"))
convertedTarget = new TypeReferenceExpression(m.Get<AstType>("declaringType").Single().Clone());
else
convertedTarget = new TypeReferenceExpression(AstBuilder.ConvertType(mr.DeclaringType));
} else {
convertedTarget = Convert(target);
if (convertedTarget == null)
return null;
}
MemberReferenceExpression mre = convertedTarget.Member(mr.Name);
GenericInstanceMethod gim = mr as GenericInstanceMethod;
if (gim != null) {
foreach (TypeReference tr in gim.GenericArguments) {
mre.TypeArguments.Add(AstBuilder.ConvertType(tr));
}
}
IList<Expression> arguments = null;
if (invocation.Arguments.Count == firstArgumentPosition + 1) {
Expression argumentArray = invocation.Arguments.ElementAt(firstArgumentPosition);
arguments = ConvertExpressionsArray(argumentArray);
}
if (arguments == null) {
arguments = new List<Expression>();
foreach (Expression argument in invocation.Arguments.Skip(firstArgumentPosition)) {
Expression convertedArgument = Convert(argument);
if (convertedArgument == null)
return null;
arguments.Add(convertedArgument);
}
}
MethodDefinition methodDef = mr.Resolve();
if (methodDef != null && methodDef.IsGetter) {
PropertyDefinition indexer = AstMethodBodyBuilder.GetIndexer(methodDef);
if (indexer != null)
return new IndexerExpression(mre.Target.Detach(), arguments).WithAnnotation(indexer);
}
return new InvocationExpression(mre, arguments).WithAnnotation(mr);
}
Expression ConvertInvoke(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 2)
return NotSupported(invocation);
Expression convertedTarget = Convert(invocation.Arguments.ElementAt(0));
IList<Expression> convertedArguments = ConvertExpressionsArray(invocation.Arguments.ElementAt(1));
if (convertedTarget != null && convertedArguments != null)
return new InvocationExpression(convertedTarget, convertedArguments);
else
return null;
}
#endregion
#region Convert Binary Operator
static readonly Pattern trueOrFalse = new Choice {
new PrimitiveExpression(true),
new PrimitiveExpression(false)
};
Expression ConvertBinaryOperator(InvocationExpression invocation, BinaryOperatorType op, bool? isChecked = null)
{
if (invocation.Arguments.Count < 2)
return NotSupported(invocation);
Expression left = Convert(invocation.Arguments.ElementAt(0));
if (left == null)
return null;
Expression right = Convert(invocation.Arguments.ElementAt(1));
if (right == null)
return null;
BinaryOperatorExpression boe = new BinaryOperatorExpression(left, op, right);
if (isChecked != null)
boe.AddAnnotation(isChecked.Value ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation);
switch (invocation.Arguments.Count) {
case 2:
return boe;
case 3:
Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(2));
if (m.Success)
return boe.WithAnnotation(m.Get<AstNode>("method").Single().Annotation<MethodReference>());
else
return null;
case 4:
if (!trueOrFalse.IsMatch(invocation.Arguments.ElementAt(2)))
return null;
m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(3));
if (m.Success)
return boe.WithAnnotation(m.Get<AstNode>("method").Single().Annotation<MethodReference>());
else
return null;
default:
return NotSupported(invocation);
}
}
#endregion
#region Convert Assignment Operator
Expression ConvertAssignmentOperator(InvocationExpression invocation, AssignmentOperatorType op, bool? isChecked = null)
{
return NotImplemented(invocation);
}
#endregion
#region Convert Unary Operator
Expression ConvertUnaryOperator(InvocationExpression invocation, UnaryOperatorType op, bool? isChecked = null)
{
if (invocation.Arguments.Count < 1)
return NotSupported(invocation);
Expression expr = Convert(invocation.Arguments.ElementAt(0));
if (expr == null)
return null;
UnaryOperatorExpression uoe = new UnaryOperatorExpression(op, expr);
if (isChecked != null)
uoe.AddAnnotation(isChecked.Value ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation);
switch (invocation.Arguments.Count) {
case 1:
return uoe;
case 2:
Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(1));
if (m.Success)
return uoe.WithAnnotation(m.Get<AstNode>("method").Single().Annotation<MethodReference>());
else
return null;
default:
return NotSupported(invocation);
}
}
#endregion
#region Convert New Object
static readonly Expression newObjectCtorPattern = new TypePattern(typeof(MethodBase)).ToType().Invoke
(
"GetMethodFromHandle",
new LdTokenPattern("ctor").ToExpression().Member("MethodHandle"),
new OptionalNode(new TypeOfExpression(new AnyNode("declaringType")).Member("TypeHandle"))
).CastTo(new TypePattern(typeof(ConstructorInfo)));
Expression ConvertNewObject(InvocationExpression invocation)
{
if (invocation.Arguments.Count < 1 || invocation.Arguments.Count > 3)
return NotSupported(invocation);
Match m = newObjectCtorPattern.Match(invocation.Arguments.First());
if (!m.Success)
return NotSupported(invocation);
MethodReference ctor = m.Get<AstNode>("ctor").Single().Annotation<MethodReference>();
if (ctor == null)
return null;
AstType declaringTypeNode;
TypeReference declaringType;
if (m.Has("declaringType")) {
declaringTypeNode = m.Get<AstType>("declaringType").Single().Clone();
declaringType = declaringTypeNode.Annotation<TypeReference>();
} else {
declaringTypeNode = AstBuilder.ConvertType(ctor.DeclaringType);
declaringType = ctor.DeclaringType;
}
if (declaringTypeNode == null)
return null;
ObjectCreateExpression oce = new ObjectCreateExpression(declaringTypeNode);
if (invocation.Arguments.Count >= 2) {
IList<Expression> arguments = ConvertExpressionsArray(invocation.Arguments.ElementAtOrDefault(1));
if (arguments == null)
return null;
oce.Arguments.AddRange(arguments);
}
if (invocation.Arguments.Count >= 3 && declaringType.IsAnonymousType()) {
MethodDefinition resolvedCtor = ctor.Resolve();
if (resolvedCtor == null || resolvedCtor.Parameters.Count != oce.Arguments.Count)
return null;
AnonymousTypeCreateExpression atce = new AnonymousTypeCreateExpression();
var arguments = oce.Arguments.ToArray();
if (AstMethodBodyBuilder.CanInferAnonymousTypePropertyNamesFromArguments(arguments, resolvedCtor.Parameters)) {
oce.Arguments.MoveTo(atce.Initializers);
} else {
for (int i = 0; i < resolvedCtor.Parameters.Count; i++) {
atce.Initializers.Add(
new NamedExpression {
Identifier = resolvedCtor.Parameters[i].Name,
Expression = arguments[i].Detach()
});
}
}
return atce;
}
return oce;
}
#endregion
#region Convert Cast
Expression ConvertCast(InvocationExpression invocation, bool isChecked)
{
if (invocation.Arguments.Count != 2)
return null;
Expression converted = Convert(invocation.Arguments.ElementAt(0));
AstType type = ConvertTypeReference(invocation.Arguments.ElementAt(1));
if (converted != null && type != null) {
CastExpression cast = converted.CastTo(type);
cast.AddAnnotation(isChecked ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation);
return cast;
}
return null;
}
#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<Expression> ConvertExpressionsArray(Expression arrayExpression)
{
Match m = expressionArrayPattern.Match(arrayExpression);
if (m.Success) {
List<Expression> result = new List<Expression>();
foreach (Expression expr in m.Get<Expression>("elements")) {
Expression converted = Convert(expr);
if (converted == null)
return null;
result.Add(converted);
}
return result;
}
return null;
}
#endregion
#region Convert TypeAs/TypeIs
static readonly TypeOfPattern typeOfPattern = new TypeOfPattern("type");
AstType ConvertTypeReference(Expression typeOfExpression)
{
Match m = typeOfPattern.Match(typeOfExpression);
if (m.Success)
return m.Get<AstType>("type").Single().Clone();
else
return null;
}
Expression ConvertTypeAs(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 2)
return null;
Expression converted = Convert(invocation.Arguments.ElementAt(0));
AstType type = ConvertTypeReference(invocation.Arguments.ElementAt(1));
if (converted != null && type != null)
return new AsExpression(converted, type);
return null;
}
Expression ConvertTypeIs(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 2)
return null;
Expression converted = Convert(invocation.Arguments.ElementAt(0));
AstType type = ConvertTypeReference(invocation.Arguments.ElementAt(1));
if (converted != null && type != null)
return new IsExpression { Expression = converted, Type = type };
return null;
}
#endregion
#region Convert Array
Expression ConvertArrayIndex(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 2)
return NotSupported(invocation);
Expression targetConverted = Convert(invocation.Arguments.First());
if (targetConverted == null)
return null;
Expression index = invocation.Arguments.ElementAt(1);
Expression indexConverted = Convert(index);
if (indexConverted != null) {
return new IndexerExpression(targetConverted, indexConverted);
}
IList<Expression> indexesConverted = ConvertExpressionsArray(index);
if (indexConverted != null) {
return new IndexerExpression(targetConverted, indexesConverted);
}
return null;
}
Expression ConvertArrayLength(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 1)
return NotSupported(invocation);
Expression targetConverted = Convert(invocation.Arguments.Single());
if (targetConverted != null)
return targetConverted.Member("Length");
else
return null;
}
Expression ConvertNewArrayInit(InvocationExpression invocation)
{
if (invocation.Arguments.Count != 2)
return NotSupported(invocation);
AstType elementType = ConvertTypeReference(invocation.Arguments.ElementAt(0));
IList<Expression> elements = ConvertExpressionsArray(invocation.Arguments.ElementAt(1));
if (elementType != null && elements != null) {
return new ArrayCreateExpression {
Type = elementType,
AdditionalArraySpecifiers = { new ArraySpecifier() },
Initializer = new ArrayInitializerExpression(elements)
};
}
return null;
}
#endregion
}
}

25
ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs

@ -227,7 +227,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
Name = variableName, Name = variableName,
Initializer = m1.Get<Expression>("initializer").Single().Detach() Initializer = m1.Get<Expression>("initializer").Single().Detach()
}.CopyAnnotationsFrom(node.Expression) }.CopyAnnotationsFrom(node.Expression)
.WithAnnotation(m1.Get<AstNode>("variable").Single().Annotation<ILVariable>()) .WithAnnotation(m1.Get<AstNode>("variable").Single().Annotation<ILVariable>())
} }
}.CopyAnnotationsFrom(node); }.CopyAnnotationsFrom(node);
} else { } else {
@ -985,28 +985,5 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return null; return null;
} }
#endregion #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<TypeReference>();
return tr != null && tr.Namespace == ns && tr.Name == name;
}
}
#endregion
} }
} }

82
ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs

@ -19,6 +19,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using ICSharpCode.NRefactory.PatternMatching; using ICSharpCode.NRefactory.PatternMatching;
using Mono.Cecil; using Mono.Cecil;
using Ast = ICSharpCode.NRefactory.CSharp; using Ast = ICSharpCode.NRefactory.CSharp;
@ -50,15 +51,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data) public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data)
{ {
base.VisitInvocationExpression(invocationExpression, data); base.VisitInvocationExpression(invocationExpression, data);
ProcessInvocationExpression(invocationExpression);
return ProcessInvocationExpression(invocationExpression); return null;
} }
internal static object ProcessInvocationExpression(InvocationExpression invocationExpression) internal static void ProcessInvocationExpression(InvocationExpression invocationExpression)
{ {
MethodReference methodRef = invocationExpression.Annotation<MethodReference>(); MethodReference methodRef = invocationExpression.Annotation<MethodReference>();
if (methodRef == null) if (methodRef == null)
return null; return;
var arguments = invocationExpression.Arguments.ToArray(); var arguments = invocationExpression.Arguments.ToArray();
// Reduce "String.Concat(a, b)" to "a + b" // Reduce "String.Concat(a, b)" to "a + b"
@ -70,7 +71,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
expr = new BinaryOperatorExpression(expr, BinaryOperatorType.Add, arguments[i]); expr = new BinaryOperatorExpression(expr, BinaryOperatorType.Add, arguments[i]);
} }
invocationExpression.ReplaceWith(expr); invocationExpression.ReplaceWith(expr);
return null; return;
} }
switch (methodRef.FullName) { switch (methodRef.FullName) {
@ -78,7 +79,34 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
if (arguments.Length == 1) { if (arguments.Length == 1) {
if (typeHandleOnTypeOfPattern.IsMatch(arguments[0])) { if (typeHandleOnTypeOfPattern.IsMatch(arguments[0])) {
invocationExpression.ReplaceWith(((MemberReferenceExpression)arguments[0]).Target); invocationExpression.ReplaceWith(((MemberReferenceExpression)arguments[0]).Target);
return null; return;
}
}
break;
case "System.Reflection.FieldInfo System.Reflection.FieldInfo::GetFieldFromHandle(System.RuntimeFieldHandle)":
if (arguments.Length == 1) {
MemberReferenceExpression mre = arguments[0] as MemberReferenceExpression;
if (mre != null && mre.MemberName == "FieldHandle" && mre.Target.Annotation<LdTokenAnnotation>() != null) {
invocationExpression.ReplaceWith(mre.Target);
return;
}
}
break;
case "System.Reflection.FieldInfo System.Reflection.FieldInfo::GetFieldFromHandle(System.RuntimeFieldHandle,System.RuntimeTypeHandle)":
if (arguments.Length == 2) {
MemberReferenceExpression mre1 = arguments[0] as MemberReferenceExpression;
MemberReferenceExpression mre2 = arguments[1] as MemberReferenceExpression;
if (mre1 != null && mre1.MemberName == "FieldHandle" && mre1.Target.Annotation<LdTokenAnnotation>() != null) {
if (mre2 != null && mre2.MemberName == "TypeHandle" && mre2.Target is TypeOfExpression) {
Expression oldArg = ((InvocationExpression)mre1.Target).Arguments.Single();
FieldReference field = oldArg.Annotation<FieldReference>();
if (field != null) {
AstType declaringType = ((TypeOfExpression)mre2.Target).Type.Detach();
oldArg.ReplaceWith(declaringType.Member(field.Name).WithAnnotation(field));
invocationExpression.ReplaceWith(mre1.Target);
return;
}
}
} }
} }
break; break;
@ -90,7 +118,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
invocationExpression.ReplaceWith( invocationExpression.ReplaceWith(
new BinaryOperatorExpression(arguments[0], bop.Value, arguments[1]).WithAnnotation(methodRef) new BinaryOperatorExpression(arguments[0], bop.Value, arguments[1]).WithAnnotation(methodRef)
); );
return null; return;
} }
UnaryOperatorType? uop = GetUnaryOperatorTypeFromMetadataName(methodRef.Name); UnaryOperatorType? uop = GetUnaryOperatorTypeFromMetadataName(methodRef.Name);
if (uop != null && arguments.Length == 1) { if (uop != null && arguments.Length == 1) {
@ -98,7 +126,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
invocationExpression.ReplaceWith( invocationExpression.ReplaceWith(
new UnaryOperatorExpression(uop.Value, arguments[0]).WithAnnotation(methodRef) new UnaryOperatorExpression(uop.Value, arguments[0]).WithAnnotation(methodRef)
); );
return null; return;
} }
if (methodRef.Name == "op_Explicit" && arguments.Length == 1) { if (methodRef.Name == "op_Explicit" && arguments.Length == 1) {
arguments[0].Remove(); // detach argument arguments[0].Remove(); // detach argument
@ -106,18 +134,18 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
arguments[0].CastTo(AstBuilder.ConvertType(methodRef.ReturnType, methodRef.MethodReturnType)) arguments[0].CastTo(AstBuilder.ConvertType(methodRef.ReturnType, methodRef.MethodReturnType))
.WithAnnotation(methodRef) .WithAnnotation(methodRef)
); );
return null; return;
} }
if (methodRef.Name == "op_Implicit" && arguments.Length == 1) { if (methodRef.Name == "op_Implicit" && arguments.Length == 1) {
invocationExpression.ReplaceWith(arguments[0]); invocationExpression.ReplaceWith(arguments[0]);
return null; return;
} }
if (methodRef.Name == "op_True" && arguments.Length == 1 && invocationExpression.Role == AstNode.Roles.Condition) { if (methodRef.Name == "op_True" && arguments.Length == 1 && invocationExpression.Role == AstNode.Roles.Condition) {
invocationExpression.ReplaceWith(arguments[0]); invocationExpression.ReplaceWith(arguments[0]);
return null; return;
} }
return null; return;
} }
static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name) static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name)
@ -292,6 +320,34 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return left is ThisReferenceExpression || left is IdentifierExpression || left is TypeReferenceExpression || left is BaseReferenceExpression; return left is ThisReferenceExpression || left is IdentifierExpression || left is TypeReferenceExpression || left is BaseReferenceExpression;
} }
static readonly Expression getMethodOrConstructorFromHandlePattern =
new TypePattern(typeof(MethodBase)).ToType().Invoke(
"GetMethodFromHandle",
new NamedNode("ldtokenNode", new LdTokenPattern("method")).ToExpression().Member("MethodHandle"),
new OptionalNode(new TypeOfExpression(new AnyNode("declaringType")).Member("TypeHandle"))
).CastTo(new Choice {
new TypePattern(typeof(MethodInfo)),
new TypePattern(typeof(ConstructorInfo))
});
public override object VisitCastExpression(CastExpression castExpression, object data)
{
base.VisitCastExpression(castExpression, data);
// Handle methodof
Match m = getMethodOrConstructorFromHandlePattern.Match(castExpression);
if (m.Success) {
MethodReference method = m.Get<AstNode>("method").Single().Annotation<MethodReference>();
if (m.Has("declaringType")) {
Expression newNode = m.Get<AstType>("declaringType").Single().Detach().Member(method.Name);
newNode = newNode.Invoke(method.Parameters.Select(p => new TypeReferenceExpression(AstBuilder.ConvertType(p.ParameterType, p))));
newNode.AddAnnotation(method);
m.Get<AstNode>("method").Single().ReplaceWith(newNode);
}
castExpression.ReplaceWith(m.Get<AstNode>("ldtokenNode").Single());
}
return null;
}
void IAstTransform.Run(AstNode node) void IAstTransform.Run(AstNode node)
{ {
node.AcceptVisitor(this, null); node.AcceptVisitor(this, null);

15
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -41,6 +41,21 @@ namespace ICSharpCode.Decompiler
} }
} }
bool expressionTrees = true;
/// <summary>
/// Decompile expression trees.
/// </summary>
public bool ExpressionTrees {
get { return expressionTrees; }
set {
if (expressionTrees != value) {
expressionTrees = value;
OnPropertyChanged("ExpressionTrees");
}
}
}
bool yieldReturn = true; bool yieldReturn = true;
/// <summary> /// <summary>

2
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -61,9 +61,11 @@
<Compile Include="Ast\Transforms\CombineQueryExpressions.cs" /> <Compile Include="Ast\Transforms\CombineQueryExpressions.cs" />
<Compile Include="Ast\Transforms\ContextTrackingVisitor.cs" /> <Compile Include="Ast\Transforms\ContextTrackingVisitor.cs" />
<Compile Include="Ast\Transforms\ConvertConstructorCallIntoInitializer.cs" /> <Compile Include="Ast\Transforms\ConvertConstructorCallIntoInitializer.cs" />
<Compile Include="Ast\Transforms\CustomPatterns.cs" />
<Compile Include="Ast\Transforms\DecimalConstantTransform.cs" /> <Compile Include="Ast\Transforms\DecimalConstantTransform.cs" />
<Compile Include="Ast\Transforms\DeclareVariables.cs" /> <Compile Include="Ast\Transforms\DeclareVariables.cs" />
<Compile Include="Ast\Transforms\DelegateConstruction.cs" /> <Compile Include="Ast\Transforms\DelegateConstruction.cs" />
<Compile Include="Ast\Transforms\ExpressionTreeConverter.cs" />
<Compile Include="Ast\Transforms\FlattenSwitchBlocks.cs" /> <Compile Include="Ast\Transforms\FlattenSwitchBlocks.cs" />
<Compile Include="Ast\Transforms\IntroduceExtensionMethods.cs" /> <Compile Include="Ast\Transforms\IntroduceExtensionMethods.cs" />
<Compile Include="Ast\Transforms\IntroduceQueryExpressions.cs" /> <Compile Include="Ast\Transforms\IntroduceQueryExpressions.cs" />

6
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -53,6 +53,7 @@ namespace ICSharpCode.Decompiler.ILAst
TransformObjectInitializers, TransformObjectInitializers,
MakeAssignmentExpression, MakeAssignmentExpression,
IntroducePostIncrement, IntroducePostIncrement,
InlineExpressionTreeParameterDeclarations,
InlineVariables2, InlineVariables2,
FindLoops, FindLoops,
FindConditions, FindConditions,
@ -171,6 +172,11 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.IntroducePostIncrement) return; if (abortBeforeStep == ILAstOptimizationStep.IntroducePostIncrement) return;
modified |= block.RunOptimization(IntroducePostIncrement); modified |= block.RunOptimization(IntroducePostIncrement);
if (abortBeforeStep == ILAstOptimizationStep.InlineExpressionTreeParameterDeclarations) return;
if (context.Settings.ExpressionTrees) {
modified |= block.RunOptimization(InlineExpressionTreeParameterDeclarations);
}
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return; if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return;
modified |= new ILInlining(method).InlineAllInBlock(block); modified |= new ILInlining(method).InlineAllInBlock(block);
new ILInlining(method).CopyPropagation(); new ILInlining(method).CopyPropagation();

6
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -328,6 +328,12 @@ namespace ICSharpCode.Decompiler.ILAst
/// becomes "NullableOf(add(valueof(...), ...))" /// becomes "NullableOf(add(valueof(...), ...))"
/// </remarks> /// </remarks>
NullableOf, NullableOf,
/// <summary>
/// Declares parameters that are used in an expression tree.
/// The last child of this node is the call constructing the expression tree, all other children are the
/// assignments to the ParameterExpression variables.
/// </summary>
ExpressionTreeParameterDeclarations
} }
public static class ILCodeUtil public static class ILCodeUtil

110
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

@ -953,17 +953,17 @@ namespace ICSharpCode.Decompiler.ILAst
var a = expr.Arguments[0]; var a = expr.Arguments[0];
ILCode c; ILCode c;
switch (a.Code) { switch (a.Code) {
case ILCode.Ceq: c = ILCode.Cne; break; case ILCode.Ceq: c = ILCode.Cne; break;
case ILCode.Cne: c = ILCode.Ceq; break; case ILCode.Cne: c = ILCode.Ceq; break;
case ILCode.Cgt: c = ILCode.Cle; break; case ILCode.Cgt: c = ILCode.Cle; break;
case ILCode.Cgt_Un: c = ILCode.Cle_Un; break; case ILCode.Cgt_Un: c = ILCode.Cle_Un; break;
case ILCode.Cge: c = ILCode.Clt; break; case ILCode.Cge: c = ILCode.Clt; break;
case ILCode.Cge_Un: c = ILCode.Clt_Un; break; case ILCode.Cge_Un: c = ILCode.Clt_Un; break;
case ILCode.Clt: c = ILCode.Cge; break; case ILCode.Clt: c = ILCode.Cge; break;
case ILCode.Clt_Un: c = ILCode.Cge_Un; break; case ILCode.Clt_Un: c = ILCode.Cge_Un; break;
case ILCode.Cle: c = ILCode.Cgt; break; case ILCode.Cle: c = ILCode.Cgt; break;
case ILCode.Cle_Un: c = ILCode.Cgt_Un; break; case ILCode.Cle_Un: c = ILCode.Cgt_Un; break;
default: return false; default: return false;
} }
a.Code = c; a.Code = c;
a.ILRanges.AddRange(expr.ILRanges); a.ILRanges.AddRange(expr.ILRanges);
@ -982,20 +982,20 @@ namespace ICSharpCode.Decompiler.ILAst
static void SimplifyShiftOperators(ILExpression expr, ref bool modified) 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); 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; return;
var a = expr.Arguments[1]; 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; return;
int mask; int mask;
switch (expr.InferredType.MetadataType) { switch (expr.InferredType.MetadataType) {
case MetadataType.Int32: case MetadataType.Int32:
case MetadataType.UInt32: mask = 31; break; case MetadataType.UInt32: mask = 31; break;
case MetadataType.Int64: case MetadataType.Int64:
case MetadataType.UInt64: mask = 63; break; case MetadataType.UInt64: mask = 63; break;
default: return; default: return;
} }
if ((int)a.Arguments[1].Operand != mask) return; if ((int)a.Arguments[1].Operand != mask) return;
var res = a.Arguments[0]; var res = a.Arguments[0];
@ -1005,5 +1005,81 @@ namespace ICSharpCode.Decompiler.ILAst
modified = true; modified = true;
} }
#endregion #endregion
#region InlineExpressionTreeParameterDeclarations
bool InlineExpressionTreeParameterDeclarations(List<ILNode> 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
} }
} }

370
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>());
}
}

3
ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj

@ -14,7 +14,7 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors> <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow> <CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
<NoWarn>67,169,1058,728</NoWarn> <NoWarn>67,169,1058,728,1720</NoWarn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'x86' "> <PropertyGroup Condition=" '$(Platform)' == 'x86' ">
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
@ -64,6 +64,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="CallOverloadedMethod.cs" /> <Compile Include="CallOverloadedMethod.cs" />
<Compile Include="CheckedUnchecked.cs" /> <Compile Include="CheckedUnchecked.cs" />
<Compile Include="ExpressionTrees.cs" />
<Compile Include="LiftedOperators.cs" /> <Compile Include="LiftedOperators.cs" />
<Compile Include="CustomShortCircuitOperators.cs" /> <Compile Include="CustomShortCircuitOperators.cs" />
<Compile Include="Helpers\CodeAssert.cs" /> <Compile Include="Helpers\CodeAssert.cs" />

14
ICSharpCode.Decompiler/Tests/InitializerTests.cs

@ -534,7 +534,7 @@ public class InitializerTests
public int[,] MultidimensionalInit() public int[,] MultidimensionalInit()
{ {
return new int[, ] return new int[,]
{ {
{ {
@ -655,7 +655,7 @@ public class InitializerTests
{ {
return new int[][,] 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 0
} }
}, },
new int[, ] new int[,]
{ {
{ {
@ -789,7 +789,7 @@ public class InitializerTests
{ {
return new int[][,,] return new int[][,,]
{ {
new int[, , ] new int[,,]
{ {
{ {
{ {
@ -827,7 +827,7 @@ public class InitializerTests
} }
}, },
new int[, , ] new int[,,]
{ {
{ {
{ {

6
ICSharpCode.Decompiler/Tests/TestRunner.cs

@ -52,6 +52,12 @@ namespace ICSharpCode.Decompiler.Tests
TestFile(@"..\..\Tests\DelegateConstruction.cs"); TestFile(@"..\..\Tests\DelegateConstruction.cs");
} }
[Test, Ignore("Not yet implemented")]
public void ExpressionTrees()
{
TestFile(@"..\..\Tests\ExpressionTrees.cs");
}
[Test] [Test]
public void ExceptionHandling() public void ExceptionHandling()
{ {

3
ILSpy/AboutPage.cs

@ -52,6 +52,7 @@ namespace ICSharpCode.ILSpy
} }
static readonly Uri UpdateUrl = new Uri("http://www.ilspy.net/updates.xml"); static readonly Uri UpdateUrl = new Uri("http://www.ilspy.net/updates.xml");
const string band = "beta";
static AvailableVersionInfo latestAvailableVersion; static AvailableVersionInfo latestAvailableVersion;
@ -193,7 +194,7 @@ namespace ICSharpCode.ILSpy
try { try {
XDocument doc = XDocument.Load(new MemoryStream(e.Result)); XDocument doc = XDocument.Load(new MemoryStream(e.Result));
var bands = doc.Root.Elements("band"); 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")); Version version = new Version((string)currentBand.Element("latestVersion"));
string url = (string)currentBand.Element("downloadUrl"); string url = (string)currentBand.Element("downloadUrl");
if (!(url.StartsWith("http://", StringComparison.Ordinal) || url.StartsWith("https://", StringComparison.Ordinal))) if (!(url.StartsWith("http://", StringComparison.Ordinal) || url.StartsWith("https://", StringComparison.Ordinal)))

Loading…
Cancel
Save