private static MethodDeclarationSyntax GenerateGetHashCodeMethod(SyntaxGenerator generator, SemanticModel semanticModel, List <PropertyDeclarationSyntax> publicProperties) { var hashCodeVariable = generator.IdentifierName("hashCode"); var hasCodeDeclaration = generator.LocalDeclarationStatement("hashCode", generator.LiteralExpression(17)); var newMethodName = SyntaxFactory.IdentifierName("GetHashCode"); var calculateHashCode = publicProperties.Select(x => { var typeInfo = semanticModel.GetTypeInfo(x.Type); if (typeInfo.Type?.IsReferenceType == true) { return(generator.AssignmentStatement(hashCodeVariable, generator.AddExpression( generator.MultiplyExpression(hashCodeVariable, generator.LiteralExpression(23)), generator.CoalesceExpression( generator.InvocationExpression( SyntaxFactory.ConditionalAccessExpression( generator.IdentifierName(x.Identifier.Text) as ExpressionSyntax, SyntaxFactory.MemberBindingExpression(newMethodName))), generator.LiteralExpression(0))))); } return(generator.AssignmentStatement(hashCodeVariable, generator.AddExpression( generator.MultiplyExpression(hashCodeVariable, generator.LiteralExpression(23)), generator.InvocationExpression( generator.MemberAccessExpression(generator.IdentifierName(x.Identifier.Text), newMethodName) )))); }); var calculateHashCodeStms = new List <SyntaxNode>() { hasCodeDeclaration, }; foreach (var node in calculateHashCode) { calculateHashCodeStms.Add(node); } calculateHashCodeStms.Add(generator.ReturnStatement(hashCodeVariable)); var getHashCode = generator.MethodDeclaration("GetHashCode", returnType: SyntaxFactory.ParseTypeName("int"), accessibility: Accessibility.Public, statements: calculateHashCodeStms, modifiers: new DeclarationModifiers().WithIsOverride(true)); return(getHashCode as MethodDeclarationSyntax); }
private static IList <SyntaxNode> CreateGetHashCodeMethodStatements( SyntaxGenerator factory, Compilation compilation, INamedTypeSymbol containingType, IList <ISymbol> members, CancellationToken cancellationToken) { const string HashCodeName = "hashCode"; // -1521134295 var permuteValue = factory.NegateExpression( factory.LiteralExpression(1521134295)); var statements = new List <SyntaxNode>(); var hashCodeNameExpression = factory.IdentifierName(HashCodeName); var firstHashValue = ComputeHashValue(factory, compilation, members[0]); if (members.Count == 1) { #if false return(this.S1.GetHashCode()); #endif statements.Add(factory.ReturnStatement(firstHashValue)); } else { #if false var hashCode = this.S1.GetHashCode(); #endif statements.Add(factory.LocalDeclarationStatement(HashCodeName, firstHashValue)); for (var i = 1; i < members.Count; i++) { #if false hashCode = hashCode * 0xA5555529 + value #endif statements.Add(factory.ExpressionStatement( factory.AssignmentStatement(hashCodeNameExpression, factory.AddExpression( factory.MultiplyExpression(hashCodeNameExpression, permuteValue), ComputeHashValue(factory, compilation, members[i]))))); } #if false return(hashCode); #endif statements.Add(factory.ReturnStatement(hashCodeNameExpression)); } return(statements); }
/// <summary> /// Recursivelly builds C# code by traversing ast /// </summary> /// <param name="root">Root node of an AST tree for a statement</param> private SyntaxNode RecursiveVisit(Node root, ref List <SyntaxNode> declared) { switch (root) { case EmptyNode emptyNode: throw new NotImplementedException(); case NumNode numNode: return(generator.LiteralExpression(numNode.Value)); case FloatNode floatNode: return(generator.LiteralExpression(floatNode.Value)); case VarNode varNode: return(this.generator.LocalDeclarationStatement( generator.TypeExpression(SpecialType.System_Object), varNode.Name )); case AssignNode assignNode: if (assignNode.variable is VarNode) { var variableDeclaration = RecursiveVisit(assignNode.variable, ref declared); declared.Add(variableDeclaration); var identifier = this.generator.IdentifierName((assignNode.variable as VarNode).Name); return(generator.AssignmentStatement(identifier, RecursiveVisit(assignNode.expression, ref declared))); } else { throw new Exception("Can't assign value to non variable."); } case InvocationNode mathFNode: //RecursiveVisit(mathFNode.arg); throw new NotImplementedException(); case BinOpNode operatorNode: return(generator.AddExpression( RecursiveVisit(operatorNode.left, ref declared), RecursiveVisit(operatorNode.right, ref declared) )); case SupNode supNode: //RecursiveVisit(supNode.baseEl); //RecursiveVisit(supNode.supEl); throw new NotImplementedException(); default: throw new NotImplementedException("Node type not implemented yet."); } }
public static ImmutableArray <SyntaxNode> CreateGetHashCodeMethodStatements( this SyntaxGenerator factory, SyntaxGeneratorInternal generatorInternal, Compilation compilation, INamedTypeSymbol containingType, ImmutableArray <ISymbol> members, bool useInt64) { var components = GetGetHashCodeComponents( factory, compilation, containingType, members, justMemberReference: false); if (components.Length == 0) { return(ImmutableArray.Create(factory.ReturnStatement(factory.LiteralExpression(0)))); } const int hashFactor = -1521134295; var initHash = 0; var baseHashCode = GetBaseGetHashCodeMethod(containingType); if (baseHashCode != null) { initHash = initHash * hashFactor + Hash.GetFNVHashCode(baseHashCode.Name); } foreach (var symbol in members) { initHash = initHash * hashFactor + Hash.GetFNVHashCode(symbol.Name); } if (components.Length == 1 && !useInt64) { // If there's just one value to hash, then we can compute and directly // return it. i.e. The full computation is: // // return initHash * hashfactor + ... // // But as we know the values of initHash and hashFactor we can just compute // is here and directly inject the result value, producing: // // return someHash + this.S1.GetHashCode(); // or var multiplyResult = initHash * hashFactor; return(ImmutableArray.Create(factory.ReturnStatement( factory.AddExpression( CreateLiteralExpression(factory, multiplyResult), components[0])))); } var statements = ArrayBuilder <SyntaxNode> .GetInstance(); // initialize the initial hashCode: // // var hashCode = initialHashCode; const string HashCodeName = "hashCode"; statements.Add(!useInt64 ? factory.SimpleLocalDeclarationStatement(generatorInternal, compilation.GetSpecialType(SpecialType.System_Int32), HashCodeName, CreateLiteralExpression(factory, initHash)) : factory.LocalDeclarationStatement(compilation.GetSpecialType(SpecialType.System_Int64), HashCodeName, CreateLiteralExpression(factory, initHash))); var hashCodeNameExpression = factory.IdentifierName(HashCodeName); // -1521134295 var permuteValue = CreateLiteralExpression(factory, hashFactor); foreach (var component in components) { // hashCode = hashCode * -1521134295 + this.S.GetHashCode(); var rightSide = factory.AddExpression( factory.MultiplyExpression(hashCodeNameExpression, permuteValue), component); if (useInt64) { rightSide = factory.InvocationExpression( factory.MemberAccessExpression(rightSide, GetHashCodeName)); } statements.Add(factory.ExpressionStatement( factory.AssignmentStatement(hashCodeNameExpression, rightSide))); } // And finally, the "return hashCode;" statement. statements.Add(!useInt64 ? factory.ReturnStatement(hashCodeNameExpression) : factory.ReturnStatement( factory.ConvertExpression( compilation.GetSpecialType(SpecialType.System_Int32), hashCodeNameExpression))); return(statements.ToImmutableAndFree()); }
/// <summary> /// Generates an override of <see cref="object.Equals(object)"/> similar to the one /// generated for anonymous types. /// </summary> private static ImmutableArray <SyntaxNode> CreateGetHashCodeMethodStatements( SyntaxGenerator factory, Compilation compilation, INamedTypeSymbol containingType, ImmutableArray <ISymbol> members, CancellationToken cancellationToken) { var hasBaseGetHashCode = HasExistingBaseGetHashCodeMethod(containingType, cancellationToken); var baseHashCode = factory.InvocationExpression( factory.MemberAccessExpression(factory.BaseExpression(), GetHashCodeName)); if (members.Length == 0) { // Trivial case. Just directly: // // return 0; or // return base.GetHashCode(); return(ImmutableArray.Create(factory.ReturnStatement( hasBaseGetHashCode ? baseHashCode : factory.LiteralExpression(0)))); } const int hashFactor = -1521134295; var initHash = 0; foreach (var symbol in members) { initHash = initHash * hashFactor + Hash.GetFNVHashCode(symbol.Name); } if (members.Length == 1 && !hasBaseGetHashCode) { // If there's just one value to hash, then we can compute and directly // return it. i.e. The full computation is: // // return initHash * hashfactor + ... // // But as we know the values of initHash and hashFactor we can just compute // is here and directly inject the result value, producing: // // return someHash + this.S1.GetHashCode(); // or var multiplyResult = initHash * hashFactor; return(ImmutableArray.Create(factory.ReturnStatement( factory.AddExpression( CreateLiteralExpression(factory, multiplyResult), ComputeHashValue(factory, compilation, members[0]))))); } var statements = ArrayBuilder <SyntaxNode> .GetInstance(); // initialize the initial hashCode: // // var hashCode = initialHashCode; const string HashCodeName = "hashCode"; statements.Add(factory.LocalDeclarationStatement(HashCodeName, CreateLiteralExpression(factory, initHash))); var hashCodeNameExpression = factory.IdentifierName(HashCodeName); // -1521134295 var permuteValue = CreateLiteralExpression(factory, hashFactor); // If our base type overrode GetHashCode, then include it's value in our hashCode // as well. if (hasBaseGetHashCode) { // hashCode = hashCode * -1521134295 + base.GetHashCode(); statements.Add(factory.ExpressionStatement( factory.AssignmentStatement(hashCodeNameExpression, factory.AddExpression( factory.MultiplyExpression(hashCodeNameExpression, permuteValue), baseHashCode)))); } foreach (var member in members) { // hashCode = hashCode * -1521134295 + this.S.GetHashCode(); statements.Add(factory.ExpressionStatement( factory.AssignmentStatement(hashCodeNameExpression, factory.AddExpression( factory.MultiplyExpression(hashCodeNameExpression, permuteValue), ComputeHashValue(factory, compilation, member))))); } // And finally, the "return hashCode;" statement. statements.Add(factory.ReturnStatement(hashCodeNameExpression)); return(statements.ToImmutableAndFree()); }