This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
parts/apicheck.pl: Add a few comments; white-space
[perl5.git] / dist / Devel-PPPort / parts / apicheck.pl
CommitLineData
adfe19db
MHM
1#!/usr/bin/perl -w
2################################################################################
3#
e27fc489 4# apicheck.pl -- generate apicheck.c: C source for automated API check
adfe19db
MHM
5#
6################################################################################
7#
b2049988 8# Version 3.x, Copyright (C) 2004-2013, Marcus Holland-Moritz.
adfe19db
MHM
9# Version 2.x, Copyright (C) 2001, Paul Marquess.
10# Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
11#
12# This program is free software; you can redistribute it and/or
13# modify it under the same terms as Perl itself.
14#
15################################################################################
16
17use strict;
3d7c117d 18require './parts/ppptools.pl';
adfe19db
MHM
19
20if (@ARGV) {
0c96388f
MHM
21 my $file = pop @ARGV;
22 open OUT, ">$file" or die "$file: $!\n";
adfe19db
MHM
23}
24else {
25 *OUT = \*STDOUT;
26}
27
e27fc489 28# Get list of functions/macros to test
679ad62d 29my @f = parse_embed(qw( parts/embed.fnc parts/apidoc.fnc parts/ppport.fnc ));
adfe19db 30
e27fc489
KW
31# Read in what we've decided in previous calls should be #ifdef'd out for this
32# call. The keys are the symbols to test; each value is a subhash, like so:
33# 'utf8_hop_forward' => {
34# 'version' => '5.025007'
35# },
36# We don't care here about other subkeys
adfe19db
MHM
37my %todo = %{&parse_todo};
38
e27fc489 39# We convert these types into these other types
adfe19db
MHM
40my %tmap = (
41 void => 'int',
42);
43
e27fc489 44# These are for special marker argument names, as mentioned in embed.fnc
adfe19db
MHM
45my %amap = (
46 SP => 'SP',
47 type => 'int',
48 cast => 'int',
49);
50
e27fc489 51# Certain return types are instead considered void
adfe19db
MHM
52my %void = (
53 void => 1,
54 Free_t => 1,
55 Signal_t => 1,
56);
57
e27fc489
KW
58# khw doesn't know why these exist. These have an explicit (void) cast added.
59# Undef'ing this hash made no difference. Maybe it's for older compilers?
adfe19db
MHM
60my %castvoid = (
61 map { ($_ => 1) } qw(
c54ae73f
KW
62 G_ARRAY
63 G_DISCARD
64 G_EVAL
65 G_NOARGS
66 G_SCALAR
67 G_VOID
68 HEf_SVKEY
69 MARK
adfe19db 70 Nullav
c54ae73f 71 Nullch
adfe19db
MHM
72 Nullcv
73 Nullhv
adfe19db 74 Nullsv
adfe19db 75 SP
adfe19db
MHM
76 SVt_IV
77 SVt_NV
c54ae73f 78 SVt_PV
adfe19db 79 SVt_PVAV
adfe19db 80 SVt_PVCV
c54ae73f
KW
81 SVt_PVHV
82 SVt_PVMG
adfe19db 83 SvUOK
adfe19db
MHM
84 XS_VERSION
85 ),
86);
87
e27fc489 88# Ignore the return value of these
adfe19db
MHM
89my %ignorerv = (
90 map { ($_ => 1) } qw(
91 newCONSTSUB
92 ),
93);
94
e27fc489
KW
95# The value of each key is a list of things that need to be declared in order
96# for the key to compile.
adfe19db 97my %stack = (
cfc3b033 98 MULTICALL => ['dMULTICALL;'],
4a582685 99 ORIGMARK => ['dORIGMARK;'],
cfc3b033
KW
100 POP_MULTICALL => ['dMULTICALL;', 'U8 gimme;' ],
101 PUSH_MULTICALL => ['dMULTICALL;', 'U8 gimme;' ],
4a582685 102 POPpbytex => ['STRLEN n_a;'],
c54ae73f 103 POPpx => ['STRLEN n_a;'],
4a582685 104 PUSHi => ['dTARG;'],
c54ae73f
KW
105 PUSHn => ['dTARG;'],
106 PUSHp => ['dTARG;'],
4a582685 107 PUSHu => ['dTARG;'],
ec4a7abf
KW
108 RESTORE_LC_NUMERIC => ['DECLARATION_FOR_LC_NUMERIC_MANIPULATION;'],
109 STORE_LC_NUMERIC_FORCE_TO_UNDERLYING => ['DECLARATION_FOR_LC_NUMERIC_MANIPULATION;'],
110 STORE_LC_NUMERIC_SET_TO_NEEDED => ['DECLARATION_FOR_LC_NUMERIC_MANIPULATION;'],
111 STORE_LC_NUMERIC_SET_TO_NEEDED_IN => ['DECLARATION_FOR_LC_NUMERIC_MANIPULATION;'],
c54ae73f
KW
112 UNDERBAR => ['dUNDERBAR;'],
113 XCPT_CATCH => ['dXCPT;'],
114 XCPT_RETHROW => ['dXCPT;'],
115 XCPT_TRY_END => ['dXCPT;'],
116 XCPT_TRY_START => ['dXCPT;'],
117 XPUSHi => ['dTARG;'],
118 XPUSHn => ['dTARG;'],
119 XPUSHp => ['dTARG;'],
120 XPUSHu => ['dTARG;'],
eae22b18
KW
121 XS_APIVERSION_BOOTCHECK => ['CV * cv;'],
122 XS_VERSION_BOOTCHECK => ['CV * cv;'],
adfe19db
MHM
123);
124
e27fc489
KW
125# Things to not try to check. Either not applicable, or too hard to get to
126# work here.
adfe19db
MHM
127my %ignore = (
128 map { ($_ => 1) } qw(
c54ae73f
KW
129 CLASS
130 dXSI32
adfe19db
MHM
131 items
132 ix
adfe19db
MHM
133 RETVAL
134 StructCopy
c54ae73f
KW
135 svtype
136 THIS
d02cf1d1
KW
137 XopDISABLE
138 XopENABLE
139 XopENTRY
140 XopENTRYCUSTOM
141 XopENTRY_set
c54ae73f 142 XS
d02cf1d1
KW
143 XS_EXTERNAL
144 XS_INTERNAL
adfe19db
MHM
145 ),
146);
147
e27fc489 148# XXX The NEED_foo lines should be autogenerated
adfe19db
MHM
149print OUT <<HEAD;
150/*
151 * !!!!!!! DO NOT EDIT THIS FILE !!!!!!!
152 * This file is built by $0.
153 * Any changes made here will be lost!
154 */
155
156#include "EXTERN.h"
157#include "perl.h"
4a582685
NC
158
159#define NO_XSLOCKS
adfe19db
MHM
160#include "XSUB.h"
161
ba120f6f
MHM
162#ifdef DPPP_APICHECK_NO_PPPORT_H
163
164/* This is just to avoid too many baseline failures with perls < 5.6.0 */
165
166#ifndef dTHX
167# define dTHX extern int Perl___notused
168#endif
169
170#else
adfe19db 171
c01be2ce 172#define NEED_PL_parser
8d137abb 173#define NEED_PL_signals
744ef08f 174#define NEED_caller_cx
8d137abb
P
175#define NEED_croak_xs_usage
176#define NEED_die_sv
adfe19db
MHM
177#define NEED_eval_pv
178#define NEED_grok_bin
179#define NEED_grok_hex
180#define NEED_grok_number
181#define NEED_grok_numeric_radix
182#define NEED_grok_oct
679ad62d 183#define NEED_load_module
8d137abb
P
184#define NEED_mess
185#define NEED_mess_nocontext
186#define NEED_mess_sv
94e22bd6 187#define NEED_mg_findext
f2ab5a41 188#define NEED_my_snprintf
c01be2ce 189#define NEED_my_sprintf
aef0a14c
MHM
190#define NEED_my_strlcat
191#define NEED_my_strlcpy
8d137abb 192#define NEED_my_strnlen
adfe19db 193#define NEED_newCONSTSUB
c83e6f19 194#define NEED_newSVpvn_share
db42c902
MHM
195#define NEED_pv_display
196#define NEED_pv_escape
197#define NEED_pv_pretty
96ad942f
MHM
198#define NEED_sv_catpvf_mg
199#define NEED_sv_catpvf_mg_nocontext
200#define NEED_sv_setpvf_mg
201#define NEED_sv_setpvf_mg_nocontext
744ef08f 202#define NEED_sv_unmagicext
8d137abb 203#define NEED_utf8_to_uvchr_buf
679ad62d 204#define NEED_vload_module
8d137abb 205#define NEED_vmess
f2ab5a41 206#define NEED_warner
96ad942f 207
adfe19db
MHM
208#include "ppport.h"
209
210#endif
211
adfe19db
MHM
212static int VARarg1;
213static char *VARarg2;
214static double VARarg3;
215
744ef08f
CBW
216#if defined(PERL_BCDVERSION) && (PERL_BCDVERSION < 0x5009005)
217/* needed to make PL_parser apicheck work */
218typedef void yy_parser;
219#endif
220
adfe19db
MHM
221HEAD
222
e27fc489 223# Caller can restrict what functions tests are generated for
0c96388f
MHM
224if (@ARGV) {
225 my %want = map { ($_ => 0) } @ARGV;
226 @f = grep { exists $want{$_->{name}} } @f;
227 for (@f) { $want{$_->{name}}++ }
228 for (keys %want) {
229 die "nothing found for '$_'\n" unless $want{$_};
230 }
231}
232
adfe19db 233my $f;
e27fc489 234for $f (@f) { # Loop through all the tests to add
adfe19db
MHM
235 $ignore{$f->{name}} and next;
236 $f->{flags}{A} or next; # only public API members
237
fbd5db69
KW
238 $ignore{$f->{name}} = 1; # ignore duplicates
239
adfe19db
MHM
240 my $Perl_ = $f->{flags}{p} ? 'Perl_' : '';
241
242 my $stack = '';
243 my @arg;
244 my $aTHX = '';
245
246 my $i = 1;
247 my $ca;
248 my $varargs = 0;
e27fc489
KW
249
250 for $ca (@{$f->{args}}) { # Loop through the function's args
251 my $a = $ca->[0]; # 1th is the name, 0th is its type
adfe19db
MHM
252 if ($a eq '...') {
253 $varargs = 1;
254 push @arg, qw(VARarg1 VARarg2 VARarg3);
255 last;
256 }
e27fc489
KW
257
258 # Split this type into its components
4a582685
NC
259 my($n, $p, $d) = $a =~ /^ (\w+(?:\s+\w+)*)\s* # type name => $n
260 (\**) # pointer => $p
d0cf06bd 261 (?:\s*\bconst\b\s*)? # const
4a582685
NC
262 ((?:\[[^\]]*\])*) # dimension => $d
263 $/x
264 or die "$0 - cannot parse argument: [$a]\n";
e27fc489
KW
265
266 # Replace a special argument name by something that will compile.
adfe19db
MHM
267 if (exists $amap{$n}) {
268 push @arg, $amap{$n};
269 next;
270 }
e27fc489
KW
271
272 # Certain types, like 'void', get remapped.
adfe19db 273 $n = $tmap{$n} || $n;
e27fc489
KW
274
275 # Use a literal of our choosing for non-format functions
d0cf06bd 276 if ($n =~ /\bconst\s+char\b/ and $p eq '*' and !$f->{flags}{f}) {
0c96388f
MHM
277 push @arg, '"foo"';
278 }
279 else {
e27fc489 280 my $v = 'arg' . $i++; # Argument number
0c96388f 281 push @arg, $v;
e27fc489 282 my $no_const_n = $n; # Get rid of any remaining 'const's
5334bcf6 283 $no_const_n =~ s/\bconst\b// unless $p;
e27fc489
KW
284
285 # Declare this argument
5334bcf6 286 $stack .= " static $no_const_n $p$v$d;\n";
0c96388f 287 }
adfe19db
MHM
288 }
289
e27fc489
KW
290 # Declare thread context for functions and macros that might need it.
291 # (Macros often fail to say they don't need it.)
5624d54c
KW
292 unless ($f->{flags}{'T'}) {
293 $stack = " dTHX;\n$stack"; # Harmless to declare even if not needed
adfe19db
MHM
294 $aTHX = @arg ? 'aTHX_ ' : 'aTHX';
295 }
296
e27fc489
KW
297 # If this function is on the list of things that need declarations, add
298 # them.
adfe19db
MHM
299 if ($stack{$f->{name}}) {
300 my $s = '';
301 for (@{$stack{$f->{name}}}) {
302 $s .= " $_\n";
303 }
304 $stack = "$s$stack";
305 }
306
307 my $args = join ', ', @arg;
e27fc489
KW
308
309 # Failure to specify a return type in the apidoc line means void
adfe19db 310 my $rvt = $f->{ret} || 'void';
e27fc489 311
adfe19db 312 my $ret;
e27fc489 313 if ($void{$rvt}) { # Certain return types are instead considered void
adfe19db
MHM
314 $ret = $castvoid{$f->{name}} ? '(void) ' : '';
315 }
316 else {
ba120f6f
MHM
317 $stack .= " $rvt rval;\n";
318 $ret = $ignorerv{$f->{name}} ? '(void) ' : "rval = ";
adfe19db 319 }
e27fc489 320
5624d54c 321 my $aTHX_args = "";
adfe19db 322
e27fc489 323 # Add parens to functions that take an argument list, even if empty
5624d54c
KW
324 unless ($f->{flags}{'n'}) {
325 $aTHX_args = "($aTHX$args)";
adfe19db 326 $args = "($args)";
adfe19db
MHM
327 }
328
329 print OUT <<HEAD;
330/******************************************************************************
331*
332* $f->{name}
333*
334******************************************************************************/
335
336HEAD
337
e27fc489 338 # #ifdef out if marked as todo (not known in) this version
adfe19db
MHM
339 if ($todo{$f->{name}}) {
340 my($ver,$sub) = $todo{$f->{name}} =~ /^5\.(\d{3})(\d{3})$/ or die;
341 for ($ver, $sub) {
342 s/^0+(\d)/$1/
343 }
344 if ($ver < 6 && $sub > 0) {
345 $sub =~ s/0$// or die;
346 }
347 print OUT "#if PERL_VERSION > $ver || (PERL_VERSION == $ver && PERL_SUBVERSION >= $sub) /* TODO */\n";
348 }
349
350 my $final = $varargs
351 ? "$Perl_$f->{name}$aTHX_args"
352 : "$f->{name}$args";
353
e27fc489 354 # If there is a '#if' associated with this, add that
adfe19db
MHM
355 $f->{cond} and print OUT "#if $f->{cond}\n";
356
357 print OUT <<END;
ba120f6f 358void _DPPP_test_$f->{name} (void)
adfe19db
MHM
359{
360 dXSARGS;
361$stack
adfe19db 362 {
ba120f6f
MHM
363#ifdef $f->{name}
364 $ret$f->{name}$args;
adfe19db 365#endif
ba120f6f 366 }
adfe19db
MHM
367
368 {
369#ifdef $f->{name}
ba120f6f 370 $ret$final;
adfe19db 371#else
ba120f6f 372 $ret$Perl_$f->{name}$aTHX_args;
adfe19db
MHM
373#endif
374 }
375}
376END
377
378 $f->{cond} and print OUT "#endif\n";
379 $todo{$f->{name}} and print OUT "#endif\n";
380
381 print OUT "\n";
382}
383
384@ARGV and close OUT;