mirror of https://github.com/icsharpcode/ILSpy.git
17 changed files with 1470 additions and 87 deletions
@ -0,0 +1,109 @@
@@ -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(...)"; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,695 @@
@@ -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
|
||||
} |
||||
} |
@ -0,0 +1,370 @@
@@ -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>()); |
||||
} |
||||
} |
Loading…
Reference in new issue