public void Run(AstNode compilationUnit)
 {
     if (!context.Settings.QueryExpressions)
     {
         return;
     }
     DecompileQueries(compilationUnit);
     // After all queries were decompiled, detect degenerate queries (queries not property terminated with 'select' or 'group')
     // and fix them, either by adding a degenerate select, or by combining them with another query.
     foreach (QueryExpression query in compilationUnit.Descendants.OfType <QueryExpression>())
     {
         QueryFromClause fromClause = (QueryFromClause)query.Clauses.First();
         if (IsDegenerateQuery(query))
         {
             // introduce select for degenerate query
             query.Clauses.Add(new QuerySelectClause {
                 Expression = IdentifierExpression.Create(fromClause.Identifier, fromClause.IdentifierToken.Annotation <object>())
             });
         }
         // See if the data source of this query is a degenerate query,
         // and combine the queries if possible.
         QueryExpression innerQuery = fromClause.Expression as QueryExpression;
         while (IsDegenerateQuery(innerQuery))
         {
             QueryFromClause innerFromClause = (QueryFromClause)innerQuery.Clauses.First();
             if (fromClause.Identifier != innerFromClause.Identifier)
             {
                 break;
             }
             // Replace the fromClause with all clauses from the inner query
             fromClause.Remove();
             QueryClause insertionPos = null;
             foreach (var clause in innerQuery.Clauses)
             {
                 query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach());
             }
             fromClause = innerFromClause;
             innerQuery = fromClause.Expression as QueryExpression;
         }
     }
 }
        /// <summary>
        /// Removes all occurrences of transparent identifiers
        /// </summary>
        void RemoveTransparentIdentifierReferences(AstNode node)
        {
            foreach (AstNode child in node.Children)
            {
                RemoveTransparentIdentifierReferences(child);
            }
            MemberReferenceExpression mre = node as MemberReferenceExpression;

            if (mre != null)
            {
                IdentifierExpression ident = mre.Target as IdentifierExpression;
                if (ident != null && IsTransparentIdentifier(ident.Identifier))
                {
                    IdentifierExpression newIdent = IdentifierExpression.Create(mre.MemberName, mre.MemberNameToken.Annotation <object>());
                    mre.TypeArguments.MoveTo(newIdent.TypeArguments);
                    newIdent.CopyAnnotationsFrom(mre);
                    newIdent.RemoveAnnotations <PropertyDeclaration>();                    // remove the reference to the property of the anonymous type
                    mre.ReplaceWith(newIdent);
                    return;
                }
            }
        }
Esempio n. 3
0
        Expression Convert(Expression expr)
        {
            InvocationExpression invocation = expr as InvocationExpression;

            if (invocation != null)
            {
                IMethod mr = invocation.Annotation <IMethod>();
                if (mr != null && mr.DeclaringType != 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(IdentifierExpression.Create(p.Name, v.IsParameter ? TextTokenKind.Parameter : TextTokenKind.Local).WithAnnotation(v));
                            }
                        }
                    }
                }
            }
            return(NotSupported(expr));
        }
Esempio n. 4
0
        public override object VisitBlockStatement(BlockStatement blockStatement, object data)
        {
            int numberOfVariablesOutsideBlock = currentlyUsedVariableNames.Count;

            base.VisitBlockStatement(blockStatement, data);
            foreach (ExpressionStatement stmt in blockStatement.Statements.OfType <ExpressionStatement>().ToArray())
            {
                Match displayClassAssignmentMatch = displayClassAssignmentPattern.Match(stmt);
                if (!displayClassAssignmentMatch.Success)
                {
                    continue;
                }

                ILVariable variable = displayClassAssignmentMatch.Get <AstNode>("variable").Single().Annotation <ILVariable>();
                if (variable == null)
                {
                    continue;
                }
                TypeDef type = variable.Type.ToTypeDefOrRef().ResolveWithinSameModule();
                if (!IsPotentialClosure(context, type))
                {
                    continue;
                }
                if (displayClassAssignmentMatch.Get <AstType>("type").Single().Annotation <ITypeDefOrRef>().ResolveWithinSameModule() != type)
                {
                    continue;
                }

                // Looks like we found a display class creation. Now let's verify that the variable is used only for field accesses:
                bool ok = true;
                foreach (var identExpr in blockStatement.Descendants.OfType <IdentifierExpression>())
                {
                    if (identExpr.Identifier == variable.Name && identExpr != displayClassAssignmentMatch.Get("variable").Single())
                    {
                        if (!(identExpr.Parent is MemberReferenceExpression && identExpr.Parent.Annotation <IField>() != null && identExpr.Parent.Annotation <IField>().IsField))
                        {
                            ok = false;
                        }
                    }
                }
                if (!ok)
                {
                    continue;
                }
                Dictionary <IField, AstNode> dict = new Dictionary <IField, AstNode>();

                // Delete the variable declaration statement:
                VariableDeclarationStatement displayClassVarDecl = PatternStatementTransform.FindVariableDeclaration(stmt, variable.Name);
                if (displayClassVarDecl != null)
                {
                    displayClassVarDecl.Remove();                    //TODO: Save ILRanges
                }
                // Delete the assignment statement:
                AstNode cur = stmt.NextSibling;
                stmt.Remove();                //TODO: Save ILRanges

                // Delete any following statements as long as they assign parameters to the display class
                BlockStatement    rootBlock            = blockStatement.Ancestors.OfType <BlockStatement>().LastOrDefault() ?? blockStatement;
                List <ILVariable> parameterOccurrances = rootBlock.Descendants.OfType <IdentifierExpression>()
                                                         .Select(n => n.Annotation <ILVariable>()).Where(p => p != null && p.IsParameter).ToList();
                AstNode next;
                for (; cur != null; cur = next)
                {
                    next = cur.NextSibling;

                    // Test for the pattern:
                    // "variableName.MemberName = right;"
                    ExpressionStatement closureFieldAssignmentPattern = new ExpressionStatement(
                        new AssignmentExpression(
                            new NamedNode("left", new MemberReferenceExpression {
                        Target     = IdentifierExpression.Create(variable.Name, variable.IsParameter ? TextTokenType.Parameter : TextTokenType.Local),
                        MemberName = Pattern.AnyString
                    }),
                            new AnyNode("right")
                            )
                        );
                    Match m = closureFieldAssignmentPattern.Match(cur);
                    if (m.Success)
                    {
                        FieldDef fieldDef    = m.Get <MemberReferenceExpression>("left").Single().Annotation <IField>().ResolveFieldWithinSameModule();
                        AstNode  right       = m.Get <AstNode>("right").Single();
                        bool     isParameter = false;
                        bool     isDisplayClassParentPointerAssignment = false;
                        if (right is ThisReferenceExpression)
                        {
                            isParameter = true;
                        }
                        else if (right is IdentifierExpression)
                        {
                            // handle parameters only if the whole method contains no other occurrence except for 'right'
                            ILVariable v = right.Annotation <ILVariable>();
                            isParameter = v.IsParameter && parameterOccurrances.Count(c => c == v) == 1;
                            if (!isParameter && IsPotentialClosure(context, v.Type.ToTypeDefOrRef().ResolveWithinSameModule()))
                            {
                                // parent display class within the same method
                                // (closure2.localsX = closure1;)
                                isDisplayClassParentPointerAssignment = true;
                            }
                        }
                        else if (right is MemberReferenceExpression)
                        {
                            // copy of parent display class reference from an outer lambda
                            // closure2.localsX = this.localsY
                            MemberReferenceExpression mre = m.Get <MemberReferenceExpression>("right").Single();
                            do
                            {
                                // descend into the targets of the mre as long as the field types are closures
                                FieldDef fieldDef2 = mre.Annotation <FieldDef>().ResolveFieldWithinSameModule();
                                if (fieldDef2 == null || !IsPotentialClosure(context, fieldDef2.FieldType.ToTypeDefOrRef().ResolveWithinSameModule()))
                                {
                                    break;
                                }
                                // if we finally get to a this reference, it's copying a display class parent pointer
                                if (mre.Target is ThisReferenceExpression)
                                {
                                    isDisplayClassParentPointerAssignment = true;
                                }
                                mre = mre.Target as MemberReferenceExpression;
                            } while (mre != null);
                        }
                        if (isParameter || isDisplayClassParentPointerAssignment)
                        {
                            if (fieldDef != null)
                            {
                                dict[fieldDef] = right;
                            }
                            cur.Remove();                            //TODO: Save ILRanges
                        }
                        else
                        {
                            break;
                        }
                    }
                    else
                    {
                        break;
                    }
                }

                // Now create variables for all fields of the display class (except for those that we already handled as parameters)
                List <Tuple <AstType, ILVariable> > variablesToDeclare = new List <Tuple <AstType, ILVariable> >();
                foreach (FieldDef field in type.Fields)
                {
                    if (field.IsStatic)
                    {
                        continue;                         // skip static fields
                    }
                    if (dict.ContainsKey(field))          // skip field if it already was handled as parameter
                    {
                        continue;
                    }
                    string capturedVariableName = field.Name;
                    if (capturedVariableName.StartsWith("$VB$Local_", StringComparison.Ordinal) && capturedVariableName.Length > 10)
                    {
                        capturedVariableName = capturedVariableName.Substring(10);
                    }
                    EnsureVariableNameIsAvailable(blockStatement, capturedVariableName);
                    currentlyUsedVariableNames.Add(capturedVariableName);
                    ILVariable ilVar = new ILVariable
                    {
                        IsGenerated = true,
                        Name        = capturedVariableName,
                        Type        = field.FieldType,
                    };
                    variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), ilVar));
                    dict[field] = IdentifierExpression.Create(capturedVariableName, TextTokenType.Local).WithAnnotation(ilVar);
                }

                // Now figure out where the closure was accessed and use the simpler replacement expression there:
                foreach (var identExpr in blockStatement.Descendants.OfType <IdentifierExpression>())
                {
                    if (identExpr.Identifier == variable.Name)
                    {
                        MemberReferenceExpression mre = (MemberReferenceExpression)identExpr.Parent;
                        AstNode replacement;
                        var     fieldDef = mre.Annotation <IField>().ResolveFieldWithinSameModule();
                        if (fieldDef != null && dict.TryGetValue(fieldDef, out replacement))
                        {
                            var newReplacement = replacement.Clone();
                            newReplacement.AddAnnotation(mre.GetAllRecursiveILRanges());
                            mre.ReplaceWith(newReplacement);
                        }
                    }
                }
                // Now insert the variable declarations (we can do this after the replacements only so that the scope detection works):
                Statement insertionPoint = blockStatement.Statements.FirstOrDefault();
                foreach (var tuple in variablesToDeclare)
                {
                    var newVarDecl = new VariableDeclarationStatement(tuple.Item2.IsParameter ? TextTokenType.Parameter : TextTokenType.Local, tuple.Item1, tuple.Item2.Name);
                    newVarDecl.Variables.Single().AddAnnotation(new CapturedVariableAnnotation());
                    newVarDecl.Variables.Single().AddAnnotation(tuple.Item2);
                    blockStatement.Statements.InsertBefore(insertionPoint, newVarDecl);
                }
            }
            currentlyUsedVariableNames.RemoveRange(numberOfVariablesOutsideBlock, currentlyUsedVariableNames.Count - numberOfVariablesOutsideBlock);
            return(null);
        }
        QueryExpression DecompileQuery(InvocationExpression invocation)
        {
            if (invocation == null)
            {
                return(null);
            }
            MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression;

            if (mre == null)
            {
                return(null);
            }
            switch (mre.MemberName)
            {
            case "Select":
            {
                if (invocation.Arguments.Count != 1)
                {
                    return(null);
                }
                string     parameterName;
                Expression body;
                if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out body))
                {
                    QueryExpression query = new QueryExpression();
                    query.Clauses.Add(new QueryFromClause {
                            IdentifierToken = Identifier.Create(parameterName).WithAnnotation(TextTokenKind.Parameter), Expression = mre.Target.Detach()
                        });
                    query.Clauses.Add(new QuerySelectClause {
                            Expression = body.Detach()
                        });
                    return(query);
                }
                return(null);
            }

            case "GroupBy":
            {
                if (invocation.Arguments.Count == 2)
                {
                    string     parameterName1, parameterName2;
                    Expression keySelector, elementSelector;
                    if (MatchSimpleLambda(invocation.Arguments.ElementAt(0), out parameterName1, out keySelector) &&
                        MatchSimpleLambda(invocation.Arguments.ElementAt(1), out parameterName2, out elementSelector) &&
                        parameterName1 == parameterName2)
                    {
                        QueryExpression query = new QueryExpression();
                        query.Clauses.Add(new QueryFromClause {
                                IdentifierToken = Identifier.Create(parameterName1).WithAnnotation(TextTokenKind.Parameter), Expression = mre.Target.Detach()
                            });
                        query.Clauses.Add(new QueryGroupClause {
                                Projection = elementSelector.Detach(), Key = keySelector.Detach()
                            });
                        return(query);
                    }
                }
                else if (invocation.Arguments.Count == 1)
                {
                    string     parameterName;
                    Expression keySelector;
                    if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out keySelector))
                    {
                        QueryExpression query = new QueryExpression();
                        query.Clauses.Add(new QueryFromClause {
                                IdentifierToken = Identifier.Create(parameterName).WithAnnotation(TextTokenKind.Parameter), Expression = mre.Target.Detach()
                            });
                        query.Clauses.Add(new QueryGroupClause {
                                Projection = IdentifierExpression.Create(parameterName, TextTokenKind.Parameter), Key = keySelector.Detach()
                            });
                        return(query);
                    }
                }
                return(null);
            }

            case "SelectMany":
            {
                if (invocation.Arguments.Count != 2)
                {
                    return(null);
                }
                string     parameterName;
                Expression collectionSelector;
                if (!MatchSimpleLambda(invocation.Arguments.ElementAt(0), out parameterName, out collectionSelector))
                {
                    return(null);
                }
                LambdaExpression lambda = invocation.Arguments.ElementAt(1) as LambdaExpression;
                if (lambda != null && lambda.Parameters.Count == 2 && lambda.Body is Expression)
                {
                    ParameterDeclaration p1 = lambda.Parameters.ElementAt(0);
                    ParameterDeclaration p2 = lambda.Parameters.ElementAt(1);
                    if (p1.Name == parameterName)
                    {
                        QueryExpression query = new QueryExpression();
                        query.Clauses.Add(new QueryFromClause {
                                IdentifierToken = Identifier.Create(p1.Name).WithAnnotation(TextTokenKind.Parameter), Expression = mre.Target.Detach()
                            });
                        query.Clauses.Add(new QueryFromClause {
                                IdentifierToken = Identifier.Create(p2.Name).WithAnnotation(TextTokenKind.Parameter), Expression = collectionSelector.Detach()
                            });
                        query.Clauses.Add(new QuerySelectClause {
                                Expression = ((Expression)lambda.Body).Detach()
                            });
                        return(query);
                    }
                }
                return(null);
            }

            case "Where":
            {
                if (invocation.Arguments.Count != 1)
                {
                    return(null);
                }
                string     parameterName;
                Expression body;
                if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out body))
                {
                    QueryExpression query = new QueryExpression();
                    query.Clauses.Add(new QueryFromClause {
                            IdentifierToken = Identifier.Create(parameterName).WithAnnotation(TextTokenKind.Parameter), Expression = mre.Target.Detach()
                        });
                    query.Clauses.Add(new QueryWhereClause {
                            Condition = body.Detach()
                        });
                    return(query);
                }
                return(null);
            }

            case "OrderBy":
            case "OrderByDescending":
            case "ThenBy":
            case "ThenByDescending":
            {
                if (invocation.Arguments.Count != 1)
                {
                    return(null);
                }
                string     parameterName;
                Expression orderExpression;
                if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out orderExpression))
                {
                    if (ValidateThenByChain(invocation, parameterName))
                    {
                        QueryOrderClause     orderClause = new QueryOrderClause();
                        InvocationExpression tmp         = invocation;
                        while (mre.MemberName == "ThenBy" || mre.MemberName == "ThenByDescending")
                        {
                            // insert new ordering at beginning
                            orderClause.Orderings.InsertAfter(
                                null, new QueryOrdering {
                                    Expression = orderExpression.Detach(),
                                    Direction  = (mre.MemberName == "ThenBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending)
                                });

                            tmp = (InvocationExpression)mre.Target;
                            mre = (MemberReferenceExpression)tmp.Target;
                            MatchSimpleLambda(tmp.Arguments.Single(), out parameterName, out orderExpression);
                        }
                        // insert new ordering at beginning
                        orderClause.Orderings.InsertAfter(
                            null, new QueryOrdering {
                                Expression = orderExpression.Detach(),
                                Direction  = (mre.MemberName == "OrderBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending)
                            });

                        QueryExpression query = new QueryExpression();
                        query.Clauses.Add(new QueryFromClause {
                                IdentifierToken = Identifier.Create(parameterName).WithAnnotation(TextTokenKind.Parameter), Expression = mre.Target.Detach()
                            });
                        query.Clauses.Add(orderClause);
                        return(query);
                    }
                }
                return(null);
            }

            case "Join":
            case "GroupJoin":
            {
                if (invocation.Arguments.Count != 4)
                {
                    return(null);
                }
                Expression source1 = mre.Target;
                Expression source2 = invocation.Arguments.ElementAt(0);
                string     elementName1, elementName2;
                Expression key1, key2;
                if (!MatchSimpleLambda(invocation.Arguments.ElementAt(1), out elementName1, out key1))
                {
                    return(null);
                }
                if (!MatchSimpleLambda(invocation.Arguments.ElementAt(2), out elementName2, out key2))
                {
                    return(null);
                }
                LambdaExpression lambda = invocation.Arguments.ElementAt(3) as LambdaExpression;
                if (lambda != null && lambda.Parameters.Count == 2 && lambda.Body is Expression)
                {
                    ParameterDeclaration p1 = lambda.Parameters.ElementAt(0);
                    ParameterDeclaration p2 = lambda.Parameters.ElementAt(1);
                    if (p1.Name == elementName1 && (p2.Name == elementName2 || mre.MemberName == "GroupJoin"))
                    {
                        QueryExpression query = new QueryExpression();
                        query.Clauses.Add(new QueryFromClause {
                                IdentifierToken = Identifier.Create(elementName1).WithAnnotation(TextTokenKind.Parameter), Expression = source1.Detach()
                            });
                        QueryJoinClause joinClause = new QueryJoinClause();
                        joinClause.JoinIdentifierToken = Identifier.Create(elementName2).WithAnnotation(TextTokenKind.Parameter); // join elementName2
                        joinClause.InExpression        = source2.Detach();                                                        // in source2
                        joinClause.OnExpression        = key1.Detach();                                                           // on key1
                        joinClause.EqualsExpression    = key2.Detach();                                                           // equals key2
                        if (mre.MemberName == "GroupJoin")
                        {
                            joinClause.IntoIdentifierToken = Identifier.Create(p2.Name).WithAnnotation(TextTokenKind.Parameter);                                             // into p2.Name
                        }
                        query.Clauses.Add(joinClause);
                        query.Clauses.Add(new QuerySelectClause {
                                Expression = ((Expression)lambda.Body).Detach()
                            });
                        return(query);
                    }
                }
                return(null);
            }

            default:
                return(null);
            }
        }