XS::Check - Check XS for some common problems
use FindBin '$Bin'; use XS::Check; my $check = XS::Check->new (); $check->check_file ("$Bin/synopsis.xs");
produces output
/usr/home/ben/projects/xs-check/examples/synopsis.xs:3: x not a constant type. /usr/home/ben/projects/xs-check/examples/synopsis.xs:3: len is not a STRLEN variable (unsigned int ).
(This example is included as synopsis.pl in the distribution.)
This documents version 0.14 of XS-Check corresponding to git commit ebf92c9ce84862ecc1565096c1c0b42352e13d78 released on Mon Jul 24 10:02:08 2023 +0900.
This module offers ways to check XS files for some common flaws which we have tripped over.
my $check = XS::Check->new ();
Make a new XS::Check object. The checks are then run using "check" or "check_file".
The messages from "check" or "check_file" are usually printed using Perl's built-in warn function. If you need to have errors reported some other way, supply a code reference to new with the key reporter as follows:
new
reporter
my $usercheck = XS::Check->new (reporter => sub { print "help!" });
The function you supply is then called back when "check" or "check_file" find something to remark on. The function is called with a hash containing the fields
the file name of the file where the error occurred, if using "check_file" or if set with "set_file", otherwise the undefined value (undef),
undef
the line number where the error occurred, starting from 1,
the message from the module, a text string.
The following example demonstrates a user-defined callback using the message and line fields:
message
line
use XS::Check; my $rchecker = XS::Check->new (reporter => \& reporter); $rchecker->check ("Perl_croak ('croaking');\n"); sub reporter { my %rstuff = @_; print "$rstuff{message} at $rstuff{line}.\n"; }
Remove the 'Perl_' prefix from Perl_croak at 1.
(This example is included as reporter.pl in the distribution.)
This was added in version 0.07 of the module..
$check->check ($xs);
See "SUGGESTIONS" for what this reports.
$check->check ($xs_file);
Convenience method to read in $xs_file then run "check" on it.
$xs_file
This assumes UTF-8 encoding of $xs_file.
$check->set_file ($file);
Set the file name for error reporting. Use any false value to clear it. For example:
use XS::Check; my $check = XS::Check->new (); my $xs = "Perl_croak (\"frog\")\n"; $check->check ($xs); $check->set_file ('Yabadabado'); $check->check ($xs); $check->set_file (''); $check->check ($xs);
1: Remove the 'Perl_' prefix from Perl_croak. Yabadabado:1: Remove the 'Perl_' prefix from Perl_croak. 1: Remove the 'Perl_' prefix from Perl_croak.
(This example is included as set-file.pl in the distribution.)
This was added in version 0.08 of the module.
This section details the possible suggestions made by the module and the motivations behind them.
Using an int type for the second argument to SvPV may cause errors on 64-bit Perls, because within the macro the address of the variable is taken, and then it is sent to a Perl function, and if the length doesn't match the length of Perl's STRLEN an error may occur.
int
SvPV
STRLEN
The pointer returned by SvPV is the actual Perl buffer, not a copy, so unless one actually wants to write into it, it's better to use const char * to make sure one does not overwrite it.
const char *
The C standard library functions malloc, calloc, realloc, and free should usually be replaced with Newx, Newxz, Renew, and Safefree respectively in Perl XS code, because the C standard library functions may cause "free to wrong pool" errors on multithreaded Windows Perls.
malloc
calloc
realloc
free
Newx
Newxz
Renew
Safefree
Functions of the form Perl_croak should usually not be used, just croak. The Perl_ prefix functions are the actual functions and croak and other such functions are actually macros, but these macros contain hidden arguments. (The hidden arguments are the pTHX_ and similar things seen in the Perl source code.)
Perl_croak
croak
Perl_
pTHX_
This was added in version 0.04 of the module.
XS functions cannot use the ANSI C (void) to indicate that they do not take any arguments, instead this results in a variable called "void" being created.
(void)
This was added in version 0.06 of the module.
One should not dereference the return value of av_fetch or hv_fetch without checking for NULL (zero pointer) since it is possible to get NULL, for example if an array is created with only a tenth element.
av_fetch
hv_fetch
NULL
For an extended discussion, see http://blogs.perl.org/users/ben_bullock/2020/02/av-fetch-can-return-null.html.
This check does not actually check that the returned value is checked for non-nullness before being dereferenced, only that there is nothing of the form * av_fetch in your code.
* av_fetch
This was added in version 0.09 of the module.
The XS manual suggests putting whitespace before # comments to distinguish them from preprocessor statements.
See https://perldoc.perl.org/perlxs#Inserting-POD,-Comments-and-C-Preprocessor-Directives.
Comments can be added to XSUBs by placing a # as the first non-whitespace of a line. Care should be taken to avoid making the comment look like a C preprocessor directive, lest it be interpreted as such. The simplest way to prevent this is to put whitespace in front of the #.
This was added in version 0.09 of the module..
The av_len function is something of a booby trap in that it returns the length of an AV * minus one, so if it is used as-is, one element of the array will be missed. The module does a simplistic check of seeing whether you have added one to av_len.
av_len
AV *
This was added in version 0.10 of the module.
To work around Perl's strings sometimes being in an ambiguous state, it is better to specify either SvPVbyte or SvPVutf8 and friends rather than plain SvPV. This check is also applied to the similar functions like SvPV_nolen.
The reasons for this are documented in perldoc perlapi for Perl versions from 5.34 onwards.
perldoc perlapi
5.34
This was added in version 0.12 of the module.
As of 0.14, the module has the following limitations.
The module is not very good at parsing struct members, so XS code like the following doesn't get dealt with properly:
s.txt = SvPV (sv, s.len);
"check_file" uses "read_text" in File::Slurper to read the text, which means it only accepts text encoded as UTF-8.
The current method of parsing variable declarations uses a very simplistic hack, and it is likely to produce false results if a variable name is used twice for two different things in the same file.
The following variable length is not dealt with correctly:
length
static void sv_to_text_fuzzy (SV * text, STRLEN length) { const unsigned char * stuff; /* Copy the string in "text" into "text_fuzzy". */ stuff = (unsigned char *) SvPV (text, length);
This supplies the regular expressions used to parse C by the module.
This is used by "check_file".
This is used to get the line numbers.
A command line tool called checkxs is installed with the module. It runs the "check_file" method on each file named on the command line.
checkxs
checkxs Some.xs
There are two options:
Switch on verbose debugging output
Print the version of XS::Check in use.
ExtUtils::ParseXS is Perl's parser for XS which converts XS code into C.
Test::XS::Check is a test module based on this one.
XS::Tutorial is a tutorial about programming in XS.
A collection of more or less obscure bugs found by CPAN testers, the original inspiration for this module.
Ben Bullock, <bkb@cpan.org>
This package and associated files are copyright (C) 2017-2023 Ben Bullock.
You can use, copy, modify and redistribute this package and associated files under the Perl Artistic Licence or the GNU General Public Licence.
To install XS::Check, copy and paste the appropriate command in to your terminal.
cpanm
cpanm XS::Check
CPAN shell
perl -MCPAN -e shell install XS::Check
For more information on module installation, please visit the detailed CPAN module installation guide.