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

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

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
  • Method Details

    • anchorClassDomain

      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)
    • classDomainFor

      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
    • anchorClassDomainToClass

      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)
    • 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
    • collectLiveForClass

      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
    • 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 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
    • invalidateMetaClass

      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)
    • 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: 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
    • 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.