Skip to content

Commit

Permalink
Static analysis: protect against mutating fields in hash calculations
Browse files Browse the repository at this point in the history
Part of #26805.
  • Loading branch information
ajcvickers committed Dec 16, 2021
1 parent 043f9d0 commit f706061
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ public AliasUniquifier(HashSet<string> usedAliases)

private sealed class TableReferenceExpression : Expression
{
private int? _hashCode;
private SelectExpression _selectExpression;

public TableReferenceExpression(SelectExpression selectExpression, string alias)
Expand Down Expand Up @@ -392,7 +393,15 @@ private bool Equals(TableReferenceExpression tableReferenceExpression)

/// <inheritdoc />
public override int GetHashCode()
=> Alias.GetHashCode();
{
// ReSharper disable NonReadonlyMemberInGetHashCode
// Ensure hashcode does not change after it has been created for this object.
Check.DebugAssert(
_hashCode == null || _hashCode == HashCode.Combine(Alias),
"Hashed value has mutated.");

return _hashCode ??= HashCode.Combine(Alias);
}
}

private sealed class ConcreteColumnExpression : ColumnExpression
Expand Down
53 changes: 3 additions & 50 deletions src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Query.Internal;

Expand Down Expand Up @@ -3498,54 +3499,6 @@ private bool Equals(SelectExpression selectExpression)

/// <inheritdoc />
public override int GetHashCode()
{
var hash = new HashCode();
hash.Add(base.GetHashCode());

foreach (var projection in _projection)
{
hash.Add(projection);
}

foreach (var projection in _clientProjections)
{
hash.Add(projection);
}

foreach (var (projectionMember, expression) in _projectionMapping)
{
hash.Add(projectionMember);
hash.Add(expression);
}

foreach (var tag in Tags)
{
hash.Add(tag);
}

foreach (var table in _tables)
{
hash.Add(table);
}

hash.Add(Predicate);

foreach (var groupingKey in _groupBy)
{
hash.Add(groupingKey);
}

hash.Add(Having);

foreach (var ordering in _orderings)
{
hash.Add(ordering);
}

hash.Add(Offset);
hash.Add(Limit);
hash.Add(IsDistinct);

return hash.ToHashCode();
}
// Since equality above is reference equality, hash code can also be based on reference.
=> RuntimeHelpers.GetHashCode(this);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions;
/// </summary>
public abstract class TableExpressionBase : Expression, IPrintableExpression
{
private int? _hashCode;

/// <summary>
/// Creates a new instance of the <see cref="TableExpressionBase" /> class.
/// </summary>
Expand Down Expand Up @@ -62,5 +64,13 @@ private bool Equals(TableExpressionBase tableExpressionBase)

/// <inheritdoc />
public override int GetHashCode()
=> HashCode.Combine(Alias);
{
// ReSharper disable NonReadonlyMemberInGetHashCode
// Ensure hashcode does not change after it has been created for this object.
Check.DebugAssert(
_hashCode == null || _hashCode == HashCode.Combine(Alias),
"Hashed value has mutated.");

return _hashCode ??= HashCode.Combine(Alias);
}
}

0 comments on commit f706061

Please sign in to comment.