#GIR GObject subclass generator

    gir/subclass is the development-time generator for concrete GObject subclasses implemented in MoonBit. It lives in the tonyfettes/gir module so it reads the same GIR parser, model, gir.json, parent chain, C types, and virtual-method signatures as the ordinary binding generator.

    Only tonyfettes/gir/subclass/parser imports moonbitlang/parser. Generated GLib, GObject, GIO, GTK, and application packages have no parser dependency.

    #Input

    The attribute declares the public MoonBit wrapper, its immediate GIR parent, and the MoonBit state retained by every native instance:

    #gobject.subclass(name=CustomButton, parent="@gtk.Button", gtype_name="ExampleCustomButton")
    struct CustomButtonState {
    mut count : Int
    }

    Keep the attribute application on one line. The current MoonBit compiler accepts package-qualified custom attributes in this form, but treats a newline directly after ( as the end of the top-level attribute.

    The names have one meaning each:

    • name is the generated public MoonBit GObject class name.
    • parent is the immediate external GObject class, written as a string containing its MoonBit import alias and type, such as "@gtk.Button".
    • The annotated struct is the MoonBit state retained by the native instance.
    • gtype_name is optional. It is the first argument to G_DEFINE_TYPE, so it must be a C identifier and becomes the process-global GType name. The default is MoonBit followed by name.

    Implementing a generated V<Class><Method> trait explicitly selects one C class-function pointer to override. The trait has one required method and no fabricated default implementation:

    impl @gtk.VButtonClicked for CustomButtonState with clicked(self, button) {
    self.count = self.count + 1
    CustomButton::parent_clicked(button)
    }

    Unselected slots, such as GtkButtonClass.activate in this example, are not touched and continue to use the inherited C implementation. Calling CustomButton::parent_clicked(button) is an explicit request to invoke the GtkButtonClass.clicked function pointer saved before the generated class installed its MoonBit callback. If that inherited Unit-returning pointer is NULL, the generated parent wrapper returns without calling anything.

    #Generate

    From this repository's workspace:

    moon runwasm gir/subclass -- \ path/to/custom_button.mbt

    From another module, use moonx to run a pinned generator release from Mooncakes:

    moonx tonyfettes/gir/subclass@0.3.2 -- \ path/to/custom_button.mbt

    The generator infers:

    • the parent binding package from a qualified parent such as @gtk.Button;
    • the canonical binding package path from the input package's moon.pkg imports, including custom aliases;
    • gir.json from the binding checkout or the consuming module's .mooncakes;
    • a C symbol prefix from the subclass GType name;
    • the output directory from the input file; and
    • the ordered GIR search path from GI_GIR_PATH, XDG_DATA_DIRS, Homebrew, MacPorts, or the standard Unix data directories.

    An unqualified parent is rejected. Virtual-method traits identify the class slot being overridden, not the binding that owns the immediate parent; for example, @gobject.VObjectDispose must not make an @gtk.Button subclass load the GObject binding as its parent.

    If moon.pkg imports "tonyfettes/pango" @rendering, write parent="@rendering.Renderer". Generated MoonBit uses @rendering, while configuration discovery reads gir.json from the canonical .mooncakes/tonyfettes/pango dependency path. The alias must be in the ordinary import block used by the subclass source, not only a test import.

    Use --config, --symbol-prefix, --output, or --gir-dir only to override an inferred value. Repeat --gir-dir to search multiple explicit directories in order; each GIR namespace is resolved independently, so the primary GIR and its dependencies may come from different directories. --symbol-prefix must be a C identifier unique to the consuming MoonBit package.

    For repeatable generation during development, call a pinned release from the package rule:

    rule(
    name: "generate-subclass",
    command: "moonx tonyfettes/gir/subclass@0.3.2 -- \"$mod_dir/$input\"",
    )

    Declare both generated .mbt and .c paths in the matching dev_build output array. Moon tracks both outputs, while only the .mbt file is added to the package source set. A pinned release avoids changing generated code when a new generator version is published.

    The command emits:

    • custom_button.generated.mbt: CustomButton, ICustomButton, parent I<Class> implementations, selected parent_<method> calls, and CustomButton::new(state).
    • custom_button.generated.c: instance/class structs, G_DEFINE_TYPE, selected class-slot assignments, C-to-MoonBit callbacks, explicit parent calls, and retained-state disposal.

    List the generated C file in the consuming package's native stubs, for example:

    options(
    link: { "native": { "stub-cc-flags": "${build.MY_GTK_CC_FLAGS}" } },
    "native-stub": [ "custom_button.generated.c" ],
    )

    MY_GTK_CC_FLAGS must be defined by the consuming module's prebuild command (normally from pkg-config --cflags gtk4). A build variable produced inside the imported tonyfettes/gtk module is not visible to the consuming module's own native stub. See examples/build.js and examples/gobject_subclass for a working package configuration.

    #Current ABI boundary

    The subclass generator supports Unit-returning instance virtual methods with ordinary input parameters. The currently supported parameter forms are:

    • GLib signed/unsigned integers, Bool, Float, and Double;
    • selected non-bitfield GIR enums and selected primitive or record aliases;
    • UTF-8 strings, including nullable strings represented as String?;
    • selected GObject classes and interfaces; and
    • selected records with a safe copy/free lifecycle, including GIR boxed types, records with configured record-lifecycles, and plain value records.

    For a borrowed GObject callback parameter, generated C takes a new reference before constructing the MoonBit wrapper. For a borrowed record, it makes a boxed copy, calls the configured copy function, or copies the plain C value. The wrapper finalizer releases that owned reference or copy. Callback code can therefore retain Font, GlyphItem, GlyphString, LayoutRun, or AttrShape after the native virtual-method invocation returns. Nullable UTF-8 input is copied into MoonBit Bytes before it is decoded.

    The generated parent_<method> has the same public MoonBit parameter types as the selected virtual-method trait. Its private C-facing declaration performs the inverse conversion before invoking the saved parent class-function pointer. See examples/pango_renderer_subclass for an application-facing example of all eleven PangoRenderer virtual methods. Application authors only write the MoonBit subclass input; the generator emits its required C stub. The handwritten native driver under examples/internal/pango_renderer_subclass_lifetime_test only verifies binding ownership and is not application code.

    Out and inout parameters, raising virtual methods, and non-Unit returns are filtered out before ordinary generated traits are published. Transferred input references, bitfields, and nullable objects/records are rejected when the concrete subclass generator validates the selected override.

    The native instance retains one MoonBit state reference. dispose clears the callback fields, releases that reference exactly once, and chains to the parent dispose. A callback temporarily retains the state while MoonBit runs because the method may release the last external object box and trigger dispose.

    Each generated virtual-method trait contains exactly one required method. A state selects only the traits it implements, and the generated C class installs only those selected function pointers. Every other native function pointer remains inherited.

    SubclassCommandError

    type SubclassCommandError derive(
    Debug
    )

    SubclassCommand

    type SubclassCommand

    SubclassCommandOptions

    type SubclassCommandOptions

    Raw CLI values. Optional overrides stay separate from the resolved command so the common one-input invocation can discover every development detail.