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 receiver’s 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.

  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 long` | `bulkInvalidationCount()` Non-category process-wide bulk event count (invalidateBulk() / invalidateUnscoped()).
    `` | `public static long` | `categoryInvalidationCount()` Category-style bulk invalidation count (invalidateCategory()).
    `` | `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 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 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