Graftt
annotation-driven bytecode surgery
Install / Use
/learn @junkdog/GrafttREADME
graftt - annotation-driven bytecode surgery
Rewrite existing classes by grafting bytecode from Transplant classes. Transplants are plain java classes and function like templates or patches; annotations define interactions with the recipient class. The entire API consists of 4 annotations.
Transplants have complete access to the recipient class: Existing methods can be wrapped, changed or replaced entirely. Interfaces can be retrofitted and additional fields added. Update annotations on classes, methods and fields.
An agent (java -javaagent) applies transplants at load-time. Alternatively,
graftt-maven-plugin finds and applies transplants within target/classes.
The core module can be used for building custom tools. It imposes few restrictions on
usage and revolves around ASM's tree API.
graftt sprung from artemis-odb/570, refer to it for the discussion leading up to this.
See wiki for more documentation.
Use-cases
- Functionality for editors: callbacks and tighter tooling integration
- Additional debugging capabilities: logging, record stack traces, additional callbacks
- Extending existing functionality for final or otherwise non-extendable classes
- The odd bug fix in imported dependencies
- Add or modify
hashcode()andtoString() - Retrofit classes with additional interfaces
Example Transplant for SomeClass
A third-party class we wish to modify
public class SomeClass {
public final void yo() {
yolo();
}
private void yolo() {
// boo! we want to call "invokedWithTransplant = true"
// here (for some reason or other), but yo() is final
// and can't be extended, and this method is private
//
// ...
//
yoloCalled = true;
}
public boolean yoloCalled = false;
public static boolean invokedWithTransplant = false;
}
Create a transplant class to donate some bytecode
@Graft.Recipient(SomeClass.class) // target to modify
public class SomeClassTransplant {
@Graft.Fuse // fuse with method in SomeClass
private void yolo() { // signature matches SomeClass.yolo()
SomeClass.invokedWithTransplant = true; // whoop-whoop
yolo(); // "recursive continuation", actually invokes SomeClass::yolo
}
}
Resulting class
Once transplanted, decompiling the modified class yields something similar to:
public class SomeClass {
public final void yo() {
yolo();
}
private void yolo() {
SomeClass.invokedWithTransplant = true;
yolo$original();
}
private void yolo$original() {
yoloCalled = true;
}
public boolean yoloCalled = false;
public static boolean invokedWithTransplant = false;
}
API
@Graft.Recipientspecifies which class to transplant to.@Graft.Fusetransplants bytecode over to@Graft.Recipient, translating anyFooTransplantreferences toFoo. Call the original method at any time by invoking the method currently being fused; e.g. FusingFooTransplant::barwithFoo::bar, any call tobar()inside the transplant will point toFoo::bar$originalonce applied.@Graft.Mockto keep the compiler happy when you need to reference fields or methods in the target class. Mocked references point to target class after transplant.@Graft.Annotationsoverrides default configuration for removal and updating of annotations. The default behavior copies all annotations from the transplanted elements to the recipient.- Interfaces implemented by the transplant are added to the recipient.
- All fields and methods, except those annotated with
@Graft.Mock, are copied to recipient.
Nice to have, but not now:
@Graft.Remove: Remove field or method from target class.
Caveats
- You're working against internal implementation; there are no semver guarantees
- No rewiring of parent type on target class
- No
@Graft.Fusefor constructors; nice to have, but not initially - No GWT support
- No android support (possible with a custom gradle task)
Usage
Current $VERSION is 0.3.0
Maven
<dependency>
<groupId>net.onedaybeard.graftt</groupId>
<artifactId>api</artifactId>
<version>${VERSION}</version>
</dependency>
Gradle
implementation "net.onedaybeard.graftt:api:${VERSION}"
Agent: Download
$ java -ea -javaagent:agent-${VERSION}.jar ...
Related Skills
node-connect
339.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.8kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
339.1kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
83.8kCommit, push, and open a PR
