Version
0.1.0
Driver
Java agent (load-time weaving)
Module
aether-weaver-engine
Diagnostic code
AW4001
What you expected
Weaving a load-time target whose method bodies merge a reference type belonging to the
application (one not visible to the system class loader) produces a class that verifies and
applies.
What happened instead
The woven class is rejected, in one of two forms that share a single root cause:
java.lang.classfile's StackMapGenerator throws
IllegalArgumentException: Could not resolve class <T> out of ClassFile.transformClass(...)
while generating frames; or
- the class fails
ClassFile.verify(...) with spurious VerifyErrors —
<class> does not verify after weaving (N errors) (AW4001).
Root cause. On the load-time path the class-hierarchy resolver is derived from the
system class loader, not from the loader that defined the class being woven, so the
application's own types cannot be resolved when frames are merged at a branch. Two sites:
- Injection and merge build the woven class with a plain
ClassFile.of()
(WeavingPipeline, StructuralWeaver) — the default ClassHierarchyResolver, which
reflects over the system class loader.
Verifier.check(...) runs ClassFile.of().verify(...) — the same default resolver.
FrameSupport.forLoadTime(ClassLoader) already builds the correct resolver
(ClassHierarchyResolver.ofResourceParsing(loader).orElse(defaultResolver())), but nothing
on the load-time path calls it, and the defining ClassLoader handed to
WeavingTransformer.transform(...) is never threaded into Weaver.weave(...).
Under verification=report the failure is silent — the class loads unwoven, so a weave is
dropped without a word. Under verification=strict / onError=fail it surfaces as AW4001
or halts the JVM. Weaves whose signatures reference only JDK types are unaffected, which is
why the failure looks selective.
Minimal reproducer
// Target: an application class (not on the system class path at weave time) whose method
// merges a reference type at a branch -- a list built in a loop, a synchronized block, etc.
// Any @Inject at RETURN forces the frames to be regenerated, which needs the hierarchy.
@Weave(targets = "com.example.app.Service", require = Require.OPTIONAL)
public final class ServiceWeave {
@Inject(method = "collect()", at = @At(Point.RETURN))
private void onCollect(final ReturnableCallback<java.util.List> callback) {
// body irrelevant; the mere injection triggers frame regeneration
}
}
Configuration
<!-- load-time, application types not on the system class path -->
-javaagent:aether-weaver-agent.jar
-Xbootclasspath/a:weaves.jar:aether-weaver-api.jar:aether-weaver-engine.jar:aether-weaver-runtime.jar
-Daether.weaver.verification=strict
Output or stack trace
java.lang.IllegalArgumentException: Could not resolve class <ApplicationType>
at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl.resolve(ClassHierarchyImpl.java:76)
at java.base/jdk.internal.classfile.impl.ClassHierarchyImpl.isInterface(ClassHierarchyImpl.java:86)
at java.base/jdk.internal.classfile.impl.StackMapGenerator$Type.mergeReferenceFrom(StackMapGenerator.java:1363)
at java.base/jdk.internal.classfile.impl.StackMapGenerator.checkJumpTarget(StackMapGenerator.java:283)
# or, under strict verification:
<class> does not verify after weaving (N errors) # AW4001
at de.splatgames.aether.weaver.engine.verify.Verifier.refuse(Verifier.java)
at de.splatgames.aether.weaver.engine.verify.Verifier.check(Verifier.java)
at de.splatgames.aether.weaver.engine.Weaver.apply(Weaver.java)
Environment
JDK: Corretto 25.0.2
OS: Windows 11
Is this a regression?
No, or I do not know
Suggested fix
Thread the defining ClassLoader — already available as the loader argument of
WeavingTransformer.transform(...) — through Weaver.weave(...) to the load-time build and
verify sites, and use the resolver FrameSupport already provides:
WeavingPipeline (injection) and StructuralWeaver (merge): build the class via
FrameSupport.forLoadTime(loader) instead of ClassFile.of().
Verifier.check(...): verify via FrameSupport.forLoadTime(loader).verify(...) instead of
ClassFile.of().verify(...).
FrameSupport.forLoadTime(ClassLoader) already exists and does exactly this; it simply has
no callers on the load-time path. Routing both stack-map generation and verification through
the same loader-aware context also keeps build-time and load-time output byte-identical,
which FrameSupportTest already asserts.
Checklist
Version
0.1.0
Driver
Java agent (load-time weaving)
Module
aether-weaver-engine
Diagnostic code
AW4001
What you expected
Weaving a load-time target whose method bodies merge a reference type belonging to the
application (one not visible to the system class loader) produces a class that verifies and
applies.
What happened instead
The woven class is rejected, in one of two forms that share a single root cause:
java.lang.classfile'sStackMapGeneratorthrowsIllegalArgumentException: Could not resolve class <T>out ofClassFile.transformClass(...)while generating frames; or
ClassFile.verify(...)with spuriousVerifyErrors —<class> does not verify after weaving (N errors)(AW4001).Root cause. On the load-time path the class-hierarchy resolver is derived from the
system class loader, not from the loader that defined the class being woven, so the
application's own types cannot be resolved when frames are merged at a branch. Two sites:
ClassFile.of()(
WeavingPipeline,StructuralWeaver) — the defaultClassHierarchyResolver, whichreflects over the system class loader.
Verifier.check(...)runsClassFile.of().verify(...)— the same default resolver.FrameSupport.forLoadTime(ClassLoader)already builds the correct resolver(
ClassHierarchyResolver.ofResourceParsing(loader).orElse(defaultResolver())), but nothingon the load-time path calls it, and the defining
ClassLoaderhanded toWeavingTransformer.transform(...)is never threaded intoWeaver.weave(...).Under
verification=reportthe failure is silent — the class loads unwoven, so a weave isdropped without a word. Under
verification=strict/onError=failit surfaces as AW4001or halts the JVM. Weaves whose signatures reference only JDK types are unaffected, which is
why the failure looks selective.
Minimal reproducer
Configuration
<!-- load-time, application types not on the system class path --> -javaagent:aether-weaver-agent.jar -Xbootclasspath/a:weaves.jar:aether-weaver-api.jar:aether-weaver-engine.jar:aether-weaver-runtime.jar -Daether.weaver.verification=strictOutput or stack trace
Environment
JDK: Corretto 25.0.2
OS: Windows 11
Is this a regression?
No, or I do not know
Suggested fix
Thread the defining
ClassLoader— already available as theloaderargument ofWeavingTransformer.transform(...)— throughWeaver.weave(...)to the load-time build andverify sites, and use the resolver
FrameSupportalready provides:WeavingPipeline(injection) andStructuralWeaver(merge): build the class viaFrameSupport.forLoadTime(loader)instead ofClassFile.of().Verifier.check(...): verify viaFrameSupport.forLoadTime(loader).verify(...)instead ofClassFile.of().verify(...).FrameSupport.forLoadTime(ClassLoader)already exists and does exactly this; it simply hasno callers on the load-time path. Routing both stack-map generation and verification through
the same loader-aware context also keeps build-time and load-time output byte-identical,
which
FrameSupportTestalready asserts.Checklist