Dryad/LinqToDryad/ExpressionMatcher.cs

459 lines
16 KiB
C#
Raw Blame History

/*
Copyright (c) Microsoft Corporation
All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License
at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER
EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF
TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions and
limitations under the License.
*/
//
// <20> Microsoft Corporation. All rights reserved.
//
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.IO;
using System.Reflection;
using System.Linq;
using System.Linq.Expressions;
using System.Diagnostics;
namespace Microsoft.Research.DryadLinq
{
// This class implements an expression matcher. This is useful for
// many interesting static analysis. Again, it would be nice if this
// is a functionality provided by C# and LINQ. But unfortunately it
// is not.
internal class ExpressionMatcher
{
public static bool Match(Expression e1, Expression e2)
{
return Match(e1, e2, Substitution.Empty);
}
// return true if e1 subsumes e2 in terms of member access
internal static bool MemberAccessSubsumes(Expression e1, Expression e2)
{
if (Match(e1, e2)) return true;
Expression e = e2;
while (e is MemberExpression)
{
e = ((MemberExpression)e).Expression;
if (Match(e1, e)) return true;
}
return false;
}
internal static bool Match(Expression e1, Expression e2, Substitution subst)
{
if (!e1.Type.Equals(e2.Type)) return false;
if (e1 is BinaryExpression)
{
return ((e2 is BinaryExpression) &&
MatchBinary((BinaryExpression)e1, (BinaryExpression)e2, subst));
}
else if (e1 is ConditionalExpression)
{
return ((e2 is ConditionalExpression) &&
MatchConditional((ConditionalExpression)e1, (ConditionalExpression)e2, subst));
}
else if (e1 is ConstantExpression)
{
return ((e2 is ConstantExpression) &&
MatchConstant((ConstantExpression)e1, (ConstantExpression)e2, subst));
}
else if (e1 is InvocationExpression)
{
return ((e2 is InvocationExpression) &&
MatchInvocation((InvocationExpression)e1, (InvocationExpression)e2, subst));
}
else if (e1 is LambdaExpression)
{
return ((e2 is LambdaExpression) &&
MatchLambda((LambdaExpression)e1, (LambdaExpression)e2, subst));
}
else if (e1 is MemberExpression)
{
return ((e2 is MemberExpression) &&
MatchMember((MemberExpression)e1, (MemberExpression)e2, subst));
}
else if (e1 is MethodCallExpression)
{
return ((e2 is MethodCallExpression) &&
MatchMethodCall((MethodCallExpression)e1, (MethodCallExpression)e2, subst));
}
else if (e1 is NewExpression)
{
return ((e2 is NewExpression) &&
MatchNew((NewExpression)e1, (NewExpression)e2, subst));
}
else if (e1 is NewArrayExpression)
{
return ((e2 is NewArrayExpression) &&
MatchNewArray((NewArrayExpression)e1, (NewArrayExpression)e2, subst));
}
else if (e1 is MemberInitExpression)
{
return ((e2 is MemberInitExpression) &&
MatchMemberInit((MemberInitExpression)e1, (MemberInitExpression)e2, subst));
}
else if (e1 is ListInitExpression)
{
return ((e2 is ListInitExpression) &&
MatchListInit((ListInitExpression)e1, (ListInitExpression)e2, subst));
}
else if (e1 is ParameterExpression)
{
return ((e2 is ParameterExpression) &&
MatchParameter((ParameterExpression)e1, (ParameterExpression)e2, subst));
}
else if (e1 is TypeBinaryExpression)
{
return ((e2 is TypeBinaryExpression) &&
MatchTypeBinary((TypeBinaryExpression)e1, (TypeBinaryExpression)e2, subst));
}
else if (e1 is UnaryExpression)
{
return ((e2 is UnaryExpression) &&
MatchUnary((UnaryExpression)e1, (UnaryExpression)e2, subst));
}
throw new DryadLinqException(HpcLinqErrorCode.ExpressionTypeNotHandled,
String.Format(SR.ExpressionTypeNotHandled,
"ExpressionMatcher", e1.NodeType));
}
private static bool MatchInvocation(InvocationExpression e1,
InvocationExpression e2,
Substitution subst)
{
ReadOnlyCollection<Expression> args1 = e1.Arguments;
ReadOnlyCollection<Expression> args2 = e2.Arguments;
if (!Match(e1.Expression, e2.Expression, subst) || args1.Count != args2.Count)
{
return false;
}
for (int i = 0; i < args1.Count; i++)
{
if (!Match(args1[i], args2[i], subst))
{
return false;
}
}
return true;
}
private static bool MatchBinary(BinaryExpression e1,
BinaryExpression e2,
Substitution subst)
{
return (e1.NodeType == e2.NodeType &&
Match(e1.Left, e2.Left, subst) &&
Match(e1.Right, e2.Right, subst));
}
private static bool MatchConditional(ConditionalExpression e1,
ConditionalExpression e2,
Substitution subst)
{
return (Match(e1.Test, e2.Test, subst) &&
Match(e1.IfTrue, e2.IfTrue, subst) &&
Match(e1.IfFalse, e2.IfFalse, subst));
}
private static bool MatchConstant(ConstantExpression e1,
ConstantExpression e2,
Substitution subst)
{
if (e1.Value == null)
{
return (e2.Value == null);
}
else
{
return e1.Value.Equals(e2.Value);
}
}
private static bool MatchLambda(LambdaExpression e1,
LambdaExpression e2,
Substitution subst)
{
if (e1.Parameters.Count != e2.Parameters.Count)
{
return false;
}
Substitution subst1 = subst;
for (int i = 0, n = e1.Parameters.Count; i < n; i++)
{
if (!e1.Parameters[i].Equals(e2.Parameters[i]))
{
subst1 = subst1.Cons(e1.Parameters[i], e2.Parameters[i]);
}
}
return Match(e1.Body, e2.Body, subst1);
}
private static bool MatchMember(MemberExpression e1,
MemberExpression e2,
Substitution subst)
{
if (e1.Expression == null)
{
if (e2.Expression != null) return false;
}
else
{
if (e2.Expression == null ||
!Match(e1.Expression, e2.Expression, subst))
{
return false;
}
}
return e1.Member.Equals(e2.Member);
}
private static bool MatchMethodCall(MethodCallExpression e1,
MethodCallExpression e2,
Substitution subst)
{
if (e1.Method != e2.Method) return false;
if (e1.Object == null || e2.Object == null)
{
if (e1.Object != e2.Object) return false;
}
else if (!Match(e1.Object, e2.Object, subst) ||
e1.Arguments.Count != e2.Arguments.Count)
{
return false;
}
for (int i = 0, n = e1.Arguments.Count; i < n; i++)
{
if (!Match(e1.Arguments[i], e2.Arguments[i], subst))
{
return false;
}
}
return true;
}
private static bool MatchNew(NewExpression e1, NewExpression e2, Substitution subst)
{
if (e1.Arguments.Count != e2.Arguments.Count)
{
return false;
}
for (int i = 0, n = e1.Arguments.Count; i < n; i++)
{
if (!Match(e1.Arguments[i], e2.Arguments[i], subst))
{
return false;
}
}
return true;
}
public static bool MatchNewArray(NewArrayExpression e1,
NewArrayExpression e2,
Substitution subst)
{
if (e1.NodeType != e2.NodeType ||
e1.Expressions.Count != e2.Expressions.Count)
{
return false;
}
for (int i = 0, n = e1.Expressions.Count; i < n; i++)
{
if (!Match(e1.Expressions[i], e2.Expressions[i], subst))
{
return false;
}
}
return true;
}
public static bool MatchMemberInit(MemberInitExpression e1,
MemberInitExpression e2,
Substitution subst)
{
if (!Match(e1.NewExpression, e2.NewExpression, subst) ||
e1.Bindings.Count != e2.Bindings.Count)
{
return false;
}
for (int i = 0, n = e1.Bindings.Count; i < n; i++)
{
if (!MatchMemberBinding(e1.Bindings[i], e2.Bindings[i], subst))
{
return false;
}
}
return true;
}
public static bool MatchListInit(ListInitExpression e1,
ListInitExpression e2,
Substitution subst)
{
if (!Match(e1.NewExpression, e2.NewExpression, subst))
{
return false;
}
if (e1.Initializers.Count != e2.Initializers.Count)
{
return false;
}
for (int i = 0, n = e1.Initializers.Count; i < n; i++)
{
ElementInit init1 = e1.Initializers[i];
ElementInit init2 = e2.Initializers[i];
if (!MatchElementInit(init1, init2, subst))
{
return false;
}
}
return true;
}
public static bool MatchParameter(ParameterExpression e1,
ParameterExpression e2,
Substitution subst)
{
if (e1.Equals(e2)) return true;
ParameterExpression e = subst.Find(e1);
return (e != null && e.Equals(e2));
}
public static bool MatchTypeBinary(TypeBinaryExpression e1,
TypeBinaryExpression e2,
Substitution subst)
{
return (e1.NodeType == ExpressionType.TypeIs &&
e2.NodeType == ExpressionType.TypeIs &&
e1.TypeOperand.Equals(e2.TypeOperand) &&
Match(e1.Expression, e2.Expression, subst));
}
public static bool MatchUnary(UnaryExpression e1, UnaryExpression e2, Substitution subst)
{
return (e1.NodeType == e2.NodeType &&
Match(e1.Operand, e2.Operand, subst));
}
private static bool MatchMemberBinding(MemberBinding b1, MemberBinding b2, Substitution subst)
{
if (b1.BindingType != b2.BindingType ||
!b1.Member.Equals(b2.Member))
{
return false;
}
if (b1 is MemberAssignment)
{
return Match(((MemberAssignment)b1).Expression,
((MemberAssignment)b2).Expression,
subst);
}
else if (b1 is MemberMemberBinding)
{
MemberMemberBinding mmb1 = (MemberMemberBinding)b1;
MemberMemberBinding mmb2 = (MemberMemberBinding)b2;
if (mmb1.Bindings.Count != mmb2.Bindings.Count)
{
return false;
}
for (int i = 0, n = mmb1.Bindings.Count; i < n; i++)
{
if (!MatchMemberBinding(mmb1.Bindings[i], mmb2.Bindings[i], subst))
{
return false;
}
}
return true;
}
else
{
MemberListBinding mlb1 = (MemberListBinding)b1;
MemberListBinding mlb2 = (MemberListBinding)b2;
if (mlb1.Initializers.Count != mlb2.Initializers.Count)
{
return false;
}
for (int i = 0, n = mlb1.Initializers.Count; i < n; i++)
{
if (!MatchElementInit(mlb1.Initializers[i], mlb2.Initializers[i], subst))
{
return false;
}
}
return true;
}
}
private static bool MatchElementInit(ElementInit init1,
ElementInit init2,
Substitution subst)
{
if (init1.AddMethod != init2.AddMethod ||
init1.Arguments.Count != init2.Arguments.Count)
{
return false;
}
for (int i = 0; i < init1.Arguments.Count; i++)
{
if (!Match(init1.Arguments[i], init2.Arguments[i], subst))
{
return false;
}
}
return true;
}
}
internal class Substitution
{
private ParameterExpression x;
private ParameterExpression y;
private Substitution next;
public static Substitution Empty = new Substitution(null, null, null);
private Substitution(ParameterExpression x, ParameterExpression y, Substitution s)
{
this.x = x;
this.y = y;
this.next = s;
}
public Substitution Cons(ParameterExpression a, ParameterExpression b)
{
return new Substitution(a, b, this);
}
public ParameterExpression Find(ParameterExpression a)
{
Substitution curSubst = this;
while (curSubst != Empty)
{
if (curSubst.x.Equals(a)) return y;
curSubst = curSubst.next;
}
return null;
}
}
}