Browse Source

Fix #1482: SequencePointBuilder fails with an assertion when trying to create sequence points for LINQ expressions

pull/1937/head
Siegfried Pammer 5 years ago
parent
commit
c2a2cf43f2
  1. 30
      ICSharpCode.Decompiler/CSharp/Annotations.cs
  2. 42
      ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs
  3. 21
      ICSharpCode.Decompiler/CSharp/Transforms/CombineQueryExpressions.cs
  4. 38
      ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs
  5. 47
      ICSharpCode.Decompiler/DebugInfo/DebugInfoGenerator.cs

30
ICSharpCode.Decompiler/CSharp/Annotations.cs

@ -289,4 +289,34 @@ namespace ICSharpCode.Decompiler.CSharp @@ -289,4 +289,34 @@ namespace ICSharpCode.Decompiler.CSharp
this.ConversionResolveResult = conversionResolveResult;
}
}
/// <summary>
/// Annotates a QueryGroupClause with the ILFunctions of each (implicit lambda) expression.
/// </summary>
public class QueryGroupClauseAnnotation
{
public readonly ILFunction KeyLambda;
public readonly ILFunction ProjectionLambda;
public QueryGroupClauseAnnotation(ILFunction key, ILFunction projection)
{
this.KeyLambda = key;
this.ProjectionLambda = projection;
}
}
/// <summary>
/// Annotates a QueryJoinClause with the ILFunctions of each (implicit lambda) expression.
/// </summary>
public class QueryJoinClauseAnnotation
{
public readonly ILFunction OnLambda;
public readonly ILFunction EqualsLambda;
public QueryJoinClauseAnnotation(ILFunction on, ILFunction equals)
{
this.OnLambda = on;
this.EqualsLambda = equals;
}
}
}

42
ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs

@ -160,6 +160,48 @@ namespace ICSharpCode.Decompiler.CSharp @@ -160,6 +160,48 @@ namespace ICSharpCode.Decompiler.CSharp
VisitAsSequencePoint(lambdaExpression.Body);
}
public override void VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause)
{
AddToSequencePoint(queryContinuationClause);
VisitAsSequencePoint(queryContinuationClause.PrecedingQuery);
}
public override void VisitQueryFromClause(QueryFromClause queryFromClause)
{
if (queryFromClause.Parent.FirstChild != queryFromClause) {
AddToSequencePoint(queryFromClause);
VisitAsSequencePoint(queryFromClause.Expression);
} else {
base.VisitQueryFromClause(queryFromClause);
}
}
public override void VisitQueryGroupClause(QueryGroupClause queryGroupClause)
{
AddToSequencePoint(queryGroupClause);
VisitAsSequencePoint(queryGroupClause.Projection);
VisitAsSequencePoint(queryGroupClause.Key);
}
public override void VisitQueryJoinClause(QueryJoinClause queryJoinClause)
{
AddToSequencePoint(queryJoinClause);
VisitAsSequencePoint(queryJoinClause.OnExpression);
VisitAsSequencePoint(queryJoinClause.EqualsExpression);
}
public override void VisitQueryLetClause(QueryLetClause queryLetClause)
{
AddToSequencePoint(queryLetClause);
VisitAsSequencePoint(queryLetClause.Expression);
}
public override void VisitQueryOrdering(QueryOrdering queryOrdering)
{
AddToSequencePoint(queryOrdering);
VisitAsSequencePoint(queryOrdering.Expression);
}
public override void VisitQuerySelectClause(QuerySelectClause querySelectClause)
{
AddToSequencePoint(querySelectClause);

21
ICSharpCode.Decompiler/CSharp/Transforms/CombineQueryExpressions.cs

@ -43,25 +43,26 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -43,25 +43,26 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
TypeArguments = { new AnyNode("targetType") }
}};
void CombineQueries(AstNode node, Dictionary<string, object> letIdentifiers)
void CombineQueries(AstNode node, Dictionary<string, object> fromOrLetIdentifiers)
{
AstNode next;
for (AstNode child = node.FirstChild; child != null; child = next) {
// store referece to next child before transformation
// store reference to next child before transformation
next = child.NextSibling;
CombineQueries(child, letIdentifiers);
CombineQueries(child, fromOrLetIdentifiers);
}
QueryExpression query = node as QueryExpression;
if (query != null) {
QueryFromClause fromClause = (QueryFromClause)query.Clauses.First();
QueryExpression innerQuery = fromClause.Expression as QueryExpression;
if (innerQuery != null) {
if (TryRemoveTransparentIdentifier(query, fromClause, innerQuery, letIdentifiers)) {
RemoveTransparentIdentifierReferences(query, letIdentifiers);
if (TryRemoveTransparentIdentifier(query, fromClause, innerQuery, fromOrLetIdentifiers)) {
RemoveTransparentIdentifierReferences(query, fromOrLetIdentifiers);
} else {
QueryContinuationClause continuation = new QueryContinuationClause();
continuation.PrecedingQuery = innerQuery.Detach();
continuation.Identifier = fromClause.Identifier;
continuation.CopyAnnotationsFrom(fromClause);
fromClause.ReplaceWith(continuation);
}
} else {
@ -119,8 +120,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -119,8 +120,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
foreach (var expr in match.Get<Expression>("expr")) {
switch (expr) {
case IdentifierExpression identifier:
// nothing to add
continue;
letClauses[identifier.Identifier] = identifier.Annotation<ILVariableResolveResult>();
break;
case MemberReferenceExpression member:
AddQueryLetClause(member.MemberName, member);
break;
@ -148,10 +149,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -148,10 +149,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// <summary>
/// Removes all occurrences of transparent identifiers
/// </summary>
void RemoveTransparentIdentifierReferences(AstNode node, Dictionary<string, object> letClauses)
void RemoveTransparentIdentifierReferences(AstNode node, Dictionary<string, object> fromOrLetIdentifiers)
{
foreach (AstNode child in node.Children) {
RemoveTransparentIdentifierReferences(child, letClauses);
RemoveTransparentIdentifierReferences(child, fromOrLetIdentifiers);
}
MemberReferenceExpression mre = node as MemberReferenceExpression;
if (mre != null) {
@ -161,7 +162,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -161,7 +162,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
mre.TypeArguments.MoveTo(newIdent.TypeArguments);
newIdent.CopyAnnotationsFrom(mre);
newIdent.RemoveAnnotations<Semantics.MemberResolveResult>(); // remove the reference to the property of the anonymous type
if (letClauses.TryGetValue(mre.MemberName, out var annotation))
if (fromOrLetIdentifiers.TryGetValue(mre.MemberName, out var annotation))
newIdent.AddAnnotation(annotation);
mre.ReplaceWith(newIdent);
return;

38
ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs

@ -107,16 +107,24 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -107,16 +107,24 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
case "GroupBy":
{
if (invocation.Arguments.Count == 2) {
if (MatchSimpleLambda(invocation.Arguments.ElementAt(0), out ParameterDeclaration parameter1, out Expression keySelector)
&& MatchSimpleLambda(invocation.Arguments.ElementAt(1), out ParameterDeclaration parameter2, out Expression elementSelector)
Expression keyLambda = invocation.Arguments.ElementAt(0);
Expression projectionLambda = invocation.Arguments.ElementAt(1);
if (MatchSimpleLambda(keyLambda, out ParameterDeclaration parameter1, out Expression keySelector)
&& MatchSimpleLambda(projectionLambda, out ParameterDeclaration parameter2, out Expression elementSelector)
&& parameter1.Name == parameter2.Name) {
QueryExpression query = new QueryExpression();
query.Clauses.Add(MakeFromClause(parameter1, mre.Target.Detach()));
query.Clauses.Add(new QueryGroupClause { Projection = elementSelector.Detach(), Key = keySelector.Detach() });
var queryGroupClause = new QueryGroupClause {
Projection = elementSelector.Detach(),
Key = keySelector.Detach()
};
queryGroupClause.AddAnnotation(new QueryGroupClauseAnnotation(keyLambda.Annotation<IL.ILFunction>(), projectionLambda.Annotation<IL.ILFunction>()));
query.Clauses.Add(queryGroupClause);
return query;
}
} else if (invocation.Arguments.Count == 1) {
if (MatchSimpleLambda(invocation.Arguments.Single(), out ParameterDeclaration parameter, out Expression keySelector)) {
Expression lambda = invocation.Arguments.Single();
if (MatchSimpleLambda(lambda, out ParameterDeclaration parameter, out Expression keySelector)) {
QueryExpression query = new QueryExpression();
query.Clauses.Add(MakeFromClause(parameter, mre.Target.Detach()));
query.Clauses.Add(new QueryGroupClause { Projection = new IdentifierExpression(parameter.Name).CopyAnnotationsFrom(parameter), Key = keySelector.Detach() });
@ -129,7 +137,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -129,7 +137,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{
if (invocation.Arguments.Count != 2)
return null;
if (!MatchSimpleLambda(invocation.Arguments.ElementAt(0), out ParameterDeclaration parameter, out Expression collectionSelector))
var fromExpressionLambda = invocation.Arguments.ElementAt(0);
if (!MatchSimpleLambda(fromExpressionLambda, out ParameterDeclaration parameter, out Expression collectionSelector))
return null;
if (IsNullConditional(collectionSelector))
return null;
@ -140,7 +149,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -140,7 +149,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (p1.Name == parameter.Name) {
QueryExpression query = new QueryExpression();
query.Clauses.Add(MakeFromClause(p1, mre.Target.Detach()));
query.Clauses.Add(MakeFromClause(p2, collectionSelector.Detach()));
query.Clauses.Add(MakeFromClause(p2, collectionSelector.Detach()).CopyAnnotationsFrom(fromExpressionLambda));
query.Clauses.Add(new QuerySelectClause { Expression = WrapExpressionInParenthesesIfNecessary(((Expression)lambda.Body).Detach(), parameter.Name) });
return query;
}
@ -171,7 +180,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -171,7 +180,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return null;
if (!IsComplexQuery(mre))
return null;
if (MatchSimpleLambda(invocation.Arguments.Single(), out ParameterDeclaration parameter, out Expression orderExpression)) {
var lambda = invocation.Arguments.Single();
if (MatchSimpleLambda(lambda, out ParameterDeclaration parameter, out Expression orderExpression)) {
if (ValidateThenByChain(invocation, parameter.Name)) {
QueryOrderClause orderClause = new QueryOrderClause();
while (mre.MemberName == "ThenBy" || mre.MemberName == "ThenByDescending") {
@ -180,18 +190,19 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -180,18 +190,19 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
null, new QueryOrdering {
Expression = orderExpression.Detach(),
Direction = (mre.MemberName == "ThenBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending)
});
}.CopyAnnotationsFrom(lambda));
InvocationExpression tmp = (InvocationExpression)mre.Target;
mre = (MemberReferenceExpression)tmp.Target;
MatchSimpleLambda(tmp.Arguments.Single(), out parameter, out orderExpression);
lambda = tmp.Arguments.Single();
MatchSimpleLambda(lambda, out parameter, out orderExpression);
}
// insert new ordering at beginning
orderClause.Orderings.InsertAfter(
null, new QueryOrdering {
Expression = orderExpression.Detach(),
Direction = (mre.MemberName == "OrderBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending)
});
}.CopyAnnotationsFrom(lambda));
QueryExpression query = new QueryExpression();
query.Clauses.Add(MakeFromClause(parameter, mre.Target.Detach()));
@ -210,9 +221,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -210,9 +221,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
Expression source2 = invocation.Arguments.ElementAt(0);
if (IsNullConditional(source2))
return null;
if (!MatchSimpleLambda(invocation.Arguments.ElementAt(1), out ParameterDeclaration element1, out Expression key1))
Expression outerLambda = invocation.Arguments.ElementAt(1);
if (!MatchSimpleLambda(outerLambda, out ParameterDeclaration element1, out Expression key1))
return null;
if (!MatchSimpleLambda(invocation.Arguments.ElementAt(2), out ParameterDeclaration element2, out Expression key2))
Expression innerLambda = invocation.Arguments.ElementAt(2);
if (!MatchSimpleLambda(innerLambda, out ParameterDeclaration element2, out Expression key2))
return null;
LambdaExpression lambda = invocation.Arguments.ElementAt(3) as LambdaExpression;
if (lambda != null && lambda.Parameters.Count == 2 && lambda.Body is Expression) {
@ -229,6 +242,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -229,6 +242,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (mre.MemberName == "GroupJoin") {
joinClause.IntoIdentifier = p2.Name; // into p2.Name
}
joinClause.AddAnnotation(new QueryJoinClauseAnnotation(outerLambda.Annotation<IL.ILFunction>(), innerLambda.Annotation<IL.ILFunction>()));
query.Clauses.Add(joinClause);
query.Clauses.Add(new QuerySelectClause { Expression = ((Expression)lambda.Body).Detach() }.CopyAnnotationsFrom(lambda));
return query;

47
ICSharpCode.Decompiler/DebugInfo/DebugInfoGenerator.cs

@ -133,6 +133,47 @@ namespace ICSharpCode.Decompiler.DebugInfo @@ -133,6 +133,47 @@ namespace ICSharpCode.Decompiler.DebugInfo
HandleMethod(anonymousMethodExpression);
}
public override void VisitQueryFromClause(QueryFromClause queryFromClause)
{
if (queryFromClause.Parent.FirstChild != queryFromClause) {
HandleMethod(queryFromClause);
} else {
base.VisitQueryFromClause(queryFromClause);
}
}
public override void VisitQueryGroupClause(QueryGroupClause queryGroupClause)
{
var annotation = queryGroupClause.Annotation<QueryGroupClauseAnnotation>();
if (annotation == null) {
base.VisitQueryGroupClause(queryGroupClause);
return;
}
HandleMethod(queryGroupClause.Projection, annotation.ProjectionLambda);
HandleMethod(queryGroupClause.Key, annotation.KeyLambda);
}
public override void VisitQueryJoinClause(QueryJoinClause queryJoinClause)
{
var annotation = queryJoinClause.Annotation<QueryJoinClauseAnnotation>();
if (annotation == null) {
base.VisitQueryJoinClause(queryJoinClause);
return;
}
HandleMethod(queryJoinClause.OnExpression, annotation.OnLambda);
HandleMethod(queryJoinClause.EqualsExpression, annotation.EqualsLambda);
}
public override void VisitQueryLetClause(QueryLetClause queryLetClause)
{
HandleMethod(queryLetClause);
}
public override void VisitQueryOrdering(QueryOrdering queryOrdering)
{
HandleMethod(queryOrdering);
}
public override void VisitQuerySelectClause(QuerySelectClause querySelectClause)
{
HandleMethod(querySelectClause);
@ -144,11 +185,15 @@ namespace ICSharpCode.Decompiler.DebugInfo @@ -144,11 +185,15 @@ namespace ICSharpCode.Decompiler.DebugInfo
}
void HandleMethod(AstNode node)
{
HandleMethod(node, node.Annotation<ILFunction>());
}
void HandleMethod(AstNode node, ILFunction function)
{
// Look into method body, e.g. in order to find lambdas
VisitChildren(node);
var function = node.Annotation<ILFunction>();
if (function == null || function.Method == null || function.Method.MetadataToken.IsNil)
return;
this.functions.Add(function);

Loading…
Cancel
Save