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