add newer malloc.c from Ilya Zakharevich <ilya@math.ohio-state.edu>
[p5sagit/p5-mst-13.2.git] / lib / ExtUtils / xsubpp
index 5f6feb8..8e253ff 100755 (executable)
@@ -6,7 +6,7 @@ xsubpp - compiler to convert Perl XS code into C code
 
 =head1 SYNOPSIS
 
-B<xsubpp> [B<-v>] [B<-C++>] [B<-except>] [B<-s pattern>] [B<-prototypes>] [B<-noversioncheck>] [B<-typemap typemap>]... file.xs
+B<xsubpp> [B<-v>] [B<-C++>] [B<-except>] [B<-s pattern>] [B<-prototypes>] [B<-noversioncheck>] [B<-nolinenumbers>] [B<-typemap typemap>] [B<-object_capi>]... file.xs
 
 =head1 DESCRIPTION
 
@@ -55,7 +55,15 @@ Disables the run time test that determines if the object file (derived
 from the C<.xs> file) and the C<.pm> files have the same version
 number.
 
-=back
+=item B<-nolinenumbers>
+
+Prevents the inclusion of `#line' directives in the output.
+
+=item B<-object_capi>
+
+Compile code as C in a PERL_OBJECT environment.
+
+back
 
 =head1 ENVIRONMENT
 
@@ -71,7 +79,7 @@ See the file F<changes.pod>.
 
 =head1 SEE ALSO
 
-perl(1), perlxs(1), perlxstut(1), perlxs(1)
+perl(1), perlxs(1), perlxstut(1)
 
 =cut
 
@@ -79,22 +87,36 @@ require 5.002;
 use Cwd;
 use vars '$cplusplus';
 
-# Global Constants
-$XSUBPP_version = "1.94001";
-$Is_VMS = $^O eq 'VMS';
+use Config;
 
 sub Q ;
 
+# Global Constants
+
+$XSUBPP_version = "1.9506";
+
+my ($Is_VMS, $SymSet);
+if ($^O eq 'VMS') {
+    $Is_VMS = 1;
+    # Establish set of global symbols with max length 28, since xsubpp
+    # will later add the 'XS_' prefix.
+    require ExtUtils::XSSymSet;
+    $SymSet = new ExtUtils::XSSymSet 28;
+}
+
 $FH = 'File0000' ;
 
-$usage = "Usage: xsubpp [-v] [-C++] [-except] [-prototypes] [-noversioncheck] [-s pattern] [-typemap typemap]... file.xs\n";
+$usage = "Usage: xsubpp [-v] [-C++] [-except] [-prototypes] [-noversioncheck] [-nolinenumbers] [-s pattern] [-typemap typemap]... file.xs\n";
 
 $proto_re = "[" . quotemeta('\$%&*@;') . "]" ;
+# mjn
+$OBJ   = 1 if $Config{'ccflags'} =~ /PERL_OBJECT/i;
 
 $except = "";
 $WantPrototypes = -1 ;
 $WantVersionChk = 1 ;
 $ProtoUsed = 0 ;
+$WantLineNumbers = 1 ;
 SWITCH: while (@ARGV and $ARGV[0] =~ /^-./) {
     $flag = shift @ARGV;
     $flag =~ s/^-// ;
@@ -104,8 +126,11 @@ SWITCH: while (@ARGV and $ARGV[0] =~ /^-./) {
     $WantPrototypes = 1, next SWITCH   if $flag eq 'prototypes';
     $WantVersionChk = 0, next SWITCH   if $flag eq 'noversioncheck';
     $WantVersionChk = 1, next SWITCH   if $flag eq 'versioncheck';
+    $WantCAPI = 1, next SWITCH    if $flag eq 'object_capi';
     $except = " TRY",  next SWITCH     if $flag eq 'except';
     push(@tm,shift),   next SWITCH     if $flag eq 'typemap';
+    $WantLineNumbers = 0, next SWITCH  if $flag eq 'nolinenumbers';
+    $WantLineNumbers = 1, next SWITCH  if $flag eq 'linenumbers';
     (print "xsubpp version $XSUBPP_version\n"), exit   
        if $flag eq 'v';
     die $usage;
@@ -118,6 +143,7 @@ else
 
 @ARGV == 1 or die $usage;
 ($dir, $filename) = $ARGV[0] =~ m#(.*)/(.*)#
+       or ($dir, $filename) = $ARGV[0] =~ m#(.*)\\(.*)#
        or ($dir, $filename) = $ARGV[0] =~ m#(.*[>\]])(.*)#
        or ($dir, $filename) = ('.', $ARGV[0]);
 chdir($dir);
@@ -128,6 +154,7 @@ $pwd = cwd();
 my(@XSStack) = ({type => 'none'});     # Stack of conditionals and INCLUDEs
 my($XSS_work_idx, $cpp_next_tmp) = (0, "XSubPPtmpAAAA");
 
+
 sub TrimWhitespace
 {
     $_[0] =~ s/^\s+|\s+$//go ;
@@ -228,13 +255,59 @@ sub check_keyword {
 }
 
 
+if ($WantLineNumbers) {
+    {
+       package xsubpp::counter;
+       sub TIEHANDLE {
+           my ($class, $cfile) = @_;
+           my $buf = "";
+           $SECTION_END_MARKER = "#line --- \"$cfile\"";
+           $line_no = 1;
+           bless \$buf;
+       }
+
+       sub PRINT {
+           my $self = shift;
+           for (@_) {
+               $$self .= $_;
+               while ($$self =~ s/^([^\n]*\n)//) {
+                   my $line = $1;
+                   ++ $line_no;
+                   $line =~ s|^\#line\s+---(?=\s)|#line $line_no|;
+                   print STDOUT $line;
+               }
+           }
+       }
+
+       sub PRINTF {
+           my $self = shift;
+           my $fmt = shift;
+           $self->PRINT(sprintf($fmt, @_));
+       }
+
+       sub DESTROY {
+           # Not necessary if we're careful to end with a "\n"
+           my $self = shift;
+           print STDOUT $$self;
+       }
+    }
+
+    my $cfile = $filename;
+    $cfile =~ s/\.xs$/.c/i or $cfile .= ".c";
+    tie(*PSEUDO_STDOUT, 'xsubpp::counter', $cfile);
+    select PSEUDO_STDOUT;
+}
+
 sub print_section {
-    my $count = 0;
-    $_ = shift(@line) while !/\S/ && @line;
+    # the "do" is required for right semantics
+    do { $_ = shift(@line) } while !/\S/ && @line;
+    
+    print("#line ", $line_no[@line_no - @line -1], " \"$filename\"\n")
+       if $WantLineNumbers && !/^\s*#\s*line\b/ && !/^#if XSubPPtmp/;
     for (;  defined($_) && !/^$BLOCK_re/o;  $_ = shift(@line)) {
-        print line_directive() unless ($count++);
        print "$_\n";
     }
+    print "$xsubpp::counter::SECTION_END_MARKER\n" if $WantLineNumbers;
 }
 
 sub process_keyword($)
@@ -244,7 +317,6 @@ sub process_keyword($)
 
     &{"${kwd}_handler"}() 
         while $kwd = check_keyword($pattern) ;
-    print line_directive();
 }
 
 sub CASE_handler {
@@ -308,6 +380,10 @@ sub INPUT_handler {
 sub OUTPUT_handler {
     for (;  !/^$BLOCK_re/o;  $_ = shift(@line)) {
        next unless /\S/;
+       if (/^\s*SETMAGIC\s*:\s*(ENABLE|DISABLE)\s*/) {
+           $DoSetMagic = ($1 eq "ENABLE" ? 1 : 0);
+           next;
+       }
        my ($outarg, $outcode) = /^\s*(\S+)\s*(.*?)\s*$/s ;
        blurt ("Error: duplicate OUTPUT argument '$outarg' ignored"), next
            if $outargs{$outarg} ++ ;
@@ -321,12 +397,12 @@ sub OUTPUT_handler {
            unless defined($args_match{$outarg});
        blurt("Error: No input definition for OUTPUT argument '$outarg' - ignored"), next
            unless defined $var_types{$outarg} ;
-       print line_directive();
+       $var_num = $args_match{$outarg};
        if ($outcode) {
            print "\t$outcode\n";
+           print "\tSvSETMAGIC(ST(" , $var_num-1 , "));\n" if $DoSetMagic;
        } else {
-           $var_num = $args_match{$outarg};
-           &generate_output($var_types{$outarg}, $var_num, $outarg); 
+           &generate_output($var_types{$outarg}, $var_num, $outarg, $DoSetMagic);
        }
     }
 }
@@ -639,11 +715,18 @@ print <<EOM ;
  */
 
 EOM
-print "#line 1 \"$filename\"\n"; 
+
+print("#line 1 \"$filename\"\n")
+    if $WantLineNumbers;
 
 while (<$FH>) {
     last if ($Module, $Package, $Prefix) =
        /^MODULE\s*=\s*([\w:]+)(?:\s+PACKAGE\s*=\s*([\w:]+))?(?:\s+PREFIX\s*=\s*(\S+))?\s*$/;
+
+    if ($OBJ) {
+        s/#if(?:def|\s+defined)\s+(\(__cplusplus\)|__cplusplus)/#if defined(__cplusplus) && !defined(PERL_OBJECT)/;
+    }
     print $_;
 }
 &Exit unless defined $_;
@@ -776,7 +859,9 @@ while (fetch_para()) {
 
     if (check_keyword("BOOT")) {
        &check_cpp;
-        push (@BootCode, $_, line_directive(), @line, "") ;
+       push (@BootCode, "#line $line_no[@line_no - @line] \"$filename\"")
+         if $WantLineNumbers && $line[0] !~ /^\s*#\s*line\b/;
+        push (@BootCode, @line, "") ;
         next PARAGRAPH ;
     }
 
@@ -798,6 +883,7 @@ while (fetch_para()) {
     ($pname = $func_name) =~ s/^($Prefix)?/$Packprefix/;
     ($clean_func_name = $func_name) =~ s/^$Prefix//;
     $Full_func_name = "${Packid}_$clean_func_name";
+    if ($Is_VMS) { $Full_func_name = $SymSet->addsym($Full_func_name); }
 
     # Check for duplicate function definition
     for $tmp (@XSStack) {
@@ -807,10 +893,12 @@ while (fetch_para()) {
     }
     $XSStack[$XSS_work_idx]{functions}{$Full_func_name} ++ ;
     %XsubAliases = %XsubAliasValues = ();
+    $DoSetMagic = 1;
 
     @args = split(/\s*,\s*/, $orig_args);
     if (defined($class)) {
-       my $arg0 = ((defined($static) or $func_name =~ /^new/) ? "CLASS" : "THIS");
+       my $arg0 = ((defined($static) or $func_name eq 'new')
+                   ? "CLASS" : "THIS");
        unshift(@args, $arg0);
        ($orig_args = "$arg0, $orig_args") =~ s/^$arg0, $/$arg0/;
     }
@@ -917,7 +1005,7 @@ EOF
 EOF
        
        if (!$thisdone && defined($class)) {
-           if (defined($static) or $func_name =~ /^new/) {
+           if (defined($static) or $func_name eq 'new') {
                print "\tchar *";
                $var_types{"CLASS"} = "char *";
                &generate_init("char *", 1, "CLASS");
@@ -962,13 +1050,13 @@ EOF
                                $wantRETVAL = 1;
                        }
                        if (defined($static)) {
-                           if ($func_name =~ /^new/) {
+                           if ($func_name eq 'new') {
                                $func_name = "$class";
                            } else {
                                print "${class}::";
                            }
                        } elsif (defined($class)) {
-                           if ($func_name =~ /^new/) {
+                           if ($func_name eq 'new') {
                                $func_name .= " $class";
                            } else {
                                print "THIS->";
@@ -990,9 +1078,9 @@ EOF
        if ($gotRETVAL && $RETVAL_code) {
            print "\t$RETVAL_code\n";
        } elsif ($gotRETVAL || $wantRETVAL) {
-           &generate_output($ret_type, 0, 'RETVAL');
+           # RETVAL almost never needs SvSETMAGIC()
+           &generate_output($ret_type, 0, 'RETVAL', 0);
        }
-       print line_directive();
 
        # do cleanup
        process_keyword("CLEANUP|ALIAS|PROTOTYPE") ;
@@ -1051,11 +1139,11 @@ EOF
     if ($ProtoThisXSUB) {
        $newXS = "newXSproto";
 
-       if ($ProtoThisXSUB == 2) {
+       if ($ProtoThisXSUB eq 2) {
            # User has specified empty prototype
            $proto = ', ""' ;
        }
-        elsif ($ProtoThisXSUB != 1) {
+        elsif ($ProtoThisXSUB ne 1) {
             # User has specified a prototype
             $proto = ', "' . $ProtoThisXSUB . '"';
         }
@@ -1092,6 +1180,19 @@ EOF
 }
 
 # print initialization routine
+if ($WantCAPI) {
+print Q<<"EOF";
+#
+##ifdef __cplusplus
+#extern "C"
+##endif
+#XS(boot__CAPI_entry)
+#[[
+#    dXSARGS;
+#    char* file = __FILE__;
+#
+EOF
+} else {
 print Q<<"EOF";
 ##ifdef __cplusplus
 #extern "C"
@@ -1102,6 +1203,7 @@ print Q<<"EOF";
 #    char* file = __FILE__;
 #
 EOF
+}
 
 print Q<<"EOF" if $WantVersionChk ;
 #    XS_VERSION_BOOTCHECK ;
@@ -1122,8 +1224,9 @@ EOF
 
 if (@BootCode)
 {
-    print "\n    /* Initialisation Section */\n" ;
-    print grep (s/$/\n/, @BootCode) ;
+    print "\n    /* Initialisation Section */\n\n" ;
+    @line = @BootCode;
+    print_section();
     print "\n    /* End of Initialisation Section */\n\n" ;
 }
 
@@ -1131,8 +1234,26 @@ print Q<<"EOF";;
 #    ST(0) = &sv_yes;
 #    XSRETURN(1);
 #]]
+#
 EOF
 
+if ($WantCAPI) { 
+print Q<<"EOF";
+#
+##define XSCAPI(name) void name(CV* cv, void* pPerl)
+#
+##ifdef __cplusplus
+#extern "C"
+##endif
+#XSCAPI(boot_$Module_cname)
+#[[
+#    SetCPerlObj(pPerl);
+#    boot__CAPI_entry(cv);
+#]]
+#
+EOF
+}
+
 warn("Please specify prototyping behavior for $filename (see perlxs manual)\n") 
     unless $ProtoUsed ;
 &Exit;
@@ -1145,15 +1266,6 @@ sub output_init {
     eval qq/print " $init\\\n"/;
 }
 
-sub line_directive
-{
-    # work out the line number
-    my $line_no = $line_no[@line_no - @line -1] ;
-    return "#line $line_no \"$filename\"\n" ;
-
-}
-
 sub Warn
 {
     # work out the line number
@@ -1223,7 +1335,7 @@ sub generate_init {
 }
 
 sub generate_output {
-    local($type, $num, $var) = @_;
+    local($type, $num, $var, $do_setmagic) = @_;
     local($arg) = "ST(" . ($num - ($num != 0)) . ")";
     local($argoff) = $num - 1;
     local($ntype);
@@ -1231,6 +1343,7 @@ sub generate_output {
     $type = TidyType($type) ;
     if ($type =~ /^array\(([^,]*),(.*)\)/) {
            print "\tsv_setpvn($arg, (char *)$var, $2 * sizeof($1)), XFree((char *)$var);\n";
+           print "\tSvSETMAGIC($arg);\n" if $do_setmagic;
     } else {
            blurt("Error: '$type' not in typemap"), return
                unless defined($type_kind{$type});
@@ -1252,6 +1365,7 @@ sub generate_output {
                $subexpr =~ s/\n\t/\n\t\t/g;
                $expr =~ s/DO_ARRAY_ELEM\n/$subexpr/;
                eval "print qq\a$expr\a";
+               print "\t\tSvSETMAGIC(ST(ix_$var));\n" if $do_setmagic;
            }
            elsif ($var eq 'RETVAL') {
                if ($expr =~ /^\t\$arg = new/) {
@@ -1259,6 +1373,7 @@ sub generate_output {
                    # mortalize it.
                    eval "print qq\a$expr\a";
                    print "\tsv_2mortal(ST(0));\n";
+                   print "\tSvSETMAGIC(ST(0));\n" if $do_setmagic;
                }
                elsif ($expr =~ /^\s*\$arg\s*=/) {
                    # We expect that $arg has refcnt >=1, so we need
@@ -1269,6 +1384,7 @@ sub generate_output {
                    # ignored by REFCNT_dec. Builtin values have REFCNT==0.
                    eval "print qq\a$expr\a";
                    print "\tif (SvREFCNT(ST(0))) sv_2mortal(ST(0));\n";
+                   print "\tSvSETMAGIC(ST(0));\n" if $do_setmagic;
                }
                else {
                    # Just hope that the entry would safely write it
@@ -1277,10 +1393,12 @@ sub generate_output {
                    # works too.
                    print "\tST(0) = sv_newmortal();\n";
                    eval "print qq\a$expr\a";
+                   # new mortals don't have set magic
                }
            }
            elsif ($arg =~ /^ST\(\d+\)$/) {
                eval "print qq\a$expr\a";
+               print "\tSvSETMAGIC($arg);\n" if $do_setmagic;
            }
     }
 }
@@ -1295,6 +1413,9 @@ sub map_type {
 
 
 sub Exit {
-    # VMS error exit: SS$_ABORT.
-    exit $errors ? ($Is_VMS ? 44 : 1) : 0;
+# If this is VMS, the exit status has meaning to the shell, so we
+# use a predictable value (SS$_Normal or SS$_Abort) rather than an
+# arbitrary number.
+#    exit ($Is_VMS ? ($errors ? 44 : 1) : $errors) ;
+    exit ($errors ? 1 : 0);
 }