Class HiddenClassDefiner
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):
- 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. - Foreign host
(
tryDefineNestmate(Class, byte[], boolean)): best-effortprivateLookupIn. Succeeds when the host package is open to the runtime (typical for unnamed-module application classes); not for strongly encapsulated packages such asjava.lang(Stringis the counter-example). Callers must handlenulland fall back toClassLoader.defineClass(byte[], int, int).
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 Errors 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:
-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final StringSystem property that disables hidden-class definitions at run time. -
Method Summary
Modifier and TypeMethodDescriptionstatic booleancanAttemptPrivateLookup(Class<?> host) Internal policy: whetherprivateLookupInfrom this utility intohostis worth attempting.static booleanWhether 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) Definesbytesas a hidden nestmate oflookup.lookupClass()with a weak lifecycle.
-
Field Details
-
PROPERTY_DISABLE
System property that disables hidden-class definitions at run time.- See Also:
-
-
Method Details
-
isEnabled
public static boolean isEnabled()Whether hidden-class definition may be attempted in this process.- Returns:
falsewhen 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) Definesbytesas a hidden nestmate oflookup.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 hostbytes- class-file bytesinitialize-trueto run<clinit>immediately- Returns:
- the hidden class, or
nullif definition is not possible
-
tryDefineNestmate
Best-effort definition of a hidden nestmate of a foreign host. UsesprivateLookupInfrom this class; gated bycanAttemptPrivateLookup(Class). Not a substitute for a host-owned lookup.- Parameters:
host- nest host and class-loader / package donorbytes- class-file bytesinitialize-trueto run<clinit>immediately- Returns:
- the hidden class, or
nullif private lookup or definition fails
-
canAttemptPrivateLookup
Internal policy: whetherprivateLookupInfrom this utility intohostis worth attempting.Returns
falsefor unusable host shapes and for named-module packages that are not open to the Groovy runtime (e.g.Stringinjava.base). Atrueresult does not guarantee success.Not a stable user API — for runtime define policy and tests only.
- Parameters:
host- candidate foreign nest host- Returns:
truewhen a private-lookup attempt is not known to be futile- Since:
- 6.0.0
-