Repository navigation
[Bug]: Load-time weaving resolves the class hierarchy through the system class loader, so weaves into application types fail to verify #14
Description
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'sStackMapGeneratorthrows
IllegalArgumentException: Could not resolve class <T>out ofClassFile.transformClass(...)
while generating frames; or- the class fails
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:
- Injection and merge build the woven class with a plain
ClassFile.of()
(WeavingPipeline,StructuralWeaver) — the defaultClassHierarchyResolver, which
reflects 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 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=strictOutput 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) andStructuralWeaver(merge): build the class via
FrameSupport.forLoadTime(loader)instead ofClassFile.of().Verifier.check(...): verify viaFrameSupport.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
- I searched the existing issues and this is not a duplicate
- I checked the diagnostic reference for the code this reported
- I am on the latest released version, or I have said why I am not
Activity
Splatcrafter commentedon Sep 15, 2026
Fixed in #15 and shipping in 0.1.1: the defining class loader is threaded from the agent through injection, merge and verification, all building through
FrameSupport.forLoadTime(loader), so load-time weaves into application types resolve and verify.