ParkerTenBroeck.github.io/content/blog/projects/generators.md
ParkerTenBroeck 5df85e391a updated style
2026-04-29 23:18:50 -04:00

2.3 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

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);
    }
}