-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: canonicalize based on ClassModel
- Loading branch information
1 parent
330bc1e
commit 25eab72
Showing
8 changed files
with
270 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
canonicalize/src/main/java/org/example/canonicalize/BytecodeTransformer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package org.example.canonicalize; | ||
|
||
import static org.example.canonicalize.BytecodeTransformerCore.writeClassfile; | ||
|
||
import java.io.File; | ||
import java.util.concurrent.Callable; | ||
import picocli.CommandLine; | ||
|
||
@CommandLine.Command( | ||
name = "bytecode-transformer", | ||
mixinStandardHelpOptions = true, | ||
description = "Transforms Java bytecode") | ||
public class BytecodeTransformer implements Callable<Integer> { | ||
|
||
@CommandLine.Parameters(index = "0", description = "The Java class file to transform") | ||
private File javaClassfile; | ||
|
||
@CommandLine.Option( | ||
names = {"-o", "--output"}, | ||
description = "The output file to write the transformed bytecode", | ||
required = true) | ||
private File outputFile; | ||
|
||
@Override | ||
public Integer call() throws Exception { | ||
BytecodeTransformerCore transformer = new BytecodeTransformerCore(javaClassfile); | ||
writeClassfile(outputFile, transformer.getTransformedBytes()); | ||
return 0; | ||
} | ||
|
||
public static void main(String[] args) { | ||
int exitCode = new CommandLine(new BytecodeTransformer()).execute(args); | ||
System.exit(exitCode); | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
canonicalize/src/main/java/org/example/canonicalize/BytecodeTransformerCore.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package org.example.canonicalize; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.lang.classfile.ClassFile; | ||
import java.lang.classfile.ClassFileBuilder; | ||
import java.lang.classfile.ClassModel; | ||
import java.nio.file.Files; | ||
|
||
public class BytecodeTransformerCore { | ||
private File javaClassfile; | ||
|
||
public BytecodeTransformerCore(File javaClassfile) { | ||
this.javaClassfile = javaClassfile; | ||
} | ||
|
||
public byte[] getTransformedBytes() throws IOException { | ||
ClassModel bytecodeModel = readClassFile(javaClassfile); | ||
ClassModel transformedModel = ConstantPoolTransform.transform(bytecodeModel); | ||
|
||
// implementation copied from java.lang.classfile.ClassTransform.ACCEPT_ALL | ||
// ClassFileBuilder::with transformer does not modify the class file | ||
return ClassFile.of().transform(transformedModel, ClassFileBuilder::with); | ||
} | ||
|
||
private static ClassModel readClassFile(File file) throws IOException { | ||
return ClassFile.of().parse(Files.readAllBytes(file.toPath())); | ||
} | ||
|
||
public static void writeClassfile(File file, byte[] bytes) throws IOException { | ||
Files.write(file.toPath(), bytes); | ||
} | ||
} |
122 changes: 122 additions & 0 deletions
122
canonicalize/src/main/java/org/example/canonicalize/ConstantPoolTransform.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package org.example.canonicalize; | ||
|
||
import java.lang.classfile.AccessFlags; | ||
import java.lang.classfile.ClassBuilder; | ||
import java.lang.classfile.ClassElement; | ||
import java.lang.classfile.ClassFile; | ||
import java.lang.classfile.ClassFileVersion; | ||
import java.lang.classfile.ClassModel; | ||
import java.lang.classfile.ClassTransform; | ||
import java.lang.classfile.CustomAttribute; | ||
import java.lang.classfile.FieldModel; | ||
import java.lang.classfile.Interfaces; | ||
import java.lang.classfile.MethodModel; | ||
import java.lang.classfile.Superclass; | ||
import java.lang.classfile.attribute.CompilationIDAttribute; | ||
import java.lang.classfile.attribute.DeprecatedAttribute; | ||
import java.lang.classfile.attribute.EnclosingMethodAttribute; | ||
import java.lang.classfile.attribute.InnerClassesAttribute; | ||
import java.lang.classfile.attribute.ModuleAttribute; | ||
import java.lang.classfile.attribute.ModuleHashesAttribute; | ||
import java.lang.classfile.attribute.ModuleMainClassAttribute; | ||
import java.lang.classfile.attribute.ModulePackagesAttribute; | ||
import java.lang.classfile.attribute.ModuleResolutionAttribute; | ||
import java.lang.classfile.attribute.ModuleTargetAttribute; | ||
import java.lang.classfile.attribute.NestHostAttribute; | ||
import java.lang.classfile.attribute.NestMembersAttribute; | ||
import java.lang.classfile.attribute.PermittedSubclassesAttribute; | ||
import java.lang.classfile.attribute.RecordAttribute; | ||
import java.lang.classfile.attribute.RuntimeInvisibleAnnotationsAttribute; | ||
import java.lang.classfile.attribute.RuntimeInvisibleTypeAnnotationsAttribute; | ||
import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute; | ||
import java.lang.classfile.attribute.RuntimeVisibleTypeAnnotationsAttribute; | ||
import java.lang.classfile.attribute.SignatureAttribute; | ||
import java.lang.classfile.attribute.SourceDebugExtensionAttribute; | ||
import java.lang.classfile.attribute.SourceFileAttribute; | ||
import java.lang.classfile.attribute.SourceIDAttribute; | ||
import java.lang.classfile.attribute.SyntheticAttribute; | ||
import java.lang.classfile.attribute.UnknownAttribute; | ||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.stream.Collectors; | ||
|
||
public class ConstantPoolTransform { | ||
|
||
private static List<Integer> correctOrder = new ArrayList<>(); | ||
private static Map<Integer, Integer> oldIndexToNewIndex = new HashMap<>(); | ||
|
||
public static ClassModel transform(ClassModel classModel) { | ||
// this_class gets index 0 (1 in constant pool) | ||
correctOrder.add(classModel.thisClass().index()); | ||
establishCorrectOrder(classModel); | ||
|
||
return classModel; | ||
} | ||
|
||
private static void establishCorrectOrder(ClassModel classModel) { | ||
ClassFile.of().transform(classModel, new ClassTransform() { | ||
|
||
@Override | ||
public void accept(ClassBuilder builder, ClassElement element) { | ||
switch (element) { | ||
case AccessFlags accessFlags -> { | ||
// Access flags are not in the constant pool | ||
} | ||
case ClassFileVersion classFileVersion -> { | ||
// Class file version is not in the constant pool | ||
} | ||
case CustomAttribute customAttribute -> {} | ||
case FieldModel fieldModel -> { | ||
correctOrder.add(fieldModel.fieldName().index()); | ||
correctOrder.add(fieldModel.fieldType().index()); | ||
} | ||
case Interfaces interfaces -> { | ||
correctOrder.addAll(interfaces.interfaces().stream() | ||
.map(i -> i.index()) | ||
.collect(Collectors.toUnmodifiableList())); | ||
} | ||
case MethodModel methodModel -> { | ||
correctOrder.add(methodModel.methodName().index()); | ||
correctOrder.add(methodModel.methodType().index()); | ||
|
||
// methodModel.code().ifPresent(code -> { | ||
// correctOrder.add(code.elementList()); | ||
// }); | ||
|
||
} | ||
case Superclass superclass -> { | ||
correctOrder.add(superclass.superclassEntry().index()); | ||
} | ||
case CompilationIDAttribute compilationIDAttribute -> {} | ||
case DeprecatedAttribute deprecatedAttribute -> {} | ||
case EnclosingMethodAttribute enclosingMethodAttribute -> {} | ||
case InnerClassesAttribute innerClassesAttribute -> {} | ||
case ModuleAttribute moduleAttribute -> {} | ||
case ModuleHashesAttribute moduleHashesAttribute -> {} | ||
case ModuleMainClassAttribute moduleMainClassAttribute -> {} | ||
case ModulePackagesAttribute modulePackagesAttribute -> {} | ||
case ModuleResolutionAttribute moduleResolutionAttribute -> {} | ||
case ModuleTargetAttribute moduleTargetAttribute -> {} | ||
case NestHostAttribute nestHostAttribute -> {} | ||
case NestMembersAttribute nestMembersAttribute -> {} | ||
case PermittedSubclassesAttribute permittedSubclassesAttribute -> {} | ||
case RecordAttribute recordAttribute -> {} | ||
case RuntimeInvisibleAnnotationsAttribute runtimeInvisibleAnnotationsAttribute -> {} | ||
case RuntimeInvisibleTypeAnnotationsAttribute runtimeInvisibleTypeAnnotationsAttribute -> {} | ||
case RuntimeVisibleAnnotationsAttribute runtimeVisibleAnnotationsAttribute -> {} | ||
case RuntimeVisibleTypeAnnotationsAttribute runtimeVisibleTypeAnnotationsAttribute -> {} | ||
case SignatureAttribute signatureAttribute -> {} | ||
case SourceDebugExtensionAttribute sourceDebugExtensionAttribute -> {} | ||
case SourceFileAttribute sourceFileAttribute -> { | ||
correctOrder.add(sourceFileAttribute.sourceFile().index()); | ||
} | ||
case SourceIDAttribute sourceIDAttribute -> {} | ||
case SyntheticAttribute syntheticAttribute -> {} | ||
case UnknownAttribute unknownAttribute -> {} | ||
} | ||
} | ||
}); | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
canonicalize/src/test/java/org/example/canonicalize/ConstantPoolTransformerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package org.example.canonicalize; | ||
|
||
import static org.example.canonicalize.BytecodeTransformerCore.writeClassfile; | ||
|
||
import java.io.IOException; | ||
import java.nio.file.Path; | ||
import org.junit.jupiter.api.Test; | ||
|
||
public class ConstantPoolTransformerTest { | ||
|
||
private static final Path TEST_RESOURCE = Path.of("src/test/resources"); | ||
|
||
@Test | ||
void test() throws IOException { | ||
BytecodeTransformerCore transformer = | ||
new BytecodeTransformerCore(TEST_RESOURCE.resolve("First.class").toFile()); | ||
writeClassfile(TEST_RESOURCE.resolve("First-transformed.class").toFile(), transformer.getTransformedBytes()); | ||
} | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters