#!/usr/bin/perl -w
-#
+#
# Regenerate (overwriting only if changed):
#
# embed.h
# embedvar.h
-# global.sym
# perlapi.c
# perlapi.h
# proto.h
BEGIN {
# Get function prototypes
- require 'regen/regen_lib.pl';
+ require './regen/regen_lib.pl';
+ require './regen/embed_lib.pl';
}
-my $SPLINT = 0; # Turn true for experimental splint support http://www.splint.org
my $unflagged_pointers;
#
# implicit interpreter context argument.
#
+my $error_count = 0;
+sub die_at_end ($) { # Keeps going for now, but makes sure the regen doesn't
+ # succeed.
+ warn shift;
+ $error_count++;
+}
+
+sub full_name ($$) { # Returns the function name with potentially the
+ # prefixes 'S_' or 'Perl_'
+ my ($func, $flags) = @_;
+
+ return "Perl_$func" if $flags =~ /p/;
+ return "S_$func" if $flags =~ /[SIi]/;
+ return $func;
+}
+
sub open_print_header {
my ($file, $quote) = @_;
copyright => [1993 .. 2009], quote => $quote });
}
-open IN, "embed.fnc" or die $!;
-
-my @embed;
-my (%has_va, %has_nocontext);
-
-while (<IN>) {
- chomp;
- next if /^:/;
- next if /^$/;
- while (s|\\$||) {
- $_ .= <IN>;
- chomp;
- }
- s/\s+$//;
- my @args;
- if (/^\s*(#|$)/) {
- @args = $_;
- }
- else {
- @args = split /\s*\|\s*/, $_;
- my $func = $args[2];
- if ($func) {
- ++$has_va{$func} if $args[-1] =~ /\.\.\./;
- ++$has_nocontext{$1} if $func =~ /(.*)_nocontext/;
- }
- }
- if (@args == 1 && $args[0] !~ /^#\s*(?:if|ifdef|ifndef|else|endif)/) {
- die "Illegal line $. '$args[0]' in embed.fnc";
- }
- push @embed, \@args;
-}
-
-open IN, 'regen/opcodes' or die $!;
-{
- my %syms;
-
- while (<IN>) {
- chop;
- next unless $_;
- next if /^#/;
- my (undef, undef, $check) = split /\t+/, $_;
- ++$syms{$check};
- }
-
- foreach (keys %syms) {
- # These are all indirectly referenced by globals.c.
- push @embed, ['pR', 'OP *', $_, 'NN OP *o'];
- }
-}
-close IN;
-
-my (@core, @ext, @api);
-{
- # Cluster entries in embed.fnc that have the same #ifdef guards.
- # Also, split out at the top level the three classes of functions.
- my @state;
- my %groups;
- my $current;
- foreach (@embed) {
- if (@$_ > 1) {
- push @$current, $_;
- next;
- }
- $_->[0] =~ s/^#\s+/#/;
- $_->[0] =~ /^\S*/;
- $_->[0] =~ s/^#ifdef\s+(\S+)/#if defined($1)/;
- $_->[0] =~ s/^#ifndef\s+(\S+)/#if !defined($1)/;
- if ($_->[0] =~ /^#if\s*(.*)/) {
- push @state, $1;
- } elsif ($_->[0] =~ /^#else\s*$/) {
- die "Unmatched #else in embed.fnc" unless @state;
- $state[-1] = "!($state[-1])";
- } elsif ($_->[0] =~ m!^#endif\s*(?:/\*.*\*/)?$!) {
- die "Unmatched #endif in embed.fnc" unless @state;
- pop @state;
- } else {
- die "Unhandled pre-processor directive '$_->[0]' in embed.fnc";
- }
- $current = \%groups;
- # Nested #if blocks are effectively &&ed together
- # For embed.fnc, ordering withing the && isn't relevant, so we can
- # sort them to try to group more functions together.
- my @sorted = sort @state;
- while (my $directive = shift @sorted) {
- $current->{$directive} ||= {};
- $current = $current->{$directive};
- }
- $current->{''} ||= [];
- $current = $current->{''};
- }
-
- sub add_level {
- my ($level, $indent, $wanted) = @_;
- my $funcs = $level->{''};
- my @entries;
- if ($funcs) {
- if (!defined $wanted) {
- @entries = @$funcs;
- } else {
- foreach (@$funcs) {
- if ($_->[0] =~ /A/) {
- push @entries, $_ if $wanted eq 'A';
- } elsif ($_->[0] =~ /E/) {
- push @entries, $_ if $wanted eq 'E';
- } else {
- push @entries, $_ if $wanted eq '';
- }
- }
- }
- @entries = sort {$a->[2] cmp $b->[2]} @entries;
- }
- foreach (sort grep {length $_} keys %$level) {
- my @conditional = add_level($level->{$_}, $indent . ' ', $wanted);
- push @entries,
- ["#${indent}if $_"], @conditional, ["#${indent}endif"]
- if @conditional;
- }
- return @entries;
- }
- @core = add_level(\%groups, '', '');
- @ext = add_level(\%groups, '', 'E');
- @api = add_level(\%groups, '', 'A');
-
- @embed = add_level(\%groups, '');
-}
-
-# walk table providing an array of components in each line to
-# subroutine, printing the result
-sub walk_table (&@) {
- my ($function, $filename) = @_;
- my $F;
- if (ref $filename) { # filehandle
- $F = $filename;
- }
- else {
- $F = open_print_header($filename);
- }
- foreach (@embed) {
- my @outs = &{$function}(@$_);
- # $function->(@args) is not 5.003
- print $F @outs;
- }
- unless (ref $filename) {
- read_only_bottom_close_and_rename($F);
- }
-}
+my ($embed, $core, $ext, $api) = setup_embed();
# generate proto.h
{
print $pr "START_EXTERN_C\n";
my $ret;
- foreach (@embed) {
+ foreach (@$embed) {
if (@$_ == 1) {
print $pr "$_->[0]\n";
next;
}
my ($flags,$retval,$plain_func,@args) = @$_;
+ if ($flags =~ / ( [^AabCDdEefFGhIiMmNnOoPpRrSsTUuWXx] ) /x) {
+ die_at_end "flag $1 is not legal (for function $plain_func)";
+ }
my @nonnull;
- my $has_context = ( $flags !~ /n/ );
+ my $args_assert_line = ( $flags !~ /G/ );
+ my $has_depth = ( $flags =~ /W/ );
+ my $has_context = ( $flags !~ /T/ );
my $never_returns = ( $flags =~ /r/ );
- my $commented_out = ( $flags =~ /m/ );
my $binarycompat = ( $flags =~ /b/ );
+ my $commented_out = ( $flags =~ /m/ );
my $is_malloc = ( $flags =~ /a/ );
- my $can_ignore = ( $flags !~ /R/ ) && !$is_malloc;
+ my $can_ignore = ( $flags !~ /R/ ) && ( $flags !~ /P/ ) && !$is_malloc;
my @names_of_nn;
my $func;
- my $splint_flags = "";
- if ( $SPLINT && !$commented_out ) {
- $splint_flags .= '/*@noreturn@*/ ' if $never_returns;
- if ($can_ignore && ($retval ne 'void') && ($retval !~ /\*/)) {
- $retval .= " /*\@alt void\@*/";
- }
+ if (! $can_ignore && $retval eq 'void') {
+ warn "It is nonsensical to require the return value of a void function ($plain_func) to be checked";
}
- if ($flags =~ /([si])/) {
- my $type = ($1 eq 's') ? "STATIC" : "PERL_STATIC_INLINE";
- warn "$func: i and s flags are mutually exclusive"
- if $flags =~ /s/ && $flags =~ /i/;
- $retval = "$type $splint_flags$retval";
- $func = "S_$plain_func";
+ die_at_end "$plain_func: S and p flags are mutually exclusive"
+ if $flags =~ /S/ && $flags =~ /p/;
+ die_at_end "$plain_func: m and $1 flags are mutually exclusive"
+ if $flags =~ /m/ && $flags =~ /([pS])/;
+
+ die_at_end "$plain_func: u flag only usable with m" if $flags =~ /u/
+ && $flags !~ /m/;
+
+ my $static_inline = 0;
+ if ($flags =~ /([SIi])/) {
+ my $type;
+ if ($never_returns) {
+ $type = {
+ 'S' => 'PERL_STATIC_NO_RET',
+ 'i' => 'PERL_STATIC_INLINE_NO_RET',
+ 'I' => 'PERL_STATIC_FORCE_INLINE_NO_RET'
+ }->{$1};
+ }
+ else {
+ $type = {
+ 'S' => 'STATIC',
+ 'i' => 'PERL_STATIC_INLINE',
+ 'I' => 'PERL_STATIC_FORCE_INLINE'
+ }->{$1};
+ }
+ $retval = "$type $retval";
+ die_at_end "Don't declare static function '$plain_func' pure" if $flags =~ /P/;
+ $static_inline = $type =~ /^PERL_STATIC(?:_FORCE)?_INLINE/;
}
else {
- $retval = "PERL_CALLCONV $splint_flags$retval";
- if ($flags =~ /[bp]/) {
- $func = "Perl_$plain_func";
- } else {
- $func = $plain_func;
+ if ($never_returns) {
+ $retval = "PERL_CALLCONV_NO_RET $retval";
+ }
+ else {
+ $retval = "PERL_CALLCONV $retval";
}
}
- $ret = "$retval\t$func(";
+
+ die_at_end "For '$plain_func', M flag requires p flag"
+ if $flags =~ /M/ && $flags !~ /p/;
+ die_at_end "For '$plain_func', C flag requires one of [pIimb] flags"
+ if $flags =~ /C/ && $flags !~ /[Iibmp]/;
+ die_at_end "For '$plain_func', X flag requires one of [Iip] flags"
+ if $flags =~ /X/ && $flags !~ /[Iip]/;
+ die_at_end "For '$plain_func', X and m flags are mutually exclusive"
+ if $flags =~ /X/ && $flags =~ /m/;
+ die_at_end "For '$plain_func', [Ii] with [ACX] requires p flag"
+ if $flags =~ /[Ii]/ && $flags =~ /[ACX]/ && $flags !~ /p/;
+ die_at_end "For '$plain_func', b and m flags are mutually exclusive"
+ . " (try M flag)" if $flags =~ /b/ && $flags =~ /m/;
+ die_at_end "For '$plain_func', b flag without M flag requires D flag"
+ if $flags =~ /b/ && $flags !~ /M/ && $flags !~ /D/;
+ die_at_end "For '$plain_func', I and i flags are mutually exclusive"
+ if $flags =~ /I/ && $flags =~ /i/;
+
+ $func = full_name($plain_func, $flags);
+ $ret = "";
+ $ret .= "$retval\t$func(";
if ( $has_context ) {
$ret .= @args ? "pTHX_ " : "pTHX";
}
if (@args) {
+ die_at_end "n flag is contradicted by having arguments"
+ if $flags =~ /n/;
my $n;
for my $arg ( @args ) {
++$n;
$temp_arg =~ s/\s*\bstruct\b\s*/ /g;
if ( ($temp_arg ne "...")
&& ($temp_arg !~ /\w+\s+(\w+)(?:\[\d+\])?\s*$/) ) {
- warn "$func: $arg ($n) doesn't have a name\n";
- }
- if ( $SPLINT && $nullok && !$commented_out ) {
- $arg = '/*@null@*/ ' . $arg;
+ die_at_end "$func: $arg ($n) doesn't have a name\n";
}
if (defined $1 && $nn && !($commented_out && !$binarycompat)) {
push @names_of_nn, $1;
else {
$ret .= "void" if !$has_context;
}
+ $ret .= " _pDEPTH" if $has_depth;
$ret .= ")";
my @attrs;
if ( $flags =~ /r/ ) {
if ( $flags =~ /P/ ) {
push @attrs, "__attribute__pure__";
}
+ if ( $flags =~ /I/ ) {
+ push @attrs, "__attribute__always_inline__";
+ }
if( $flags =~ /f/ ) {
my $prefix = $has_context ? 'pTHX_' : '';
- my $args = scalar @args;
- my $pat = $args - 1;
- my $macro = @nonnull && $nonnull[-1] == $pat
+ my ($args, $pat);
+ if ($args[-1] eq '...') {
+ $args = scalar @args;
+ $pat = $args - 1;
+ $args = $prefix . $args;
+ }
+ else {
+ # don't check args, and guess which arg is the pattern
+ # (one of 'fmt', 'pat', 'f'),
+ $args = 0;
+ my @fmts = grep $args[$_] =~ /\b(f|pat|fmt)$/, 0..$#args;
+ if (@fmts != 1) {
+ die "embed.pl: '$plain_func': can't determine pattern arg\n";
+ }
+ $pat = $fmts[0] + 1;
+ }
+ my $macro = grep($_ == $pat, @nonnull)
? '__attribute__format__'
: '__attribute__format__null_ok__';
- push @attrs, sprintf "%s(__printf__,%s%d,%s%d)", $macro,
- $prefix, $pat, $prefix, $args;
+ if ($plain_func =~ /strftime/) {
+ push @attrs, sprintf "%s(__strftime__,%s1,0)", $macro, $prefix;
+ }
+ else {
+ push @attrs, sprintf "%s(__printf__,%s%d,%s)", $macro,
+ $prefix, $pat, $args;
+ }
}
- if ( @nonnull ) {
- my @pos = map { $has_context ? "pTHX_$_" : $_ } @nonnull;
- push @attrs, map { sprintf( "__attribute__nonnull__(%s)", $_ ) } @pos;
+ elsif ((grep { $_ eq '...' } @args) && $flags !~ /F/) {
+ die_at_end "$plain_func: Function with '...' arguments must have"
+ . " f or F flag";
}
if ( @attrs ) {
$ret .= "\n";
}
$ret .= ";";
$ret = "/* $ret */" if $commented_out;
- if (@names_of_nn) {
- $ret .= "\n#define PERL_ARGS_ASSERT_\U$plain_func\E\t\\\n\t"
- . join '; ', map "assert($_)", @names_of_nn;
- }
+
+ $ret .= "\n#define PERL_ARGS_ASSERT_\U$plain_func\E"
+ if $args_assert_line || @names_of_nn;
+ $ret .= "\t\\\n\t" . join '; ', map "assert($_)", @names_of_nn
+ if @names_of_nn;
+
+ $ret = "#ifndef PERL_NO_INLINE_FUNCTIONS\n$ret\n#endif" if $static_inline;
+ $ret = "#ifndef NO_MATHOMS\n$ret\n#endif" if $binarycompat;
$ret .= @attrs ? "\n\n" : "\n";
print $pr $ret;
END_EXTERN_C
EOF
- read_only_bottom_close_and_rename($pr);
-}
-
-# generates global.sym (API export list)
-{
- my %seen;
- sub write_global_sym {
- if (@_ > 1) {
- my ($flags,$retval,$func,@args) = @_;
- if ($flags =~ /[AX]/ && $flags !~ /[xm]/
- || $flags =~ /b/) { # public API, so export
- # If a function is defined twice, for example before and after
- # an #else, only export its name once.
- return '' if $seen{$func}++;
- $func = "Perl_$func" if $flags =~ /[pbX]/;
- return "$func\n";
- }
- }
- return '';
- }
+ read_only_bottom_close_and_rename($pr) if ! $error_count;
}
-warn "$unflagged_pointers pointer arguments to clean up\n" if $unflagged_pointers;
-walk_table(\&write_global_sym, "global.sym");
+die_at_end "$unflagged_pointers pointer arguments to clean up\n" if $unflagged_pointers;
sub readvars {
my ($file, $pre) = @_;
local (*FILE, $_);
my %seen;
- open(FILE, "< $file")
+ open(FILE, '<', $file)
or die "embed.pl: Can't open $file: $!\n";
while (<FILE>) {
s/[ \t]*#.*//; # Delete comments.
if (/PERLVARA?I?C?\($pre,\s*(\w+)/) {
- warn "duplicate symbol $1 while processing $file line $.\n"
+ die_at_end "duplicate symbol $1 while processing $file line $.\n"
if $seen{$1}++;
}
}
}
my $ret = "";
my ($flags,$retval,$func,@args) = @$_;
- unless ($flags =~ /[om]/) {
+ unless ($flags =~ /[omM]/) {
my $args = scalar @args;
- if ($flags =~ /n/) {
- if ($flags =~ /s/) {
- $ret = hide($func,"S_$func");
- }
- elsif ($flags =~ /p/) {
- $ret = hide($func,"Perl_$func");
- }
+ if ($flags =~ /T/) {
+ my $full_name = full_name($func, $flags);
+ next if $full_name eq $func; # Don't output a no-op.
+ $ret = hide($func, $full_name);
}
elsif ($args and $args[$args-1] =~ /\.\.\./) {
if ($flags =~ /p/) {
# we're out of luck for varargs functions under CPP
# So we can only do these macros for no implicit context:
$ret = "#ifndef PERL_IMPLICIT_CONTEXT\n"
- . hide($func,"Perl_$func") . "#endif\n";
+ . hide($func, full_name($func, $flags)) . "#endif\n";
}
}
else {
$ret = "#define $func($alist)";
my $t = int(length($ret) / 8);
$ret .= "\t" x ($t < 4 ? 4 - $t : 1);
- if ($flags =~ /[si]/) {
- $ret .= "S_$func(aTHX";
- }
- elsif ($flags =~ /p/) {
- $ret .= "Perl_$func(aTHX";
- }
+ $ret .= full_name($func, $flags) . "(aTHX";
$ret .= "_ " if $alist;
- $ret .= $alist . ")\n";
+ $ret .= $alist;
+ if ($flags =~ /W/) {
+ if ($alist) {
+ $ret .= " _aDEPTH";
+ } else {
+ die "Can't use W without other args (currently)";
+ }
+ }
+ $ret .= ")\n";
}
+ $ret = "#ifndef NO_MATHOMS\n$ret#endif\n" if $flags =~ /b/;
}
$lines .= $ret;
}
print $em "#endif\n" if $guard;
}
-embed_h('', \@api);
-embed_h('#if defined(PERL_CORE) || defined(PERL_EXT)', \@ext);
-embed_h('#ifdef PERL_CORE', \@core);
+embed_h('', $api);
+embed_h('#if defined(PERL_CORE) || defined(PERL_EXT)', $ext);
+embed_h('#ifdef PERL_CORE', $core);
print $em <<'END';
# define perl_atexit(a,b) call_atexit(a,b)
END
-walk_table {
- my ($flags,$retval,$func,@args) = @_;
- return unless $func;
- return unless $flags =~ /O/;
+foreach (@$embed) {
+ my ($flags, $retval, $func, @args) = @$_;
+ next unless $func;
+ next unless $flags =~ /O/;
my $alist = join ",", @az[0..$#args];
my $ret = "# define perl_$func($alist)";
my $t = (length $ret) >> 3;
$ret .= "\t" x ($t < 5 ? 5 - $t : 1);
- "$ret$func($alist)\n";
-} $em;
+ print $em "$ret$func($alist)\n";
+}
+
+my @nocontext;
+{
+ my (%has_va, %has_nocontext);
+ foreach (@$embed) {
+ next unless @$_ > 1;
+ ++$has_va{$_->[2]} if $_->[-1] =~ /\.\.\./;
+ ++$has_nocontext{$1} if $_->[2] =~ /(.*)_nocontext/;
+ }
+
+ @nocontext = sort grep {
+ $has_nocontext{$_}
+ && !/printf/ # Not clear to me why these are skipped but they are.
+ } keys %has_va;
+}
print $em <<'END';
#if defined(PERL_IMPLICIT_CONTEXT) && !defined(PERL_NO_SHORT_NAMES)
END
-foreach (sort keys %has_va) {
- next unless $has_nocontext{$_};
- next if /printf/; # Not clear to me why these are skipped but they are.
+foreach (@nocontext) {
print $em hide($_, "Perl_${_}_nocontext", " ");
}
/* undefined symbols, point them back at the usual ones */
END
-foreach (sort keys %has_va) {
- next unless $has_nocontext{$_};
- next if /printf/; # Not clear to me why these are skipped but they are.
+foreach (@nocontext) {
print $em hide("Perl_${_}_nocontext", "Perl_$_", " ");
}
#endif
END
-read_only_bottom_close_and_rename($em);
+read_only_bottom_close_and_rename($em) if ! $error_count;
$em = open_print_header('embedvar.h');
my $sym;
for $sym (@intrp) {
+ if ($sym eq 'sawampersand') {
+ print $em "#ifndef PL_sawampersand\n";
+ }
print $em multon($sym,'I','vTHX->');
+ if ($sym eq 'sawampersand') {
+ print $em "#endif\n";
+ }
}
print $em <<'END';
for $sym (@globvar) {
print $em "#ifdef OS2\n" if $sym eq 'sh_path';
+ print $em "#ifdef __VMS\n" if $sym eq 'perllib_sep';
print $em multon($sym, 'G','my_vars->');
print $em multon("G$sym",'', 'my_vars->');
print $em "#endif\n" if $sym eq 'sh_path';
+ print $em "#endif\n" if $sym eq 'perllib_sep';
}
print $em <<'END';
#endif /* PERL_GLOBAL_STRUCT */
END
-read_only_bottom_close_and_rename($em);
+read_only_bottom_close_and_rename($em) if ! $error_count;
my $capih = open_print_header('perlapi.h');
#endif /* __perlapi_h__ */
EOT
-read_only_bottom_close_and_rename($capih);
+read_only_bottom_close_and_rename($capih) if ! $error_count;
my $capi = open_print_header('perlapi.c', <<'EOQ');
*
#endif /* MULTIPLICITY && PERL_GLOBAL_STRUCT */
EOT
-read_only_bottom_close_and_rename($capi);
+read_only_bottom_close_and_rename($capi) if ! $error_count;
+
+die "$error_count errors found" if $error_count;
# ex: set ts=8 sts=4 sw=4 noet: