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

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