=head1 SEE ALSO
-perl(1), perlxs(1), perlxstut(1), perlapi(1)
+perl(1), perlxs(1), perlxstut(1), perlxs(1)
=cut
# Global Constants
-$XSUBPP_version = "1.937";
+$XSUBPP_version = "1.940";
require 5.002;
use vars '$cplusplus';
SWITCH: while (@ARGV and $ARGV[0] =~ /^-./) {
$flag = shift @ARGV;
$flag =~ s/^-// ;
- $spat = shift, next SWITCH if $flag eq 's';
+ $spat = quotemeta shift, next SWITCH if $flag eq 's';
$cplusplus = 1, next SWITCH if $flag eq 'C++';
$WantPrototypes = 0, next SWITCH if $flag eq 'noprototypes';
$WantPrototypes = 1, next SWITCH if $flag eq 'prototypes';
$BLOCK_re= '\s*(' . join('|', qw(
REQUIRE BOOT CASE PREINIT INPUT INIT CODE PPCODE OUTPUT
CLEANUP ALIAS PROTOTYPES PROTOTYPE VERSIONCHECK INCLUDE
+ SCOPE
)) . "|$END)\\s*:";
# Input: ($_, @line) == unparsed input.
}
+sub SCOPE_handler ()
+{
+ death("Error: Only 1 SCOPE declaration allowed per xsub")
+ if $scope_in_this_xsub ++ ;
+
+ for (; !/^$BLOCK_re/o; $_ = shift(@line)) {
+ next unless /\S/;
+ TrimWhitespace($_) ;
+ if ($_ =~ /^DISABLE/i) {
+ $ScopeThisXSUB = 0
+ }
+ elsif ($_ =~ /^ENABLE/i) {
+ $ScopeThisXSUB = 1
+ }
+ }
+
+}
+
sub PROTOTYPES_handler ()
{
# the rest of the current line should contain either ENABLE or
$Module = $1;
$Package = defined($2) ? $2 : ''; # keep -w happy
$Prefix = defined($3) ? $3 : ''; # keep -w happy
+ $Prefix = quotemeta $Prefix ;
($Module_cname = $Module) =~ s/\W/_/g;
($Packid = $Package) =~ tr/:/_/;
$Packprefix = $Package;
$XSStack[$XSS_work_idx]{varname} = $cpp_next_tmp++;
}
- death ("Code is not inside a function")
+ death ("Code is not inside a function"
+ ." (maybe last function was ended by a blank line "
+ ." followed by a a statement on column one?)")
if $line[0] =~ /^\s/;
# initialize info arrays
undef(%arg_list) ;
undef(@proto_arg) ;
undef($proto_in_this_xsub) ;
+ undef($scope_in_this_xsub) ;
$ProtoThisXSUB = $WantPrototypes ;
+ $ScopeThisXSUB = 0;
$_ = shift(@line);
while ($kwd = check_keyword("REQUIRE|PROTOTYPES|VERSIONCHECK|INCLUDE")) {
($class, $func_name, $orig_args) = ($1, $2, $3) ;
($pname = $func_name) =~ s/^($Prefix)?/$Packprefix/;
- $Full_func_name = "${Packid}_$func_name";
+ ($clean_func_name = $func_name) =~ s/^$Prefix//;
+ $Full_func_name = "${Packid}_$clean_func_name";
# Check for duplicate function definition
for $tmp (@XSStack) {
next unless defined $tmp->{functions}{$Full_func_name};
- Warn("Warning: duplicate function definition '$func_name' detected");
+ Warn("Warning: duplicate function definition '$clean_func_name' detected");
last;
}
$XSStack[$XSS_work_idx]{functions}{$Full_func_name} ++ ;
# print function header
print Q<<"EOF";
-#XS(XS_${Packid}_$func_name)
+#XS(XS_${Full_func_name})
#[[
# dXSARGS;
EOF
$gotRETVAL = 0;
INPUT_handler() ;
- process_keyword("INPUT|PREINIT|ALIAS|PROTOTYPE") ;
+ process_keyword("INPUT|PREINIT|ALIAS|PROTOTYPE|SCOPE") ;
+ print Q<<"EOF" if $ScopeThisXSUB;
+# ENTER;
+# [[
+EOF
+
if (!$thisdone && defined($class)) {
if (defined($static) or $func_name =~ /^new/) {
print "\tchar *";
$args_match{"RETVAL"} = 0;
$var_types{"RETVAL"} = $ret_type;
}
+
print $deferred;
- process_keyword("INIT|ALIAS|PROTOTYPE") ;
+
+ process_keyword("INIT|ALIAS|PROTOTYPE") ;
if (check_keyword("PPCODE")) {
print_section();
death ("PPCODE must be last thing") if @line;
+ print "\tLEAVE;\n" if $ScopeThisXSUB;
print "\tPUTBACK;\n\treturn;\n";
} elsif (check_keyword("CODE")) {
print_section() ;
# do cleanup
process_keyword("CLEANUP|ALIAS|PROTOTYPE") ;
+ print Q<<"EOF" if $ScopeThisXSUB;
+# ]]
+EOF
+ print Q<<"EOF" if $ScopeThisXSUB and not $PPCODE;
+# LEAVE;
+EOF
+
# print function trailer
print Q<<EOF;
# ]]
$subexpr =~ s/\$var/${var}[ix_$var - $argoff]/;
$expr =~ s/DO_ARRAY_ELEM/$subexpr/;
}
+ if ($expr =~ m#/\*.*scope.*\*/#i) { # "scope" in C comments
+ $ScopeThisXSUB = 1;
+ }
if (defined($defaults{$var})) {
$expr =~ s/(\t+)/$1 /g;
$expr =~ s/ /\t/g;
eval qq/print "\\t$var;\\n"/;
$deferred .= eval qq/"\\n\\tif (items < $num)\\n\\t $var = $defaults{$var};\\n\\telse {\\n$expr;\\n\\t}\\n"/;
- } elsif ($expr !~ /^\t\$var =/) {
+ } elsif ($ScopeThisXSUB or $expr !~ /^\t\$var =/) {
eval qq/print "\\t$var;\\n"/;
$deferred .= eval qq/"\\n$expr;\\n"/;
} else {
eval "print qq\a$expr\a";
}
elsif ($var eq 'RETVAL') {
- if ($expr =~ /^\t\$arg\s*=\s*\$var\s*;/) {
- eval "print qq\a$expr\a";
- print "\tif (SvREFCNT(ST(0))) sv_2mortal(ST(0));\n";
- }
- elsif ($expr =~ /^\t\$arg = /) {
+ if ($expr =~ /^\t\$arg = new/) {
+ # We expect that $arg has refcnt 1, so we need to
+ # mortalize it.
eval "print qq\a$expr\a";
print "\tsv_2mortal(ST(0));\n";
}
+ elsif ($expr =~ /^\s*\$arg\s*=/) {
+ # We expect that $arg has refcnt >=1, so we need
+ # to mortalize it. However, the extension may have
+ # returned the built-in perl value, which is
+ # read-only, thus not mortalizable. However, it is
+ # safe to leave it as it is, since it would be
+ # 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";
+ }
else {
+ # Just hope that the entry would safely write it
+ # over an already mortalized value. By
+ # coincidence, something like $arg = &sv_undef
+ # works too.
print "\tST(0) = sv_newmortal();\n";
eval "print qq\a$expr\a";
}