mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
875 lines
31 KiB
875 lines
31 KiB
// 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": |
|
case "ArrayIndex": |
|
return ConvertArrayIndex(invocation); |
|
case "ArrayLength": |
|
return ConvertArrayLength(invocation); |
|
case "Assign": |
|
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Assign); |
|
case "Call": |
|
return ConvertCall(invocation); |
|
case "Coalesce": |
|
return ConvertBinaryOperator(invocation, BinaryOperatorType.NullCoalescing); |
|
case "Condition": |
|
return ConvertCondition(invocation); |
|
case "Constant": |
|
if (invocation.Arguments.Count >= 1) |
|
return invocation.Arguments.First().Clone(); |
|
else |
|
return NotSupported(expr); |
|
case "Convert": |
|
return ConvertCast(invocation, false); |
|
case "ConvertChecked": |
|
return ConvertCast(invocation, true); |
|
case "Divide": |
|
return ConvertBinaryOperator(invocation, BinaryOperatorType.Divide); |
|
case "DivideAssign": |
|
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Divide); |
|
case "Equal": |
|
return ConvertBinaryOperator(invocation, BinaryOperatorType.Equality); |
|
case "ExclusiveOr": |
|
return ConvertBinaryOperator(invocation, BinaryOperatorType.ExclusiveOr); |
|
case "ExclusiveOrAssign": |
|
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.ExclusiveOr); |
|
case "Field": |
|
return ConvertField(invocation); |
|
case "GreaterThan": |
|
return ConvertBinaryOperator(invocation, BinaryOperatorType.GreaterThan); |
|
case "GreaterThanOrEqual": |
|
return ConvertBinaryOperator(invocation, BinaryOperatorType.GreaterThanOrEqual); |
|
case "Invoke": |
|
return ConvertInvoke(invocation); |
|
case "Lambda": |
|
return ConvertLambda(invocation); |
|
case "LeftShift": |
|
return ConvertBinaryOperator(invocation, BinaryOperatorType.ShiftLeft); |
|
case "LeftShiftAssign": |
|
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.ShiftLeft); |
|
case "LessThan": |
|
return ConvertBinaryOperator(invocation, BinaryOperatorType.LessThan); |
|
case "LessThanOrEqual": |
|
return ConvertBinaryOperator(invocation, BinaryOperatorType.LessThanOrEqual); |
|
case "ListInit": |
|
return ConvertListInit(invocation); |
|
case "MemberInit": |
|
return ConvertMemberInit(invocation); |
|
case "Modulo": |
|
return ConvertBinaryOperator(invocation, BinaryOperatorType.Modulus); |
|
case "ModuloAssign": |
|
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Modulus); |
|
case "Multiply": |
|
return ConvertBinaryOperator(invocation, BinaryOperatorType.Multiply, false); |
|
case "MultiplyChecked": |
|
return ConvertBinaryOperator(invocation, BinaryOperatorType.Multiply, true); |
|
case "MultiplyAssign": |
|
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Multiply, false); |
|
case "MultiplyAssignChecked": |
|
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Multiply, true); |
|
case "Negate": |
|
return ConvertUnaryOperator(invocation, UnaryOperatorType.Minus, false); |
|
case "NegateChecked": |
|
return ConvertUnaryOperator(invocation, UnaryOperatorType.Minus, true); |
|
case "New": |
|
return ConvertNewObject(invocation); |
|
case "NewArrayBounds": |
|
return ConvertNewArrayBounds(invocation); |
|
case "NewArrayInit": |
|
return ConvertNewArrayInit(invocation); |
|
case "Not": |
|
return ConvertUnaryOperator(invocation, UnaryOperatorType.Not); |
|
case "NotEqual": |
|
return ConvertBinaryOperator(invocation, BinaryOperatorType.InEquality); |
|
case "OnesComplement": |
|
return ConvertUnaryOperator(invocation, UnaryOperatorType.BitNot); |
|
case "Or": |
|
return ConvertBinaryOperator(invocation, BinaryOperatorType.BitwiseOr); |
|
case "OrAssign": |
|
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.BitwiseOr); |
|
case "OrElse": |
|
return ConvertBinaryOperator(invocation, BinaryOperatorType.ConditionalOr); |
|
case "Property": |
|
return ConvertProperty(invocation); |
|
case "Quote": |
|
if (invocation.Arguments.Count == 1) |
|
return Convert(invocation.Arguments.Single()); |
|
else |
|
return NotSupported(invocation); |
|
case "RightShift": |
|
return ConvertBinaryOperator(invocation, BinaryOperatorType.ShiftRight); |
|
case "RightShiftAssign": |
|
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.ShiftRight); |
|
case "Subtract": |
|
return ConvertBinaryOperator(invocation, BinaryOperatorType.Subtract, false); |
|
case "SubtractChecked": |
|
return ConvertBinaryOperator(invocation, BinaryOperatorType.Subtract, true); |
|
case "SubtractAssign": |
|
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Subtract, false); |
|
case "SubtractAssignChecked": |
|
return ConvertAssignmentOperator(invocation, AssignmentOperatorType.Subtract, true); |
|
case "TypeAs": |
|
return ConvertTypeAs(invocation); |
|
case "TypeIs": |
|
return ConvertTypeIs(invocation); |
|
} |
|
} |
|
} |
|
IdentifierExpression ident = expr as IdentifierExpression; |
|
if (ident != null) { |
|
ILVariable v = ident.Annotation<ILVariable>(); |
|
if (v != null) { |
|
foreach (LambdaExpression lambda in activeLambdas) { |
|
foreach (ParameterDeclaration p in lambda.Parameters) { |
|
if (p.Annotation<ILVariable>() == v) |
|
return new IdentifierExpression(p.Name).WithAnnotation(v); |
|
} |
|
} |
|
} |
|
} |
|
return NotSupported(expr); |
|
} |
|
|
|
Expression NotSupported(Expression expr) |
|
{ |
|
Debug.WriteLine("Expression Tree Conversion Failed: '" + expr + "' is not supported"); |
|
return null; |
|
} |
|
#endregion |
|
|
|
#region Convert Lambda |
|
static readonly Expression emptyArrayPattern = new ArrayCreateExpression { |
|
Type = new AnyNode(), |
|
Arguments = { new PrimitiveExpression(0) } |
|
}; |
|
|
|
Expression ConvertLambda(InvocationExpression invocation) |
|
{ |
|
if (invocation.Arguments.Count != 2) |
|
return NotSupported(invocation); |
|
LambdaExpression lambda = new LambdaExpression(); |
|
Expression body = invocation.Arguments.First(); |
|
ArrayCreateExpression parameterArray = invocation.Arguments.Last() as ArrayCreateExpression; |
|
if (parameterArray == null) |
|
return NotSupported(invocation); |
|
|
|
var annotation = body.Annotation<ParameterDeclarationAnnotation>(); |
|
if (annotation != null) { |
|
lambda.Parameters.AddRange(annotation.Parameters); |
|
} else { |
|
// No parameter declaration annotation found. |
|
if (!emptyArrayPattern.IsMatch(parameterArray)) |
|
return null; |
|
} |
|
|
|
activeLambdas.Push(lambda); |
|
Expression convertedBody = Convert(body); |
|
activeLambdas.Pop(); |
|
if (convertedBody == null) |
|
return null; |
|
lambda.Body = convertedBody; |
|
return lambda; |
|
} |
|
#endregion |
|
|
|
#region Convert Field |
|
static readonly Expression getFieldFromHandlePattern = |
|
new TypePattern(typeof(FieldInfo)).ToType().Invoke( |
|
"GetFieldFromHandle", |
|
new LdTokenPattern("field").ToExpression().Member("FieldHandle"), |
|
new OptionalNode(new TypeOfExpression(new AnyNode("declaringType")).Member("TypeHandle")) |
|
); |
|
|
|
Expression ConvertField(InvocationExpression invocation) |
|
{ |
|
if (invocation.Arguments.Count != 2) |
|
return NotSupported(invocation); |
|
|
|
Expression fieldInfoExpr = invocation.Arguments.ElementAt(1); |
|
Match m = getFieldFromHandlePattern.Match(fieldInfoExpr); |
|
if (!m.Success) |
|
return NotSupported(invocation); |
|
|
|
FieldReference fr = m.Get<AstNode>("field").Single().Annotation<FieldReference>(); |
|
if (fr == null) |
|
return null; |
|
|
|
Expression target = invocation.Arguments.ElementAt(0); |
|
Expression convertedTarget; |
|
if (target is NullReferenceExpression) { |
|
if (m.Has("declaringType")) |
|
convertedTarget = new TypeReferenceExpression(m.Get<AstType>("declaringType").Single().Clone()); |
|
else |
|
convertedTarget = new TypeReferenceExpression(AstBuilder.ConvertType(fr.DeclaringType)); |
|
} else { |
|
convertedTarget = Convert(target); |
|
if (convertedTarget == null) |
|
return null; |
|
} |
|
|
|
return convertedTarget.Member(fr.Name).WithAnnotation(fr); |
|
} |
|
#endregion |
|
|
|
#region Convert Property |
|
static readonly Expression getMethodFromHandlePattern = |
|
new TypePattern(typeof(MethodBase)).ToType().Invoke( |
|
"GetMethodFromHandle", |
|
new LdTokenPattern("method").ToExpression().Member("MethodHandle"), |
|
new OptionalNode(new TypeOfExpression(new AnyNode("declaringType")).Member("TypeHandle")) |
|
).CastTo(new TypePattern(typeof(MethodInfo))); |
|
|
|
Expression ConvertProperty(InvocationExpression invocation) |
|
{ |
|
if (invocation.Arguments.Count != 2) |
|
return NotSupported(invocation); |
|
|
|
Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(1)); |
|
if (!m.Success) |
|
return NotSupported(invocation); |
|
|
|
MethodReference mr = m.Get<AstNode>("method").Single().Annotation<MethodReference>(); |
|
if (mr == null) |
|
return null; |
|
|
|
Expression target = invocation.Arguments.ElementAt(0); |
|
Expression convertedTarget; |
|
if (target is NullReferenceExpression) { |
|
if (m.Has("declaringType")) |
|
convertedTarget = new TypeReferenceExpression(m.Get<AstType>("declaringType").Single().Clone()); |
|
else |
|
convertedTarget = new TypeReferenceExpression(AstBuilder.ConvertType(mr.DeclaringType)); |
|
} else { |
|
convertedTarget = Convert(target); |
|
if (convertedTarget == null) |
|
return null; |
|
} |
|
|
|
return convertedTarget.Member(GetPropertyName(mr)).WithAnnotation(mr); |
|
} |
|
|
|
string GetPropertyName(MethodReference accessor) |
|
{ |
|
string name = accessor.Name; |
|
if (name.StartsWith("get_", StringComparison.Ordinal) || name.StartsWith("set_", StringComparison.Ordinal)) |
|
name = name.Substring(4); |
|
return name; |
|
} |
|
#endregion |
|
|
|
#region Convert Call |
|
Expression ConvertCall(InvocationExpression invocation) |
|
{ |
|
if (invocation.Arguments.Count < 2) |
|
return NotSupported(invocation); |
|
|
|
Expression target; |
|
int firstArgumentPosition; |
|
|
|
Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(0)); |
|
if (m.Success) { |
|
target = null; |
|
firstArgumentPosition = 1; |
|
} else { |
|
m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(1)); |
|
if (!m.Success) |
|
return NotSupported(invocation); |
|
target = invocation.Arguments.ElementAt(0); |
|
firstArgumentPosition = 2; |
|
} |
|
|
|
MethodReference mr = m.Get<AstNode>("method").Single().Annotation<MethodReference>(); |
|
if (mr == null) |
|
return null; |
|
|
|
Expression convertedTarget; |
|
if (target == null || target is NullReferenceExpression) { |
|
// static method |
|
if (m.Has("declaringType")) |
|
convertedTarget = new TypeReferenceExpression(m.Get<AstType>("declaringType").Single().Clone()); |
|
else |
|
convertedTarget = new TypeReferenceExpression(AstBuilder.ConvertType(mr.DeclaringType)); |
|
} else { |
|
convertedTarget = Convert(target); |
|
if (convertedTarget == null) |
|
return null; |
|
} |
|
|
|
MemberReferenceExpression mre = convertedTarget.Member(mr.Name); |
|
GenericInstanceMethod gim = mr as GenericInstanceMethod; |
|
if (gim != null) { |
|
foreach (TypeReference tr in gim.GenericArguments) { |
|
mre.TypeArguments.Add(AstBuilder.ConvertType(tr)); |
|
} |
|
} |
|
IList<Expression> arguments = null; |
|
if (invocation.Arguments.Count == firstArgumentPosition + 1) { |
|
Expression argumentArray = invocation.Arguments.ElementAt(firstArgumentPosition); |
|
arguments = ConvertExpressionsArray(argumentArray); |
|
} |
|
if (arguments == null) { |
|
arguments = new List<Expression>(); |
|
foreach (Expression argument in invocation.Arguments.Skip(firstArgumentPosition)) { |
|
Expression convertedArgument = Convert(argument); |
|
if (convertedArgument == null) |
|
return null; |
|
arguments.Add(convertedArgument); |
|
} |
|
} |
|
MethodDefinition methodDef = mr.Resolve(); |
|
if (methodDef != null && methodDef.IsGetter) { |
|
PropertyDefinition indexer = AstMethodBodyBuilder.GetIndexer(methodDef); |
|
if (indexer != null) |
|
return new IndexerExpression(mre.Target.Detach(), arguments).WithAnnotation(indexer); |
|
} |
|
return new InvocationExpression(mre, arguments).WithAnnotation(mr); |
|
} |
|
|
|
Expression ConvertInvoke(InvocationExpression invocation) |
|
{ |
|
if (invocation.Arguments.Count != 2) |
|
return NotSupported(invocation); |
|
|
|
Expression convertedTarget = Convert(invocation.Arguments.ElementAt(0)); |
|
IList<Expression> convertedArguments = ConvertExpressionsArray(invocation.Arguments.ElementAt(1)); |
|
if (convertedTarget != null && convertedArguments != null) |
|
return new InvocationExpression(convertedTarget, convertedArguments); |
|
else |
|
return null; |
|
} |
|
#endregion |
|
|
|
#region Convert Binary Operator |
|
static readonly Pattern trueOrFalse = new Choice { |
|
new PrimitiveExpression(true), |
|
new PrimitiveExpression(false) |
|
}; |
|
|
|
Expression ConvertBinaryOperator(InvocationExpression invocation, BinaryOperatorType op, bool? isChecked = null) |
|
{ |
|
if (invocation.Arguments.Count < 2) |
|
return NotSupported(invocation); |
|
|
|
Expression left = Convert(invocation.Arguments.ElementAt(0)); |
|
if (left == null) |
|
return null; |
|
Expression right = Convert(invocation.Arguments.ElementAt(1)); |
|
if (right == null) |
|
return null; |
|
|
|
BinaryOperatorExpression boe = new BinaryOperatorExpression(left, op, right); |
|
if (isChecked != null) |
|
boe.AddAnnotation(isChecked.Value ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation); |
|
|
|
switch (invocation.Arguments.Count) { |
|
case 2: |
|
return boe; |
|
case 3: |
|
Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(2)); |
|
if (m.Success) |
|
return boe.WithAnnotation(m.Get<AstNode>("method").Single().Annotation<MethodReference>()); |
|
else |
|
return null; |
|
case 4: |
|
if (!trueOrFalse.IsMatch(invocation.Arguments.ElementAt(2))) |
|
return null; |
|
m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(3)); |
|
if (m.Success) |
|
return boe.WithAnnotation(m.Get<AstNode>("method").Single().Annotation<MethodReference>()); |
|
else |
|
return null; |
|
default: |
|
return NotSupported(invocation); |
|
} |
|
} |
|
#endregion |
|
|
|
#region Convert Assignment Operator |
|
Expression ConvertAssignmentOperator(InvocationExpression invocation, AssignmentOperatorType op, bool? isChecked = null) |
|
{ |
|
return NotSupported(invocation); |
|
} |
|
#endregion |
|
|
|
#region Convert Unary Operator |
|
Expression ConvertUnaryOperator(InvocationExpression invocation, UnaryOperatorType op, bool? isChecked = null) |
|
{ |
|
if (invocation.Arguments.Count < 1) |
|
return NotSupported(invocation); |
|
|
|
Expression expr = Convert(invocation.Arguments.ElementAt(0)); |
|
if (expr == null) |
|
return null; |
|
|
|
UnaryOperatorExpression uoe = new UnaryOperatorExpression(op, expr); |
|
if (isChecked != null) |
|
uoe.AddAnnotation(isChecked.Value ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation); |
|
|
|
switch (invocation.Arguments.Count) { |
|
case 1: |
|
return uoe; |
|
case 2: |
|
Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(1)); |
|
if (m.Success) |
|
return uoe.WithAnnotation(m.Get<AstNode>("method").Single().Annotation<MethodReference>()); |
|
else |
|
return null; |
|
default: |
|
return NotSupported(invocation); |
|
} |
|
} |
|
#endregion |
|
|
|
#region Convert Condition Operator |
|
Expression ConvertCondition(InvocationExpression invocation) |
|
{ |
|
if (invocation.Arguments.Count != 3) |
|
return NotSupported(invocation); |
|
|
|
Expression condition = Convert(invocation.Arguments.ElementAt(0)); |
|
Expression trueExpr = Convert(invocation.Arguments.ElementAt(1)); |
|
Expression falseExpr = Convert(invocation.Arguments.ElementAt(2)); |
|
if (condition != null && trueExpr != null && falseExpr != null) |
|
return new ConditionalExpression(condition, trueExpr, falseExpr); |
|
else |
|
return null; |
|
} |
|
#endregion |
|
|
|
#region Convert New Object |
|
static readonly Expression newObjectCtorPattern = new TypePattern(typeof(MethodBase)).ToType().Invoke |
|
( |
|
"GetMethodFromHandle", |
|
new LdTokenPattern("ctor").ToExpression().Member("MethodHandle"), |
|
new OptionalNode(new TypeOfExpression(new AnyNode("declaringType")).Member("TypeHandle")) |
|
).CastTo(new TypePattern(typeof(ConstructorInfo))); |
|
|
|
Expression ConvertNewObject(InvocationExpression invocation) |
|
{ |
|
if (invocation.Arguments.Count < 1 || invocation.Arguments.Count > 3) |
|
return NotSupported(invocation); |
|
|
|
Match m = newObjectCtorPattern.Match(invocation.Arguments.First()); |
|
if (!m.Success) |
|
return NotSupported(invocation); |
|
|
|
MethodReference ctor = m.Get<AstNode>("ctor").Single().Annotation<MethodReference>(); |
|
if (ctor == null) |
|
return null; |
|
|
|
AstType declaringTypeNode; |
|
TypeReference declaringType; |
|
if (m.Has("declaringType")) { |
|
declaringTypeNode = m.Get<AstType>("declaringType").Single().Clone(); |
|
declaringType = declaringTypeNode.Annotation<TypeReference>(); |
|
} else { |
|
declaringTypeNode = AstBuilder.ConvertType(ctor.DeclaringType); |
|
declaringType = ctor.DeclaringType; |
|
} |
|
if (declaringTypeNode == null) |
|
return null; |
|
|
|
ObjectCreateExpression oce = new ObjectCreateExpression(declaringTypeNode); |
|
if (invocation.Arguments.Count >= 2) { |
|
IList<Expression> arguments = ConvertExpressionsArray(invocation.Arguments.ElementAtOrDefault(1)); |
|
if (arguments == null) |
|
return null; |
|
oce.Arguments.AddRange(arguments); |
|
} |
|
if (invocation.Arguments.Count >= 3 && declaringType.IsAnonymousType()) { |
|
MethodDefinition resolvedCtor = ctor.Resolve(); |
|
if (resolvedCtor == null || resolvedCtor.Parameters.Count != oce.Arguments.Count) |
|
return null; |
|
AnonymousTypeCreateExpression atce = new AnonymousTypeCreateExpression(); |
|
var arguments = oce.Arguments.ToArray(); |
|
if (AstMethodBodyBuilder.CanInferAnonymousTypePropertyNamesFromArguments(arguments, resolvedCtor.Parameters)) { |
|
oce.Arguments.MoveTo(atce.Initializers); |
|
} else { |
|
for (int i = 0; i < resolvedCtor.Parameters.Count; i++) { |
|
atce.Initializers.Add( |
|
new NamedExpression { |
|
Name = resolvedCtor.Parameters[i].Name, |
|
Expression = arguments[i].Detach() |
|
}); |
|
} |
|
} |
|
return atce; |
|
} |
|
|
|
return oce; |
|
} |
|
#endregion |
|
|
|
#region Convert ListInit |
|
static readonly Pattern elementInitArrayPattern = ArrayInitializationPattern( |
|
typeof(System.Linq.Expressions.ElementInit), |
|
new TypePattern(typeof(System.Linq.Expressions.Expression)).ToType().Invoke("ElementInit", new AnyNode("methodInfos"), new AnyNode("addArgumentsArrays")) |
|
); |
|
|
|
Expression ConvertListInit(InvocationExpression invocation) |
|
{ |
|
if (invocation.Arguments.Count != 2) |
|
return NotSupported(invocation); |
|
ObjectCreateExpression oce = Convert(invocation.Arguments.ElementAt(0)) as ObjectCreateExpression; |
|
if (oce == null) |
|
return null; |
|
Expression elementsArray = invocation.Arguments.ElementAt(1); |
|
ArrayInitializerExpression initializer = ConvertElementInit(elementsArray); |
|
if (initializer != null) { |
|
oce.Initializer = initializer; |
|
return oce; |
|
} else { |
|
return null; |
|
} |
|
} |
|
|
|
ArrayInitializerExpression ConvertElementInit(Expression elementsArray) |
|
{ |
|
IList<Expression> elements = ConvertExpressionsArray(elementsArray); |
|
if (elements != null) { |
|
return new ArrayInitializerExpression(elements); |
|
} |
|
Match m = elementInitArrayPattern.Match(elementsArray); |
|
if (!m.Success) |
|
return null; |
|
ArrayInitializerExpression result = new ArrayInitializerExpression(); |
|
foreach (var elementInit in m.Get<Expression>("addArgumentsArrays")) { |
|
IList<Expression> arguments = ConvertExpressionsArray(elementInit); |
|
if (arguments == null) |
|
return null; |
|
result.Elements.Add(new ArrayInitializerExpression(arguments)); |
|
} |
|
return result; |
|
} |
|
#endregion |
|
|
|
#region Convert MemberInit |
|
Expression ConvertMemberInit(InvocationExpression invocation) |
|
{ |
|
if (invocation.Arguments.Count != 2) |
|
return NotSupported(invocation); |
|
ObjectCreateExpression oce = Convert(invocation.Arguments.ElementAt(0)) as ObjectCreateExpression; |
|
if (oce == null) |
|
return null; |
|
Expression elementsArray = invocation.Arguments.ElementAt(1); |
|
ArrayInitializerExpression bindings = ConvertMemberBindings(elementsArray); |
|
if (bindings == null) |
|
return null; |
|
oce.Initializer = bindings; |
|
return oce; |
|
} |
|
|
|
static readonly Pattern memberBindingArrayPattern = ArrayInitializationPattern(typeof(System.Linq.Expressions.MemberBinding), new AnyNode("binding")); |
|
static readonly INode expressionTypeReference = new TypeReferenceExpression(new TypePattern(typeof(System.Linq.Expressions.Expression))); |
|
|
|
ArrayInitializerExpression ConvertMemberBindings(Expression elementsArray) |
|
{ |
|
Match m = memberBindingArrayPattern.Match(elementsArray); |
|
if (!m.Success) |
|
return null; |
|
ArrayInitializerExpression result = new ArrayInitializerExpression(); |
|
foreach (var binding in m.Get<Expression>("binding")) { |
|
InvocationExpression bindingInvocation = binding as InvocationExpression; |
|
if (bindingInvocation == null || bindingInvocation.Arguments.Count != 2) |
|
return null; |
|
MemberReferenceExpression bindingMRE = bindingInvocation.Target as MemberReferenceExpression; |
|
if (bindingMRE == null || !expressionTypeReference.IsMatch(bindingMRE.Target)) |
|
return null; |
|
|
|
Expression bindingTarget = bindingInvocation.Arguments.ElementAt(0); |
|
Expression bindingValue = bindingInvocation.Arguments.ElementAt(1); |
|
|
|
string memberName; |
|
Match m2 = getMethodFromHandlePattern.Match(bindingTarget); |
|
if (m2.Success) { |
|
MethodReference setter = m2.Get<AstNode>("method").Single().Annotation<MethodReference>(); |
|
if (setter == null) |
|
return null; |
|
memberName = GetPropertyName(setter); |
|
} else { |
|
return null; |
|
} |
|
|
|
Expression convertedValue; |
|
switch (bindingMRE.MemberName) { |
|
case "Bind": |
|
convertedValue = Convert(bindingValue); |
|
break; |
|
case "MemberBind": |
|
convertedValue = ConvertMemberBindings(bindingValue); |
|
break; |
|
case "ListBind": |
|
convertedValue = ConvertElementInit(bindingValue); |
|
break; |
|
default: |
|
return null; |
|
} |
|
if (convertedValue == null) |
|
return null; |
|
result.Elements.Add(new NamedExpression(memberName, convertedValue)); |
|
} |
|
return result; |
|
} |
|
#endregion |
|
|
|
#region Convert Cast |
|
Expression ConvertCast(InvocationExpression invocation, bool isChecked) |
|
{ |
|
if (invocation.Arguments.Count < 2) |
|
return null; |
|
Expression converted = Convert(invocation.Arguments.ElementAt(0)); |
|
AstType type = ConvertTypeReference(invocation.Arguments.ElementAt(1)); |
|
if (converted != null && type != null) { |
|
CastExpression cast = converted.CastTo(type); |
|
cast.AddAnnotation(isChecked ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation); |
|
switch (invocation.Arguments.Count) { |
|
case 2: |
|
return cast; |
|
case 3: |
|
Match m = getMethodFromHandlePattern.Match(invocation.Arguments.ElementAt(2)); |
|
if (m.Success) |
|
return cast.WithAnnotation(m.Get<AstNode>("method").Single().Annotation<MethodReference>()); |
|
else |
|
return null; |
|
} |
|
} |
|
return null; |
|
} |
|
#endregion |
|
|
|
#region ConvertExpressionsArray |
|
static Pattern ArrayInitializationPattern(Type arrayElementType, INode elementPattern) |
|
{ |
|
return new Choice { |
|
new ArrayCreateExpression { |
|
Type = new TypePattern(arrayElementType), |
|
Arguments = { new PrimitiveExpression(0) } |
|
}, |
|
new ArrayCreateExpression { |
|
Type = new TypePattern(arrayElementType), |
|
AdditionalArraySpecifiers = { new ArraySpecifier() }, |
|
Initializer = new ArrayInitializerExpression { |
|
Elements = { new Repeat(elementPattern) } |
|
} |
|
} |
|
}; |
|
} |
|
|
|
static readonly Pattern expressionArrayPattern = ArrayInitializationPattern(typeof(System.Linq.Expressions.Expression), new AnyNode("elements")); |
|
|
|
IList<Expression> ConvertExpressionsArray(Expression arrayExpression) |
|
{ |
|
Match m = expressionArrayPattern.Match(arrayExpression); |
|
if (m.Success) { |
|
List<Expression> result = new List<Expression>(); |
|
foreach (Expression expr in m.Get<Expression>("elements")) { |
|
Expression converted = Convert(expr); |
|
if (converted == null) |
|
return null; |
|
result.Add(converted); |
|
} |
|
return result; |
|
} |
|
return null; |
|
} |
|
#endregion |
|
|
|
#region Convert TypeAs/TypeIs |
|
static readonly TypeOfPattern typeOfPattern = new TypeOfPattern("type"); |
|
|
|
AstType ConvertTypeReference(Expression typeOfExpression) |
|
{ |
|
Match m = typeOfPattern.Match(typeOfExpression); |
|
if (m.Success) |
|
return m.Get<AstType>("type").Single().Clone(); |
|
else |
|
return null; |
|
} |
|
|
|
Expression ConvertTypeAs(InvocationExpression invocation) |
|
{ |
|
if (invocation.Arguments.Count != 2) |
|
return null; |
|
Expression converted = Convert(invocation.Arguments.ElementAt(0)); |
|
AstType type = ConvertTypeReference(invocation.Arguments.ElementAt(1)); |
|
if (converted != null && type != null) |
|
return new AsExpression(converted, type); |
|
return null; |
|
} |
|
|
|
Expression ConvertTypeIs(InvocationExpression invocation) |
|
{ |
|
if (invocation.Arguments.Count != 2) |
|
return null; |
|
Expression converted = Convert(invocation.Arguments.ElementAt(0)); |
|
AstType type = ConvertTypeReference(invocation.Arguments.ElementAt(1)); |
|
if (converted != null && type != null) |
|
return new IsExpression { Expression = converted, Type = type }; |
|
return null; |
|
} |
|
#endregion |
|
|
|
#region Convert Array |
|
Expression ConvertArrayIndex(InvocationExpression invocation) |
|
{ |
|
if (invocation.Arguments.Count != 2) |
|
return NotSupported(invocation); |
|
|
|
Expression targetConverted = Convert(invocation.Arguments.First()); |
|
if (targetConverted == null) |
|
return null; |
|
|
|
Expression index = invocation.Arguments.ElementAt(1); |
|
Expression indexConverted = Convert(index); |
|
if (indexConverted != null) { |
|
return new IndexerExpression(targetConverted, indexConverted); |
|
} |
|
IList<Expression> indexesConverted = ConvertExpressionsArray(index); |
|
if (indexesConverted != null) { |
|
return new IndexerExpression(targetConverted, indexesConverted); |
|
} |
|
return null; |
|
} |
|
|
|
Expression ConvertArrayLength(InvocationExpression invocation) |
|
{ |
|
if (invocation.Arguments.Count != 1) |
|
return NotSupported(invocation); |
|
|
|
Expression targetConverted = Convert(invocation.Arguments.Single()); |
|
if (targetConverted != null) |
|
return targetConverted.Member("Length"); |
|
else |
|
return null; |
|
} |
|
|
|
Expression ConvertNewArrayInit(InvocationExpression invocation) |
|
{ |
|
if (invocation.Arguments.Count != 2) |
|
return NotSupported(invocation); |
|
|
|
AstType elementType = ConvertTypeReference(invocation.Arguments.ElementAt(0)); |
|
IList<Expression> elements = ConvertExpressionsArray(invocation.Arguments.ElementAt(1)); |
|
if (elementType != null && elements != null) { |
|
if (ContainsAnonymousType(elementType)) { |
|
elementType = null; |
|
} |
|
return new ArrayCreateExpression { |
|
Type = elementType, |
|
AdditionalArraySpecifiers = { new ArraySpecifier() }, |
|
Initializer = new ArrayInitializerExpression(elements) |
|
}; |
|
} |
|
return null; |
|
} |
|
|
|
Expression ConvertNewArrayBounds(InvocationExpression invocation) |
|
{ |
|
if (invocation.Arguments.Count != 2) |
|
return NotSupported(invocation); |
|
|
|
AstType elementType = ConvertTypeReference(invocation.Arguments.ElementAt(0)); |
|
IList<Expression> arguments = ConvertExpressionsArray(invocation.Arguments.ElementAt(1)); |
|
if (elementType != null && arguments != null) { |
|
if (ContainsAnonymousType(elementType)) { |
|
elementType = null; |
|
} |
|
ArrayCreateExpression ace = new ArrayCreateExpression(); |
|
ace.Type = elementType; |
|
ace.Arguments.AddRange(arguments); |
|
return ace; |
|
} |
|
return null; |
|
} |
|
|
|
bool ContainsAnonymousType(AstType type) |
|
{ |
|
foreach (AstType t in type.DescendantsAndSelf.OfType<AstType>()) { |
|
TypeReference tr = t.Annotation<TypeReference>(); |
|
if (tr != null && tr.IsAnonymousType()) |
|
return true; |
|
} |
|
return false; |
|
} |
|
#endregion |
|
} |
|
}
|
|
|