This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Improvements to OP_ISBOOL
[perl5.git] / regen / embed.pl
1 #!/usr/bin/perl -w
2 #
3 # Regenerate (overwriting only if changed):
4 #
5 #    embed.h
6 #    embedvar.h
7 #    proto.h
8 #
9 # from information stored in
10 #
11 #    embed.fnc
12 #    intrpvar.h
13 #    perlvars.h
14 #    regen/opcodes
15 #
16 # Accepts the standard regen_lib -q and -v args.
17 #
18 # This script is normally invoked from regen.pl.
19
20 require 5.004;  # keep this compatible, an old perl is all we may have before
21                 # we build the new one
22
23 use strict;
24
25 BEGIN {
26     # Get function prototypes
27     require './regen/regen_lib.pl';
28     require './regen/embed_lib.pl';
29 }
30
31 my $unflagged_pointers;
32
33 #
34 # See database of global and static function prototypes in embed.fnc
35 # This is used to generate prototype headers under various configurations,
36 # export symbols lists for different platforms, and macros to provide an
37 # implicit interpreter context argument.
38 #
39
40 my $error_count = 0;
41 sub die_at_end ($) { # Keeps going for now, but makes sure the regen doesn't
42                      # succeed.
43     warn shift;
44     $error_count++;
45 }
46
47 sub full_name ($$) { # Returns the function name with potentially the
48                      # prefixes 'S_' or 'Perl_'
49     my ($func, $flags) = @_;
50
51     return "Perl_$func" if $flags =~ /p/;
52     return "S_$func" if $flags =~ /[SIi]/;
53     return $func;
54 }
55
56 sub open_print_header {
57     my ($file, $quote) = @_;
58
59     return open_new($file, '>',
60                     { file => $file, style => '*', by => 'regen/embed.pl',
61                       from => ['data in embed.fnc', 'regen/embed.pl',
62                                'regen/opcodes', 'intrpvar.h', 'perlvars.h'],
63                       final => "\nEdit those files and run 'make regen_headers' to effect changes.\n",
64                       copyright => [1993 .. 2009], quote => $quote });
65 }
66
67 my ($embed, $core, $ext, $api) = setup_embed();
68
69 # generate proto.h
70 {
71     my $pr = open_print_header("proto.h");
72     print $pr "START_EXTERN_C\n";
73     my $ret;
74
75     foreach (@$embed) {
76         if (@$_ == 1) {
77             print $pr "$_->[0]\n";
78             next;
79         }
80
81         my ($flags,$retval,$plain_func,@args) = @$_;
82         if ($flags =~ / ( [^AabCDdEefFGhIiMmNnOoPpRrSsTUuWXx] ) /x) {
83             die_at_end "flag $1 is not legal (for function $plain_func)";
84         }
85         my @nonnull;
86         my $args_assert_line = ( $flags !~ /G/ );
87         my $has_depth = ( $flags =~ /W/ );
88         my $has_context = ( $flags !~ /T/ );
89         my $never_returns = ( $flags =~ /r/ );
90         my $binarycompat = ( $flags =~ /b/ );
91         my $commented_out = ( $flags =~ /m/ );
92         my $is_malloc = ( $flags =~ /a/ );
93         my $can_ignore = ( $flags !~ /R/ ) && ( $flags !~ /P/ ) && !$is_malloc;
94         my @names_of_nn;
95         my $func;
96
97         if (! $can_ignore && $retval eq 'void') {
98             warn "It is nonsensical to require the return value of a void function ($plain_func) to be checked";
99         }
100
101         die_at_end "$plain_func: S and p flags are mutually exclusive"
102                                             if $flags =~ /S/ && $flags =~ /p/;
103         die_at_end "$plain_func: m and $1 flags are mutually exclusive"
104                                         if $flags =~ /m/ && $flags =~ /([pS])/;
105
106         die_at_end "$plain_func: u flag only usable with m" if $flags =~ /u/
107                                                             && $flags !~ /m/;
108
109         my $static_inline = 0;
110         if ($flags =~ /([SIi])/) {
111             my $type;
112             if ($never_returns) {
113                 $type = {
114                     'S' => 'PERL_STATIC_NO_RET',
115                     'i' => 'PERL_STATIC_INLINE_NO_RET',
116                     'I' => 'PERL_STATIC_FORCE_INLINE_NO_RET'
117                 }->{$1};
118             }
119             else {
120                 $type = {
121                     'S' => 'STATIC',
122                     'i' => 'PERL_STATIC_INLINE',
123                     'I' => 'PERL_STATIC_FORCE_INLINE'
124                 }->{$1};
125             }
126             $retval = "$type $retval";
127             die_at_end "Don't declare static function '$plain_func' pure" if $flags =~ /P/;
128             $static_inline = $type =~ /^PERL_STATIC(?:_FORCE)?_INLINE/;
129         }
130         else {
131             if ($never_returns) {
132                 $retval = "PERL_CALLCONV_NO_RET $retval";
133             }
134             else {
135                 $retval = "PERL_CALLCONV $retval";
136             }
137         }
138
139         die_at_end "For '$plain_func', M flag requires p flag"
140                                             if $flags =~ /M/ && $flags !~ /p/;
141         die_at_end "For '$plain_func', C flag requires one of [pIimb] flags"
142                                             if $flags =~ /C/ && $flags !~ /[Iibmp]/;
143         die_at_end "For '$plain_func', X flag requires one of [Iip] flags"
144                                             if $flags =~ /X/ && $flags !~ /[Iip]/;
145         die_at_end "For '$plain_func', X and m flags are mutually exclusive"
146                                             if $flags =~ /X/ && $flags =~ /m/;
147         die_at_end "For '$plain_func', [Ii] with [ACX] requires p flag"
148                         if $flags =~ /[Ii]/ && $flags =~ /[ACX]/ && $flags !~ /p/;
149         die_at_end "For '$plain_func', b and m flags are mutually exclusive"
150                  . " (try M flag)" if $flags =~ /b/ && $flags =~ /m/;
151         die_at_end "For '$plain_func', b flag without M flag requires D flag"
152                             if $flags =~ /b/ && $flags !~ /M/ && $flags !~ /D/;
153         die_at_end "For '$plain_func', I and i flags are mutually exclusive"
154                                             if $flags =~ /I/ && $flags =~ /i/;
155
156         $func = full_name($plain_func, $flags);
157         $ret = "";
158         $ret .= "$retval\t$func(";
159         if ( $has_context ) {
160             $ret .= @args ? "pTHX_ " : "pTHX";
161         }
162         if (@args) {
163             die_at_end "n flag is contradicted by having arguments"
164                                                                 if $flags =~ /n/;
165             my $n;
166             for my $arg ( @args ) {
167                 ++$n;
168                 if (   $args_assert_line
169                     && $arg =~ /\*/
170                     && $arg !~ /\b(NN|NULLOK)\b/ )
171                 {
172                     warn "$func: $arg needs NN or NULLOK\n";
173                     ++$unflagged_pointers;
174                 }
175                 my $nn = ( $arg =~ s/\s*\bNN\b\s+// );
176                 push( @nonnull, $n ) if $nn;
177
178                 my $nullok = ( $arg =~ s/\s*\bNULLOK\b\s+// ); # strip NULLOK with no effect
179
180                 # Make sure each arg has at least a type and a var name.
181                 # An arg of "int" is valid C, but want it to be "int foo".
182                 my $temp_arg = $arg;
183                 $temp_arg =~ s/\*//g;
184                 $temp_arg =~ s/\s*\bstruct\b\s*/ /g;
185                 if ( ($temp_arg ne "...")
186                      && ($temp_arg !~ /\w+\s+(\w+)(?:\[\d+\])?\s*$/) ) {
187                     die_at_end "$func: $arg ($n) doesn't have a name\n";
188                 }
189                 if (defined $1 && $nn && !($commented_out && !$binarycompat)) {
190                     push @names_of_nn, $1;
191                 }
192             }
193             $ret .= join ", ", @args;
194         }
195         else {
196             $ret .= "void" if !$has_context;
197         }
198         $ret .= " _pDEPTH" if $has_depth;
199         $ret .= ")";
200         my @attrs;
201         if ( $flags =~ /r/ ) {
202             push @attrs, "__attribute__noreturn__";
203         }
204         if ( $flags =~ /D/ ) {
205             push @attrs, "__attribute__deprecated__";
206         }
207         if ( $is_malloc ) {
208             push @attrs, "__attribute__malloc__";
209         }
210         if ( !$can_ignore ) {
211             push @attrs, "__attribute__warn_unused_result__";
212         }
213         if ( $flags =~ /P/ ) {
214             push @attrs, "__attribute__pure__";
215         }
216         if ( $flags =~ /I/ ) {
217             push @attrs, "__attribute__always_inline__";
218         }
219         if( $flags =~ /f/ ) {
220             my $prefix  = $has_context ? 'pTHX_' : '';
221             my ($args, $pat);
222             if ($args[-1] eq '...') {
223                 $args   = scalar @args;
224                 $pat    = $args - 1;
225                 $args   = $prefix . $args;
226             }
227             else {
228                 # don't check args, and guess which arg is the pattern
229                 # (one of 'fmt', 'pat', 'f'),
230                 $args = 0;
231                 my @fmts = grep $args[$_] =~ /\b(f|pat|fmt)$/, 0..$#args;
232                 if (@fmts != 1) {
233                     die "embed.pl: '$plain_func': can't determine pattern arg\n";
234                 }
235                 $pat = $fmts[0] + 1;
236             }
237             my $macro   = grep($_ == $pat, @nonnull)
238                                 ? '__attribute__format__'
239                                 : '__attribute__format__null_ok__';
240             if ($plain_func =~ /strftime/) {
241                 push @attrs, sprintf "%s(__strftime__,%s1,0)", $macro, $prefix;
242             }
243             else {
244                 push @attrs, sprintf "%s(__printf__,%s%d,%s)", $macro,
245                                     $prefix, $pat, $args;
246             }
247         }
248         elsif ((grep { $_ eq '...' } @args) && $flags !~ /F/) {
249             die_at_end "$plain_func: Function with '...' arguments must have"
250                      . " f or F flag";
251         }
252         if ( @attrs ) {
253             $ret .= "\n";
254             $ret .= join( "\n", map { "\t\t\t$_" } @attrs );
255         }
256         $ret .= ";";
257         $ret = "/* $ret */" if $commented_out;
258
259         $ret .= "\n#define PERL_ARGS_ASSERT_\U$plain_func\E"
260                                             if $args_assert_line || @names_of_nn;
261         $ret .= "\t\\\n\t" . join '; ', map "assert($_)", @names_of_nn
262                                                                 if @names_of_nn;
263
264         $ret = "#ifndef PERL_NO_INLINE_FUNCTIONS\n$ret\n#endif" if $static_inline;
265         $ret = "#ifndef NO_MATHOMS\n$ret\n#endif" if $binarycompat;
266         $ret .= @attrs ? "\n\n" : "\n";
267
268         print $pr $ret;
269     }
270
271     print $pr <<'EOF';
272 #ifdef PERL_CORE
273 #  include "pp_proto.h"
274 #endif
275 END_EXTERN_C
276 EOF
277
278     read_only_bottom_close_and_rename($pr) if ! $error_count;
279 }
280
281 die_at_end "$unflagged_pointers pointer arguments to clean up\n" if $unflagged_pointers;
282
283 sub readvars {
284     my ($file, $pre) = @_;
285     local (*FILE, $_);
286     my %seen;
287     open(FILE, '<', $file)
288         or die "embed.pl: Can't open $file: $!\n";
289     while (<FILE>) {
290         s/[ \t]*#.*//;          # Delete comments.
291         if (/PERLVARA?I?C?\($pre,\s*(\w+)/) {
292             die_at_end "duplicate symbol $1 while processing $file line $.\n"
293                 if $seen{$1}++;
294         }
295     }
296     close(FILE);
297     return sort keys %seen;
298 }
299
300 my @intrp = readvars 'intrpvar.h','I';
301 my @globvar = readvars 'perlvars.h','G';
302
303 sub hide {
304     my ($from, $to, $indent) = @_;
305     $indent = '' unless defined $indent;
306     my $t = int(length("$indent$from") / 8);
307     "#${indent}define $from" . "\t" x ($t < 3 ? 3 - $t : 1) . "$to\n";
308 }
309
310 sub multon ($$$) {
311     my ($sym,$pre,$ptr) = @_;
312     hide("PL_$sym", "($ptr$pre$sym)");
313 }
314
315 my $em = open_print_header('embed.h');
316
317 print $em <<'END';
318 /* (Doing namespace management portably in C is really gross.) */
319
320 /* By defining PERL_NO_SHORT_NAMES (not done by default) the short forms
321  * (like warn instead of Perl_warn) for the API are not defined.
322  * Not defining the short forms is a good thing for cleaner embedding.
323  * BEWARE that a bunch of macros don't have long names, so either must be
324  * added or don't use them if you define this symbol */
325
326 #ifndef PERL_NO_SHORT_NAMES
327
328 /* Hide global symbols */
329
330 END
331
332 my @az = ('a'..'z');
333
334 sub embed_h {
335     my ($guard, $funcs) = @_;
336     print $em "$guard\n" if $guard;
337
338     my $lines;
339     foreach (@$funcs) {
340         if (@$_ == 1) {
341             my $cond = $_->[0];
342             # Indent the conditionals if we are wrapped in an #if/#endif pair.
343             $cond =~ s/#(.*)/#  $1/ if $guard;
344             $lines .= "$cond\n";
345             next;
346         }
347         my $ret = "";
348         my ($flags,$retval,$func,@args) = @$_;
349         unless ($flags =~ /[omM]/) {
350             my $args = scalar @args;
351             if ($flags =~ /T/) {
352                 my $full_name = full_name($func, $flags);
353                 next if $full_name eq $func;    # Don't output a no-op.
354                 $ret = hide($func, $full_name);
355             }
356             elsif ($args and $args[$args-1] =~ /\.\.\./) {
357                 if ($flags =~ /p/) {
358                     # we're out of luck for varargs functions under CPP
359                     # So we can only do these macros for non-MULTIPLICITY perls:
360                     $ret = "#ifndef MULTIPLICITY\n"
361                         . hide($func, full_name($func, $flags)) . "#endif\n";
362                 }
363             }
364             else {
365                 my $alist = join(",", @az[0..$args-1]);
366                 $ret = "#define $func($alist)";
367                 my $t = int(length($ret) / 8);
368                 $ret .=  "\t" x ($t < 4 ? 4 - $t : 1);
369                 $ret .= full_name($func, $flags) . "(aTHX";
370                 $ret .= "_ " if $alist;
371                 $ret .= $alist;
372                 if ($flags =~ /W/) {
373                     if ($alist) {
374                         $ret .= " _aDEPTH";
375                     } else {
376                         die "Can't use W without other args (currently)";
377                     }
378                 }
379                 $ret .= ")\n";
380             }
381             $ret = "#ifndef NO_MATHOMS\n$ret#endif\n" if $flags =~ /b/;
382         }
383         $lines .= $ret;
384     }
385     # Prune empty #if/#endif pairs.
386     while ($lines =~ s/#\s*if[^\n]+\n#\s*endif\n//) {
387     }
388     # Merge adjacent blocks.
389     while ($lines =~ s/(#ifndef MULTIPLICITY
390 [^\n]+
391 )#endif
392 #ifndef MULTIPLICITY
393 /$1/) {
394     }
395
396     print $em $lines;
397     print $em "#endif\n" if $guard;
398 }
399
400 embed_h('', $api);
401 embed_h('#if defined(PERL_CORE) || defined(PERL_EXT)', $ext);
402 embed_h('#ifdef PERL_CORE', $core);
403
404 print $em <<'END';
405
406 #endif  /* #ifndef PERL_NO_SHORT_NAMES */
407
408 /* Compatibility stubs.  Compile extensions with -DPERL_NOCOMPAT to
409    disable them.
410  */
411
412 #if !defined(PERL_CORE)
413 #  define sv_setptrobj(rv,ptr,name)     sv_setref_iv(rv,name,PTR2IV(ptr))
414 #  define sv_setptrref(rv,ptr)          sv_setref_iv(rv,NULL,PTR2IV(ptr))
415 #endif
416
417 #if !defined(PERL_CORE) && !defined(PERL_NOCOMPAT)
418
419 /* Compatibility for various misnamed functions.  All functions
420    in the API that begin with "perl_" (not "Perl_") take an explicit
421    interpreter context pointer.
422    The following are not like that, but since they had a "perl_"
423    prefix in previous versions, we provide compatibility macros.
424  */
425 #  define perl_atexit(a,b)              call_atexit(a,b)
426 END
427
428 foreach (@$embed) {
429     my ($flags, $retval, $func, @args) = @$_;
430     next unless $func;
431     next unless $flags =~ /O/;
432
433     my $alist = join ",", @az[0..$#args];
434     my $ret = "#  define perl_$func($alist)";
435     my $t = (length $ret) >> 3;
436     $ret .=  "\t" x ($t < 5 ? 5 - $t : 1);
437     print $em "$ret$func($alist)\n";
438 }
439
440 my @nocontext;
441 {
442     my (%has_va, %has_nocontext);
443     foreach (@$embed) {
444         next unless @$_ > 1;
445         ++$has_va{$_->[2]} if $_->[-1] =~ /\.\.\./;
446         ++$has_nocontext{$1} if $_->[2] =~ /(.*)_nocontext/;
447     }
448
449     @nocontext = sort grep {
450         $has_nocontext{$_}
451             && !/printf/ # Not clear to me why these are skipped but they are.
452     } keys %has_va;
453 }
454
455 print $em <<'END';
456
457 /* varargs functions can't be handled with CPP macros. :-(
458    This provides a set of compatibility functions that don't take
459    an extra argument but grab the context pointer using the macro
460    dTHX.
461  */
462 #if defined(MULTIPLICITY) && !defined(PERL_NO_SHORT_NAMES)
463 END
464
465 foreach (@nocontext) {
466     print $em hide($_, "Perl_${_}_nocontext", "  ");
467 }
468
469 print $em <<'END';
470 #endif
471
472 #endif /* !defined(PERL_CORE) && !defined(PERL_NOCOMPAT) */
473
474 #if !defined(MULTIPLICITY)
475 /* undefined symbols, point them back at the usual ones */
476 END
477
478 foreach (@nocontext) {
479     print $em hide("Perl_${_}_nocontext", "Perl_$_", "  ");
480 }
481
482 print $em <<'END';
483 #endif
484 END
485
486 read_only_bottom_close_and_rename($em) if ! $error_count;
487
488 $em = open_print_header('embedvar.h');
489
490 print $em <<'END';
491 #if defined(MULTIPLICITY)
492 #  define vTHX  aTHX
493 END
494
495 my $sym;
496
497 for $sym (@intrp) {
498     if ($sym eq 'sawampersand') {
499         print $em "#ifndef PL_sawampersand\n";
500     }
501     print $em multon($sym,'I','vTHX->');
502     if ($sym eq 'sawampersand') {
503         print $em "#endif\n";
504     }
505 }
506
507 print $em <<'END';
508
509 #endif  /* MULTIPLICITY */
510 END
511
512 read_only_bottom_close_and_rename($em) if ! $error_count;
513
514 die "$error_count errors found" if $error_count;
515
516 # ex: set ts=8 sts=4 sw=4 noet: