H2JVM – A Haskell Library for Writing JVM Bytecode

rowbin1 pts0 comments

H2JVM - A Haskell Library for writing JVM Bytecode - Show and Tell - Haskell Community

= 40rem)" rel="stylesheet" data-target="desktop" />

= 40rem)" rel="stylesheet" data-target="chat_desktop" /><br>= 40rem)" rel="stylesheet" data-target="poll_desktop" />

H2JVM - A Haskell Library for writing JVM Bytecode

Show and Tell

bristermitten

June 1, 2026, 12:06pm

Hi everyone!

I have been working on a new library for writing JVM bytecode with Haskell in a nice, high level way and I’d love some feedback on it! The motivation here is for compilers to the JVM so they can focus on the actual code generation, meanwhile H2JVM takes care of all the messy details like StackMapTable analysis, label/offset resolution, etc.

Here is a quick example taken from the readme. It generates a simple class file with a single method static int add(int, int) which adds 2 numbers:

main :: IO ()<br>main = do<br>-- Define the class name, method descriptor, and access flags<br>let className = "Calculator"<br>methodDesc = MethodDescriptor -- int (int, int)<br>[PrimitiveFieldType JInt, PrimitiveFieldType JInt]<br>(TypeReturn (PrimitiveFieldType JInt))

-- Construct the class using ClassBuilder<br>result putStrLn $ "Error building class: " <> show err<br>Right (classFile, _) -> do<br>-- Serialise the class to a file<br>let path = classFilePath classFile -- returns "Calculator.class"<br>case classFileBytes classFile of<br>Left err -> putStrLn $ "Error generating bytecode: " <> show err<br>Right bytes -> LBS.writeFile path bytes

This is a less contrived example from real usage of the library in my compiler, which shows how label resolution “just works”. This implements the > operator in my language in the way you’d probably expect

IR.BinaryOp op lhs rhs -> do<br>emitExpr lhs<br>emitExpr rhs<br>case op of<br>IR.GreaterThan -> do<br>trueLabel<br>The generated code here looks something like this:

29: blah (pushing lhs and rhs onto stack)<br>32: if_icmpgt 39 -- trueLabel resolved as offset 39<br>35: iconst_0<br>36: goto 40 -- endLabel resolved as offset 40<br>39: iconst_1<br>40: blah blah (code after the binaryop, whatever that may be)

The library is still in very very very early stages (only a very small subset of the instructions and attributes are supported), but I would love some preliminary feedback on things like the design!

Happy to answer any questions anyone might have too

If you’re interested, here’s the GitHub repo: GitHub - ElaraLang/h2jvm: Haskell library for writing JVM bytecode in a high level format · GitHub

Thanks in advance!

15 Likes

jeffstyr

June 3, 2026, 5:01pm

Nice! I like the idea.

What happens if you call addAccessFlag twice (perhaps with different flags)?

1 Like

evincar

June 3, 2026, 5:04pm

Looks good and plainly written, I’ll bookmark it for next time I do something with the JVM

Browsed around and reviewed a little, have a few random suggestions

You can avoid the possibility of an UnmarkedLabel error using a MonadFix instance and rec or mdo

emitNewLabel = do<br>label<br>mdo<br>emit $ JVM.IfICmp (IfGt trueLabel)<br>emit JVM.IConst0<br>emit $ Goto endLabel<br>emit JVM.IConst1<br>trueLabel<br>I’d rather use rec to spell out the scope, but this case is better with mdo because the labels are used throughout

In CodeState instead of storing a list in reverse order you can use a DList Instruction

Code that uses IfCond is repetitive because it’s denormalised

data IfCond label = IfEq label | IfNe label | IfLt label | IfGe label | IfGt label | IfLe label

Instead you can factor:

1x+1x+1x+1x+1x+1x

= (1+1+1+1+1+1)x

= 6x

data If label = If Cond label<br>data Cond = Eq | Ne | Lt | Ge | Gt | Le

Can also keep the current patterns with synonyms like pattern IfEq label = If Eq label and a {-# COMPLETE #-} pragma

In clauses of Pretty instances like this

pretty (InvokeSpecial c n d) =<br>"invokespecial" pretty c <> "." <> pretty n <> pretty d

I like to use ViewPatterns (or a synonym) so the shape of the output is less cluttered by calls to pretty, and it’s only called once if used more than once

pretty (InvokeSpecial (pretty -> c) (pretty -> n) (pretty -> d)) =<br>"invokespecial" c <> "." <> n <> d

I dunno how “pretty” you actually want for the pretty-printing to be, but I find that Pretty instances in general tend to overuse the explicit non-breaking horizontal (hcat, hsep, <>, ) and vertical (vcat, vsep) layouts without enough grouping, and should rather use the grouped combinators that allow breaks between all (cat, sep, surround softline', surround softline) or between any (fillCat, fillSep)

2 Likes

jeffstyr

June 4, 2026, 1:09am

evincar:

instead of storing a list in reverse order you can use a DList

I’m always suspicious of DList in that physically it is essentially storing a list in reverse order, just less clearly: each closure is closing over one new element (actually, over another closure capturing that element) and over the closure recursively referencing the rest of the elements, which is the same structure as a cons cell (in either case, a data structure with two pointers, one pointing to the...

label pretty like library data haskell

Related Articles