Search code examples
javajvmbytecode

Generating java class file headers from jvm bytecode


Currently I am tinkering with the jvm bytecode instructions. I made a simple compiler that given source code (C like style) generates valid jvm bytecode representation. For example, the following code:

float x = 3;
float y = 4.5;
float z = x + y;
print z;

Compiles to:

ldc 3
i2f
fstore 1
ldc 4.5
fstore 2
fload 1
fload 2
fadd
fstore 3
getstatic java/lang/System/out Ljava/io/PrintStream;
fload 3
invokevirtual java/io/PrintStream/println(F)V
return

(I know the generated java code is not the most efficient as of now, but that is not the point).

Using a Java Bytecode Editor, I loaded a compiled main class and replaced the main method code with my code. After that, I was able to run the class file with my code perfectly fine. My question is, is there a tool/script without UI that can take java bytecode and generate the appropriate headers for the class file (in other words, take the bytecode and make a valid class file out of it). I guess I can write a script myself, but that would take some time that I might not have now.


Solution

  • The Krakatau assembler allows you to write bytecode in a textual format and assembles it into a classfile, handling all the binary encoding details for you.

    It's similar to the older Jasmin assembler, but with minor syntax changes in order to remove ambiguity and to support classfile features that Jasmin can't handle. Unlike Jasmin, it fully supports the entire Java 8 classfile format and optionally allows full control over the binary representation of the classfile.

    For example, here's a class using lambdas in Krakatau assembly format.

    .version 52 0
    .class public super LambdaTest1
    .super java/lang/Object
    
    .method public <init> : ()V
        .code stack 1 locals 1
            aload_0
            invokespecial Method java/lang/Object <init> ()V
            return
        .end code
    .end method
    
    .method public static varargs main : ([Ljava/lang/String;)V
        .code stack 4 locals 2
            invokedynamic InvokeDynamic invokeStatic Method java/lang/invoke/LambdaMetafactory metafactory (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; MethodType (J)J MethodHandle invokeStatic Method LambdaTest1 lambda$main$0 (J)J MethodType (J)J : applyAsLong ()Ljava/util/function/LongUnaryOperator;
            astore_1
            getstatic Field java/lang/System out Ljava/io/PrintStream;
            aload_1
            ldc2_w 42L
            invokeinterface InterfaceMethod java/util/function/LongUnaryOperator applyAsLong (J)J 3
            invokevirtual Method java/io/PrintStream println (J)V
            return
        .end code
    .end method
    
    .method private static synthetic lambda$main$0 : (J)J
        .code stack 4 locals 2
            lload_0
            lload_0
            l2i
            lshl
            lreturn
        .end code
    .end method
    
    .innerclasses
        java/lang/invoke/MethodHandles$Lookup java/lang/invoke/MethodHandles Lookup public static final
    .end innerclasses
    
    .end class