summaryrefslogtreecommitdiff
path: root/ext/xsubpp
diff options
context:
space:
mode:
Diffstat (limited to 'ext/xsubpp')
-rwxr-xr-xext/xsubpp529
1 files changed, 529 insertions, 0 deletions
diff --git a/ext/xsubpp b/ext/xsubpp
new file mode 100755
index 0000000000..2cc1486c7e
--- /dev/null
+++ b/ext/xsubpp
@@ -0,0 +1,529 @@
+#!/usr/bin/perl
+# $Header$
+
+$usage = "Usage: xsubpp [-a] [-s] [-c] typemap file.xs\n";
+die $usage unless (@ARGV >= 2 && @ARGV <= 6);
+
+SWITCH: while ($ARGV[0] =~ /^-/) {
+ $flag = shift @ARGV;
+ $aflag = 1, next SWITCH if $flag =~ /^-a$/;
+ $spat = $1, next SWITCH if $flag =~ /^-s(.*)$/;
+ $cflag = 1, next SWITCH if $flag =~ /^-c$/;
+ $eflag = 1, next SWITCH if $flag =~ /^-e$/;
+ die $usage;
+}
+
+$typemap = shift @ARGV;
+open(TYPEMAP, $typemap) || die "cannot open $typemap\n";
+while (<TYPEMAP>) {
+ next if /^\s*$/ || /^#/;
+ chop;
+ ($typename, $kind) = split(/\t+/, $_, 2);
+ $type_kind{$typename} = $kind;
+}
+close(TYPEMAP);
+
+%input_expr = (JUNK, split(/\n(T_\w*)\s*\n/, <<'T_END'));
+
+T_INT
+ $var = (int)SvIV($arg)
+T_ENUM
+ $var = ($type)SvIV($arg)
+T_U_INT
+ $var = (unsigned int)SvIV($arg)
+T_SHORT
+ $var = (short)SvIV($arg)
+T_U_SHORT
+ $var = (unsigned short)SvIV($arg)
+T_LONG
+ $var = (long)SvIV($arg)
+T_U_LONG
+ $var = (unsigned long)SvIV($arg)
+T_CHAR
+ $var = (char)*SvPV($arg,na)
+T_U_CHAR
+ $var = (unsigned char)SvIV($arg)
+T_FLOAT
+ $var = (float)SvNV($arg)
+T_DOUBLE
+ $var = SvNV($arg)
+T_STRING
+ $var = SvPV($arg,na)
+T_PTR
+ $var = ($type)(unsigned long)SvNV($arg)
+T_PTRREF
+ if (SvTYPE($arg) == SVt_REF)
+ $var = ($type)(unsigned long)SvNV((SV*)SvANY($arg));
+ else
+ croak(\"$var is not a reference\")
+T_PTROBJ
+ if (sv_isa($arg, \"${ntype}\"))
+ $var = ($type)(unsigned long)SvNV((SV*)SvANY($arg));
+ else
+ croak(\"$var is not of type ${ntype}\")
+T_PTRDESC
+ if (sv_isa($arg, \"${ntype}\")) {
+ ${type}_desc = (\U${type}_DESC\E*)(unsigned long)SvNV((SV*)SvANY($arg));
+ $var = ${type}_desc->ptr;
+ }
+ else
+ croak(\"$var is not of type ${ntype}\")
+T_REFREF
+ if (SvTYPE($arg) == SVt_REF)
+ $var = *($type)(unsigned long)SvNV((SV*)SvANY($arg));
+ else
+ croak(\"$var is not a reference\")
+T_REFOBJ
+ if (sv_isa($arg, \"${ntype}\"))
+ $var = *($type)(unsigned long)SvNV((SV*)SvANY($arg));
+ else
+ croak(\"$var is not of type ${ntype}\")
+T_OPAQUE
+ $var NOT IMPLEMENTED
+T_OPAQUEPTR
+ $var = ($type)SvPV($arg,na)
+T_PACKED
+ $var = XS_unpack_$ntype($arg)
+T_PACKEDARRAY
+ $var = XS_unpack_$ntype($arg)
+T_CALLBACK
+ $var = make_perl_cb_$type($arg)
+T_ARRAY
+ $var = $ntype(items -= $argoff);
+ U32 ix_$var = $argoff;
+ while (items--) {
+ DO_ARRAY_ELEM;
+ }
+T_DATUM
+ $var.dptr = SvPV($arg, $var.dsize);
+T_GDATUM
+ UNIMPLEMENTED
+T_PLACEHOLDER
+T_END
+
+$* = 1; %output_expr = (JUNK, split(/^(T_\w*)\s*\n/, <<'T_END')); $* = 0;
+T_INT
+ sv_setiv($arg, (I32)$var);
+T_ENUM
+ sv_setiv($arg, (I32)$var);
+T_U_INT
+ sv_setiv($arg, (I32)$var);
+T_SHORT
+ sv_setiv($arg, (I32)$var);
+T_U_SHORT
+ sv_setiv($arg, (I32)$var);
+T_LONG
+ sv_setiv($arg, (I32)$var);
+T_U_LONG
+ sv_setiv($arg, (I32)$var);
+T_CHAR
+ sv_setpvn($arg, (char *)&$var, 1);
+T_U_CHAR
+ sv_setiv($arg, (I32)$var);
+T_FLOAT
+ sv_setnv($arg, (double)$var);
+T_DOUBLE
+ sv_setnv($arg, $var);
+T_STRING
+ sv_setpv($arg, $var);
+T_PTR
+ sv_setnv($arg, (double)(unsigned long)$var);
+T_PTRREF
+ sv_setptrref($arg, $var);
+T_PTROBJ
+ sv_setptrobj($arg, $var, \"${ntype}\");
+T_PTRDESC
+ sv_setptrobj($arg, (void*)new\U${type}_DESC\E($var), \"${ntype}\");
+T_REFREF
+ sv_setrefref($arg, \"${ntype}\", XS_service_$ntype,
+ ($var ? (void*)new $ntype($var) : 0));
+T_REFOBJ
+ NOT IMPLEMENTED
+T_OPAQUE
+ sv_setpvn($arg, (char *)&$var, sizeof($var));
+T_OPAQUEPTR
+ sv_setpvn($arg, (char *)$var, sizeof(*$var)), XFree((char *)$var);
+T_PACKED
+ XS_pack_$ntype($arg, $var);
+T_PACKEDARRAY
+ XS_pack_$ntype($arg, $var, count_$ntype);
+T_DATAUNIT
+ sv_setpvn($arg, $var.chp(), $var.size());
+T_CALLBACK
+ sv_setpvn($arg, $var.context.value().chp(),
+ $var.context.value().size());
+T_ARRAY
+ ST_EXTEND($var.size);
+ for (U32 ix_$var = 0; ix_$var < $var.size; ix_$var++) {
+ ST(ix_$var) = sv_mortalcopy(&sv_undef);
+ DO_ARRAY_ELEM
+ }
+ sp += $var.size - 1;
+T_DATUM
+ sv_setpvn($arg, $var.dptr, $var.dsize);
+T_GDATUM
+ sv_usepvn($arg, $var.dptr, $var.dsize);
+T_END
+
+$uvfile = shift @ARGV;
+open(F, $uvfile) || die "cannot open $uvfile\n";
+
+if ($eflag) {
+ print qq|#include "cfm/basic.h"\n|;
+}
+
+while (<F>) {
+ last if ($Module, $foo, $Package, $foo1, $Prefix) =
+ /^MODULE\s*=\s*(\w+)(\s+PACKAGE\s*=\s*(\w+))?(\s+PREFIX\s*=\s*(.+))?$/;
+ print $_;
+}
+$Pack = $Package;
+$Package .= "::" if defined $Package && $Package ne "";
+$/ = "";
+
+while (<F>) {
+ # parse paragraph
+ chop;
+ next if /^\s*$/;
+ next if /^(#.*\n?)+$/;
+ if (/^MODULE\s*=\s*(\w+)(\s+PACKAGE\s*=\s*(\w+))?(\s+PREFIX\s*=\s*(.+))?$/) {
+ $Module = $1;
+ $foo = $2;
+ $Package = $3;
+ $Pack = $Package;
+ $foo1 = $4;
+ $Prefix = $5;
+ $Package .= "::" if defined $Package && $Package ne "";
+ next;
+ }
+ split(/[\t ]*\n/);
+
+ # initialize info arrays
+ undef(%args_match);
+ undef(%var_types);
+ undef(%var_addr);
+ undef(%defaults);
+ undef($class);
+ undef($static);
+ undef($elipsis);
+
+ # extract return type, function name and arguments
+ $ret_type = shift(@_);
+ if ($ret_type =~ /^static\s+(.*)$/) {
+ $static = 1;
+ $ret_type = $1;
+ }
+ $func_header = shift(@_);
+ ($func_name, $orig_args) = $func_header =~ /^([\w:]+)\s*\((.*)\)$/;
+ if ($func_name =~ /(.*)::(.*)/) {
+ $class = $1;
+ $func_name = $2;
+ }
+ ($pname = $func_name) =~ s/^($Prefix)?/$Package/;
+ push(@Func_name, "${Pack}_$func_name");
+ push(@Func_pname, $pname);
+ @args = split(/\s*,\s*/, $orig_args);
+ if (defined($class) && !defined($static)) {
+ unshift(@args, "THIS");
+ $orig_args = "THIS, $orig_args";
+ $orig_args =~ s/^THIS, $/THIS/;
+ }
+ $orig_args =~ s/"/\\"/g;
+ $min_args = $num_args = @args;
+ foreach $i (0..$num_args-1) {
+ if ($args[$i] =~ s/\.\.\.//) {
+ $elipsis = 1;
+ $min_args--;
+ if ($args[i] eq '' && $i == $num_args - 1) {
+ pop(@args);
+ last;
+ }
+ }
+ if ($args[$i] =~ /([^=]*\S)\s*=\s*(.*)/) {
+ $min_args--;
+ $args[$i] = $1;
+ $defaults{$args[$i]} = $2;
+ $defaults{$args[$i]} =~ s/"/\\"/g;
+ }
+ }
+ if (defined($class) && !defined($static)) {
+ $func_args = join(", ", @args[1..$#args]);
+ } else {
+ $func_args = join(", ", @args);
+ }
+ @args_match{@args} = 1..@args;
+
+ # print function header
+ print <<"EOF" if $aflag;
+static int
+XS_${Pack}_$func_name(int, int sp, int items)
+EOF
+ print <<"EOF" if !$aflag;
+static int
+XS_${Pack}_$func_name(ix, sp, items)
+register int ix;
+register int sp;
+register int items;
+EOF
+ print <<"EOF" if $elipsis;
+{
+ if (items < $min_args) {
+ croak("Usage: $pname($orig_args)");
+ }
+EOF
+ print <<"EOF" if !$elipsis;
+{
+ if (items < $min_args || items > $num_args) {
+ croak("Usage: $pname($orig_args)");
+ }
+EOF
+
+# Now do a block of some sort.
+
+$condnum = 0;
+if (!@_) {
+ @_ = "CLEANUP:";
+}
+while (@_) {
+ if ($_[0] =~ s/^\s*CASE\s*:\s*//) {
+ $cond = shift(@_);
+ if ($condnum == 0) {
+ print " if ($cond)\n";
+ }
+ elsif ($cond ne '') {
+ print " else if ($cond)\n";
+ }
+ else {
+ print " else\n";
+ }
+ $condnum++;
+ }
+
+ print <<"EOF" if $eflag;
+ TRY {
+EOF
+ print <<"EOF" if !$eflag;
+ {
+EOF
+
+ # do initialization of input variables
+ $thisdone = 0;
+ $retvaldone = 0;
+ $deferred = "";
+ while ($_ = shift(@_)) {
+ last if /^\s*NOT_IMPLEMENTED_YET/;
+ last if /^\s*(CODE|OUTPUT|CLEANUP|CASE)\s*:/;
+ ($var_type, $var_name, $var_init) =
+ /\s*([^\t]+)\s*([^\s=]+)\s*(=.*)?/;
+ if ($var_name =~ /^&/) {
+ $var_name =~ s/^&//;
+ $var_addr{$var_name} = 1;
+ }
+ $thisdone |= $var_name eq "THIS";
+ $retvaldone |= $var_name eq "RETVAL";
+ $var_types{$var_name} = $var_type;
+ print "\t" . &map_type($var_type);
+ $var_num = $args_match{$var_name};
+ if ($var_addr{$var_name}) {
+ $func_args =~ s/\b($var_name)\b/&\1/;
+ }
+ if ($var_init !~ /^=\s*NO_INIT\s*$/) {
+ if ($var_init !~ /^\s*$/) {
+ &output_init($var_type, $var_num,
+ "$var_name $var_init");
+ } elsif ($var_num) {
+ # generate initialization code
+ &generate_init($var_type, $var_num, $var_name);
+ } else {
+ print ";\n";
+ }
+ } else {
+ print "\t$var_name;\n";
+ }
+ }
+ if (!$thisdone && defined($class) && !defined($static)) {
+ print "\t$class *";
+ $var_types{"THIS"} = "$class *";
+ &generate_init("$class *", 1, "THIS");
+ }
+
+ # do code
+ if (/^\s*NOT_IMPLEMENTED_YET/) {
+ print "\ncroak(\"$pname: not implemented yet\");\n";
+ } else {
+ if ($ret_type ne "void") {
+ print "\t" . &map_type($ret_type) . "\tRETVAL;\n"
+ if !$retvaldone;
+ $args_match{"RETVAL"} = 0;
+ $var_types{"RETVAL"} = $ret_type;
+ }
+ print $deferred;
+ if (/^\s*CODE:/) {
+ while ($_ = shift(@_)) {
+ last if /^\s*(OUTPUT|CLEANUP|CASE)\s*:/;
+ print "$_\n";
+ }
+ } else {
+ print "\n\t";
+ if ($ret_type ne "void") {
+ print "RETVAL = ";
+ }
+ if (defined($static)) {
+ print "$class::";
+ } elsif (defined($class)) {
+ print "THIS->";
+ }
+ if (defined($spat) && $func_name =~ /^($spat)(.*)$/) {
+ $func_name = $2;
+ }
+ print "$func_name($func_args);\n";
+ &generate_output($ret_type, 0, "RETVAL")
+ unless $ret_type eq "void";
+ }
+ }
+
+ # do output variables
+ if (/^\s*OUTPUT\s*:/) {
+ while ($_ = shift(@_)) {
+ last if /^\s*CLEANUP\s*:/;
+ s/^\s+//;
+ ($outarg, $outcode) = split(/\t+/);
+ if ($outcode) {
+ print "\t$outcode\n";
+ } else {
+ die "$outarg not an argument"
+ unless defined($args_match{$outarg});
+ $var_num = $args_match{$outarg};
+ &generate_output($var_types{$outarg}, $var_num,
+ $outarg);
+ }
+ }
+ }
+ # do cleanup
+ if (/^\s*CLEANUP\s*:/) {
+ while ($_ = shift(@_)) {
+ last if /^\s*CASE\s*:/;
+ print "$_\n";
+ }
+ }
+ # print function trailer
+ print <<EOF if $eflag;
+ }
+ BEGHANDLERS
+ CATCHALL
+ croak("%s: %s\\tpropagated", Xname, Xreason);
+ ENDHANDLERS
+EOF
+ print <<EOF if !$eflag;
+ }
+EOF
+ if (/^\s*CASE\s*:/) {
+ unshift(@_, $_);
+ }
+}
+ print <<EOF;
+ return sp;
+}
+
+EOF
+}
+
+# print initialization routine
+print qq/extern "C"\n/ if $cflag;
+print <<"EOF";
+int init_$Module(ix,sp,items)
+int ix;
+int sp;
+int items;
+{
+ char* file = __FILE__;
+
+EOF
+
+for (@Func_name) {
+ $pname = shift(@Func_pname);
+ print " newXSUB(\"$pname\", 0, XS_$_, file);\n";
+}
+print "}\n";
+
+sub output_init {
+ local($type, $num, $init) = @_;
+ local($arg) = "ST($num)";
+
+ eval qq/print " $init\\\n"/;
+}
+
+sub generate_init {
+ local($type, $num, $var) = @_;
+ local($arg) = "ST($num)";
+ local($argoff) = $num - 1;
+ local($ntype);
+
+ die "$type not in typemap" if !defined($type_kind{$type});
+ ($ntype = $type) =~ s/\s*\*/Ptr/g;
+ $subtype = $ntype;
+ $subtype =~ s/Ptr$//;
+ $subtype =~ s/Array$//;
+ $expr = $input_expr{$type_kind{$type}};
+ if ($expr =~ /DO_ARRAY_ELEM/) {
+ $subexpr = $input_expr{$type_kind{$subtype}};
+ $subexpr =~ s/ntype/subtype/g;
+ $subexpr =~ s/\$arg/ST(ix_$var)/g;
+ $subexpr =~ s/\n\t/\n\t\t/g;
+ $subexpr =~ s/is not of (.*")/[arg %d] is not of $1, ix_$var + 1/g;
+ $subexpr =~ s/\$var/$var[ix_$var - $argoff]/;
+ $expr =~ s/DO_ARRAY_ELEM/$subexpr/;
+ }
+ 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 =/) {
+ eval qq/print "\\t$var;\\n"/;
+ $deferred .= eval qq/"\\n$expr;\\n"/;
+ } else {
+ eval qq/print "$expr;\\n"/;
+ }
+}
+
+sub generate_output {
+ local($type, $num, $var) = @_;
+ local($arg) = "ST($num)";
+ local($argoff) = $num - 1;
+ local($ntype);
+
+ if ($type =~ /^array\(([^,]*),(.*)\)/) {
+ print "\tsv_setpvn($arg, (char *)$var, $2 * sizeof($1)), XFree((char *)$var);\n";
+ } else {
+ die "$type not in typemap" if !defined($type_kind{$type});
+ ($ntype = $type) =~ s/\s*\*/Ptr/g;
+ $ntype =~ s/\(\)//g;
+ $subtype = $ntype;
+ $subtype =~ s/Ptr$//;
+ $subtype =~ s/Array$//;
+ $expr = $output_expr{$type_kind{$type}};
+ if ($expr =~ /DO_ARRAY_ELEM/) {
+ $subexpr = $output_expr{$type_kind{$subtype}};
+ $subexpr =~ s/ntype/subtype/g;
+ $subexpr =~ s/\$arg/ST(ix_$var)/g;
+ $subexpr =~ s/\$var/${var}[ix_$var]/g;
+ $subexpr =~ s/\n\t/\n\t\t/g;
+ $expr =~ s/DO_ARRAY_ELEM\n/$subexpr/;
+ }
+ elsif ($arg eq 'ST(0)') {
+ print "\tST(0) = sv_mortalcopy(&sv_undef);\n";
+ }
+ eval "print qq\f$expr\f";
+ }
+}
+
+sub map_type {
+ local($type) = @_;
+
+ if ($type =~ /^array\(([^,]*),(.*)\)/) {
+ return "$1 *";
+ } else {
+ return $type;
+ }
+}