Applies transformations to .java and .class files using a unified high level API.
JavaTransformer is organized into several key packages:
dev.minco.javatransformer.api
: Public API interfaces and classesdev.minco.javatransformer.internal
: Internal implementation detailsdev.minco.javatransformer.internal.asm
: ASM-specific utilities and implementationsdev.minco.javatransformer.internal.util
: General utility classes
-
Unified Abstraction: The project provides a unified interface (
ClassInfo
,MethodInfo
,FieldInfo
) for working with both bytecode and source code, allowing transformers to be written agnostic of the underlying format. -
Multi-format Support: JavaTransformer can load, transform, and save both bytecode (.class) and source code (.java) files, seamlessly switching between formats as needed.
-
Flexible Transformation: The transformer system allows for both general transformers (applied to all classes) and targeted transformers (applied to specific classes), providing fine-grained control over the transformation process.
-
Code Fragment Insertion: The
CodeFragment
system allows for inserting new code into existing methods, supporting both bytecode and source code formats. This enables complex transformations that go beyond simple structural changes. -
Type Resolution: The
ResolutionContext
andClassPath
components provide robust type resolution capabilities, ensuring that transformations can accurately work with types across different classes and packages. -
Bytecode Generation: For bytecode transformations, JavaTransformer uses ASM to generate and modify bytecode, providing low-level control over the JVM instructions.
-
Source Code Generation: For source code transformations, JavaTransformer uses JavaParser to generate and modify Java source code, preserving formatting and comments where possible.
The JavaTransformer
class (src/main/java/dev/minco/javatransformer/api/JavaTransformer.java
) serves as the main entry point for the transformation process. It orchestrates the loading, transformation, and saving of Java classes and source files.
Key implementation details:
- Maintains lists of general and targeted transformers
- Uses a
ClassPath
object for type resolution - Implements separate logic for handling JAR files and folders
- Delegates actual transformation to
ClassInfo
implementations
The ClassInfo
interface provides a unified abstraction for both bytecode and source code representations of Java classes.
ByteCodeInfo
represents classes loaded from bytecode (.class files).
Implementation details:
- Uses ASM's
ClassNode
as the underlying representation (see ASM ClassNode documentation) - Lazily parses bytecode using
AsmUtil.getClassNode()
- Implements
MethodInfo
andFieldInfo
using ASM'sMethodNode
andFieldNode
SourceInfo
represents classes loaded from source code (.java files) using JavaParser.
Implementation details:
- Uses JavaParser's
TypeDeclaration
as the underlying representation - Parses source code using JavaParser's
StaticJavaParser
- Implements
MethodInfo
andFieldInfo
using JavaParser'sMethodDeclaration
andFieldDeclaration
-
Loading:
- JAR files are processed using
ZipInputStream
- Folders are traversed using
Files.walkFileTree()
- Files are categorized as bytecode, source code, or other based on file extension
- JAR files are processed using
-
Parsing:
- Bytecode: ASM's
ClassReader
is used to create aClassNode
- Source code: JavaParser is used to create a
CompilationUnit
and extractTypeDeclaration
s
- Bytecode: ASM's
-
Abstraction:
ByteCodeInfo
orSourceInfo
objects are created, wrapping the parsed data- These objects implement the
ClassInfo
interface, providing a unified API
-
Transformation:
- General transformers are applied to all
ClassInfo
objects - Targeted transformers are applied to specific classes based on name matching
- Transformers modify the
ClassInfo
,MethodInfo
, andFieldInfo
objects
- General transformers are applied to all
-
Code Generation:
- Bytecode: Modified
ClassNode
objects are written using ASM'sClassWriter
- Source code: Modified AST nodes are converted back to source code using JavaParser's pretty-printing capabilities
- Bytecode: Modified
-
Saving:
- Transformed classes are written back to JAR files or folders
- The original file structure is preserved
The CodeFragment
interface represents a piece of code that can be inserted into a method, supporting both bytecode and source code formats.
Represents bytecode instructions.
Implementation details:
- Stores a list of ASM
AbstractInsnNode
objects - Can be created from raw bytecode instructions or higher-level representations
Represents source code snippets.
Implementation details:
- Stores JavaParser
Statement
orExpression
objects - Can be created from string literals or pre-parsed AST nodes
The CodeFragmentGenerator
interface provides methods to generate CodeFragment
objects from various input formats.
Generates ByteCodeFragment
objects.
Implementation details:
- Uses ASM to parse and generate bytecode instructions
- Handles conversion between high-level code representations and low-level bytecode
Generates SourceCodeFragment
objects.
Implementation details:
- Uses JavaParser to parse source code snippets into AST nodes
- Handles conversion between string literals and structured AST representations
The ResolutionContext
class provides context for resolving types and members within a class or method.
Implementation details:
- Manages imports, type parameters, and scoping information
- Uses the
ClassPath
to resolve external types - Implements separate resolution logic for bytecode and source code contexts
The ClassPath
class represents the classpath used for resolving types during transformation.
Implementation details:
- Supports both file system and in-memory class loading
- Caches resolved classes for performance
- Handles resolution of array types and primitives
Provides utility methods for working with ASM.
Key functionalities:
- Reading and writing class files
- Converting between ASM and JavaTransformer type representations
- Generating bytecode for common operations (e.g., method calls, field access)
Provides utility methods for working with JavaParser.
Key functionalities:
- Parsing and manipulating Java source code
- Converting between JavaParser and JavaTransformer type representations
- Generating AST nodes for common operations
Provides utility methods for working with JVM-related concepts.
Key functionalities:
- Converting between class names and file names
- Handling JVM type descriptors and signatures
JavaTransformer employs a sophisticated class loading and resolution system to support its transformation capabilities:
-
ClassPath Hierarchy: The
ClassPath
interface and its implementations (FileClassPath
) provide a flexible way to represent and search for classes across multiple sources, including the system classpath, JARs, and directories. -
Lazy Initialization: The
FileClassPath
uses lazy initialization to load classes only when needed, improving performance for large classpaths. -
Multiple Formats: JavaTransformer can load and process both
.class
(bytecode) and.java
(source code) files, providing a unifiedClassInfo
representation for both. -
Resolution Context: The
ResolutionContext
class plays a crucial role in resolving types, methods, and fields within the context of a specific class or method. This context-aware resolution is essential for accurate transformations, especially when dealing with complex type hierarchies or generic types. -
Type Parameter Resolution: JavaTransformer includes logic for resolving generic type parameters, as seen in the
Expressions
class, allowing it to handle complex generic scenarios in both bytecode and source code transformations.
These class loading and resolution capabilities enable JavaTransformer to perform accurate and context-aware transformations across a wide range of Java code structures and formats.
JavaTransformer aims to maintain compatibility with future JDK versions through several mechanisms:
-
Fallback to Reflection: As noted in the
ClassPaths
class, there's a planned feature to fall back to reflection if ASM cannot load JDK classes:// TODO: self-test, if we can't load JDK classes with current asm version fall back to reflection
This fallback mechanism would allow JavaTransformer to understand the structure of JDK classes for use in
ResolutionContext
, even if it cannot modify them directly. While this limits the ability to transform these classes, it ensures that type resolution and other critical functionalities remain operational. -
Modular JDK Support: The
SystemClassPath
implementation already includes support for the modular JDK structure introduced in Java 9:val fs = FileSystems.getFileSystem(URI.create("jrt:/")); return new FileClassPath(null, Collections.singletonList(fs.getPath("modules/java.base/")));
This allows JavaTransformer to work with both traditional and modular JDK structures.
-
Abstract Representations: By using abstract representations like
ClassInfo
,MethodInfo
, andFieldInfo
, JavaTransformer can potentially adapt to changes in underlying class file formats without requiring extensive modifications to the core API.
While these mechanisms provide a degree of forward compatibility, users should be aware that full support for new JDK features may require updates to JavaTransformer, particularly for transformations involving new language constructs or bytecode instructions.
To extend or customize JavaTransformer, consider the following areas:
-
Custom Transformers: Implement the
Transformer
interface to create new transformation logic. -
New CodeFragment Types: Extend
CodeFragment
to support new ways of representing and inserting code. -
Additional File Formats: Extend the loading and saving logic in
JavaTransformer
to support new file formats beyond JAR and folders. -
Enhanced Type Resolution: Extend
ResolutionContext
orClassPath
to improve type resolution capabilities, especially for complex scenarios involving generics or lambda expressions. -
Optimization Passes: Implement transformers that perform bytecode or source code optimizations.
-
Integration with Other Tools: Create adapters or wrappers to integrate JavaTransformer with other bytecode or source code manipulation tools.