Package org.apache.groovy.util

# Class HiddenClassDefiner

java.lang.Object
org.apache.groovy.util.HiddenClassDefiner

public final class HiddenClassDefiner extends Object
Central facility for defining hidden classes (JEP 371).

## Lookup ownership

`MethodHandles.lookup()` is caller-sensitive: it returns a full-privilege lookup only for the class that literally contains the call. Production call sites capture a lookup on the intended nest host:

     private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
     Class<?> hidden = HiddenClassDefiner.tryDefineNestmate(LOOKUP, bytecode, false);

`MethodHandles.Lookup.lookupClass()` fixes the hidden class's defining loader, package, protection domain, and nest host. Lookups from different runtime classes are not interchangeable as nest hosts even though they share the same module rights (see below).

The lookup captured inside this class is used only as the caller argument to `MethodHandles.privateLookupIn(Class, Lookup)` for the foreign-host overload — never as a production nest host.

## Module rights vs nest host

Every production call site lives in the Groovy runtime, so `lookup()` always grants the same module-level access. Capturing it in different runtime classes does not open a third-party module that never opened itself to the runtime. What differs is the nest host (package / loader / nest membership), which still matters for unloadability and linkage.

## What nestmates cover (modules A / B / C)

With modules A (Java library), B (Groovy program), C (Groovy runtime):

1. Caller-owned lookup (`tryDefineNestmate(Lookup, byte[], boolean)`): nestmate of a runtime class. Works when every type named by the bytecode is resolvable from the runtime loader. No private access into foreign modules.
2. Foreign host (`tryDefineNestmate(Class, byte[], boolean)`): best-effort `privateLookupIn`. Succeeds when the host package is open to the runtime (typical for unnamed-module application classes); not for strongly encapsulated packages such as `java.lang` (`String` is the counter-example). Callers must handle `null` and fall back to `ClassLoader.defineClass(byte[], int, int)`.

Nestmates cover C and often open/unnamed B. They do not tunnel private access into A. Visible `defineClass` is the intentional safety net.

## Soft-fail contract

`try*` methods return `null` on expected failures (`IllegalAccessException`, `SecurityException`, `LinkageError`, invalid class-file / ASM exceptions, GraalVM `UnsupportedFeatureError` matched by class name). Other `Error`s are rethrown.

## Enablement

`isEnabled()` is evaluated per call so `-Dgroovy.hidden.classes.disable=true` works under native-image build-time init, and is always `false` when `org.graalvm.nativeimage.imagecode=runtime`.

Since:
  6.0.0
See Also:
  - `MethodHandles.Lookup.defineHiddenClass(byte[], boolean, Lookup.ClassOption...)`

- ## Field Summary

  Fields
  Modifier and Type
  Field
  Description
  `static final String`
  `PROPERTY_DISABLE`
  System property that disables hidden-class definitions at run time.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `static boolean`
  `canAttemptPrivateLookup(Class<?> host)`
  Internal policy: whether `privateLookupIn` from this utility into `host` is worth attempting.
  `static boolean`
  `isEnabled()`
  Whether hidden-class definition may be attempted in this process.
  `static Class<?>`
  `tryDefineNestmate(Class<?> host, byte[] bytes, boolean initialize)`
  Best-effort definition of a hidden nestmate of a foreign host.
  `static Class<?>`
  `tryDefineNestmate(MethodHandles.Lookup lookup, byte[] bytes, boolean initialize)`
  Defines `bytes` as a hidden nestmate of `lookup.lookupClass()` with a weak lifecycle.
  `static MethodHandles.Lookup`
  `tryDefineNestmateLookup(Class<?> host, byte[] bytes, boolean initialize)`
  Foreign host: `privateLookupIn(host, LOOKUP)` then the Lookup-returning define.
  `static MethodHandles.Lookup`
  `tryDefineNestmateLookup(MethodHandles.Lookup lookup, byte[] bytes, boolean initialize)`
  Like `tryDefineNestmate(Lookup, byte[], boolean)`, but returns the hidden `MethodHandles.Lookup` so the caller can `findConstructor` / read classData without a public-constructor round-trip.
  `static MethodHandles.Lookup`
  `tryDefineNestmateWithClassData(MethodHandles.Lookup lookup, byte[] bytes, Object classData, boolean initialize)`
  Defines `bytes` as a hidden nestmate of `lookup.lookupClass()` with `classData` attached (`MethodHandles.Lookup.defineHiddenClassWithClassData(byte[], java.lang.Object, boolean, java.lang.invoke.MethodHandles.Lookup.ClassOption...)`).

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

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

- ## Field Details

  - ### PROPERTY_DISABLE

    public static final String PROPERTY_DISABLE
    System property that disables hidden-class definitions at run time.
    See Also:
      - Constant Field Values

- ## Method Details

  - ### isEnabled

    public static boolean isEnabled()
    Whether hidden-class definition may be attempted in this process.
    Returns:
      `false` when the kill switch is set or when running inside a GraalVM native image at run time

  - ### tryDefineNestmate

    public static Class<?> tryDefineNestmate(MethodHandles.Lookup lookup, byte[] bytes, boolean initialize)
    Defines `bytes` as a hidden nestmate of `lookup.lookupClass()` with a weak lifecycle. The class-file package is rewritten to match the lookup class before definition.
    Parameters:
      `lookup` - full-privilege lookup for the nest host
      `bytes` - class-file bytes
      `initialize` - `true` to run `<clinit>` immediately
    Returns:
      the hidden class, or `null` if definition is not possible

  - ### tryDefineNestmate

    public static Class<?> tryDefineNestmate(Class<?> host, byte[] bytes, boolean initialize)
    Best-effort definition of a hidden nestmate of a foreign host. Uses `privateLookupIn` from this class; gated by `canAttemptPrivateLookup(Class)`. Not a substitute for a host-owned lookup.
    Parameters:
      `host` - nest host and class-loader / package donor
      `bytes` - class-file bytes
      `initialize` - `true` to run `<clinit>` immediately
    Returns:
      the hidden class, or `null` if private lookup or definition fails

  - ### tryDefineNestmateLookup

    public static MethodHandles.Lookup tryDefineNestmateLookup(MethodHandles.Lookup lookup, byte[] bytes, boolean initialize)
    Like `tryDefineNestmate(Lookup, byte[], boolean)`, but returns the hidden `MethodHandles.Lookup` so the caller can `findConstructor` / read classData without a public-constructor round-trip.

    `ExceptionInInitializerError` and `NoClassDefFoundError` are sticky-failed as `null` (in addition to the usual soft-fail set). Needed from `InvokerFactory` in another package; not user API.

    Parameters:
      `lookup` - full-privilege lookup for the nest host
      `bytes` - class-file bytes
      `initialize` - `true` to run `<clinit>` immediately
    Returns:
      the hidden lookup, or `null` if definition is not possible
    Since:
      6.0.0

  - ### tryDefineNestmateLookup

    public static MethodHandles.Lookup tryDefineNestmateLookup(Class<?> host, byte[] bytes, boolean initialize)
    Foreign host: `privateLookupIn(host, LOOKUP)` then the Lookup-returning define. Nest host is `host`, not this utility.
    Parameters:
      `host` - nest host and class-loader / package donor
      `bytes` - class-file bytes
      `initialize` - `true` to run `<clinit>` immediately
    Returns:
      the hidden lookup, or `null` if private lookup or definition fails
    Since:
      6.0.0

  - ### tryDefineNestmateWithClassData

    public static MethodHandles.Lookup tryDefineNestmateWithClassData(MethodHandles.Lookup lookup, byte[] bytes, Object classData, boolean initialize)
    Defines `bytes` as a hidden nestmate of `lookup.lookupClass()` with `classData` attached (`MethodHandles.Lookup.defineHiddenClassWithClassData(byte[], java.lang.Object, boolean, java.lang.invoke.MethodHandles.Lookup.ClassOption...)`). Returns the hidden `MethodHandles.Lookup`. Initialization errors sticky-fail as `null`. Needed from `InvokerFactory` in another package; not user API.
    Parameters:
      `lookup` - full-privilege lookup for the nest host
      `bytes` - class-file bytes
      `classData` - value retrieved by `MethodHandles.classData(java.lang.invoke.MethodHandles.Lookup, java.lang.String, java.lang.Class<T>)` / `ConstantDynamic` in the hidden class
      `initialize` - `true` to run `<clinit>` immediately
    Returns:
      the hidden lookup, or `null` if definition is not possible
    Since:
      6.0.0

  - ### canAttemptPrivateLookup

    public static boolean canAttemptPrivateLookup(Class<?> host)
    Internal policy: whether `privateLookupIn` from this utility into `host` is worth attempting.

    Returns `false` for unusable host shapes and for named-module packages that are not open to the Groovy runtime (e.g. `String` in `java.base`). A `true` result does not guarantee success.

    Not a stable user API — for runtime define policy and tests only.

    Parameters:
      `host` - candidate foreign nest host
    Returns:
      `true` when a private-lookup attempt is not known to be futile
    Since:
      6.0.0