A Java class file library used to read and write java bytecode written in Scala 3.
This library will attempt to counteract malicious bytecode which will crash other similar libraries.
Add HippoCafe to your project.
repositories {
maven("https://jitpack.io")
}
dependencies {
implementation("rip.hippo:HippoCafe:2.2.9")
}
Or using Scala SBT
resolvers += "jitpack" at "https://jitpack.io"
libraryDependencies += "rip.hippo" % "HippoCafe" % "2.2.9"
With the ClassReader
you are able to read class files via a Array[Byte]
or an InputStream
.
It is recommended to use scala.util.Using
to automatically close the reader.
Using(new ClassReader(...)) {
classReader =>
// code
}
The ClassFile
gives you access to change every aspect of the class file, if you are familiar with any other Class File library then HippoCafe will be easy to adapt to.
val classFile: ClassFile = classReader.classFile
val methods: ListBuffer[MethodInfo] = classFile.methods // list of methods
val fields: ListBuffer[FieldInfo] = classfile.fields // list of fields
methods.foreach(method => {
val instructions: ListBuffer[Instruction] = method.instructions // list of method instructions
})
Once you have a class file that needs to be written you are able to use the ClassWriter
It is recommended to use scala.util.Using
to automatically close the writer.
Using(new ClassWriter(classFile)) {
classWriter =>
val bytecode: Array[Byte] = classWriter.write // class files bytecode
}
There are optional flags you can pass into the ClassWriter
- calculateMaxes - Calculate the max stack and max locals of each method
- generateFrames - Generates stack map frames for each method
- appendConstantPool - Preserves the original classes constant pool, and appends to it if needed.
The options are applied in a builder pattern style fashion, for an example if you wanted a ClassWriter
to calculateMaxes and generateFrames you would do as such:
new ClassWriter(classFile).calculateMaxes.generateFrames
HippoCafe provides an intuitive class file builder to easily build class files.
val classFile: Classfile = ClassBuilder(
SE8,
"MyClass",
"java/lang/Object",
ACC_PUBLIC, ACC_FINAL
).method(
"<init>",
"()V",
ACC_PUBLIC
).apply(instructions => {
instructions += SimpleInstruction(ALOAD_0)
instructions += ReferenceInstruction(INVOKESPECIAL, "java/lang/Object", "<init>", "()V")
instructions += SimpleInstruction(RETURN)
}).method(
"main",
"([Ljava/lang/String;)V",
ACC_PUBLIC, ACC_STATIC
).apply(instructions => {
instructions += ReferenceInstruction(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;")
instructions += ConstantInstruction(StringConstant("Build from HippoCafe class builder!"))
instructions += ReferenceInstruction(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V")
instructions += SimpleInstruction(RETURN)
}).result