Package: org.apache.groovy.runtime.indy

## [Java] Class IndyInvalidation

- org.apache.groovy.runtime.indy.IndyInvalidation

-     public final class IndyInvalidation
      extends Object

  Scoped invokedynamic SwitchPoint invalidation for the Groovy MOP (GROOVY-12191).

  Domain: one SwitchPoint domain per Class, owned by its ClassInfo and covering every MetaClass generation of that class (pre-MetaClass link included). The domain outlives the MetaClass object, so exact-class invalidation still reaches installed guards after a soft/weak MetaClass has been collected. Monomorphic indy sites install a single `guardWithTest` on the class-level domain observed at link time.

  ## Width policy (two axes only)

  - Exact class invalidateClass(Class): stock `MetaClassImpl` / EMC changes (including ClassInfo.incVersion, registry replace, per-instance MetaClass).
  - All loaded process-wide retire used for category enter/leave (invalidateCategory()), unattributed registry events (invalidateUnscoped()), and non-stock custom MetaClass kinds (invalidateBulk()).
  There is no parent?child SwitchPoint fan-out. Indy selection consults only the receivers own MetaClass tables; ancestor-dependent resolution on the missing-method / missing-property path re-enters `MetaClass.invokeMethod` / property miss and walks the hierarchy live from the registry each call. Construction-time snapshots of ancestor expando methods are pre-existing MOP behaviour and are not refreshed by SwitchPoint retirement. See also MetaClassImpl.findMethodInClassHierarchy.

  Optional stats: `-Dgroovy.indy.invalidation.stats=true`. Production guards: `IndyInterface.applyMopSwitchPoints`; tests may use guardWithMopSwitchPoints.

  ## Layering

  This class is the policy half of a two-level subsystem: SwitchPointInvalidator is the mechanism (one domain's SwitchPoint lifecycle plus the process-wide live registry) and makes no policy decisions; this class decides invalidation width, labels the reasons, owns reclaim anchoring and when the global ClassValue store reclaims values (GROOVY-12281 soft mode) per-Class domain continuity. The only other supported consumer of the mechanism is ClassInfo, which owns domain instances and performs the local operations (allocate, invalidate, detach) directly. No other code should construct or invalidate SwitchPointInvalidator instances; both classes are internal and may change incompatibly.
  Since:
    6.0.0

- - ### Methods Summary

    Methods
    Type Params | Return Type | Name and description
    --- | --- | ---
    `` | `public static void` | `anchorClassDomain(ClassInfo owner, SwitchPointInvalidator domain)` Anchors a reclaim reference for a ClassInfo-owned class domain: when the ClassInfo (and with it the class) becomes unreachable while the domain still has a live registered SwitchPoint, the reference is delivered by the shared weak-bundle manager and the domain is retired installed guards keep only the SwitchPoint's internal invoker reachable, so retiring the orphan forces straggler sites to re-link, which is always safe.
    `` | `public static void` | `anchorClassDomainToClass(Class<?> owner, SwitchPointInvalidator domain)` Anchors a reclaim reference keyed to the `Class` rather than a ClassInfo instance: soft mode retires a domain only when the class dies (no receiver can reach an installed guard afterwards), because ClassInfo instances are replaceable and their successors adopt the same domain.
    `` | `public static long` | `bulkInvalidationCount()` Non-category process-wide bulk event count (invalidateBulk() / invalidateUnscoped()).
    `` | `public static long` | `categoryInvalidationCount()` Category-style bulk invalidation count (invalidateCategory()).
    `` | `public static SwitchPointInvalidator` | `classDomainFor(Class<?> type, SwitchPointInvalidator candidate)` Returns the canonical domain for `type`, seeding the per-Class map with `candidate` on first touch.
    `` | `public static long` | `classInvalidationCount()` Exact-class invalidation event count (invalidateClass(Class)).
    `` | `public static SwitchPoint` | `classSwitchPointFor(Object receiver)` Returns the class-level MetaClass SwitchPoint for the given receiver.
    `` | `public static SwitchPoint` | `classSwitchPointFor(Class<?> type)` Returns the SwitchPoint for the given class at link time: the current generation of the ClassInfo-owned class domain (defineClass-safe does not create or consult a MetaClass).
    `` | `public static void` | `collectLiveForClass(Class<?> type, List<SwitchPoint> out)` Collects the live SwitchPoint (if any) of `type`'s class domain.
    `` | `public static MethodHandle` | `guardWithMopSwitchPoints(MethodHandle handle, MethodHandle fallback, Object receiver)` Installs a MetaClass SwitchPoint guard on `handle`.
    `` | `public static MethodHandle` | `guardWithMopSwitchPoints(MethodHandle handle, MethodHandle fallback, Class<?> receiverClass)` Installs a MetaClass SwitchPoint guard on `handle`.
    `` | `public static void` | `invalidateBulk()` Process-wide bulk retirement for non-stock (custom) MetaClass registry events correctness-first when selection may consult state outside the stock miss walk.
    `` | `public static void` | `invalidateCategory()` Category enter/leave and `VMPlugin.invalidateCallSites()`.
    `` | `public static void` | `invalidateClass(Class<?> type)` Retires only `type`'s MetaClass SwitchPoint domain (exact class; no subtype fan-out).
    `` | `public static void` | `invalidateForMetaClassChange(MetaClassRegistryChangeEvent event)` Registry-driven invalidation: exact class for stock MetaClass kinds and per-instance changes; process-wide bulk for custom MetaClass kinds or unattributed events.
    `` | `public static void` | `invalidateMetaClass(MetaClass mc)` Retires the class domain of `mc`'s class (if a generation was allocated).
    `` | `public static void` | `invalidateUnscoped()` Process-wide bulk retirement when a MetaClass registry event carries no `Class` attribution.
    `` | `public static boolean` | `isStockMetaClass(MetaClass mc)` Whether `mc` is a stock MOP MetaClass for which exact-class invalidation is sufficient: `null`, or unwrapped MetaClassImpl (includes EMC and subclasses).
    `` | `public static boolean` | `needsBulkInvalidation(MetaClass oldMc, MetaClass newMc)` Whether a class-level MetaClass change involving `oldMc`/`newMc` requires process-wide bulk invalidation.
    `` | `public static void` | `resetCountersForTesting()` Resets process-wide counters (tests only).
    `` | `public static Class<?>` | `switchPointClassFor(Object receiver)` Resolves the class used for the MetaClass SwitchPoint domain of a receiver.
    `` | `public static SwitchPoint` | `switchPointForMetaClass(MetaClass mc)` Returns the class-domain SwitchPoint for a MetaClass.

  - ### Inherited Methods Summary

    Inherited Methods
    Methods inherited from class | Name
    --- | ---
    `class Object` | `equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- - ### Method Detail

    - #### public static void anchorClassDomain(ClassInfo owner, SwitchPointInvalidator domain)

      Anchors a reclaim reference for a ClassInfo-owned class domain: when the ClassInfo (and with it the class) becomes unreachable while the domain still has a live registered SwitchPoint, the reference is delivered by the shared weak-bundle manager and the domain is retired installed guards keep only the SwitchPoint's internal invoker reachable, so retiring the orphan forces straggler sites to re-link, which is always safe.

      Reachability: the live-SwitchPoint registry holds the invalidator strongly, and the invalidator holds the reclaim reference, so cleanup stays reachable exactly as long as there is something to clean; once the domain is retired the whole chain becomes collectable.

      Parameters:
        `owner` - the ClassInfo owning the domain (must not be `null`)
        `domain` - the class-level domain (must not be `null`)

    - #### public static void anchorClassDomainToClass(Class<?> owner, SwitchPointInvalidator domain)

      Anchors a reclaim reference keyed to the `Class` rather than a ClassInfo instance: soft mode retires a domain only when the class dies (no receiver can reach an installed guard afterwards), because ClassInfo instances are replaceable and their successors adopt the same domain.

      Parameters:
        `owner` - the class owning the domain (must not be `null`)
        `domain` - the class-level domain (must not be `null`)

    - #### public static long bulkInvalidationCount()

      Non-category process-wide bulk event count (invalidateBulk() / invalidateUnscoped()).

      Returns:
        bulk invalidation count

    - #### public static long categoryInvalidationCount()

      Category-style bulk invalidation count (invalidateCategory()).

      Returns:
        category invalidation count

    - #### public static SwitchPointInvalidator classDomainFor(Class<?> type, SwitchPointInvalidator candidate)

      Returns the canonical domain for `type`, seeding the per-Class map with `candidate` on first touch. Soft-mode only (callers gate).

      Parameters:
        `type` - the class whose domain is requested (must not be `null`)
        `candidate` - the caller-owned domain to install if none is mapped yet
      Returns:
        the canonical per-Class domain

    - #### public static long classInvalidationCount()

      Exact-class invalidation event count (invalidateClass(Class)).

      Returns:
        class invalidation count

    - #### public static SwitchPoint classSwitchPointFor(Object receiver)

      Returns the class-level MetaClass SwitchPoint for the given receiver.

      Parameters:
        `receiver` - the call receiver (may be `null`)
      Returns:
        the MetaClass-domain switch point

    - #### public static SwitchPoint classSwitchPointFor(Class<?> type)

      Returns the SwitchPoint for the given class at link time: the current generation of the ClassInfo-owned class domain (defineClass-safe does not create or consult a MetaClass).

      Parameters:
        `type` - the class (must not be `null`)
      Returns:
        the SwitchPoint for monomorphic MOP guards

    - #### public static void collectLiveForClass(Class<?> type, List<SwitchPoint> out)

      Collects the live SwitchPoint (if any) of `type`'s class domain. Does not create a MetaClass and does not allocate a domain generation.

      Parameters:
        `type` - class to inspect (must not be `null`)
        `out` - destination list

    - #### public static MethodHandle guardWithMopSwitchPoints(MethodHandle handle, MethodHandle fallback, Object receiver)

      Installs a MetaClass SwitchPoint guard on `handle`. Public entry for tests; production uses `IndyInterface.applyMopSwitchPoints`.

      Parameters:
        `handle` - the fast-path handle
        `fallback` - the re-link / fallback handle
        `receiver` - the receiver used to select the MetaClass-domain switch point
      Returns:
        the guarded handle

    - #### public static MethodHandle guardWithMopSwitchPoints(MethodHandle handle, MethodHandle fallback, Class<?> receiverClass)

      Installs a MetaClass SwitchPoint guard on `handle`.

      Parameters:
        `handle` - the fast-path handle
        `fallback` - the re-link / fallback handle
        `receiverClass` - the class whose class-level MetaClass domain guards this site
      Returns:
        the guarded handle

    - #### public static void invalidateBulk()

      Process-wide bulk retirement for non-stock (custom) MetaClass registry events correctness-first when selection may consult state outside the stock miss walk. Rare.

    - #### public static void invalidateCategory()

      Category enter/leave and `VMPlugin.invalidateCallSites()`. Retires every loaded class-level domain so sites re-link under the new category state. No separate category SwitchPoint on the hot path.

    - #### public static void invalidateClass(Class<?> type)

      Retires only `type`'s MetaClass SwitchPoint domain (exact class; no subtype fan-out). No-op when `type` is `null`. Does not bump ClassInfo.getVersion.

      Used by ClassInfo.incVersion, stock registry MetaClass changes, and per-instance MetaClass changes. Non-stock MetaClass kinds use invalidateBulk() instead.

      Parameters:
        `type` - the class whose MetaClass changed

    - #### public static void invalidateForMetaClassChange(MetaClassRegistryChangeEvent event)

      Registry-driven invalidation: exact class for stock MetaClass kinds and per-instance changes; process-wide bulk for custom MetaClass kinds or unattributed events. See class javadoc.

      Parameters:
        `event` - the registry change event (must not be `null`)

    - #### public static void invalidateMetaClass(MetaClass mc)

      Retires the class domain of `mc`'s class (if a generation was allocated). Does not bump counters (local domain retire; width policy is separate).

      Parameters:
        `mc` - MetaClass (may be `null`)

    - #### public static void invalidateUnscoped()

      Process-wide bulk retirement when a MetaClass registry event carries no `Class` attribution.

    - #### public static boolean isStockMetaClass(MetaClass mc)

      Whether `mc` is a stock MOP MetaClass for which exact-class invalidation is sufficient: `null`, or unwrapped MetaClassImpl (includes EMC and subclasses). Any other kind is non-stock and triggers bulk invalidation on registry replace.

      Parameters:
        `mc` - MetaClass to classify (may be `null`)
      Returns:
        `true` if exact-class invalidation is sufficient

    - #### public static boolean needsBulkInvalidation(MetaClass oldMc, MetaClass newMc)

      Whether a class-level MetaClass change involving `oldMc`/`newMc` requires process-wide bulk invalidation.

      Bulk is reserved for non-stock MetaClass kinds: not MetaClassImpl after adapter unwrap. Stock pairs stay exact-class.

      Parameters:
        `oldMc` - previous MetaClass (may be `null`)
        `newMc` - new MetaClass (may be `null` on remove)
      Returns:
        `true` if bulk invalidation is required

    - #### public static void resetCountersForTesting()

      Resets process-wide counters (tests only). Does not reset live switch points.

    - #### public static Class<?> switchPointClassFor(Object receiver)

      Resolves the class used for the MetaClass SwitchPoint domain of a receiver. `null` maps to NullObject; a Class receiver uses itself.

      Parameters:
        `receiver` - the call receiver (may be `null`)
      Returns:
        the class whose class-level MetaClass SwitchPoint guards this site

    - #### public static SwitchPoint switchPointForMetaClass(MetaClass mc)

      Returns the class-domain SwitchPoint for a MetaClass. The domain belongs to the class, not the MetaClass instance, so every generation (and every adapter wrapping) of a class's MetaClass shares one domain.

      Parameters:
        `mc` - MetaClass (must not be `null`)
      Returns:
        the class-domain SwitchPoint