ParkerTenBroeck.github.io/content/blog/projects/generators.md

2.7 KiB

+++ title = "Generators" description = "A java 24 library that adds stackless coroutines to standard java code by transforming method bytecode into state machines at load time" date = 2025-05-07

[taxonomies] tags = ["java", "bytecode"] category = ["project"] +++

With the release of Java 24 came the official Class-File API giving a stable and (relatively) ergonomic interface for reading, modifying, and writing java classes/bytecode at runtime with no external dependencies. Seeing this I decided to play around with it and see what fun I could have.

Bytecode, Stack Machines, and Registers

Java (and other JVM languages) are compiled to a set of bytecode instructions. These instructions primarily operate on a stack (fifo) using 0-n operands from the top and pushing 0-1 values back onto the stack.

An example would be the iadd instruction which pops two integers off the stack and pushes the sum of them.

A more complex example would be something like invokevirtual <methodref_index> which calls a method on a class. methodref_index is an index (in this case a 2 byte index) into the class files constant_pool1

Note

Highlights information that users should take into account, even when skimming.

Tip

Optional information to help a user be more successful.

Important

Crucial information necessary for users to succeed.

Warning

Critical content demanding immediate user attention due to potential risks.

Caution

Negative potential consequences of an action.

Stack manipulation

Sometimes it's

Registers

Stack Machine and Registers

Decompilation

Interlude, what's a stack map?

Tracking everything everywhere

Putting everything together

Injection

Take a look at the GitHub

public static Future<Void, IOException> echo(
    @Cancellation("close") Socket socket
) throws IOException {
    try(socket){
        var buffer = ByteBuffer.allocate(4096*2);
        while(true){
            bytes_received = socket.read(buffer).await() + bytes_received;
            buffer.flip();
            bytes_sent = socket.write_all(buffer).await() + bytes_sent;
            buffer.clear().limit(buffer.capacity());
        }
    }
}
public static Gen<Long, Void> primes() {
    long number = 1;
    Gen.yield(2L);
    outer: while(true){
        number += 2;
        for(long i=2; i <= Math.sqrt(number); i ++){
            if(number%i==0)continue outer;
        }
        Gen.yield(number);
    }
}