|
RPNXCortado
C++23 JVM class-file, bytecode, validation, and JAR toolkit
|
This tutorial creates a Java 17 class named tutorial.Generated. It adds a field and a valid default constructor, builds methods from symbolic bytecode, validates the resulting model, and serializes it as a .class file.
JVM class names are internal names: packages use /, not ., and there is no leading L or trailing ;. The version pair {0, 61} selects Java 17. Cortado supports newer versions, but Java 17 is a practical baseline when generated artifacts must run on widely deployed VMs.
class_file_builder begins with public and ACC_SUPER class flags. Use access_flags() when generating an interface, annotation, enum, module, final class, or non-public class. add_field validates the descriptor and interns the name and descriptor, but it deliberately does not invent field attributes such as ConstantValue.
The constructor illustrates the two instruction layers:
aload({0}) selects the shortest valid local-variable encoding and uses wide automatically for large indexes.invokespecial(...) interns the owner, method name, and descriptor and emits the compatible constant-pool reference.append<opcode::return_>() appends an operand-free instruction whose type is checked at compile time.Operand-bearing opcodes do not work with the template append overload. Use a symbolic convenience such as bipush, iload, or getfield, or append the corresponding exact instruction alternative as shown in Exact instructions and the constant pool.
add_method receives a complete JVM method descriptor, access flags, and a code_builder. For example, the descriptor (I)I means one int parameter and an int result. Static parameters start at local slot zero; instance methods reserve slot zero for this.
The tutorial executable adds sumTo(int) using the code shown in Symbolic control flow and exception regions. A smaller arithmetic method has this shape:
By default, finalization resolves symbolic constants and labels, computes max_stack and max_locals, and creates verifier frames when the class-file version needs them. You do not specify byte offsets or instruction widths.
build() returns an owning snapshot. Calling it does not consume the builder, so a code generator can keep adding members and request another snapshot later.
validate_class_file returns structured diagnostics and does not throw for model errors. Strict serialize_class_file validates again and throws validation_error rather than emitting an invalid class. Keeping the explicit validation step is useful in a compiler because it preserves the diagnostic code and location instead of reducing the failure to one exception message.
The runnable example writes tutorial/Generated.class and tutorial-codegen.jar: