PERLCLASSGUTS(1perl)

πŸ“¦
version 5.44.0 Β· 2 versions
, in arch rolling

✨ NAME ✨

perlclassguts β€” the actual tea on how feature 'class' and the whole class syntax situation works inside the interpreter πŸ’… (this is the perlclassguts(1perl) page from the Perl Programmers Reference Guide, bestie, and we're going in)


πŸ¦‹ DESCRIPTION πŸ¦‹

Okay so this doc? It's the deep-lore version. It's got all the in-depth info about how the perl interpreter itself pulls off the feature 'class' syntax and how the whole thing behaves. It is not a beginner how-to. If you want the "how do I actually use this" guide, go read perlclass.

The reader (that's you, babe) is assumed to already be generally comfy with perl interpreter internals overall. And for a more general overview of these details, check out perlguts too. πŸ“š


πŸŽ€ DATA STORAGE πŸŽ€

πŸ‘‘ Classes

A class is fundamentally a package, no cap. It lives in the symbol table as an HV with an aux structure, exactly like a non-class package does. What makes it a class and not just some regular package? The fact that the HvSTASH_IS_CLASS() macro returns true on it. That's the whole vibe check right there.

All the extra info about it being a class is stored in the struct xpvhv_aux structure attached to the stash, in these little fields here:

    HV          *xhv_class_superclass;
    CV          *xhv_class_initfields_cv;
    AV          *xhv_class_adjust_blocks;
    PADNAMELIST *xhv_class_fields;
    PADOFFSET    xhv_class_next_fieldix;
    HV          *xhv_class_param_map;
  • xhv_class_superclass will be NULL for a class with no superclass. If a parent class was set (via the :isa() class attribute), this points directly at that parent's stash. 🫢
  • xhv_class_initfields_cv holds a CV * pointing to a function that gets invoked as part of the constructor of this class or any subclass of it. This CV is responsible for initializing all the fields defined by this class for a new instance. Real ones know: this CV is an anonymous real function β€” meaning yeah, it has no name and no GV, but it is not a protosub, and it may be directly invoked.
  • xhv_class_adjust_blocks may point to an AV containing CV pointers to each of the ADJUST blocks defined on the class. If the class has a superclass, this array will additionally contain duplicate pointers of the CVs of its parent class. The AV gets created lazily, the first time an element is pushed to it. It is completely valid for there to be no AV at all, and this pointer will be NULL in that case.
    • Those CVs are stored directly, not by way of RVs. And each CV is an anonymous real function.
  • xhv_class_fields will point to a PADNAMELIST containing PADNAMEs, each one being a defined field of the class. They're stored in order of declaration. But heads up though β€” the index into this array is not necessarily equal to the fieldix of each field. Why? Because in the case of a subclass, the array begins at zero but the index of the first field in it will be non-zero if its parent class contains any fields at all. Sneaky little offset, ik.
    • For more info on how individual fields are represented, see "Fields" below.
  • xhv_class_next_fieldix gives the field index that will be assigned to the next field added to the class. It's only useful at compile-time, so don't come for it at runtime.
  • xhv_class_param_map may point to an HV that maps field :param attribute names to the field index of the field with that name. This mapping gets copied from parent classes β€” each class will contain the sum total of all its parents, plus its own. Inheriting like a queen. πŸ‘‘

🌸 Fields

A field is still fundamentally a lexical variable declared in a scope, and it lives in the PADNAMELIST of its corresponding CV. Methods and other method-like CVs can still capture them exactly the way they can with regular lexicals. A field is distinguished from other kinds of pad entry by the PadnameIsFIELD() macro returning true on it. Yup, another vibe check.

The extra info about it being a field lives in an additional structure you get at via the PadnameFIELDINFO() macro on the padname. That structure has these fields:

    PADOFFSET  fieldix;
    HV        *fieldstash;
    OP        *defop;
    SV        *paramname;
    bool       def_if_undef;
    bool       def_if_false;
  • fieldix stores the "field index" of the field β€” that is, the index into the instance field array where this field's value will be stored. Note that the first index in that array is not specially reserved, okay? The first field in a class will start from field index 0. Zero, periodt.
  • fieldstash stores a pointer to the stash of the class that defined this field. This is necessary in case there are multiple classes defined within the same scope; it's how we disambiguate the fields of each. Like this:
    {
        class C1; field $x;
        class C2; field $x;
    }
  • defop may store a pointer to a defaulting expression optree for this field. Defaulting expressions are optional; this field may be NULL. No stress.
  • paramname may point to a regular string SV containing the :param name attribute given to the field. If there isn't one, it'll be NULL.
  • One of def_if_undef and def_if_false will be true if the defaulting expression was set using the //= or ||= operators respectively.

πŸ’… Methods

A method is still fundamentally a CV, bestie, and it has the same basic representation as one. It has an optree and a pad, and it's stored via a GV in the stash of its containing package. What makes it a method and not a non-method CV? The CvIsMETHOD() macro returns true on it. Iconic.

(Okay important note: do not confuse this macro with the one that used to be called CvMETHOD(). That one has nothing to do with the class system at all, and it got renamed to CvNOWARN_AMBIGUOUS() specifically to avoid this exact confusion. Stay woke. 🧠)

There is currently no extra information that needs to be stored about a method CV, so the structure does not add any new fields.


🫢 Instances

Object instances are represented by an entirely new SV type, whose base type is SVt_PVOBJ. It should still be blessed into its class stash and wrapped in an RV in the usual manner for a classical object. Old habits, you know?

Since these are their own unique container type β€” distinct from hashes or arrays β€” the core builtin::reftype function returns a new value when asked about these. And that value is OBJECT. Slay.

Internally, such an object is an array of SV pointers whose size is fixed at creation time (because the number of fields in a class is known after compilation). An object instance stores the max field index within it (for basic error-checking on access), plus a fixed-size array of SV pointers storing the individual field values.

Fields of array and hash type directly store AV or HV pointers into that array; they are not stored via an intervening RV. No middleman, no wasted hops.


⭐ API ⭐

The data structures we just described are supported by the following API functions. Let's get into it. πŸ”₯

🧚 Class Manipulation

class_setup_stash

void class_setup_stash(HV *stash);

Called by the parser when it encounters the class keyword. It upgrades the stash into being a class and prepares it for receiving class-specific items like methods and fields. Glow-up moment.

class_seal_stash

void class_seal_stash(HV *stash);

Called by the parser at the end of a class block β€” or, for unit classes, at its containing scope. This function performs various finalisation activities that are required before instances of the class can be constructed, but that couldn't have been done until all the information about the members of the class is known.

Any additions to or modifications of the class under compilation must be performed between these two function calls. Classes cannot be modified once they have been sealed. It's a done deal, no edits, no take-backs. πŸ”’

class_add_field

void class_add_field(HV *stash, PADNAME *pn);

Called by pad.c as part of defining a new field name in the current pad. Note that this function does not create the padname; that must already be done by pad.c. This API function just simply informs the class that the new field name has been created and is now available for it. Like a little heads-up text. πŸ“±

class_add_ADJUST

void class_add_ADJUST(HV *stash, CV *cv);

Called by the parser once it has parsed and constructed a CV for a new ADJUST block. This gets added to the list stored by the class.


πŸ“ Field Manipulation

class_prepare_initfield_parse

void class_prepare_initfield_parse();

Called by the parser just before parsing an initializing expression for a field variable. This makes use of a suspended compcv to combine all the field initializing expressions into the same CV.

class_set_field_defop

void class_set_field_defop(PADNAME *pn, OPCODE defmode, OP *defop);

Called by the parser after it has parsed an initializing expression for the field. Sets the defaulting expression and mode of application. defmode should either be zero, or one of OP_ORASSIGN or OP_DORASSIGN β€” depending on which defaulting mode you're on.

padadd_FIELD

#define padadd_FIELD

This flag constant tells the pad_add_name_* family of functions that the new name should be added as a field. And good news: there is no need to call class_add_field() β€” this will be done automatically. She does it herself. ✨


🧚 Method Manipulation

class_prepare_method_parse

void class_prepare_method_parse(CV *cv);

Called by the parser after start_subparse() but immediately before doing anything else. This prepares the PL_compcv for parsing a method β€” arranging for the CvIsMETHOD test to be true, adding the $self lexical, and any other activities that may be required. Setting the stage, essentially. 🎬

class_wrap_method_body

OP *class_wrap_method_body(OP *o);

Called by the parser at the end of parsing a method body into an optree, but just before wrapping it in the eventual CV. This function inserts extra ops into the optree to make the method work correctly. Finishing touches, lowkey essential.


πŸ’œ Object Instances

SVt_PVOBJ

#define SVt_PVOBJ

An SV type constant used for comparison with the SvTYPE() macro.

ObjectMAXFIELD

SSize_t ObjectMAXFIELD(sv);

A function-like macro that obtains the maximum valid field index that can be accessed from the ObjectFIELDS array. Basically your "you shall not pass" boundary marker. 🚧

ObjectFIELDS

SV **ObjectFIELDS(sv);

A function-like macro that obtains the fields array directly out of an object instance. Fields can be accessed by their field index, from 0 up to the maximum valid index given by ObjectMAXFIELD.

πŸ‘‘ OPCODES πŸ‘‘

🌟 OP_METHSTART

newUNOP_AUX(OP_METHSTART, ...);

An OP_METHSTART is an UNOP_AUX, bestie, and it must be sitting right at the start of a method CV or that CV just doesn't work properly, periodt. It gets inserted by class_wrap_method_body(), and it literally shows up before any optree fragment that's tied to signature argument checking or extraction. Front of the line. Main character behavior. πŸ’…

This op has two jobs and it does both flawlessly:

  • It shifts the value of $self out of the arguments list, and binds any field variables the method needs to peek at into the pad. The AUX vector is carrying all the deets about the field/pad index pairings required.
  • It does a sanity check on the invocant value β€” like, is this definitely an object reference of a compatible class type? If it isn't, an exception gets thrown. Vibe check: failed. πŸ’€

And okay, one more thing: if the op_private field includes the OPpINITFIELDS flag, that means this op begins the special xhv_class_initfields_cv CV. In that case it should additionally take the second value from the arguments list β€” which should be a plain HV pointer, passed directly, not via RV β€” and bind it to the second pad slot, because that's exactly where the generated optree will be expecting to find it. Trust. 🫢

🌟 OP_INITFIELD

An OP_INITFIELD only ever gets invoked as part of the xhv_class_initfields_cv CV, during the construction phase of an instance. This is the moment the individual SVs that make up the mutable fields of the instance (AVs and HVs included, not just plain SVs) actually get assigned into the ObjectFIELDS array. ✨

The private flags OPpINITFIELD_AV and OPpINITFIELD_HV tell you whether it's creating an AV or an HV β€” and if neither one is set, then it's an SV that's being made.

If the op has the OPf_STACKED flag, it expects to find an initializing value on the stack. For SVs, that's the topmost SV on the data stack. For AVs and HVs, it expects a marked list. Noted. πŸ“


πŸ’– COMPILE-TIME BEHAVIOUR πŸ’–

🌸 ADJUST Phasers 🌸

During compiletime, parsing an ADJUST phaser is handled in a fundamentally different way from the existing perl phasers (BEGIN, etc...). Like, completely different lane. πŸ¦‹

Instead of taking the usual route, the tokenizer straight up recognises that the ADJUST keyword introduces a phaser block. Then the parser parses the body of that block the same way it would parse an (anonymous) method body, creating a CV that has no name GV. That CV then gets inserted directly into the class information by calling class_add_ADJUST β€” entirely bypassing the symbol table. Sneaky? No. Iconic? Yes. πŸ‘‘

🌸 Attributes 🌸

During compilation, attributes of both classes and fields are handled differently from the existing perl attributes on subroutines and lexical variables.

The parser still forms an OP_LIST optree of OP_CONST nodes β€” but those get passed to the class_apply_attributes or class_apply_field_attributes functions. And instead of doing a class lookup for a method in the class being parsed, a fixed internal list of known attributes is used to find the functions that apply the attribute to the class or field.

In future this may support user-supplied extension attribute, though at present it only recognises ones defined by the core itself. Living within its means fr fr. πŸ™ˆ

🌸 Field Initializing Expressions 🌸

During compilation, the parser makes use of a suspended compcv when it's parsing the defaulting expression for a field. And get this β€” all the expressions for all the fields in the class share that same suspended compcv, which then gets compiled up into the same internal CV that the constructor calls to initialize all the fields provided by that class. One compcv, communal energy, we love to see it. 🫢


πŸ”₯ RUNTIME BEHAVIOUR πŸ”₯

⭐ Constructor ⭐

The generated constructor for a class is itself an XSUB, and it does three tasks, in order:

  1. It creates the instance SV itself.
  2. It invokes the field initializers.
  3. It invokes the ADJUST block CVs.

And the constructor for any class is always the same basic shape β€” no cap β€” regardless of whether the class has a superclass or not. Consistent queen. πŸ‘‘

The field initializers are all collected into a generated optree-based CV called the field initializer CV. That's the CV holding every optree fragment for the field initializing expressions. When it's invoked, the field initializer CV might make a chained call to the superclass initializer if one exists, before invoking all of the individual field initialization ops.

The field initializer CV is invoked with two items on the stack: the instance SV, and a direct HV containing the constructor parameters. Note carefully, bestie: this HV is passed directly, not via an RV reference. That's allowed because both the caller and the callee are directly generated code β€” not arbitrary pure-perl subroutines. 🧚

Meanwhile, the ADJUST block CVs are all collected into a single flat list, merging in all of the ones defined by the superclass as well. They're all invoked in order, after the field initializer CV. Squad assembled. πŸ’œ

⭐ $self Access During Methods ⭐

When class_prepare_method_parse() is called, it arranges things so that the pad of the new CV body begins with a lexical called $self. Since the pad should be freshly-created at this point, that means it gets the pad index of 1. The function checks this and aborts if that's not the case. It does not play about pad indices. πŸ’€

Because of this fact, code within the body of a method or method-like CV can reliably use pad index 1 to obtain the invocant reference. The OP_INITFIELD opcode also relies on this fact.

In similar fashion, during the xhv_class_initfields_cv the next pad slot is relied on to store the constructor parameters HV, at pad index 2. Two slots, two jobs, zero ambiguity. ✨


πŸ’œ AUTHORS πŸ’œ

Paul Evans


And that's the whole page, bestie πŸŽ€ β€” all of this was for perl v5.44.0, dated 2026-10-17, and it's the perlclassguts manual. Go build something class-y with it. Slay, hydrate, and always check your pad index. πŸ’…πŸ¦‹πŸ’–

πŸ“¦ where this page comes from
  • arch rolling: perl 5.44.0-1 Β· Artistic-1.0-Perl AND GPL-1.0-or-later
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms