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