2021/02/03

Writing a Perl Core Feature - part 1: feature.pm

Index | < Prev | Next >

The first step towards adding a new feature to Perl is introducing the new name into feature.pm, so that it may be requested by

use feature 'banana';

To accomplish this we don't actually edit feature.pm directly, because that is a file which is automatically generated from other source. The primary file we need to work on that lives in the regen/ directory, called regen/feature.pl.

For example, when adding the isa feature this was the change made there: (github.com/Perl/perl5).

--- a/regen/feature.pl
+++ b/regen/feature.pl
@@ -35,6 +35,7 @@ my %feature = (
     unicode_strings => 'unicode',
     fc              => 'fc',
     signatures      => 'signatures',
+    isa             => 'isa',
 );
 
 # NOTE: If a feature is ever enabled in a non-contiguous range of Perl
@@ -752,6 +753,14 @@ Reference to a Variable> for examples.
 
 This feature is available from Perl 5.26 onwards.
 
+=head2 The 'isa' feature
+
+This allows the use of the C<isa> infix operator, which tests whether the
+scalar given by the left operand is an object of the class given by the
+right operand. See L<perlop/Class Instance Operator> for more details.
+
+This feature is available from Perl 5.32 onwards.
+
 =head1 FEATURE BUNDLES
 
 It's possible to load multiple features together, using

We can see two distinct parts in here. The first, a single line addition to the %feature hash, is the part which actually introduces the new name. The second part adds some documentation for it, which will appear in the generated feature.pm file.

To add our new banana feature then, this is where we must start editing. For now don't worry too much about the documentation part - we'll come back to that later. Just add a single line into the %feature hash.

leo@shy:~/src/bleadperl/perl [git]
$ nvim regen/feature.pl

Once we've made our required changes in here, we run the script to get it to regenerate its files. Note that we need to use a perl to run this, but it doesn't have to be the one we are trying to build (indeed - that would be problematic would it not? ;) ). Any recently up-to-date system Perl install will be fine.

leo@shy:~/src/bleadperl/perl [git]
$ perl regen/feature.pl 
Changed: lib/feature.pm feature.h

Here we can see that it has regenerated two files. The first of these is the lib/feature.pm file that the perl VM will use at runtime to implement the actual use feature pragma with. The second file is feature.h which is used during compiling the interpreter itself and contains the various feature-test macros. If you want, take a look now at the changes it has made.

Specifically, notice that:

  • A new FEATURE_BANANA_BIT macro has been created, and a value assigned to it. These features are kept in numerical order, so also notice that the subsequent features have been renumbered. This is fine - the bit fields are only used internally and there are no API guarantees of numerical stability between major versions of Perl.
  • A new FEATURE_BANANA_IS_ENABLED macro has been created, which other code may use to test if the feature is currently in effect during compile-time. Keep note of this - we will be seeing it again later on.
  • The other change in the file is in the S_magic_sethint_feature() function, which adds code to recognise the string name of the new feature; this is ultimately used by use feature ... line itself to recognise the names of the requested features.

At this point already, we can test that the newly-created feature is at least recognised by the feature.pm file itself:

leo@shy:~/src/bleadperl/perl [git]
$ make -j4 perl
...

leo@shy:~/src/bleadperl/perl [git]
$ ./perl -Ilib -ce 'use feature "banana";'
-e syntax OK

It actually turns out that the particular commit that added isa was somewhat atypical. It didn't actually need to change the $VERSION of the generated file, because another change earlier in the history had already done so. This is unlikely to be the case most of the time.

Now would be a good time to introduce the porting tests. This is a subset of the full test suite, which checks various details to do with whether the source code is being maintained properly. We can run these directly:

leo@shy:~/src/bleadperl/perl [git]
$ make test_porting
...
porting/cmp_version.t ..... 1/4 # not ok 3 - lib/feature.pm version 1.62
porting/cmp_version.t ..... Failed 1/4 subtests 
...
Test Summary Report
-------------------
porting/cmp_version.t   (Wstat: 0 Tests: 4 Failed: 1)
  Failed test:  3
Files=32, Tests=44043, 188 wallclock secs ( 7.88 usr  0.16 sys + 186.14 cusr  3.98 csys = 198.16 CPU)
Result: FAIL

Here indeed we see that for our banana feature we have forgotten to bump the version number. No matter, we can do that now and test again:

leo@shy:~/src/bleadperl/perl [git]
$ nvim regen/feature.pl 

leo@shy:~/src/bleadperl/perl [git]
$ perl regen/feature.pl
Changed: lib/feature.pm

leo@shy:~/src/bleadperl/perl [git]
$ git diff
...
--- a/lib/feature.pm
+++ b/lib/feature.pm
@@ -5,7 +5,7 @@
 
 package feature;
 
-our $VERSION = '1.62';
+our $VERSION = '1.63';
 
 our %feature = (
     fc                   => 'feature_fc',
...

leo@shy:~/src/bleadperl/perl [git]
$ make test_porting
...
All tests successful.
Files=32, Tests=44044, 175 wallclock secs ( 7.32 usr  0.12 sys + 174.11 cusr  3.58 csys = 185.13 CPU)
Result: PASS

While working on core features it's often a good idea to make use of the porting tests regularly at least. The full test suite takes quite a while to run and likely most of it won't affect the particular parts of a new feature you are working on (especially as new features should be lexically guarded and thus limited in impact in the vast majority of the exiting test suite which won't be expecting it), but the porting tests are designed to be fairly small and lightweight to run often enough and keep an eye on the most likely things to check.

Index | < Prev | Next >

No comments:

Post a comment