RPNXCortado
C++23 JVM class-file, bytecode, validation, and JAR toolkit
Loading...
Searching...
No Matches
Generate your first class

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.

Choose names and a class-file version

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.

rpnx::cortado::class_file_builder builder("tutorial/Generated", "java/lang/Object", {0, 61});
static_cast< void >(builder.add_field(
"invocations",
"I",
rpnx::cortado::field_access_flags::is_public |
rpnx::cortado::field_access_flags::is_static));
constructor.aload({0})
.invokespecial("java/lang/Object", "<init>", "()V")
.append< rpnx::cortado::opcode::return_ >();
static_cast< void >(builder.add_method(
"<init>",
"()V",
rpnx::cortado::method_access_flags::is_public,
constructor));

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 a static method

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:

add.iload({0})
.iload({1})
.append< rpnx::cortado::opcode::iadd >()
.append< rpnx::cortado::opcode::ireturn >();
static_cast< void >(builder.add_method(
"add",
"(II)I",
rpnx::cortado::method_access_flags::is_public |
rpnx::cortado::method_access_flags::is_static,
add));
An owning symbolic JVM code builder.
Definition builders.hpp:356
auto iload(local_variable_index index) -> code_builder &
Loads an int from a local using the shortest valid encoding.

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, validate, and serialize

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.

rpnx::cortado::class_file const generated_class = builder.build();
if (!report.valid())
{
throw std::runtime_error(report.diagnostics.front().message);
}
std::vector< std::byte > const class_bytes = rpnx::cortado::serialize_class_file(generated_class);

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:

./rpnx_cortado_tutorial_codegen ./generated-output
javap -verbose ./generated-output/tutorial/Generated.class

Next: Symbolic control flow and exception regions.