Skip to content

Conversation

@devalgupta404
Copy link

@devalgupta404 devalgupta404 commented Oct 18, 2025

This PR implements hashCode caching for Freezed models to improve performance for large, deeply nested objects. Currently, hashCode is recomputed every time it's accessed, which can be expensive for complex models.

Changes

  • Added hashCode caching: Implemented int? _cachedHashCode; field and int get hashCode => _cachedHashCode ??= $hashCodeExpression; pattern
  • Updated template logic: Modified hashCodeMethod to conditionally apply caching based on isConst and source parameters
  • Fixed template parameters: Added missing isConst parameter to hashCodeMethod function and updated all call sites
  • Template syntax fixes: Corrected string interpolation syntax in hashCode expression generation

Implementation Details

The caching is applied to:

  • Non-const classes (isConst: false)
  • Synthetic classes (source: Source.syntheticClass)
  • Classes with fewer than 20 properties (larger classes use Object.hashAll)

Const classes and mixins continue to use the non-cached version for performance reasons.

Fixes

Resolves #1293

Example Generated Code

Before:

@override
int get hashCode => Object.hash(runtimeType, property1, property2);

After:

int? _cachedHashCode;
@override
int get hashCode => _cachedHashCode ??= Object.hash(runtimeType, property1, property2);

This change ensures that hashCode computation is only performed once per object instance, providing significant performance benefits for applications with large, deeply nested Freezed models.

Summary by CodeRabbit

  • Bug Fixes

    • Equality comparisons for collections now correctly use deep structural equality instead of reference comparison.
  • Performance

    • HashCode computation now includes caching for improved performance in non-const scenarios.

@coderabbitai
Copy link

coderabbitai bot commented Oct 18, 2025

Walkthrough

The changes implement hashCode caching for Freezed models through a new isConst parameter. When false, generated hashCode methods use lazy initialization with _cachedHashCode ??= .... Template files propagate this parameter, and collection equality helpers now use deep structural comparison for consistent hashing behavior.

Changes

Cohort / File(s) Summary
HashCode Caching with isConst Parameter
packages/freezed/lib/src/templates/abstract_template.dart, packages/freezed/lib/src/templates/concrete_template.dart
Updated method generators to accept isConst parameter. When false, generated hashCode uses _cachedHashCode ??= ... for lazy caching. Concrete template computes hashCodeExpression with property-count optimization (single, Object.hash, or Object.hashAll).
Deep Collection Equality
packages/freezed_annotation/lib/freezed_annotation.dart
Replaced direct member equality in EqualUnmodifiable*View classes with DeepCollectionEquality().equals() and updated hashCode to use DeepCollectionEquality().hash() for consistent structural comparison.

Sequence Diagram

sequenceDiagram
    participant Code as Generated Code
    participant Getter as hashCode Getter
    participant Cache as _cachedHashCode Field
    
    Note over Code: isConst = false
    Code->>Getter: Access hashCode
    Getter->>Cache: Check if cached
    alt Cache exists
        Cache-->>Getter: Return cached value
    else Cache is null
        Getter->>Getter: Compute Object.hash(...)
        Getter->>Cache: Store result (_cachedHashCode ??=)
        Cache-->>Getter: Return computed value
    end
    Getter-->>Code: Return hashCode
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

The changes introduce new logic density in hashCode generation with conditional caching behavior, require understanding parameter threading across multiple templates, and involve subtle semantic shifts in collection equality. The alterations are focused and consistent, but each file demands separate reasoning for correctness.

Possibly related PRs

  • #1224: Modifies concrete_template.dart's equality/hashCode generation with super-aware logic, sharing similar code generation patterns and hashCode computation concerns.

Poem

🐰 A bunny's ode to cached hashes:

Once hashCode was recomputed fast,
Now lazy caching makes it last!
The ??= operator keeps the score,
No duplication—just one more... cache!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The changes to abstract_template.dart and concrete_template.dart are clearly in scope as they implement the core caching mechanism. However, the freezed_annotation.dart changes appear tangential to the stated objective. These modifications replace direct member equality checks with DeepCollectionEquality and update hashCode implementations for unmodifiable collections—changes related to equality semantics rather than hashCode caching. The linked issue #1293 focuses specifically on caching hashCode computation and does not reference changes to equality or deep collection semantics for unmodifiable views, making these modifications appear outside the stated scope. The freezed_annotation.dart changes should either be removed from this PR and submitted separately if they're unrelated improvements, or the PR description should explicitly document how these equality semantics changes support the caching feature and justify their inclusion as in-scope modifications for issue #1293.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "Cache hashCode computation in Freezed models to avoid expensive recomputation" directly aligns with the primary changes across the modified files. The concrete_template.dart introduces the caching mechanism with a nullable backing field and lazy evaluation pattern, abstract_template.dart plumbs the isConst parameter needed for conditional caching, and the changes collectively implement the hashCode caching feature. The title is concise, specific, and clearly communicates the main objective without vagueness or noise.
Linked Issues Check ✅ Passed The code changes substantially meet the objectives from linked issue #1293. The concrete_template.dart implements the core requirement by introducing a nullable _cachedHashCode field and employing the ??= cached getter pattern described in the issue. The implementation conditionally applies caching to non-const classes while preserving non-cached behavior for const classes and mixins, maintaining existing semantics as required. The abstract_template.dart and parameter plumbing support this caching logic by introducing the isConst parameter needed for conditional cache application across template calls.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c9baf6 and 0d931ae.

📒 Files selected for processing (3)
  • packages/freezed/lib/src/templates/abstract_template.dart (1 hunks)
  • packages/freezed/lib/src/templates/concrete_template.dart (4 hunks)
  • packages/freezed_annotation/lib/freezed_annotation.dart (3 hunks)
🧰 Additional context used
🪛 GitHub Actions: Build
packages/freezed_annotation/lib/freezed_annotation.dart

[error] 1-1: Command failed: dart format --set-exit-if-changed . — Dart format detected formatting changes (3 files formatted, 1 changed). Please run 'dart format --set-exit-if-changed .' to fix the code style issues.

packages/freezed/lib/src/templates/concrete_template.dart

[error] 1-1: Dart formatter changed 1 file: lib/src/templates/concrete_template.dart. Run 'dart format' to apply changes.

🔇 Additional comments (7)
packages/freezed_annotation/lib/freezed_annotation.dart (2)

21-21: LGTM: Deep equality implementation is correct.

The use of DeepCollectionEquality().equals() properly handles nested collections and the argument order is correct for equality semantics.

Also applies to: 39-39, 58-58


25-25: LGTM: HashCode implementation is consistent with equality.

Using DeepCollectionEquality().hash(_source) ensures the hashCode is consistent with the deep equality implementation, which is essential for correct behavior in hash-based collections.

Also applies to: 43-43, 62-62

packages/freezed/lib/src/templates/abstract_template.dart (1)

49-50: LGTM: Correct parameter for mixin generation.

Passing isConst: false for mixins is appropriate since mixins cannot be const, ensuring they receive the cached hashCode implementation.

packages/freezed/lib/src/templates/concrete_template.dart (4)

53-53: LGTM: Parameter threading is correct.

The isConst parameter is properly threaded from the constructor through methods() to hashCodeMethod(), enabling conditional hashCode caching based on class mutability.

Also applies to: 328-328, 334-334


496-500: LGTM: HashCode expression logic is sound.

The tiered approach is well-designed:

  • Single property: direct .hashCode (optimal)
  • < 20 properties: Object.hash() (efficient)
  • ≥ 20 properties: Object.hashAll() (handles any count)

This correctly respects Object.hash()'s 20-parameter limit.


502-508: LGTM: Const and mixin path correctly avoids caching.

Const classes and mixins appropriately use direct hashCode computation without caching, as const objects are immutable and mixins cannot store instance fields.


512-514: LGTM: Cached hashCode implementation is correct.

The lazy initialization pattern using _cachedHashCode ??= hashCodeExpression effectively caches the computed hash value. While not atomic, this is acceptable for immutable Freezed models where concurrent computations would produce identical results.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow caching of hashCode on Freezed models

1 participant