Package org.apache.groovy.runtime.indy

# Class IndyInvalidation

java.lang.Object
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 per `MetaClass` instance (weak identity map here). When no class-level MetaClass is installed yet, sites use a ClassInfo pending domain (retired on first install). 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(java.lang.Class, java.lang.String, java.lang.Class[], groovy.lang.MetaClass)`.

Optional stats: `-Dgroovy.indy.invalidation.stats=true`. Production guards: `IndyInterface.applyMopSwitchPoints`; tests may use `guardWithMopSwitchPoints(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.Object)`.

Since:
  6.0.0

- ## Method Summary

  Modifier and Type
  Method
  Description
  `static long`
  `bulkInvalidationCount()`
  Non-category process-wide bulk event count (`invalidateBulk()` / `invalidateUnscoped()`).
  `static long`
  `categoryInvalidationCount()`
  Category-style bulk invalidation count (`invalidateCategory()`).
  `static long`
  `classInvalidationCount()`
  Exact-class invalidation event count (`invalidateClass(Class)`).
  `static SwitchPoint`
  `classSwitchPointFor(Class<?> type)`
  Returns the SwitchPoint for the given class at link time.
  `static SwitchPoint`
  `classSwitchPointFor(Object receiver)`
  Returns the class-level MetaClass SwitchPoint for the given receiver.
  `static void`
  `collectLiveForClass(Class<?> type, List<SwitchPoint> out)`
  Collects live SwitchPoint(s) for `type`: installed MetaClass domain and ClassInfo pending domain.
  `static void`
  `collectLiveForMetaClass(MetaClass mc, List<SwitchPoint> out)`
  Detaches the live SwitchPoint for `mc`'s domain (if any) into `out`.
  `static MethodHandle`
  `guardWithMopSwitchPoints(MethodHandle handle, MethodHandle fallback, Class<?> receiverClass)`
  Installs a MetaClass SwitchPoint guard on `handle`.
  `static MethodHandle`
  `guardWithMopSwitchPoints(MethodHandle handle, MethodHandle fallback, Object receiver)`
  Installs a MetaClass SwitchPoint guard on `handle`.
  `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.
  `static void`
  `invalidateCategory()`
  Category enter/leave and `VMPlugin.invalidateCallSites()`.
  `static void`
  `invalidateClass(Class<?> type)`
  Retires only `type`'s MetaClass SwitchPoint domain (exact class; no subtype fan-out).
  `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.
  `static void`
  `invalidateMetaClass(MetaClass mc)`
  Retires the SwitchPoint domain for `mc` (if one was allocated).
  `static void`
  `invalidateUnscoped()`
  Process-wide bulk retirement when a MetaClass registry event carries no `Class` attribution.
  `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).
  `static boolean`
  `needsBulkInvalidation(MetaClass oldMc, MetaClass newMc)`
  Whether a class-level MetaClass change involving `oldMc`/`newMc` requires process-wide bulk invalidation.
  `static SwitchPointInvalidator`
  `newPendingInvalidator()`
  Creates a SwitchPoint invalidator for a ClassInfo pending domain (pre-MetaClass link only).
  `static void`
  `resetCountersForTesting()`
  Resets process-wide counters (tests only).
  `static Class<?>`
  `switchPointClassFor(Object receiver)`
  Resolves the class used for the MetaClass SwitchPoint domain of a receiver.
  `static SwitchPoint`
  `switchPointForMetaClass(MetaClass mc)`
  Returns the SwitchPoint domain for a MetaClass instance (unwraps adapters).

  ### Methods inherited from class java.lang.Object

  `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## Method Details

  - ### newPendingInvalidator

    public static SwitchPointInvalidator newPendingInvalidator()
    Creates a SwitchPoint invalidator for a ClassInfo pending domain (pre-MetaClass link only).
    Returns:
      a new invalidator

  - ### invalidateClass

    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

  - ### invalidateCategory

    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.

  - ### invalidateBulk

    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.

  - ### invalidateUnscoped

    public static void invalidateUnscoped()
    Process-wide bulk retirement when a MetaClass registry event carries no `Class` attribution.

  - ### invalidateForMetaClassChange

    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`)

  - ### needsBulkInvalidation

    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

  - ### isStockMetaClass

    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

  - ### collectLiveForMetaClass

    public static void collectLiveForMetaClass(MetaClass mc, List<SwitchPoint> out)
    Detaches the live SwitchPoint for `mc`'s domain (if any) into `out`. Does not allocate a domain when none exists yet.
    Parameters:
      `mc` - MetaClass (may be `null`)
      `out` - destination list (must not be `null`)

  - ### collectLiveForClass

    public static void collectLiveForClass(Class<?> type, List<SwitchPoint> out)
    Collects live SwitchPoint(s) for `type`: installed MetaClass domain and ClassInfo pending domain. Does not create a MetaClass.
    Parameters:
      `type` - class to inspect (must not be `null`)
      `out` - destination list

  - ### switchPointClassFor

    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

  - ### switchPointForMetaClass

    public static SwitchPoint switchPointForMetaClass(MetaClass mc)
    Returns the SwitchPoint domain for a MetaClass instance (unwraps adapters).
    Parameters:
      `mc` - MetaClass (must not be `null`)
    Returns:
      the MetaClass-domain SwitchPoint

  - ### invalidateMetaClass

    public static void invalidateMetaClass(MetaClass mc)
    Retires the SwitchPoint domain for `mc` (if one was allocated). Does not bump counters (local domain retire; width policy is separate).
    Parameters:
      `mc` - MetaClass (may be `null`)

  - ### classSwitchPointFor

    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

  - ### classSwitchPointFor

    public static SwitchPoint classSwitchPointFor(Class<?> type)
    Returns the SwitchPoint for the given class at link time. Prefers the installed class-level MetaClass domain; if none is installed yet, uses the ClassInfo pending domain (defineClass-safe).
    Parameters:
      `type` - the class (must not be `null`)
    Returns:
      the SwitchPoint for monomorphic MOP guards

  - ### guardWithMopSwitchPoints

    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

  - ### guardWithMopSwitchPoints

    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

  - ### classInvalidationCount

    public static long classInvalidationCount()
    Exact-class invalidation event count (`invalidateClass(Class)`).
    Returns:
      class invalidation count

  - ### bulkInvalidationCount

    public static long bulkInvalidationCount()
    Non-category process-wide bulk event count (`invalidateBulk()` / `invalidateUnscoped()`).
    Returns:
      bulk invalidation count

  - ### categoryInvalidationCount

    public static long categoryInvalidationCount()
    Category-style bulk invalidation count (`invalidateCategory()`).
    Returns:
      category invalidation count

  - ### resetCountersForTesting

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