Home / Alt manpages / perlclassguts(1)

  • perlclassguts(1)
  • User command
  • linux

Trace Perl 5.38 Classes from Syntax to Object Storage

You will build a small Perl class, inspect the optree that the compiler produces, and confirm the runtime type of the resulting object. This is a guided way to connect class syntax to the interpreter structures documented by perlclassguts, rather than a general introduction to writing Perl classes.

Allow about 20 minutes. You need Perl 5.38.2 with the perl-doc package installed. The commands below were checked on x86_64 Linux with that version. The class feature is experimental, so expect warnings unless you suppress them deliberately during a larger experiment.

Checkpoint 1: confirm the interpreter you are inspecting

Start by recording the Perl version. The internal types and APIs discussed here describe the Perl implementation, not a portable object model shared by every Perl release.

$ perl -v
$ perl -MConfig -e 'print "$Config{version} $Config{archname}\n"'

On the reference system, the second command reports 5.38.2 x86_64-linux-gnu-thread-multi. If your version differs, keep that difference visible in notes: a successful example does not prove that internal structure is unchanged.

Checkpoint 2: make a class with observable work

Use a temporary file so the source can be inspected and reused. This class has one lexical field, a default value, and two methods. The value method gives us a safe way to observe the private field without reaching into implementation details from Perl code.

$ cat > /tmp/perl-class-guts.pl <<'PERL'
use feature 'class';

class Counter {
    field $n = 0;

    method inc {
        $n++;
    }

    method value {
        $n;
    }
}

my $counter = Counter->new;
$counter->inc;
print $counter->value, "\n";
print ref($counter), " ", builtin::reftype($counter), "\n";
PERL

$ perl /tmp/perl-class-guts.pl

The useful output is:

1
Counter OBJECT

The experimental-feature warnings are emitted on this Perl build before the output. They are diagnostics, not evidence that object construction failed. The first line proves that the field initializer supplied zero and that the method changed the stored value. The second shows the class name returned by ref and the new OBJECT value returned by builtin::reftype.

What the interpreter stores

perlclassguts starts with a boundary that prevents a common debugging mistake: a class is fundamentally a package. It still has a stash in the symbol table, represented by an HV, but HvSTASH_IS_CLASS() identifies the stash as a class. Class-specific data is held in the stash auxiliary structure.

That auxiliary data records the direct superclass, the generated field-initialiser CV, the list of ADJUST CVs, the declared fields, the next field index, and the map used by :param attributes. The fields are stored in declaration order. A subclass starts its own field list at zero, but its field indexes follow those inherited from the parent, so list position and field index are not interchangeable.

A field remains a lexical variable in a pad. Its extra field information records the owning class, a field index, an optional defaulting optree, an optional parameter name, and whether the default uses //= or ||=. This explains why a method can capture a field like an ordinary lexical while the runtime can still place the value in an object field array.

Methods are CVs with optrees and pads, stored through GVs in the containing package stash. The class implementation marks them so CvIsMETHOD() recognises them. An instance is different: it is the SVt_PVOBJ scalar type, blessed into the class stash and wrapped in an ordinary reference. Internally it has a fixed-size array of SV pointers, sized when the object is created.

Checkpoint 3: inspect compilation instead of guessing

The optree is a practical bridge between source syntax and interpreter internals. Compile the class with B::Concise and discard the executable output:

$ perl -Mfeature=class -MO=Concise -e 'class Counter { field $n = 0; method inc { $n++ } }'

The command ends with -e syntax OK and prints optree entries such as enter, nextstate, and loop or scope operations. The exact tree is an implementation detail and can change with source shape or Perl version. Use it to answer a narrow question, such as whether a construct compiled, rather than treating its numeric operation labels as a stable API.

The internal documentation gives two particularly useful landmarks. A method CV begins with OP_METHSTART. This operation removes $self from the argument list, binds the field lexicals needed by the method, and checks that the invocant is an object of a compatible class. The generated field-initialiser CV uses OP_INITFIELD to place scalar, array, or hash storage into the object's field array.

Follow construction in runtime order

For a call such as Counter->new, the generated constructor follows a fixed high-level sequence:

  1. It creates the object scalar.
  2. It runs the field-initialiser CV, including the superclass initialiser when one exists.
  3. It runs the collected ADJUST blocks in order.

The field-initialiser CV receives the instance and a constructor-parameter HV. That HV is passed directly rather than through a reference because both sides are generated interpreter code. The detail matters when reading debugger output or Perl source: it does not describe an ordinary user-callable subroutine convention.

ADJUST is also not stored as a normal named method. During compilation the parser makes an anonymous CV for each block and puts the CV directly into the class data. Superclass blocks are merged into the flat list used at construction time.

Common traps and safe boundaries

  • Do not treat a field as a hash key. The documented object representation is a fixed array indexed by field index, not a hash of field names.
  • Do not infer portability from builtin::reftype. The OBJECT result is a deliberate signal for this class-object representation in the inspected Perl implementation.
  • Do not edit a class after its class block has been sealed. The parser performs finalisation at the end of the block, and the internal API says later additions or modifications are not supported.
  • Do not use ObjectFIELDS or related macros from ordinary Perl. They are C-level internals for code built against the Perl core, not a supported replacement for methods.

Nothing in this guide changes the system Perl installation. The only file created by the examples is /tmp/perl-class-guts.pl. Remove it when finished:

$ rm -- /tmp/perl-class-guts.pl

If you need to keep the experiment, skip that command. If you remove it accidentally, recreate it by repeating Checkpoint 2; no package or service state is involved.

Done means

  • You recorded the Perl version before relying on implementation details.
  • The class example printed 1 and then Counter OBJECT.
  • You inspected a compiled optree with B::Concise.
  • You can distinguish the class stash, lexical field pad, method CV, and fixed object field array.
  • You know that OP_METHSTART, OP_INITFIELD, constructor initialisation, and ADJUST ordering are implementation details tied to the Perl version you inspected.