2 ################################################################################
4 # apicheck.pl -- generate apicheck.c: C source for automated API check
6 # WARNING: This script will be run on very old perls. You need to not use
7 # modern constructs. See HACKERS file for examples.
9 ################################################################################
11 # Version 3.x, Copyright (C) 2004-2013, Marcus Holland-Moritz.
12 # Version 2.x, Copyright (C) 2001, Paul Marquess.
13 # Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
15 # This program is free software; you can redistribute it and/or
16 # modify it under the same terms as Perl itself.
18 ################################################################################
21 require './parts/ppptools.pl';
25 open OUT, ">$file" or die "$file: $!\n";
31 # Arguments passed to us in this variable are of the form
32 # '--a=foo --b=bar', so split first on space, then the =, and then the hash is
33 # of the form { a => foo, b => bar }
34 my %script_args = map { split /=/ } split(/\s+/, $ENV{'DPPP_ARGUMENTS'});
36 # Get list of functions/macros to test
37 my @f = parse_embed(qw( parts/embed.fnc parts/apidoc.fnc parts/ppport.fnc ));
39 # Read in what we've decided in previous calls should be #ifdef'd out for this
40 # call. The keys are the symbols to test; each value is a subhash, like so:
41 # 'utf8_hop_forward' => {
42 # 'version' => '5.025007'
44 # We don't care here about other subkeys
45 my %todo = %{&parse_todo($script_args{'--todo-dir'})};
47 # We convert these types into these other types
52 # These are for special marker argument names, as mentioned in embed.fnc
61 # Certain return types are instead considered void
68 # khw doesn't know why these exist. These have an explicit (void) cast added.
69 # Undef'ing this hash made no difference. Maybe it's for older compilers?
98 # Ignore the return value of these
100 map { ($_ => 1) } qw(
105 my @simple_my_cxt_prereqs = ( 'typedef struct { int count; } my_cxt_t;', 'START_MY_CXT;' );
106 my @my_cxt_prereqs = ( @simple_my_cxt_prereqs, 'MY_CXT_INIT;' );
108 # The value of each key is a list of things that need to be declared in order
109 # for the key to compile.
111 MULTICALL => ['dMULTICALL;'],
112 ORIGMARK => ['dORIGMARK;'],
113 POP_MULTICALL => ['dMULTICALL;', 'U8 gimme;' ],
114 PUSH_MULTICALL => ['dMULTICALL;', 'U8 gimme;' ],
115 POPpbytex => ['STRLEN n_a;'],
116 POPpx => ['STRLEN n_a;'],
121 RESTORE_LC_NUMERIC => ['DECLARATION_FOR_LC_NUMERIC_MANIPULATION;'],
122 STORE_LC_NUMERIC_FORCE_TO_UNDERLYING => ['DECLARATION_FOR_LC_NUMERIC_MANIPULATION;'],
123 STORE_LC_NUMERIC_SET_TO_NEEDED => ['DECLARATION_FOR_LC_NUMERIC_MANIPULATION;'],
124 STORE_LC_NUMERIC_SET_TO_NEEDED_IN => ['DECLARATION_FOR_LC_NUMERIC_MANIPULATION;'],
126 UNDERBAR => ['dUNDERBAR;'],
127 XCPT_CATCH => ['dXCPT;'],
128 XCPT_RETHROW => ['dXCPT;'],
129 XCPT_TRY_END => ['dXCPT;'],
130 XCPT_TRY_START => ['dXCPT;'],
131 XPUSHi => ['dTARG;'],
132 XPUSHn => ['dTARG;'],
133 XPUSHp => ['dTARG;'],
134 XPUSHu => ['dTARG;'],
135 XS_APIVERSION_BOOTCHECK => ['CV * cv;'],
136 XS_VERSION_BOOTCHECK => ['CV * cv;'],
137 MY_CXT_INIT => [ @simple_my_cxt_prereqs ],
138 MY_CXT_CLONE => [ @simple_my_cxt_prereqs ],
139 dMY_CXT => [ @simple_my_cxt_prereqs ],
140 MY_CXT => [ @my_cxt_prereqs ],
141 _aMY_CXT => [ @my_cxt_prereqs ],
142 aMY_CXT => [ @my_cxt_prereqs ],
143 aMY_CXT_ => [ @my_cxt_prereqs ],
144 pMY_CXT => [ @my_cxt_prereqs ],
147 # The entries in %ignore have two components, separated by this.
150 # Things to not try to check. (The component after $sep is empty.)
151 my %ignore = map { ("$_$sep" => 1) } keys %{&known_but_hard_to_test_for()};
155 * !!!!!!! DO NOT EDIT THIS FILE !!!!!!!
156 * This file is built by $0.
157 * Any changes made here will be lost!
164 # These may not have gotten #included, and don't exist in all versions
166 for $hdr (qw(time64 perliol malloc_ctl perl_inc_macro patchlevel)) {
169 if (-e "$dir/CORE/$hdr.h") {
170 print OUT "#include \"$hdr.h\"\n";
181 #ifdef DPPP_APICHECK_NO_PPPORT_H
183 /* This is just to avoid too many baseline failures with perls < 5.6.0 */
186 # define dTHX extern int Perl___notused
191 $ENV{'DPPP_NEED'} /* All the requisite NEED_foo #defines */
198 static char *VARarg2;
199 static double VARarg3;
201 #if defined(PERL_BCDVERSION) && (PERL_BCDVERSION < 0x5009005)
202 /* needed to make PL_parser apicheck work */
203 typedef void yy_parser;
206 /* Handle both 5.x.y and 7.x.y and up
207 #ifndef PERL_VERSION_MAJOR
208 # define PERL_VERSION_MAJOR PERL_REVISION
210 #ifndef PERL_VERSION_MINOR
211 # define PERL_VERSION_MINOR PERL_VERSION
213 #ifndef PERL_VERSION_PATCH
214 # define PERL_VERSION_PATCH PERL_SUBVERSION
217 /* This causes some functions to compile that otherwise wouldn't, so we can
218 * get their info; and doesn't seem to harm anything */
219 #define PERL_IMPLICIT_CONTEXT
223 # Caller can restrict what functions tests are generated for
225 my %want = map { ($_ => 0) } @ARGV;
226 @f = grep { exists $want{$_->{'name'}} } @f;
227 for (@f) { $want{$_->{'name'}}++ }
229 die "nothing found for '$_'\n" unless $want{$_};
234 for $f (@f) { # Loop through all the tests to add
236 # Just the name isn't unique; We also need the #if or #else condition
237 my $unique = "$f->{'name'}$sep$f->{'cond'}";
238 $ignore{$unique} and next;
240 # only public API members, except those in ppport.fnc are there because we
241 # want them to be tested even if non-public. X,M functions are supposed to
242 # be considered to have just the macro form public.
244 or $f->{'ppport_fnc'}
245 or ($f->{'flags'}{'X'} and $f->{'flags'}{'M'})
248 # Don't test unorthodox things that we aren't set up to do
249 $f->{'flags'}{'u'} and next;
250 $f->{'flags'}{'y'} and next;
252 $ignore{$unique} = 1; # ignore duplicates
254 my $Perl_ = $f->{'flags'}{'p'} ? 'Perl_' : '';
264 for $ca (@{$f->{'args'}}) { # Loop through the function's args
265 my $a = $ca->[0]; # 1th is the name, 0th is its type
268 push @arg, qw(VARarg1 VARarg2 VARarg3);
272 # Split this type into its components
273 my($n, $p, $d) = $a =~ /^ ( (?: " [^"]* " ) # literal string type => $n
274 | (?: \w+ (?: \s+ \w+ )* ) # name of type => $n
277 ( \** ) # optional pointer(s) => $p
278 (?: \s* \b const \b \s* )? # opt. const
279 ( (?: \[ [^\]]* \] )* ) # opt. dimension(s)=> $d
281 or die "$0 - cannot parse argument: [$a] in $f->{'name'}\n";
283 # Replace a special argument name by something that will compile.
284 if (exists $amap{$n}) {
285 die "$f->{'name'} had type $n, which should have been the whole type"
287 push @arg, $amap{$n};
291 # Certain types, like 'void', get remapped.
292 $n = $tmap{$n} || $n;
294 if ($n =~ / ^ " [^"]* " $/x) { # Use the literal string, literally
298 my $v = 'arg' . $i++; # Argument number
300 my $no_const_n = $n; # Get rid of any remaining 'const's
301 $no_const_n =~ s/\bconst\b// unless $p;
303 # Declare this argument
304 $stack .= " static $no_const_n $p$v$d;\n";
308 # Declare thread context for functions and macros that might need it.
309 # (Macros often fail to say they don't need it.)
310 unless ($f->{'flags'}{'T'}) {
311 $stack = " dTHX;\n$stack"; # Harmless to declare even if not needed
312 $aTHX = @arg ? 'aTHX_ ' : 'aTHX';
315 # If this function is on the list of things that need declarations, add
317 if ($stack{$f->{'name'}}) {
319 for (@{$stack{$f->{'name'}}}) {
325 my $args = join ', ', @arg;
328 my $rvt = $f->{'ret'};
330 # Replace generic 'type'
331 $rvt = 'int' if defined $rvt && $rvt eq 'type';
333 # Failure to specify a return type in the apidoc line means void
334 $rvt = 'void' unless $rvt;;
337 if ($void{$rvt}) { # Certain return types are instead considered void
338 $ret = $castvoid{$f->{'name'}} ? '(void) ' : '';
341 $stack .= " $rvt rval;\n";
342 $ret = $ignorerv{$f->{'name'}} ? '(void) ' : "rval = ";
348 # Add parens to functions that take an argument list, even if empty
349 unless ($f->{'flags'}{'n'}) {
350 $THX_suffix = "($aTHX$args)";
354 # Single trailing underscore in name means is a comma operator
355 if ($f->{'name'} =~ /[^_]_$/) {
360 # Single leading underscore in a few names means is a comma operator
361 if ($f->{'name'} =~ /^ _[ adp] (?: THX | MY_CXT ) /x) {
368 /******************************************************************************
371 * $f->{'name'} $script_args{'--todo-dir'} $script_args{'--todo'}
373 ******************************************************************************/
377 # #ifdef out if marked as todo (not known in) this version
378 if (exists $todo{$f->{'name'}}) {
379 my($rev, $ver,$sub) = parse_version($todo{$f->{'name'}}{'version'});
381 #if PERL_VERSION_MAJOR > $rev \\
382 || ( PERL_VERSION_MAJOR == $rev \\
383 && ( PERL_VERSION_MINOR > $ver \\
384 || ( PERL_VERSION_MINOR == $ver \\
385 && PERL_VERSION_PATCH >= $sub))) /* TODO */
390 ? "$THX_prefix$Perl_$f->{'name'}$THX_suffix"
391 : "$prefix$f->{'name'}$args";
393 # If there is a '#if' associated with this, add that
394 $f->{'cond'} and print OUT "#if $f->{'cond'}\n";
396 # If only to be tested when ppport.h is enabled
397 $f->{'ppport_fnc'} and print OUT "#ifndef DPPP_APICHECK_NO_PPPORT_H\n";
400 void DPPP_test_$f->{'name'} (void)
407 # If M is a flag here, it means the 'Perl_' form is not for general use, but
408 # the macro (tested above) is.
409 if ($f->{'flags'}{'M'}) {
412 $ret$prefix$f->{'name'}$args;
422 $ret$prefix$f->{'name'}$args;
430 $ret$THX_prefix$Perl_$f->{'name'}$THX_suffix;
438 $f->{'ppport_fnc'} and print OUT "#endif\n";
439 $f->{'cond'} and print OUT "#endif\n";
440 exists $todo{$f->{'name'}} and print OUT "#endif\n";