diff options
Diffstat (limited to 'gnu/usr.bin/perl/ext')
114 files changed, 19818 insertions, 22 deletions
diff --git a/gnu/usr.bin/perl/ext/B/B.pm b/gnu/usr.bin/perl/ext/B/B.pm new file mode 100644 index 00000000000..75dcfb3b74d --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/B.pm @@ -0,0 +1,831 @@ +# B.pm +# +# Copyright (c) 1996, 1997, 1998 Malcolm Beattie +# +# You may distribute under the terms of either the GNU General Public +# License or the Artistic License, as specified in the README file. +# +package B; +require DynaLoader; +require Exporter; +@ISA = qw(Exporter DynaLoader); +@EXPORT_OK = qw(byteload_fh byteload_string minus_c ppname + class peekop cast_I32 cstring cchar hash threadsv_names + main_root main_start main_cv svref_2object + walkoptree walkoptree_slow walkoptree_exec walksymtable + parents comppadlist sv_undef compile_stats timing_info init_av); + +use strict; +@B::SV::ISA = 'B::OBJECT'; +@B::NULL::ISA = 'B::SV'; +@B::PV::ISA = 'B::SV'; +@B::IV::ISA = 'B::SV'; +@B::NV::ISA = 'B::IV'; +@B::RV::ISA = 'B::SV'; +@B::PVIV::ISA = qw(B::PV B::IV); +@B::PVNV::ISA = qw(B::PV B::NV); +@B::PVMG::ISA = 'B::PVNV'; +@B::PVLV::ISA = 'B::PVMG'; +@B::BM::ISA = 'B::PVMG'; +@B::AV::ISA = 'B::PVMG'; +@B::GV::ISA = 'B::PVMG'; +@B::HV::ISA = 'B::PVMG'; +@B::CV::ISA = 'B::PVMG'; +@B::IO::ISA = 'B::PVMG'; +@B::FM::ISA = 'B::CV'; + +@B::OP::ISA = 'B::OBJECT'; +@B::UNOP::ISA = 'B::OP'; +@B::BINOP::ISA = 'B::UNOP'; +@B::LOGOP::ISA = 'B::UNOP'; +@B::CONDOP::ISA = 'B::UNOP'; +@B::LISTOP::ISA = 'B::BINOP'; +@B::SVOP::ISA = 'B::OP'; +@B::GVOP::ISA = 'B::OP'; +@B::PVOP::ISA = 'B::OP'; +@B::CVOP::ISA = 'B::OP'; +@B::LOOP::ISA = 'B::LISTOP'; +@B::PMOP::ISA = 'B::LISTOP'; +@B::COP::ISA = 'B::OP'; + +@B::SPECIAL::ISA = 'B::OBJECT'; + +{ + # Stop "-w" from complaining about the lack of a real B::OBJECT class + package B::OBJECT; +} + +my $debug; +my $op_count = 0; +my @parents = (); + +sub debug { + my ($class, $value) = @_; + $debug = $value; + walkoptree_debug($value); +} + +# sub OPf_KIDS; +# add to .xs for perl5.002 +sub OPf_KIDS () { 4 } + +sub class { + my $obj = shift; + my $name = ref $obj; + $name =~ s/^.*:://; + return $name; +} + +sub parents { \@parents } + +# For debugging +sub peekop { + my $op = shift; + return sprintf("%s (0x%x) %s", class($op), $$op, $op->ppaddr); +} + +sub walkoptree_slow { + my($op, $method, $level) = @_; + $op_count++; # just for statistics + $level ||= 0; + warn(sprintf("walkoptree: %d. %s\n", $level, peekop($op))) if $debug; + $op->$method($level); + if ($$op && ($op->flags & OPf_KIDS)) { + my $kid; + unshift(@parents, $op); + for ($kid = $op->first; $$kid; $kid = $kid->sibling) { + walkoptree_slow($kid, $method, $level + 1); + } + shift @parents; + } +} + +sub compile_stats { + return "Total number of OPs processed: $op_count\n"; +} + +sub timing_info { + my ($sec, $min, $hr) = localtime; + my ($user, $sys) = times; + sprintf("%02d:%02d:%02d user=$user sys=$sys", + $hr, $min, $sec, $user, $sys); +} + +my %symtable; +sub savesym { + my ($obj, $value) = @_; +# warn(sprintf("savesym: sym_%x => %s\n", $$obj, $value)); # debug + $symtable{sprintf("sym_%x", $$obj)} = $value; +} + +sub objsym { + my $obj = shift; + return $symtable{sprintf("sym_%x", $$obj)}; +} + +sub walkoptree_exec { + my ($op, $method, $level) = @_; + my ($sym, $ppname); + my $prefix = " " x $level; + for (; $$op; $op = $op->next) { + $sym = objsym($op); + if (defined($sym)) { + print $prefix, "goto $sym\n"; + return; + } + savesym($op, sprintf("%s (0x%lx)", class($op), $$op)); + $op->$method($level); + $ppname = $op->ppaddr; + if ($ppname =~ /^pp_(or|and|mapwhile|grepwhile|entertry)$/) { + print $prefix, uc($1), " => {\n"; + walkoptree_exec($op->other, $method, $level + 1); + print $prefix, "}\n"; + } elsif ($ppname eq "pp_match" || $ppname eq "pp_subst") { + my $pmreplstart = $op->pmreplstart; + if ($$pmreplstart) { + print $prefix, "PMREPLSTART => {\n"; + walkoptree_exec($pmreplstart, $method, $level + 1); + print $prefix, "}\n"; + } + } elsif ($ppname eq "pp_substcont") { + print $prefix, "SUBSTCONT => {\n"; + walkoptree_exec($op->other->pmreplstart, $method, $level + 1); + print $prefix, "}\n"; + $op = $op->other; + } elsif ($ppname eq "pp_cond_expr") { + # pp_cond_expr never returns op_next + print $prefix, "TRUE => {\n"; + walkoptree_exec($op->true, $method, $level + 1); + print $prefix, "}\n"; + $op = $op->false; + redo; + } elsif ($ppname eq "pp_range") { + print $prefix, "TRUE => {\n"; + walkoptree_exec($op->true, $method, $level + 1); + print $prefix, "}\n", $prefix, "FALSE => {\n"; + walkoptree_exec($op->false, $method, $level + 1); + print $prefix, "}\n"; + } elsif ($ppname eq "pp_enterloop") { + print $prefix, "REDO => {\n"; + walkoptree_exec($op->redoop, $method, $level + 1); + print $prefix, "}\n", $prefix, "NEXT => {\n"; + walkoptree_exec($op->nextop, $method, $level + 1); + print $prefix, "}\n", $prefix, "LAST => {\n"; + walkoptree_exec($op->lastop, $method, $level + 1); + print $prefix, "}\n"; + } elsif ($ppname eq "pp_subst") { + my $replstart = $op->pmreplstart; + if ($$replstart) { + print $prefix, "SUBST => {\n"; + walkoptree_exec($replstart, $method, $level + 1); + print $prefix, "}\n"; + } + } + } +} + +sub walksymtable { + my ($symref, $method, $recurse, $prefix) = @_; + my $sym; + no strict 'vars'; + local(*glob); + while (($sym, *glob) = each %$symref) { + if ($sym =~ /::$/) { + $sym = $prefix . $sym; + if ($sym ne "main::" && &$recurse($sym)) { + walksymtable(\%glob, $method, $recurse, $sym); + } + } else { + svref_2object(\*glob)->EGV->$method(); + } + } +} + +{ + package B::Section; + my $output_fh; + my %sections; + + sub new { + my ($class, $section, $symtable, $default) = @_; + $output_fh ||= FileHandle->new_tmpfile; + my $obj = bless [-1, $section, $symtable, $default], $class; + $sections{$section} = $obj; + return $obj; + } + + sub get { + my ($class, $section) = @_; + return $sections{$section}; + } + + sub add { + my $section = shift; + while (defined($_ = shift)) { + print $output_fh "$section->[1]\t$_\n"; + $section->[0]++; + } + } + + sub index { + my $section = shift; + return $section->[0]; + } + + sub name { + my $section = shift; + return $section->[1]; + } + + sub symtable { + my $section = shift; + return $section->[2]; + } + + sub default { + my $section = shift; + return $section->[3]; + } + + sub output { + my ($section, $fh, $format) = @_; + my $name = $section->name; + my $sym = $section->symtable || {}; + my $default = $section->default; + + seek($output_fh, 0, 0); + while (<$output_fh>) { + chomp; + s/^(.*?)\t//; + if ($1 eq $name) { + s{(s\\_[0-9a-f]+)} { + exists($sym->{$1}) ? $sym->{$1} : $default; + }ge; + printf $fh $format, $_; + } + } + } +} + +bootstrap B; + +1; + +__END__ + +=head1 NAME + +B - The Perl Compiler + +=head1 SYNOPSIS + + use B; + +=head1 DESCRIPTION + +The C<B> module supplies classes which allow a Perl program to delve +into its own innards. It is the module used to implement the +"backends" of the Perl compiler. Usage of the compiler does not +require knowledge of this module: see the F<O> module for the +user-visible part. The C<B> module is of use to those who want to +write new compiler backends. This documentation assumes that the +reader knows a fair amount about perl's internals including such +things as SVs, OPs and the internal symbol table and syntax tree +of a program. + +=head1 OVERVIEW OF CLASSES + +The C structures used by Perl's internals to hold SV and OP +information (PVIV, AV, HV, ..., OP, SVOP, UNOP, ...) are modelled on a +class hierarchy and the C<B> module gives access to them via a true +object hierarchy. Structure fields which point to other objects +(whether types of SV or types of OP) are represented by the C<B> +module as Perl objects of the appropriate class. The bulk of the C<B> +module is the methods for accessing fields of these structures. Note +that all access is read-only: you cannot modify the internals by +using this module. + +=head2 SV-RELATED CLASSES + +B::IV, B::NV, B::RV, B::PV, B::PVIV, B::PVNV, B::PVMG, B::BM, B::PVLV, +B::AV, B::HV, B::CV, B::GV, B::FM, B::IO. These classes correspond in +the obvious way to the underlying C structures of similar names. The +inheritance hierarchy mimics the underlying C "inheritance". Access +methods correspond to the underlying C macros for field access, +usually with the leading "class indication" prefix removed (Sv, Av, +Hv, ...). The leading prefix is only left in cases where its removal +would cause a clash in method name. For example, C<GvREFCNT> stays +as-is since its abbreviation would clash with the "superclass" method +C<REFCNT> (corresponding to the C function C<SvREFCNT>). + +=head2 B::SV METHODS + +=over 4 + +=item REFCNT + +=item FLAGS + +=back + +=head2 B::IV METHODS + +=over 4 + +=item IV + +=item IVX + +=item needs64bits + +=item packiv + +=back + +=head2 B::NV METHODS + +=over 4 + +=item NV + +=item NVX + +=back + +=head2 B::RV METHODS + +=over 4 + +=item RV + +=back + +=head2 B::PV METHODS + +=over 4 + +=item PV + +=back + +=head2 B::PVMG METHODS + +=over 4 + +=item MAGIC + +=item SvSTASH + +=back + +=head2 B::MAGIC METHODS + +=over 4 + +=item MOREMAGIC + +=item PRIVATE + +=item TYPE + +=item FLAGS + +=item OBJ + +=item PTR + +=back + +=head2 B::PVLV METHODS + +=over 4 + +=item TARGOFF + +=item TARGLEN + +=item TYPE + +=item TARG + +=back + +=head2 B::BM METHODS + +=over 4 + +=item USEFUL + +=item PREVIOUS + +=item RARE + +=item TABLE + +=back + +=head2 B::GV METHODS + +=over 4 + +=item NAME + +=item STASH + +=item SV + +=item IO + +=item FORM + +=item AV + +=item HV + +=item EGV + +=item CV + +=item CVGEN + +=item LINE + +=item FILEGV + +=item GvREFCNT + +=item FLAGS + +=back + +=head2 B::IO METHODS + +=over 4 + +=item LINES + +=item PAGE + +=item PAGE_LEN + +=item LINES_LEFT + +=item TOP_NAME + +=item TOP_GV + +=item FMT_NAME + +=item FMT_GV + +=item BOTTOM_NAME + +=item BOTTOM_GV + +=item SUBPROCESS + +=item IoTYPE + +=item IoFLAGS + +=back + +=head2 B::AV METHODS + +=over 4 + +=item FILL + +=item MAX + +=item OFF + +=item ARRAY + +=item AvFLAGS + +=back + +=head2 B::CV METHODS + +=over 4 + +=item STASH + +=item START + +=item ROOT + +=item GV + +=item FILEGV + +=item DEPTH + +=item PADLIST + +=item OUTSIDE + +=item XSUB + +=item XSUBANY + +=item CvFLAGS + +=back + +=head2 B::HV METHODS + +=over 4 + +=item FILL + +=item MAX + +=item KEYS + +=item RITER + +=item NAME + +=item PMROOT + +=item ARRAY + +=back + +=head2 OP-RELATED CLASSES + +B::OP, B::UNOP, B::BINOP, B::LOGOP, B::CONDOP, B::LISTOP, B::PMOP, +B::SVOP, B::GVOP, B::PVOP, B::CVOP, B::LOOP, B::COP. +These classes correspond in +the obvious way to the underlying C structures of similar names. The +inheritance hierarchy mimics the underlying C "inheritance". Access +methods correspond to the underlying C structre field names, with the +leading "class indication" prefix removed (op_). + +=head2 B::OP METHODS + +=over 4 + +=item next + +=item sibling + +=item ppaddr + +This returns the function name as a string (e.g. pp_add, pp_rv2av). + +=item desc + +This returns the op description from the global C PL_op_desc array +(e.g. "addition" "array deref"). + +=item targ + +=item type + +=item seq + +=item flags + +=item private + +=back + +=head2 B::UNOP METHOD + +=over 4 + +=item first + +=back + +=head2 B::BINOP METHOD + +=over 4 + +=item last + +=back + +=head2 B::LOGOP METHOD + +=over 4 + +=item other + +=back + +=head2 B::CONDOP METHODS + +=over 4 + +=item true + +=item false + +=back + +=head2 B::LISTOP METHOD + +=over 4 + +=item children + +=back + +=head2 B::PMOP METHODS + +=over 4 + +=item pmreplroot + +=item pmreplstart + +=item pmnext + +=item pmregexp + +=item pmflags + +=item pmpermflags + +=item precomp + +=back + +=head2 B::SVOP METHOD + +=over 4 + +=item sv + +=back + +=head2 B::GVOP METHOD + +=over 4 + +=item gv + +=back + +=head2 B::PVOP METHOD + +=over 4 + +=item pv + +=back + +=head2 B::LOOP METHODS + +=over 4 + +=item redoop + +=item nextop + +=item lastop + +=back + +=head2 B::COP METHODS + +=over 4 + +=item label + +=item stash + +=item filegv + +=item cop_seq + +=item arybase + +=item line + +=back + +=head1 FUNCTIONS EXPORTED BY C<B> + +The C<B> module exports a variety of functions: some are simple +utility functions, others provide a Perl program with a way to +get an initial "handle" on an internal object. + +=over 4 + +=item main_cv + +Return the (faked) CV corresponding to the main part of the Perl +program. + +=item init_av + +Returns the AV object (i.e. in class B::AV) representing INIT blocks. + +=item main_root + +Returns the root op (i.e. an object in the appropriate B::OP-derived +class) of the main part of the Perl program. + +=item main_start + +Returns the starting op of the main part of the Perl program. + +=item comppadlist + +Returns the AV object (i.e. in class B::AV) of the global comppadlist. + +=item sv_undef + +Returns the SV object corresponding to the C variable C<sv_undef>. + +=item sv_yes + +Returns the SV object corresponding to the C variable C<sv_yes>. + +=item sv_no + +Returns the SV object corresponding to the C variable C<sv_no>. + +=item walkoptree(OP, METHOD) + +Does a tree-walk of the syntax tree based at OP and calls METHOD on +each op it visits. Each node is visited before its children. If +C<walkoptree_debug> (q.v.) has been called to turn debugging on then +the method C<walkoptree_debug> is called on each op before METHOD is +called. + +=item walkoptree_debug(DEBUG) + +Returns the current debugging flag for C<walkoptree>. If the optional +DEBUG argument is non-zero, it sets the debugging flag to that. See +the description of C<walkoptree> above for what the debugging flag +does. + +=item walksymtable(SYMREF, METHOD, RECURSE) + +Walk the symbol table starting at SYMREF and call METHOD on each +symbol visited. When the walk reached package symbols "Foo::" it +invokes RECURSE and only recurses into the package if that sub +returns true. + +=item svref_2object(SV) + +Takes any Perl variable and turns it into an object in the +appropriate B::OP-derived or B::SV-derived class. Apart from functions +such as C<main_root>, this is the primary way to get an initial +"handle" on a internal perl data structure which can then be followed +with the other access methods. + +=item ppname(OPNUM) + +Return the PP function name (e.g. "pp_add") of op number OPNUM. + +=item hash(STR) + +Returns a string in the form "0x..." representing the value of the +internal hash function used by perl on string STR. + +=item cast_I32(I) + +Casts I to the internal I32 type used by that perl. + + +=item minus_c + +Does the equivalent of the C<-c> command-line option. Obviously, this +is only useful in a BEGIN block or else the flag is set too late. + + +=item cstring(STR) + +Returns a double-quote-surrounded escaped version of STR which can +be used as a string in C source code. + +=item class(OBJ) + +Returns the class of an object without the part of the classname +preceding the first "::". This is used to turn "B::UNOP" into +"UNOP" for example. + +=item threadsv_names + +In a perl compiled for threads, this returns a list of the special +per-thread threadsv variables. + +=item byteload_fh(FILEHANDLE) + +Load the contents of FILEHANDLE as bytecode. See documentation for +the B<Bytecode> module in F<B::Backend> for how to generate bytecode. + +=back + +=head1 AUTHOR + +Malcolm Beattie, C<mbeattie@sable.ox.ac.uk> + +=cut diff --git a/gnu/usr.bin/perl/ext/B/B.xs b/gnu/usr.bin/perl/ext/B/B.xs new file mode 100644 index 00000000000..6610ae8d523 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/B.xs @@ -0,0 +1,1219 @@ +/* B.xs + * + * Copyright (c) 1996 Malcolm Beattie + * + * You may distribute under the terms of either the GNU General Public + * License or the Artistic License, as specified in the README file. + * + */ + +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" +#include "INTERN.h" + +#ifdef PERL_OBJECT +#undef op_name +#undef opargs +#undef op_desc +#define op_name (pPerl->Perl_get_op_names()) +#define opargs (pPerl->Perl_get_opargs()) +#define op_desc (pPerl->Perl_get_op_descs()) +#endif + +#ifdef PerlIO +typedef PerlIO * InputStream; +#else +typedef FILE * InputStream; +#endif + + +static char *svclassnames[] = { + "B::NULL", + "B::IV", + "B::NV", + "B::RV", + "B::PV", + "B::PVIV", + "B::PVNV", + "B::PVMG", + "B::BM", + "B::PVLV", + "B::AV", + "B::HV", + "B::CV", + "B::GV", + "B::FM", + "B::IO", +}; + +typedef enum { + OPc_NULL, /* 0 */ + OPc_BASEOP, /* 1 */ + OPc_UNOP, /* 2 */ + OPc_BINOP, /* 3 */ + OPc_LOGOP, /* 4 */ + OPc_CONDOP, /* 5 */ + OPc_LISTOP, /* 6 */ + OPc_PMOP, /* 7 */ + OPc_SVOP, /* 8 */ + OPc_GVOP, /* 9 */ + OPc_PVOP, /* 10 */ + OPc_CVOP, /* 11 */ + OPc_LOOP, /* 12 */ + OPc_COP /* 13 */ +} opclass; + +static char *opclassnames[] = { + "B::NULL", + "B::OP", + "B::UNOP", + "B::BINOP", + "B::LOGOP", + "B::CONDOP", + "B::LISTOP", + "B::PMOP", + "B::SVOP", + "B::GVOP", + "B::PVOP", + "B::CVOP", + "B::LOOP", + "B::COP" +}; + +static int walkoptree_debug = 0; /* Flag for walkoptree debug hook */ + +static opclass +cc_opclass(OP *o) +{ + if (!o) + return OPc_NULL; + + if (o->op_type == 0) + return (o->op_flags & OPf_KIDS) ? OPc_UNOP : OPc_BASEOP; + + if (o->op_type == OP_SASSIGN) + return ((o->op_private & OPpASSIGN_BACKWARDS) ? OPc_UNOP : OPc_BINOP); + + switch (opargs[o->op_type] & OA_CLASS_MASK) { + case OA_BASEOP: + return OPc_BASEOP; + + case OA_UNOP: + return OPc_UNOP; + + case OA_BINOP: + return OPc_BINOP; + + case OA_LOGOP: + return OPc_LOGOP; + + case OA_CONDOP: + return OPc_CONDOP; + + case OA_LISTOP: + return OPc_LISTOP; + + case OA_PMOP: + return OPc_PMOP; + + case OA_SVOP: + return OPc_SVOP; + + case OA_GVOP: + return OPc_GVOP; + + case OA_PVOP: + return OPc_PVOP; + + case OA_LOOP: + return OPc_LOOP; + + case OA_COP: + return OPc_COP; + + case OA_BASEOP_OR_UNOP: + /* + * UNI(OP_foo) in toke.c returns token UNI or FUNC1 depending on + * whether parens were seen. perly.y uses OPf_SPECIAL to + * signal whether a BASEOP had empty parens or none. + * Some other UNOPs are created later, though, so the best + * test is OPf_KIDS, which is set in newUNOP. + */ + return (o->op_flags & OPf_KIDS) ? OPc_UNOP : OPc_BASEOP; + + case OA_FILESTATOP: + /* + * The file stat OPs are created via UNI(OP_foo) in toke.c but use + * the OPf_REF flag to distinguish between OP types instead of the + * usual OPf_SPECIAL flag. As usual, if OPf_KIDS is set, then we + * return OPc_UNOP so that walkoptree can find our children. If + * OPf_KIDS is not set then we check OPf_REF. Without OPf_REF set + * (no argument to the operator) it's an OP; with OPf_REF set it's + * a GVOP (and op_gv is the GV for the filehandle argument). + */ + return ((o->op_flags & OPf_KIDS) ? OPc_UNOP : + (o->op_flags & OPf_REF) ? OPc_GVOP : OPc_BASEOP); + + case OA_LOOPEXOP: + /* + * next, last, redo, dump and goto use OPf_SPECIAL to indicate that a + * label was omitted (in which case it's a BASEOP) or else a term was + * seen. In this last case, all except goto are definitely PVOP but + * goto is either a PVOP (with an ordinary constant label), an UNOP + * with OPf_STACKED (with a non-constant non-sub) or an UNOP for + * OP_REFGEN (with goto &sub) in which case OPf_STACKED also seems to + * get set. + */ + if (o->op_flags & OPf_STACKED) + return OPc_UNOP; + else if (o->op_flags & OPf_SPECIAL) + return OPc_BASEOP; + else + return OPc_PVOP; + } + warn("can't determine class of operator %s, assuming BASEOP\n", + op_name[o->op_type]); + return OPc_BASEOP; +} + +static char * +cc_opclassname(OP *o) +{ + return opclassnames[cc_opclass(o)]; +} + +static SV * +make_sv_object(SV *arg, SV *sv) +{ + char *type = 0; + IV iv; + + for (iv = 0; iv < sizeof(PL_specialsv_list)/sizeof(SV*); iv++) { + if (sv == PL_specialsv_list[iv]) { + type = "B::SPECIAL"; + break; + } + } + if (!type) { + type = svclassnames[SvTYPE(sv)]; + iv = (IV)sv; + } + sv_setiv(newSVrv(arg, type), iv); + return arg; +} + +static SV * +make_mg_object(SV *arg, MAGIC *mg) +{ + sv_setiv(newSVrv(arg, "B::MAGIC"), (IV)mg); + return arg; +} + +static SV * +cstring(SV *sv) +{ + SV *sstr = newSVpv("", 0); + STRLEN len; + char *s; + + if (!SvOK(sv)) + sv_setpvn(sstr, "0", 1); + else + { + /* XXX Optimise? */ + s = SvPV(sv, len); + sv_catpv(sstr, "\""); + for (; len; len--, s++) + { + /* At least try a little for readability */ + if (*s == '"') + sv_catpv(sstr, "\\\""); + else if (*s == '\\') + sv_catpv(sstr, "\\\\"); + else if (*s >= ' ' && *s < 127) /* XXX not portable */ + sv_catpvn(sstr, s, 1); + else if (*s == '\n') + sv_catpv(sstr, "\\n"); + else if (*s == '\r') + sv_catpv(sstr, "\\r"); + else if (*s == '\t') + sv_catpv(sstr, "\\t"); + else if (*s == '\a') + sv_catpv(sstr, "\\a"); + else if (*s == '\b') + sv_catpv(sstr, "\\b"); + else if (*s == '\f') + sv_catpv(sstr, "\\f"); + else if (*s == '\v') + sv_catpv(sstr, "\\v"); + else + { + /* no trigraph support */ + char escbuff[5]; /* to fit backslash, 3 octals + trailing \0 */ + /* Don't want promotion of a signed -1 char in sprintf args */ + unsigned char c = (unsigned char) *s; + sprintf(escbuff, "\\%03o", c); + sv_catpv(sstr, escbuff); + } + /* XXX Add line breaks if string is long */ + } + sv_catpv(sstr, "\""); + } + return sstr; +} + +static SV * +cchar(SV *sv) +{ + SV *sstr = newSVpv("'", 0); + STRLEN n_a; + char *s = SvPV(sv, n_a); + + if (*s == '\'') + sv_catpv(sstr, "\\'"); + else if (*s == '\\') + sv_catpv(sstr, "\\\\"); + else if (*s >= ' ' && *s < 127) /* XXX not portable */ + sv_catpvn(sstr, s, 1); + else if (*s == '\n') + sv_catpv(sstr, "\\n"); + else if (*s == '\r') + sv_catpv(sstr, "\\r"); + else if (*s == '\t') + sv_catpv(sstr, "\\t"); + else if (*s == '\a') + sv_catpv(sstr, "\\a"); + else if (*s == '\b') + sv_catpv(sstr, "\\b"); + else if (*s == '\f') + sv_catpv(sstr, "\\f"); + else if (*s == '\v') + sv_catpv(sstr, "\\v"); + else + { + /* no trigraph support */ + char escbuff[5]; /* to fit backslash, 3 octals + trailing \0 */ + /* Don't want promotion of a signed -1 char in sprintf args */ + unsigned char c = (unsigned char) *s; + sprintf(escbuff, "\\%03o", c); + sv_catpv(sstr, escbuff); + } + sv_catpv(sstr, "'"); + return sstr; +} + +#ifdef INDIRECT_BGET_MACROS +void freadpv(U32 len, void *data) +{ + New(666, pv.xpv_pv, len, char); + fread(pv.xpv_pv, 1, len, (FILE*)data); + pv.xpv_len = len; + pv.xpv_cur = len - 1; +} + +void byteload_fh(InputStream fp) +{ + struct bytestream bs; + bs.data = fp; + bs.fgetc = (int(*) _((void*)))fgetc; + bs.fread = (int(*) _((char*,size_t,size_t,void*)))fread; + bs.freadpv = freadpv; + byterun(bs); +} + +static int fgetc_fromstring(void *data) +{ + char **strp = (char **)data; + return *(*strp)++; +} + +static int fread_fromstring(char *argp, size_t elemsize, size_t nelem, + void *data) +{ + char **strp = (char **)data; + size_t len = elemsize * nelem; + + memcpy(argp, *strp, len); + *strp += len; + return (int)len; +} + +static void freadpv_fromstring(U32 len, void *data) +{ + char **strp = (char **)data; + + New(666, pv.xpv_pv, len, char); + memcpy(pv.xpv_pv, *strp, len); + pv.xpv_len = len; + pv.xpv_cur = len - 1; + *strp += len; +} + +void byteload_string(char *str) +{ + struct bytestream bs; + bs.data = &str; + bs.fgetc = fgetc_fromstring; + bs.fread = fread_fromstring; + bs.freadpv = freadpv_fromstring; + byterun(bs); +} +#else +void byteload_fh(InputStream fp) +{ + byterun(fp); +} + +void byteload_string(char *str) +{ + croak("Must compile with -DINDIRECT_BGET_MACROS for byteload_string"); +} +#endif /* INDIRECT_BGET_MACROS */ + +void +walkoptree(SV *opsv, char *method) +{ + dSP; + OP *o; + + if (!SvROK(opsv)) + croak("opsv is not a reference"); + opsv = sv_mortalcopy(opsv); + o = (OP*)SvIV((SV*)SvRV(opsv)); + if (walkoptree_debug) { + PUSHMARK(sp); + XPUSHs(opsv); + PUTBACK; + perl_call_method("walkoptree_debug", G_DISCARD); + } + PUSHMARK(sp); + XPUSHs(opsv); + PUTBACK; + perl_call_method(method, G_DISCARD); + if (o && (o->op_flags & OPf_KIDS)) { + OP *kid; + for (kid = ((UNOP*)o)->op_first; kid; kid = kid->op_sibling) { + /* Use the same opsv. Rely on methods not to mess it up. */ + sv_setiv(newSVrv(opsv, cc_opclassname(kid)), (IV)kid); + walkoptree(opsv, method); + } + } +} + +typedef OP *B__OP; +typedef UNOP *B__UNOP; +typedef BINOP *B__BINOP; +typedef LOGOP *B__LOGOP; +typedef CONDOP *B__CONDOP; +typedef LISTOP *B__LISTOP; +typedef PMOP *B__PMOP; +typedef SVOP *B__SVOP; +typedef GVOP *B__GVOP; +typedef PVOP *B__PVOP; +typedef LOOP *B__LOOP; +typedef COP *B__COP; + +typedef SV *B__SV; +typedef SV *B__IV; +typedef SV *B__PV; +typedef SV *B__NV; +typedef SV *B__PVMG; +typedef SV *B__PVLV; +typedef SV *B__BM; +typedef SV *B__RV; +typedef AV *B__AV; +typedef HV *B__HV; +typedef CV *B__CV; +typedef GV *B__GV; +typedef IO *B__IO; + +typedef MAGIC *B__MAGIC; + +MODULE = B PACKAGE = B PREFIX = B_ + +PROTOTYPES: DISABLE + +BOOT: + INIT_SPECIALSV_LIST; + +#define B_main_cv() PL_main_cv +#define B_init_av() PL_initav +#define B_main_root() PL_main_root +#define B_main_start() PL_main_start +#define B_comppadlist() (PL_main_cv ? CvPADLIST(PL_main_cv) : CvPADLIST(PL_compcv)) +#define B_sv_undef() &PL_sv_undef +#define B_sv_yes() &PL_sv_yes +#define B_sv_no() &PL_sv_no + +B::AV +B_init_av() + +B::CV +B_main_cv() + +B::OP +B_main_root() + +B::OP +B_main_start() + +B::AV +B_comppadlist() + +B::SV +B_sv_undef() + +B::SV +B_sv_yes() + +B::SV +B_sv_no() + +MODULE = B PACKAGE = B + + +void +walkoptree(opsv, method) + SV * opsv + char * method + +int +walkoptree_debug(...) + CODE: + RETVAL = walkoptree_debug; + if (items > 0 && SvTRUE(ST(1))) + walkoptree_debug = 1; + OUTPUT: + RETVAL + +int +byteload_fh(fp) + InputStream fp + CODE: + byteload_fh(fp); + RETVAL = 1; + OUTPUT: + RETVAL + +void +byteload_string(str) + char * str + +#define address(sv) (IV)sv + +IV +address(sv) + SV * sv + +B::SV +svref_2object(sv) + SV * sv + CODE: + if (!SvROK(sv)) + croak("argument is not a reference"); + RETVAL = (SV*)SvRV(sv); + OUTPUT: + RETVAL + +void +ppname(opnum) + int opnum + CODE: + ST(0) = sv_newmortal(); + if (opnum >= 0 && opnum < PL_maxo) { + sv_setpvn(ST(0), "pp_", 3); + sv_catpv(ST(0), op_name[opnum]); + } + +void +hash(sv) + SV * sv + CODE: + char *s; + STRLEN len; + U32 hash = 0; + char hexhash[11]; /* must fit "0xffffffff" plus trailing \0 */ + s = SvPV(sv, len); + while (len--) + hash = hash * 33 + *s++; + sprintf(hexhash, "0x%x", hash); + ST(0) = sv_2mortal(newSVpv(hexhash, 0)); + +#define cast_I32(foo) (I32)foo +IV +cast_I32(i) + IV i + +void +minus_c() + CODE: + PL_minus_c = TRUE; + +SV * +cstring(sv) + SV * sv + +SV * +cchar(sv) + SV * sv + +void +threadsv_names() + PPCODE: +#ifdef USE_THREADS + int i; + STRLEN len = strlen(PL_threadsv_names); + + EXTEND(sp, len); + for (i = 0; i < len; i++) + PUSHs(sv_2mortal(newSVpv(&PL_threadsv_names[i], 1))); +#endif + + +#define OP_next(o) o->op_next +#define OP_sibling(o) o->op_sibling +#define OP_desc(o) op_desc[o->op_type] +#define OP_targ(o) o->op_targ +#define OP_type(o) o->op_type +#define OP_seq(o) o->op_seq +#define OP_flags(o) o->op_flags +#define OP_private(o) o->op_private + +MODULE = B PACKAGE = B::OP PREFIX = OP_ + +B::OP +OP_next(o) + B::OP o + +B::OP +OP_sibling(o) + B::OP o + +char * +OP_ppaddr(o) + B::OP o + CODE: + ST(0) = sv_newmortal(); + sv_setpvn(ST(0), "pp_", 3); + sv_catpv(ST(0), op_name[o->op_type]); + +char * +OP_desc(o) + B::OP o + +U16 +OP_targ(o) + B::OP o + +U16 +OP_type(o) + B::OP o + +U16 +OP_seq(o) + B::OP o + +U8 +OP_flags(o) + B::OP o + +U8 +OP_private(o) + B::OP o + +#define UNOP_first(o) o->op_first + +MODULE = B PACKAGE = B::UNOP PREFIX = UNOP_ + +B::OP +UNOP_first(o) + B::UNOP o + +#define BINOP_last(o) o->op_last + +MODULE = B PACKAGE = B::BINOP PREFIX = BINOP_ + +B::OP +BINOP_last(o) + B::BINOP o + +#define LOGOP_other(o) o->op_other + +MODULE = B PACKAGE = B::LOGOP PREFIX = LOGOP_ + +B::OP +LOGOP_other(o) + B::LOGOP o + +#define CONDOP_true(o) o->op_true +#define CONDOP_false(o) o->op_false + +MODULE = B PACKAGE = B::CONDOP PREFIX = CONDOP_ + +B::OP +CONDOP_true(o) + B::CONDOP o + +B::OP +CONDOP_false(o) + B::CONDOP o + +#define LISTOP_children(o) o->op_children + +MODULE = B PACKAGE = B::LISTOP PREFIX = LISTOP_ + +U32 +LISTOP_children(o) + B::LISTOP o + +#define PMOP_pmreplroot(o) o->op_pmreplroot +#define PMOP_pmreplstart(o) o->op_pmreplstart +#define PMOP_pmnext(o) o->op_pmnext +#define PMOP_pmregexp(o) o->op_pmregexp +#define PMOP_pmflags(o) o->op_pmflags +#define PMOP_pmpermflags(o) o->op_pmpermflags + +MODULE = B PACKAGE = B::PMOP PREFIX = PMOP_ + +void +PMOP_pmreplroot(o) + B::PMOP o + OP * root = NO_INIT + CODE: + ST(0) = sv_newmortal(); + root = o->op_pmreplroot; + /* OP_PUSHRE stores an SV* instead of an OP* in op_pmreplroot */ + if (o->op_type == OP_PUSHRE) { + sv_setiv(newSVrv(ST(0), root ? + svclassnames[SvTYPE((SV*)root)] : "B::SV"), + (IV)root); + } + else { + sv_setiv(newSVrv(ST(0), cc_opclassname(root)), (IV)root); + } + +B::OP +PMOP_pmreplstart(o) + B::PMOP o + +B::PMOP +PMOP_pmnext(o) + B::PMOP o + +U16 +PMOP_pmflags(o) + B::PMOP o + +U16 +PMOP_pmpermflags(o) + B::PMOP o + +void +PMOP_precomp(o) + B::PMOP o + REGEXP * rx = NO_INIT + CODE: + ST(0) = sv_newmortal(); + rx = o->op_pmregexp; + if (rx) + sv_setpvn(ST(0), rx->precomp, rx->prelen); + +#define SVOP_sv(o) o->op_sv + +MODULE = B PACKAGE = B::SVOP PREFIX = SVOP_ + + +B::SV +SVOP_sv(o) + B::SVOP o + +#define GVOP_gv(o) o->op_gv + +MODULE = B PACKAGE = B::GVOP PREFIX = GVOP_ + + +B::GV +GVOP_gv(o) + B::GVOP o + +MODULE = B PACKAGE = B::PVOP PREFIX = PVOP_ + +void +PVOP_pv(o) + B::PVOP o + CODE: + /* + * OP_TRANS uses op_pv to point to a table of 256 shorts + * whereas other PVOPs point to a null terminated string. + */ + ST(0) = sv_2mortal(newSVpv(o->op_pv, (o->op_type == OP_TRANS) ? + 256 * sizeof(short) : 0)); + +#define LOOP_redoop(o) o->op_redoop +#define LOOP_nextop(o) o->op_nextop +#define LOOP_lastop(o) o->op_lastop + +MODULE = B PACKAGE = B::LOOP PREFIX = LOOP_ + + +B::OP +LOOP_redoop(o) + B::LOOP o + +B::OP +LOOP_nextop(o) + B::LOOP o + +B::OP +LOOP_lastop(o) + B::LOOP o + +#define COP_label(o) o->cop_label +#define COP_stash(o) o->cop_stash +#define COP_filegv(o) o->cop_filegv +#define COP_cop_seq(o) o->cop_seq +#define COP_arybase(o) o->cop_arybase +#define COP_line(o) o->cop_line + +MODULE = B PACKAGE = B::COP PREFIX = COP_ + +char * +COP_label(o) + B::COP o + +B::HV +COP_stash(o) + B::COP o + +B::GV +COP_filegv(o) + B::COP o + +U32 +COP_cop_seq(o) + B::COP o + +I32 +COP_arybase(o) + B::COP o + +U16 +COP_line(o) + B::COP o + +MODULE = B PACKAGE = B::SV PREFIX = Sv + +U32 +SvREFCNT(sv) + B::SV sv + +U32 +SvFLAGS(sv) + B::SV sv + +MODULE = B PACKAGE = B::IV PREFIX = Sv + +IV +SvIV(sv) + B::IV sv + +IV +SvIVX(sv) + B::IV sv + +MODULE = B PACKAGE = B::IV + +#define needs64bits(sv) ((I32)SvIVX(sv) != SvIVX(sv)) + +int +needs64bits(sv) + B::IV sv + +void +packiv(sv) + B::IV sv + CODE: + if (sizeof(IV) == 8) { + U32 wp[2]; + IV iv = SvIVX(sv); + /* + * The following way of spelling 32 is to stop compilers on + * 32-bit architectures from moaning about the shift count + * being >= the width of the type. Such architectures don't + * reach this code anyway (unless sizeof(IV) > 8 but then + * everything else breaks too so I'm not fussed at the moment). + */ + wp[0] = htonl(((U32)iv) >> (sizeof(IV)*4)); + wp[1] = htonl(iv & 0xffffffff); + ST(0) = sv_2mortal(newSVpv((char *)wp, 8)); + } else { + U32 w = htonl((U32)SvIVX(sv)); + ST(0) = sv_2mortal(newSVpv((char *)&w, 4)); + } + +MODULE = B PACKAGE = B::NV PREFIX = Sv + +double +SvNV(sv) + B::NV sv + +double +SvNVX(sv) + B::NV sv + +MODULE = B PACKAGE = B::RV PREFIX = Sv + +B::SV +SvRV(sv) + B::RV sv + +MODULE = B PACKAGE = B::PV PREFIX = Sv + +void +SvPV(sv) + B::PV sv + CODE: + ST(0) = sv_newmortal(); + sv_setpvn(ST(0), SvPVX(sv), SvCUR(sv)); + +MODULE = B PACKAGE = B::PVMG PREFIX = Sv + +void +SvMAGIC(sv) + B::PVMG sv + MAGIC * mg = NO_INIT + PPCODE: + for (mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic) + XPUSHs(make_mg_object(sv_newmortal(), mg)); + +MODULE = B PACKAGE = B::PVMG + +B::HV +SvSTASH(sv) + B::PVMG sv + +#define MgMOREMAGIC(mg) mg->mg_moremagic +#define MgPRIVATE(mg) mg->mg_private +#define MgTYPE(mg) mg->mg_type +#define MgFLAGS(mg) mg->mg_flags +#define MgOBJ(mg) mg->mg_obj + +MODULE = B PACKAGE = B::MAGIC PREFIX = Mg + +B::MAGIC +MgMOREMAGIC(mg) + B::MAGIC mg + +U16 +MgPRIVATE(mg) + B::MAGIC mg + +char +MgTYPE(mg) + B::MAGIC mg + +U8 +MgFLAGS(mg) + B::MAGIC mg + +B::SV +MgOBJ(mg) + B::MAGIC mg + +void +MgPTR(mg) + B::MAGIC mg + CODE: + ST(0) = sv_newmortal(); + if (mg->mg_ptr) + sv_setpvn(ST(0), mg->mg_ptr, mg->mg_len); + +MODULE = B PACKAGE = B::PVLV PREFIX = Lv + +U32 +LvTARGOFF(sv) + B::PVLV sv + +U32 +LvTARGLEN(sv) + B::PVLV sv + +char +LvTYPE(sv) + B::PVLV sv + +B::SV +LvTARG(sv) + B::PVLV sv + +MODULE = B PACKAGE = B::BM PREFIX = Bm + +I32 +BmUSEFUL(sv) + B::BM sv + +U16 +BmPREVIOUS(sv) + B::BM sv + +U8 +BmRARE(sv) + B::BM sv + +void +BmTABLE(sv) + B::BM sv + STRLEN len = NO_INIT + char * str = NO_INIT + CODE: + str = SvPV(sv, len); + /* Boyer-Moore table is just after string and its safety-margin \0 */ + ST(0) = sv_2mortal(newSVpv(str + len + 1, 256)); + +MODULE = B PACKAGE = B::GV PREFIX = Gv + +void +GvNAME(gv) + B::GV gv + CODE: + ST(0) = sv_2mortal(newSVpv(GvNAME(gv), GvNAMELEN(gv))); + +B::HV +GvSTASH(gv) + B::GV gv + +B::SV +GvSV(gv) + B::GV gv + +B::IO +GvIO(gv) + B::GV gv + +B::CV +GvFORM(gv) + B::GV gv + +B::AV +GvAV(gv) + B::GV gv + +B::HV +GvHV(gv) + B::GV gv + +B::GV +GvEGV(gv) + B::GV gv + +B::CV +GvCV(gv) + B::GV gv + +U32 +GvCVGEN(gv) + B::GV gv + +U16 +GvLINE(gv) + B::GV gv + +B::GV +GvFILEGV(gv) + B::GV gv + +MODULE = B PACKAGE = B::GV + +U32 +GvREFCNT(gv) + B::GV gv + +U8 +GvFLAGS(gv) + B::GV gv + +MODULE = B PACKAGE = B::IO PREFIX = Io + +long +IoLINES(io) + B::IO io + +long +IoPAGE(io) + B::IO io + +long +IoPAGE_LEN(io) + B::IO io + +long +IoLINES_LEFT(io) + B::IO io + +char * +IoTOP_NAME(io) + B::IO io + +B::GV +IoTOP_GV(io) + B::IO io + +char * +IoFMT_NAME(io) + B::IO io + +B::GV +IoFMT_GV(io) + B::IO io + +char * +IoBOTTOM_NAME(io) + B::IO io + +B::GV +IoBOTTOM_GV(io) + B::IO io + +short +IoSUBPROCESS(io) + B::IO io + +MODULE = B PACKAGE = B::IO + +char +IoTYPE(io) + B::IO io + +U8 +IoFLAGS(io) + B::IO io + +MODULE = B PACKAGE = B::AV PREFIX = Av + +SSize_t +AvFILL(av) + B::AV av + +SSize_t +AvMAX(av) + B::AV av + +#define AvOFF(av) ((XPVAV*)SvANY(av))->xof_off + +IV +AvOFF(av) + B::AV av + +void +AvARRAY(av) + B::AV av + PPCODE: + if (AvFILL(av) >= 0) { + SV **svp = AvARRAY(av); + I32 i; + for (i = 0; i <= AvFILL(av); i++) + XPUSHs(make_sv_object(sv_newmortal(), svp[i])); + } + +MODULE = B PACKAGE = B::AV + +U8 +AvFLAGS(av) + B::AV av + +MODULE = B PACKAGE = B::CV PREFIX = Cv + +B::HV +CvSTASH(cv) + B::CV cv + +B::OP +CvSTART(cv) + B::CV cv + +B::OP +CvROOT(cv) + B::CV cv + +B::GV +CvGV(cv) + B::CV cv + +B::GV +CvFILEGV(cv) + B::CV cv + +long +CvDEPTH(cv) + B::CV cv + +B::AV +CvPADLIST(cv) + B::CV cv + +B::CV +CvOUTSIDE(cv) + B::CV cv + +void +CvXSUB(cv) + B::CV cv + CODE: + ST(0) = sv_2mortal(newSViv((IV)CvXSUB(cv))); + + +void +CvXSUBANY(cv) + B::CV cv + CODE: + ST(0) = sv_2mortal(newSViv(CvXSUBANY(cv).any_iv)); + +MODULE = B PACKAGE = B::CV + +U8 +CvFLAGS(cv) + B::CV cv + + +MODULE = B PACKAGE = B::HV PREFIX = Hv + +STRLEN +HvFILL(hv) + B::HV hv + +STRLEN +HvMAX(hv) + B::HV hv + +I32 +HvKEYS(hv) + B::HV hv + +I32 +HvRITER(hv) + B::HV hv + +char * +HvNAME(hv) + B::HV hv + +B::PMOP +HvPMROOT(hv) + B::HV hv + +void +HvARRAY(hv) + B::HV hv + PPCODE: + if (HvKEYS(hv) > 0) { + SV *sv; + char *key; + I32 len; + (void)hv_iterinit(hv); + EXTEND(sp, HvKEYS(hv) * 2); + while (sv = hv_iternextsv(hv, &key, &len)) { + PUSHs(newSVpv(key, len)); + PUSHs(make_sv_object(sv_newmortal(), sv)); + } + } diff --git a/gnu/usr.bin/perl/ext/B/B/Asmdata.pm b/gnu/usr.bin/perl/ext/B/B/Asmdata.pm new file mode 100644 index 00000000000..f3e57a17d03 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/B/Asmdata.pm @@ -0,0 +1,170 @@ +# +# Copyright (c) 1996-1998 Malcolm Beattie +# +# You may distribute under the terms of either the GNU General Public +# License or the Artistic License, as specified in the README file. +# +# +# +# This file is autogenerated from bytecode.pl. Changes made here will be lost. +# +package B::Asmdata; +use Exporter; +@ISA = qw(Exporter); +@EXPORT_OK = qw(%insn_data @insn_name @optype @specialsv_name); +use vars qw(%insn_data @insn_name @optype @specialsv_name); + +@optype = qw(OP UNOP BINOP LOGOP CONDOP LISTOP PMOP SVOP GVOP PVOP LOOP COP); +@specialsv_name = qw(Nullsv &PL_sv_undef &PL_sv_yes &PL_sv_no); + +# XXX insn_data is initialised this way because with a large +# %insn_data = (foo => [...], bar => [...], ...) initialiser +# I get a hard-to-track-down stack underflow and segfault. +$insn_data{comment} = [35, \&PUT_comment_t, "GET_comment_t"]; +$insn_data{nop} = [10, \&PUT_none, "GET_none"]; +$insn_data{ret} = [0, \&PUT_none, "GET_none"]; +$insn_data{ldsv} = [1, \&PUT_svindex, "GET_svindex"]; +$insn_data{ldop} = [2, \&PUT_opindex, "GET_opindex"]; +$insn_data{stsv} = [3, \&PUT_U32, "GET_U32"]; +$insn_data{stop} = [4, \&PUT_U32, "GET_U32"]; +$insn_data{ldspecsv} = [5, \&PUT_U8, "GET_U8"]; +$insn_data{newsv} = [6, \&PUT_U8, "GET_U8"]; +$insn_data{newop} = [7, \&PUT_U8, "GET_U8"]; +$insn_data{newopn} = [8, \&PUT_U8, "GET_U8"]; +$insn_data{newpv} = [9, \&PUT_PV, "GET_PV"]; +$insn_data{pv_cur} = [11, \&PUT_U32, "GET_U32"]; +$insn_data{pv_free} = [12, \&PUT_none, "GET_none"]; +$insn_data{sv_upgrade} = [13, \&PUT_U8, "GET_U8"]; +$insn_data{sv_refcnt} = [14, \&PUT_U32, "GET_U32"]; +$insn_data{sv_refcnt_add} = [15, \&PUT_I32, "GET_I32"]; +$insn_data{sv_flags} = [16, \&PUT_U32, "GET_U32"]; +$insn_data{xrv} = [17, \&PUT_svindex, "GET_svindex"]; +$insn_data{xpv} = [18, \&PUT_none, "GET_none"]; +$insn_data{xiv32} = [19, \&PUT_I32, "GET_I32"]; +$insn_data{xiv64} = [20, \&PUT_IV64, "GET_IV64"]; +$insn_data{xnv} = [21, \&PUT_double, "GET_double"]; +$insn_data{xlv_targoff} = [22, \&PUT_U32, "GET_U32"]; +$insn_data{xlv_targlen} = [23, \&PUT_U32, "GET_U32"]; +$insn_data{xlv_targ} = [24, \&PUT_svindex, "GET_svindex"]; +$insn_data{xlv_type} = [25, \&PUT_U8, "GET_U8"]; +$insn_data{xbm_useful} = [26, \&PUT_I32, "GET_I32"]; +$insn_data{xbm_previous} = [27, \&PUT_U16, "GET_U16"]; +$insn_data{xbm_rare} = [28, \&PUT_U8, "GET_U8"]; +$insn_data{xfm_lines} = [29, \&PUT_I32, "GET_I32"]; +$insn_data{xio_lines} = [30, \&PUT_I32, "GET_I32"]; +$insn_data{xio_page} = [31, \&PUT_I32, "GET_I32"]; +$insn_data{xio_page_len} = [32, \&PUT_I32, "GET_I32"]; +$insn_data{xio_lines_left} = [33, \&PUT_I32, "GET_I32"]; +$insn_data{xio_top_name} = [34, \&PUT_pvcontents, "GET_pvcontents"]; +$insn_data{xio_top_gv} = [36, \&PUT_svindex, "GET_svindex"]; +$insn_data{xio_fmt_name} = [37, \&PUT_pvcontents, "GET_pvcontents"]; +$insn_data{xio_fmt_gv} = [38, \&PUT_svindex, "GET_svindex"]; +$insn_data{xio_bottom_name} = [39, \&PUT_pvcontents, "GET_pvcontents"]; +$insn_data{xio_bottom_gv} = [40, \&PUT_svindex, "GET_svindex"]; +$insn_data{xio_subprocess} = [41, \&PUT_U16, "GET_U16"]; +$insn_data{xio_type} = [42, \&PUT_U8, "GET_U8"]; +$insn_data{xio_flags} = [43, \&PUT_U8, "GET_U8"]; +$insn_data{xcv_stash} = [44, \&PUT_svindex, "GET_svindex"]; +$insn_data{xcv_start} = [45, \&PUT_opindex, "GET_opindex"]; +$insn_data{xcv_root} = [46, \&PUT_opindex, "GET_opindex"]; +$insn_data{xcv_gv} = [47, \&PUT_svindex, "GET_svindex"]; +$insn_data{xcv_filegv} = [48, \&PUT_svindex, "GET_svindex"]; +$insn_data{xcv_depth} = [49, \&PUT_I32, "GET_I32"]; +$insn_data{xcv_padlist} = [50, \&PUT_svindex, "GET_svindex"]; +$insn_data{xcv_outside} = [51, \&PUT_svindex, "GET_svindex"]; +$insn_data{xcv_flags} = [52, \&PUT_U8, "GET_U8"]; +$insn_data{av_extend} = [53, \&PUT_I32, "GET_I32"]; +$insn_data{av_push} = [54, \&PUT_svindex, "GET_svindex"]; +$insn_data{xav_fill} = [55, \&PUT_I32, "GET_I32"]; +$insn_data{xav_max} = [56, \&PUT_I32, "GET_I32"]; +$insn_data{xav_flags} = [57, \&PUT_U8, "GET_U8"]; +$insn_data{xhv_riter} = [58, \&PUT_I32, "GET_I32"]; +$insn_data{xhv_name} = [59, \&PUT_pvcontents, "GET_pvcontents"]; +$insn_data{hv_store} = [60, \&PUT_svindex, "GET_svindex"]; +$insn_data{sv_magic} = [61, \&PUT_U8, "GET_U8"]; +$insn_data{mg_obj} = [62, \&PUT_svindex, "GET_svindex"]; +$insn_data{mg_private} = [63, \&PUT_U16, "GET_U16"]; +$insn_data{mg_flags} = [64, \&PUT_U8, "GET_U8"]; +$insn_data{mg_pv} = [65, \&PUT_pvcontents, "GET_pvcontents"]; +$insn_data{xmg_stash} = [66, \&PUT_svindex, "GET_svindex"]; +$insn_data{gv_fetchpv} = [67, \&PUT_strconst, "GET_strconst"]; +$insn_data{gv_stashpv} = [68, \&PUT_strconst, "GET_strconst"]; +$insn_data{gp_sv} = [69, \&PUT_svindex, "GET_svindex"]; +$insn_data{gp_refcnt} = [70, \&PUT_U32, "GET_U32"]; +$insn_data{gp_refcnt_add} = [71, \&PUT_I32, "GET_I32"]; +$insn_data{gp_av} = [72, \&PUT_svindex, "GET_svindex"]; +$insn_data{gp_hv} = [73, \&PUT_svindex, "GET_svindex"]; +$insn_data{gp_cv} = [74, \&PUT_svindex, "GET_svindex"]; +$insn_data{gp_filegv} = [75, \&PUT_svindex, "GET_svindex"]; +$insn_data{gp_io} = [76, \&PUT_svindex, "GET_svindex"]; +$insn_data{gp_form} = [77, \&PUT_svindex, "GET_svindex"]; +$insn_data{gp_cvgen} = [78, \&PUT_U32, "GET_U32"]; +$insn_data{gp_line} = [79, \&PUT_U16, "GET_U16"]; +$insn_data{gp_share} = [80, \&PUT_svindex, "GET_svindex"]; +$insn_data{xgv_flags} = [81, \&PUT_U8, "GET_U8"]; +$insn_data{op_next} = [82, \&PUT_opindex, "GET_opindex"]; +$insn_data{op_sibling} = [83, \&PUT_opindex, "GET_opindex"]; +$insn_data{op_ppaddr} = [84, \&PUT_strconst, "GET_strconst"]; +$insn_data{op_targ} = [85, \&PUT_U32, "GET_U32"]; +$insn_data{op_type} = [86, \&PUT_U16, "GET_U16"]; +$insn_data{op_seq} = [87, \&PUT_U16, "GET_U16"]; +$insn_data{op_flags} = [88, \&PUT_U8, "GET_U8"]; +$insn_data{op_private} = [89, \&PUT_U8, "GET_U8"]; +$insn_data{op_first} = [90, \&PUT_opindex, "GET_opindex"]; +$insn_data{op_last} = [91, \&PUT_opindex, "GET_opindex"]; +$insn_data{op_other} = [92, \&PUT_opindex, "GET_opindex"]; +$insn_data{op_true} = [93, \&PUT_opindex, "GET_opindex"]; +$insn_data{op_false} = [94, \&PUT_opindex, "GET_opindex"]; +$insn_data{op_children} = [95, \&PUT_U32, "GET_U32"]; +$insn_data{op_pmreplroot} = [96, \&PUT_opindex, "GET_opindex"]; +$insn_data{op_pmreplrootgv} = [97, \&PUT_svindex, "GET_svindex"]; +$insn_data{op_pmreplstart} = [98, \&PUT_opindex, "GET_opindex"]; +$insn_data{op_pmnext} = [99, \&PUT_opindex, "GET_opindex"]; +$insn_data{pregcomp} = [100, \&PUT_pvcontents, "GET_pvcontents"]; +$insn_data{op_pmflags} = [101, \&PUT_U16, "GET_U16"]; +$insn_data{op_pmpermflags} = [102, \&PUT_U16, "GET_U16"]; +$insn_data{op_sv} = [103, \&PUT_svindex, "GET_svindex"]; +$insn_data{op_gv} = [104, \&PUT_svindex, "GET_svindex"]; +$insn_data{op_pv} = [105, \&PUT_pvcontents, "GET_pvcontents"]; +$insn_data{op_pv_tr} = [106, \&PUT_op_tr_array, "GET_op_tr_array"]; +$insn_data{op_redoop} = [107, \&PUT_opindex, "GET_opindex"]; +$insn_data{op_nextop} = [108, \&PUT_opindex, "GET_opindex"]; +$insn_data{op_lastop} = [109, \&PUT_opindex, "GET_opindex"]; +$insn_data{cop_label} = [110, \&PUT_pvcontents, "GET_pvcontents"]; +$insn_data{cop_stash} = [111, \&PUT_svindex, "GET_svindex"]; +$insn_data{cop_filegv} = [112, \&PUT_svindex, "GET_svindex"]; +$insn_data{cop_seq} = [113, \&PUT_U32, "GET_U32"]; +$insn_data{cop_arybase} = [114, \&PUT_I32, "GET_I32"]; +$insn_data{cop_line} = [115, \&PUT_U16, "GET_U16"]; +$insn_data{main_start} = [116, \&PUT_opindex, "GET_opindex"]; +$insn_data{main_root} = [117, \&PUT_opindex, "GET_opindex"]; +$insn_data{curpad} = [118, \&PUT_svindex, "GET_svindex"]; + +my ($insn_name, $insn_data); +while (($insn_name, $insn_data) = each %insn_data) { + $insn_name[$insn_data->[0]] = $insn_name; +} +# Fill in any gaps +@insn_name = map($_ || "unused", @insn_name); + +1; + +__END__ + +=head1 NAME + +B::Asmdata - Autogenerated data about Perl ops, used to generate bytecode + +=head1 SYNOPSIS + + use Asmdata; + +=head1 DESCRIPTION + +See F<ext/B/B/Asmdata.pm>. + +=head1 AUTHOR + +Malcolm Beattie, C<mbeattie@sable.ox.ac.uk> + +=cut diff --git a/gnu/usr.bin/perl/ext/B/B/Assembler.pm b/gnu/usr.bin/perl/ext/B/B/Assembler.pm new file mode 100644 index 00000000000..06e00adeb54 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/B/Assembler.pm @@ -0,0 +1,229 @@ +# Assembler.pm +# +# Copyright (c) 1996 Malcolm Beattie +# +# You may distribute under the terms of either the GNU General Public +# License or the Artistic License, as specified in the README file. +package B::Assembler; +use Exporter; +use B qw(ppname); +use B::Asmdata qw(%insn_data @insn_name); + +@ISA = qw(Exporter); +@EXPORT_OK = qw(assemble_fh assemble_insn strip_comments + parse_statement uncstring); + +use strict; +my %opnumber; +my ($i, $opname); +for ($i = 0; defined($opname = ppname($i)); $i++) { + $opnumber{$opname} = $i; +} + +my ($linenum, $errors); + +sub error { + my $str = shift; + warn "$linenum: $str\n"; + $errors++; +} + +my $debug = 0; +sub debug { $debug = shift } + +# +# First define all the data conversion subs to which Asmdata will refer +# + +sub B::Asmdata::PUT_U8 { + my $arg = shift; + my $c = uncstring($arg); + if (defined($c)) { + if (length($c) != 1) { + error "argument for U8 is too long: $c"; + $c = substr($c, 0, 1); + } + } else { + $c = chr($arg); + } + return $c; +} + +sub B::Asmdata::PUT_U16 { pack("n", $_[0]) } +sub B::Asmdata::PUT_U32 { pack("N", $_[0]) } +sub B::Asmdata::PUT_I32 { pack("N", $_[0]) } +sub B::Asmdata::PUT_objindex { pack("N", $_[0]) } # could allow names here +sub B::Asmdata::PUT_svindex { &B::Asmdata::PUT_objindex } +sub B::Asmdata::PUT_opindex { &B::Asmdata::PUT_objindex } + +sub B::Asmdata::PUT_strconst { + my $arg = shift; + $arg = uncstring($arg); + if (!defined($arg)) { + error "bad string constant: $arg"; + return ""; + } + if ($arg =~ s/\0//g) { + error "string constant argument contains NUL: $arg"; + } + return $arg . "\0"; +} + +sub B::Asmdata::PUT_pvcontents { + my $arg = shift; + error "extraneous argument: $arg" if defined $arg; + return ""; +} +sub B::Asmdata::PUT_PV { + my $arg = shift; + $arg = uncstring($arg); + error "bad string argument: $arg" unless defined($arg); + return pack("N", length($arg)) . $arg; +} +sub B::Asmdata::PUT_comment_t { + my $arg = shift; + $arg = uncstring($arg); + error "bad string argument: $arg" unless defined($arg); + if ($arg =~ s/\n//g) { + error "comment argument contains linefeed: $arg"; + } + return $arg . "\n"; +} +sub B::Asmdata::PUT_double { sprintf("%s\0", $_[0]) } +sub B::Asmdata::PUT_none { + my $arg = shift; + error "extraneous argument: $arg" if defined $arg; + return ""; +} +sub B::Asmdata::PUT_op_tr_array { + my $arg = shift; + my @ary = split(/\s*,\s*/, $arg); + if (@ary != 256) { + error "wrong number of arguments to op_tr_array"; + @ary = (0) x 256; + } + return pack("n256", @ary); +} +# XXX Check this works +sub B::Asmdata::PUT_IV64 { + my $arg = shift; + return pack("NN", $arg >> 32, $arg & 0xffffffff); +} + +my %unesc = (n => "\n", r => "\r", t => "\t", a => "\a", + b => "\b", f => "\f", v => "\013"); + +sub uncstring { + my $s = shift; + $s =~ s/^"// and $s =~ s/"$// or return undef; + $s =~ s/\\(\d\d\d|.)/length($1) == 3 ? chr(oct($1)) : ($unesc{$1}||$1)/eg; + return $s; +} + +sub strip_comments { + my $stmt = shift; + # Comments only allowed in instructions which don't take string arguments + $stmt =~ s{ + (?sx) # Snazzy extended regexp coming up. Also, treat + # string as a single line so .* eats \n characters. + ^\s* # Ignore leading whitespace + ( + [^"]* # A double quote '"' indicates a string argument. If we + # find a double quote, the match fails and we strip nothing. + ) + \s*\# # Any amount of whitespace plus the comment marker... + .*$ # ...which carries on to end-of-string. + }{$1}; # Keep only the instruction and optional argument. + return $stmt; +} + +sub parse_statement { + my $stmt = shift; + my ($insn, $arg) = $stmt =~ m{ + (?sx) + ^\s* # allow (but ignore) leading whitespace + (.*?) # Instruction continues up until... + (?: # ...an optional whitespace+argument group + \s+ # first whitespace. + (.*) # The argument is all the rest (newlines included). + )?$ # anchor at end-of-line + }; + if (defined($arg)) { + if ($arg =~ s/^0x(?=[0-9a-fA-F]+$)//) { + $arg = hex($arg); + } elsif ($arg =~ s/^0(?=[0-7]+$)//) { + $arg = oct($arg); + } elsif ($arg =~ /^pp_/) { + $arg =~ s/\s*$//; # strip trailing whitespace + my $opnum = $opnumber{$arg}; + if (defined($opnum)) { + $arg = $opnum; + } else { + error qq(No such op type "$arg"); + $arg = 0; + } + } + } + return ($insn, $arg); +} + +sub assemble_insn { + my ($insn, $arg) = @_; + my $data = $insn_data{$insn}; + if (defined($data)) { + my ($bytecode, $putsub) = @{$data}[0, 1]; + my $argcode = &$putsub($arg); + return chr($bytecode).$argcode; + } else { + error qq(no such instruction "$insn"); + return ""; + } +} + +sub assemble_fh { + my ($fh, $out) = @_; + my ($line, $insn, $arg); + $linenum = 0; + $errors = 0; + while ($line = <$fh>) { + $linenum++; + chomp $line; + if ($debug) { + my $quotedline = $line; + $quotedline =~ s/\\/\\\\/g; + $quotedline =~ s/"/\\"/g; + &$out(assemble_insn("comment", qq("$quotedline"))); + } + $line = strip_comments($line) or next; + ($insn, $arg) = parse_statement($line); + &$out(assemble_insn($insn, $arg)); + if ($debug) { + &$out(assemble_insn("nop", undef)); + } + } + if ($errors) { + die "Assembly failed with $errors error(s)\n"; + } +} + +1; + +__END__ + +=head1 NAME + +B::Assembler - Assemble Perl bytecode + +=head1 SYNOPSIS + + use Assembler; + +=head1 DESCRIPTION + +See F<ext/B/B/Assembler.pm>. + +=head1 AUTHOR + +Malcolm Beattie, C<mbeattie@sable.ox.ac.uk> + +=cut diff --git a/gnu/usr.bin/perl/ext/B/B/Bblock.pm b/gnu/usr.bin/perl/ext/B/B/Bblock.pm new file mode 100644 index 00000000000..a54431b4ce7 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/B/Bblock.pm @@ -0,0 +1,162 @@ +package B::Bblock; +use Exporter (); +@ISA = "Exporter"; +@EXPORT_OK = qw(find_leaders); + +use B qw(peekop walkoptree walkoptree_exec + main_root main_start svref_2object); +use B::Terse; +use strict; + +my $bblock; +my @bblock_ends; + +sub mark_leader { + my $op = shift; + if ($$op) { + $bblock->{$$op} = $op; + } +} + +sub find_leaders { + my ($root, $start) = @_; + $bblock = {}; + mark_leader($start); + walkoptree($root, "mark_if_leader"); + return $bblock; +} + +# Debugging +sub walk_bblocks { + my ($root, $start) = @_; + my ($op, $lastop, $leader, $bb); + $bblock = {}; + mark_leader($start); + walkoptree($root, "mark_if_leader"); + my @leaders = values %$bblock; + while ($leader = shift @leaders) { + $lastop = $leader; + $op = $leader->next; + while ($$op && !exists($bblock->{$$op})) { + $bblock->{$$op} = $leader; + $lastop = $op; + $op = $op->next; + } + push(@bblock_ends, [$leader, $lastop]); + } + foreach $bb (@bblock_ends) { + ($leader, $lastop) = @$bb; + printf "%s .. %s\n", peekop($leader), peekop($lastop); + for ($op = $leader; $$op != $$lastop; $op = $op->next) { + printf " %s\n", peekop($op); + } + printf " %s\n", peekop($lastop); + } + print "-------\n"; + walkoptree_exec($start, "terse"); +} + +sub walk_bblocks_obj { + my $cvref = shift; + my $cv = svref_2object($cvref); + walk_bblocks($cv->ROOT, $cv->START); +} + +sub B::OP::mark_if_leader {} + +sub B::COP::mark_if_leader { + my $op = shift; + if ($op->label) { + mark_leader($op); + } +} + +sub B::LOOP::mark_if_leader { + my $op = shift; + mark_leader($op->next); + mark_leader($op->nextop); + mark_leader($op->redoop); + mark_leader($op->lastop->next); +} + +sub B::LOGOP::mark_if_leader { + my $op = shift; + my $ppaddr = $op->ppaddr; + mark_leader($op->next); + if ($ppaddr eq "pp_entertry") { + mark_leader($op->other->next); + } else { + mark_leader($op->other); + } +} + +sub B::CONDOP::mark_if_leader { + my $op = shift; + mark_leader($op->next); + mark_leader($op->true); + mark_leader($op->false); +} + +sub B::PMOP::mark_if_leader { + my $op = shift; + if ($op->ppaddr ne "pp_pushre") { + my $replroot = $op->pmreplroot; + if ($$replroot) { + mark_leader($replroot); + mark_leader($op->next); + mark_leader($op->pmreplstart); + } + } +} + +# PMOP stuff omitted + +sub compile { + my @options = @_; + if (@options) { + return sub { + my $objname; + foreach $objname (@options) { + $objname = "main::$objname" unless $objname =~ /::/; + eval "walk_bblocks_obj(\\&$objname)"; + die "walk_bblocks_obj(\\&$objname) failed: $@" if $@; + } + } + } else { + return sub { walk_bblocks(main_root, main_start) }; + } +} + +# Basic block leaders: +# Any COP (pp_nextstate) with a non-NULL label +# [The op after a pp_enter] Omit +# [The op after a pp_entersub. Don't count this one.] +# The ops pointed at by nextop, redoop and lastop->op_next of a LOOP +# The ops pointed at by op_next and op_other of a LOGOP, except +# for pp_entertry which has op_next and op_other->op_next +# The ops pointed at by op_true and op_false of a CONDOP +# The op pointed at by op_pmreplstart of a PMOP +# The op pointed at by op_other->op_pmreplstart of pp_substcont? +# [The op after a pp_return] Omit + +1; + +__END__ + +=head1 NAME + +B::Bblock - Walk basic blocks + +=head1 SYNOPSIS + + perl -MO=Bblock[,OPTIONS] foo.pl + +=head1 DESCRIPTION + +See F<ext/B/README>. + +=head1 AUTHOR + +Malcolm Beattie, C<mbeattie@sable.ox.ac.uk> + +=cut diff --git a/gnu/usr.bin/perl/ext/B/B/Bytecode.pm b/gnu/usr.bin/perl/ext/B/B/Bytecode.pm new file mode 100644 index 00000000000..0c5a58dc542 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/B/Bytecode.pm @@ -0,0 +1,908 @@ +# Bytecode.pm +# +# Copyright (c) 1996-1998 Malcolm Beattie +# +# You may distribute under the terms of either the GNU General Public +# License or the Artistic License, as specified in the README file. +# +package B::Bytecode; +use strict; +use Carp; +use IO::File; + +use B qw(minus_c main_cv main_root main_start comppadlist + class peekop walkoptree svref_2object cstring walksymtable); +use B::Asmdata qw(@optype @specialsv_name); +use B::Assembler qw(assemble_fh); + +my %optype_enum; +my $i; +for ($i = 0; $i < @optype; $i++) { + $optype_enum{$optype[$i]} = $i; +} + +# Following is SVf_POK|SVp_POK +# XXX Shouldn't be hardwired +sub POK () { 0x04040000 } + +# Following is SVf_IOK|SVp_OK +# XXX Shouldn't be hardwired +sub IOK () { 0x01010000 } + +my ($verbose, $module_only, $no_assemble, $debug_bc, $debug_cv); +my $assembler_pid; + +# Optimisation options. On the command line, use hyphens instead of +# underscores for compatibility with gcc-style options. We use +# underscores here because they are OK in (strict) barewords. +my ($strip_syntree, $compress_nullops, $omit_seq, $bypass_nullops); +my %optimise = (strip_syntax_tree => \$strip_syntree, + compress_nullops => \$compress_nullops, + omit_sequence_numbers => \$omit_seq, + bypass_nullops => \$bypass_nullops); + +my $nextix = 0; +my %symtable; # maps object addresses to object indices. + # Filled in at allocation (newsv/newop) time. +my %saved; # maps object addresses (for SVish classes) to "saved yet?" + # flag. Set at FOO::bytecode time usually by SV::bytecode. + # Manipulated via saved(), mark_saved(), unmark_saved(). + +my $svix = -1; # we keep track of when the sv register contains an element + # of the object table to avoid unnecessary repeated + # consecutive ldsv instructions. +my $opix = -1; # Ditto for the op register. + +sub ldsv { + my $ix = shift; + if ($ix != $svix) { + print "ldsv $ix\n"; + $svix = $ix; + } +} + +sub stsv { + my $ix = shift; + print "stsv $ix\n"; + $svix = $ix; +} + +sub set_svix { + $svix = shift; +} + +sub ldop { + my $ix = shift; + if ($ix != $opix) { + print "ldop $ix\n"; + $opix = $ix; + } +} + +sub stop { + my $ix = shift; + print "stop $ix\n"; + $opix = $ix; +} + +sub set_opix { + $opix = shift; +} + +sub pvstring { + my $str = shift; + if (defined($str)) { + return cstring($str . "\0"); + } else { + return '""'; + } +} + +sub saved { $saved{${$_[0]}} } +sub mark_saved { $saved{${$_[0]}} = 1 } +sub unmark_saved { $saved{${$_[0]}} = 0 } + +sub debug { $debug_bc = shift } + +sub B::OBJECT::nyi { + my $obj = shift; + warn sprintf("bytecode save method for %s (0x%x) not yet implemented\n", + class($obj), $$obj); +} + +# +# objix may stomp on the op register (for op objects) +# or the sv register (for SV objects) +# +sub B::OBJECT::objix { + my $obj = shift; + my $ix = $symtable{$$obj}; + if (defined($ix)) { + return $ix; + } else { + $obj->newix($nextix); + return $symtable{$$obj} = $nextix++; + } +} + +sub B::SV::newix { + my ($sv, $ix) = @_; + printf "newsv %d\t# %s\n", $sv->FLAGS & 0xf, class($sv); + stsv($ix); +} + +sub B::GV::newix { + my ($gv, $ix) = @_; + my $gvname = $gv->NAME; + my $name = cstring($gv->STASH->NAME . "::" . $gvname); + print "gv_fetchpv $name\n"; + stsv($ix); +} + +sub B::HV::newix { + my ($hv, $ix) = @_; + my $name = $hv->NAME; + if ($name) { + # It's a stash + printf "gv_stashpv %s\n", cstring($name); + stsv($ix); + } else { + # It's an ordinary HV. Fall back to ordinary newix method + $hv->B::SV::newix($ix); + } +} + +sub B::SPECIAL::newix { + my ($sv, $ix) = @_; + # Special case. $$sv is not the address of the SV but an + # index into svspecialsv_list. + printf "ldspecsv $$sv\t# %s\n", $specialsv_name[$$sv]; + stsv($ix); +} + +sub B::OP::newix { + my ($op, $ix) = @_; + my $class = class($op); + my $typenum = $optype_enum{$class}; + croak "OP::newix: can't understand class $class" unless defined($typenum); + print "newop $typenum\t# $class\n"; + stop($ix); +} + +sub B::OP::walkoptree_debug { + my $op = shift; + warn(sprintf("walkoptree: %s\n", peekop($op))); +} + +sub B::OP::bytecode { + my $op = shift; + my $next = $op->next; + my $nextix; + my $sibix = $op->sibling->objix; + my $ix = $op->objix; + my $type = $op->type; + + if ($bypass_nullops) { + $next = $next->next while $$next && $next->type == 0; + } + $nextix = $next->objix; + + printf "# %s\n", peekop($op) if $debug_bc; + ldop($ix); + print "op_next $nextix\n"; + print "op_sibling $sibix\n" unless $strip_syntree; + printf "op_type %s\t# %d\n", $op->ppaddr, $type; + printf("op_seq %d\n", $op->seq) unless $omit_seq; + if ($type || !$compress_nullops) { + printf "op_targ %d\nop_flags 0x%x\nop_private 0x%x\n", + $op->targ, $op->flags, $op->private; + } +} + +sub B::UNOP::bytecode { + my $op = shift; + my $firstix = $op->first->objix; + $op->B::OP::bytecode; + if (($op->type || !$compress_nullops) && !$strip_syntree) { + print "op_first $firstix\n"; + } +} + +sub B::LOGOP::bytecode { + my $op = shift; + my $otherix = $op->other->objix; + $op->B::UNOP::bytecode; + print "op_other $otherix\n"; +} + +sub B::SVOP::bytecode { + my $op = shift; + my $sv = $op->sv; + my $svix = $sv->objix; + $op->B::OP::bytecode; + print "op_sv $svix\n"; + $sv->bytecode; +} + +sub B::GVOP::bytecode { + my $op = shift; + my $gv = $op->gv; + my $gvix = $gv->objix; + $op->B::OP::bytecode; + print "op_gv $gvix\n"; + $gv->bytecode; +} + +sub B::PVOP::bytecode { + my $op = shift; + my $pv = $op->pv; + $op->B::OP::bytecode; + # + # This would be easy except that OP_TRANS uses a PVOP to store an + # endian-dependent array of 256 shorts instead of a plain string. + # + if ($op->ppaddr eq "pp_trans") { + my @shorts = unpack("s256", $pv); # assembler handles endianness + print "op_pv_tr ", join(",", @shorts), "\n"; + } else { + printf "newpv %s\nop_pv\n", pvstring($pv); + } +} + +sub B::BINOP::bytecode { + my $op = shift; + my $lastix = $op->last->objix; + $op->B::UNOP::bytecode; + if (($op->type || !$compress_nullops) && !$strip_syntree) { + print "op_last $lastix\n"; + } +} + +sub B::CONDOP::bytecode { + my $op = shift; + my $trueix = $op->true->objix; + my $falseix = $op->false->objix; + $op->B::UNOP::bytecode; + print "op_true $trueix\nop_false $falseix\n"; +} + +sub B::LISTOP::bytecode { + my $op = shift; + my $children = $op->children; + $op->B::BINOP::bytecode; + if (($op->type || !$compress_nullops) && !$strip_syntree) { + print "op_children $children\n"; + } +} + +sub B::LOOP::bytecode { + my $op = shift; + my $redoopix = $op->redoop->objix; + my $nextopix = $op->nextop->objix; + my $lastopix = $op->lastop->objix; + $op->B::LISTOP::bytecode; + print "op_redoop $redoopix\nop_nextop $nextopix\nop_lastop $lastopix\n"; +} + +sub B::COP::bytecode { + my $op = shift; + my $stash = $op->stash; + my $stashix = $stash->objix; + my $filegv = $op->filegv; + my $filegvix = $filegv->objix; + my $line = $op->line; + if ($debug_bc) { + printf "# line %s:%d\n", $filegv->SV->PV, $line; + } + $op->B::OP::bytecode; + printf <<"EOT", pvstring($op->label), $op->cop_seq, $op->arybase; +newpv %s +cop_label +cop_stash $stashix +cop_seq %d +cop_filegv $filegvix +cop_arybase %d +cop_line $line +EOT + $filegv->bytecode; + $stash->bytecode; +} + +sub B::PMOP::bytecode { + my $op = shift; + my $replroot = $op->pmreplroot; + my $replrootix = $replroot->objix; + my $replstartix = $op->pmreplstart->objix; + my $ppaddr = $op->ppaddr; + # pmnext is corrupt in some PMOPs (see misc.t for example) + #my $pmnextix = $op->pmnext->objix; + + if ($$replroot) { + # OP_PUSHRE (a mutated version of OP_MATCH for the regexp + # argument to a split) stores a GV in op_pmreplroot instead + # of a substitution syntax tree. We don't want to walk that... + if ($ppaddr eq "pp_pushre") { + $replroot->bytecode; + } else { + walkoptree($replroot, "bytecode"); + } + } + $op->B::LISTOP::bytecode; + if ($ppaddr eq "pp_pushre") { + printf "op_pmreplrootgv $replrootix\n"; + } else { + print "op_pmreplroot $replrootix\nop_pmreplstart $replstartix\n"; + } + my $re = pvstring($op->precomp); + # op_pmnext omitted since a perl bug means it's sometime corrupt + printf <<"EOT", $op->pmflags, $op->pmpermflags; +op_pmflags 0x%x +op_pmpermflags 0x%x +newpv $re +pregcomp +EOT +} + +sub B::SV::bytecode { + my $sv = shift; + return if saved($sv); + my $ix = $sv->objix; + my $refcnt = $sv->REFCNT; + my $flags = sprintf("0x%x", $sv->FLAGS); + ldsv($ix); + print "sv_refcnt $refcnt\nsv_flags $flags\n"; + mark_saved($sv); +} + +sub B::PV::bytecode { + my $sv = shift; + return if saved($sv); + $sv->B::SV::bytecode; + printf("newpv %s\nxpv\n", pvstring($sv->PV)) if $sv->FLAGS & POK; +} + +sub B::IV::bytecode { + my $sv = shift; + return if saved($sv); + my $iv = $sv->IVX; + $sv->B::SV::bytecode; + printf "%s $iv\n", $sv->needs64bits ? "xiv64" : "xiv32"; +} + +sub B::NV::bytecode { + my $sv = shift; + return if saved($sv); + $sv->B::SV::bytecode; + printf "xnv %s\n", $sv->NVX; +} + +sub B::RV::bytecode { + my $sv = shift; + return if saved($sv); + my $rv = $sv->RV; + my $rvix = $rv->objix; + $rv->bytecode; + $sv->B::SV::bytecode; + print "xrv $rvix\n"; +} + +sub B::PVIV::bytecode { + my $sv = shift; + return if saved($sv); + my $iv = $sv->IVX; + $sv->B::PV::bytecode; + printf "%s $iv\n", $sv->needs64bits ? "xiv64" : "xiv32"; +} + +sub B::PVNV::bytecode { + my ($sv, $flag) = @_; + # The $flag argument is passed through PVMG::bytecode by BM::bytecode + # and AV::bytecode and indicates special handling. $flag = 1 is used by + # BM::bytecode and means that we should ensure we save the whole B-M + # table. It consists of 257 bytes (256 char array plus a final \0) + # which follow the ordinary PV+\0 and the 257 bytes are *not* reflected + # in SvCUR. $flag = 2 is used by AV::bytecode and means that we only + # call SV::bytecode instead of saving PV and calling NV::bytecode since + # PV/NV/IV stuff is different for AVs. + return if saved($sv); + if ($flag == 2) { + $sv->B::SV::bytecode; + } else { + my $pv = $sv->PV; + $sv->B::IV::bytecode; + printf "xnv %s\n", $sv->NVX; + if ($flag == 1) { + $pv .= "\0" . $sv->TABLE; + printf "newpv %s\npv_cur %d\nxpv\n", pvstring($pv),length($pv)-257; + } else { + printf("newpv %s\nxpv\n", pvstring($pv)) if $sv->FLAGS & POK; + } + } +} + +sub B::PVMG::bytecode { + my ($sv, $flag) = @_; + # See B::PVNV::bytecode for an explanation of $flag. + return if saved($sv); + # XXX We assume SvSTASH is already saved and don't save it later ourselves + my $stashix = $sv->SvSTASH->objix; + my @mgchain = $sv->MAGIC; + my (@mgobjix, $mg); + # + # We need to traverse the magic chain and get objix for each OBJ + # field *before* we do B::PVNV::bytecode since objix overwrites + # the sv register. However, we need to write the magic-saving + # bytecode *after* B::PVNV::bytecode since sv isn't initialised + # to refer to $sv until then. + # + @mgobjix = map($_->OBJ->objix, @mgchain); + $sv->B::PVNV::bytecode($flag); + print "xmg_stash $stashix\n"; + foreach $mg (@mgchain) { + printf "sv_magic %s\nmg_obj %d\nnewpv %s\nmg_pv\n", + cstring($mg->TYPE), shift(@mgobjix), pvstring($mg->PTR); + } +} + +sub B::PVLV::bytecode { + my $sv = shift; + return if saved($sv); + $sv->B::PVMG::bytecode; + printf <<'EOT', $sv->TARGOFF, $sv->TARGLEN, cstring($sv->TYPE); +xlv_targoff %d +xlv_targlen %d +xlv_type %s +EOT +} + +sub B::BM::bytecode { + my $sv = shift; + return if saved($sv); + # See PVNV::bytecode for an explanation of what the argument does + $sv->B::PVMG::bytecode(1); + printf "xbm_useful %d\nxbm_previous %d\nxbm_rare %d\n", + $sv->USEFUL, $sv->PREVIOUS, $sv->RARE; +} + +sub B::GV::bytecode { + my $gv = shift; + return if saved($gv); + my $ix = $gv->objix; + mark_saved($gv); + my $gvname = $gv->NAME; + my $name = cstring($gv->STASH->NAME . "::" . $gvname); + my $egv = $gv->EGV; + my $egvix = $egv->objix; + ldsv($ix); + printf <<"EOT", $gv->FLAGS, $gv->GvFLAGS, $gv->LINE; +sv_flags 0x%x +xgv_flags 0x%x +gp_line %d +EOT + my $refcnt = $gv->REFCNT; + printf("sv_refcnt_add %d\n", $refcnt - 1) if $refcnt > 1; + my $gvrefcnt = $gv->GvREFCNT; + printf("gp_refcnt_add %d\n", $gvrefcnt - 1) if $gvrefcnt > 1; + if ($gvrefcnt > 1 && $ix != $egvix) { + print "gp_share $egvix\n"; + } else { + if ($gvname !~ /^([^A-Za-z]|STDIN|STDOUT|STDERR|ARGV|SIG|ENV)$/) { + my $i; + my @subfield_names = qw(SV AV HV CV FILEGV FORM IO); + my @subfields = map($gv->$_(), @subfield_names); + my @ixes = map($_->objix, @subfields); + # Reset sv register for $gv + ldsv($ix); + for ($i = 0; $i < @ixes; $i++) { + printf "gp_%s %d\n", lc($subfield_names[$i]), $ixes[$i]; + } + # Now save all the subfields + my $sv; + foreach $sv (@subfields) { + $sv->bytecode; + } + } + } +} + +sub B::HV::bytecode { + my $hv = shift; + return if saved($hv); + mark_saved($hv); + my $name = $hv->NAME; + my $ix = $hv->objix; + if (!$name) { + # It's an ordinary HV. Stashes have NAME set and need no further + # saving beyond the gv_stashpv that $hv->objix already ensures. + my @contents = $hv->ARRAY; + my ($i, @ixes); + for ($i = 1; $i < @contents; $i += 2) { + push(@ixes, $contents[$i]->objix); + } + for ($i = 1; $i < @contents; $i += 2) { + $contents[$i]->bytecode; + } + ldsv($ix); + for ($i = 0; $i < @contents; $i += 2) { + printf("newpv %s\nhv_store %d\n", + pvstring($contents[$i]), $ixes[$i / 2]); + } + printf "sv_refcnt %d\nsv_flags 0x%x\n", $hv->REFCNT, $hv->FLAGS; + } +} + +sub B::AV::bytecode { + my $av = shift; + return if saved($av); + my $ix = $av->objix; + my $fill = $av->FILL; + my $max = $av->MAX; + my (@array, @ixes); + if ($fill > -1) { + @array = $av->ARRAY; + @ixes = map($_->objix, @array); + my $sv; + foreach $sv (@array) { + $sv->bytecode; + } + } + # See PVNV::bytecode for the meaning of the flag argument of 2. + $av->B::PVMG::bytecode(2); + # Recover sv register and set AvMAX and AvFILL to -1 (since we + # create an AV with NEWSV and SvUPGRADE rather than doing newAV + # which is what sets AvMAX and AvFILL. + ldsv($ix); + printf "xav_flags 0x%x\nxav_max -1\nxav_fill -1\n", $av->AvFLAGS; + if ($fill > -1) { + my $elix; + foreach $elix (@ixes) { + print "av_push $elix\n"; + } + } else { + if ($max > -1) { + print "av_extend $max\n"; + } + } +} + +sub B::CV::bytecode { + my $cv = shift; + return if saved($cv); + my $ix = $cv->objix; + $cv->B::PVMG::bytecode; + my $i; + my @subfield_names = qw(ROOT START STASH GV FILEGV PADLIST OUTSIDE); + my @subfields = map($cv->$_(), @subfield_names); + my @ixes = map($_->objix, @subfields); + # Save OP tree from CvROOT (first element of @subfields) + my $root = shift @subfields; + if ($$root) { + walkoptree($root, "bytecode"); + } + # Reset sv register for $cv (since above ->objix calls stomped on it) + ldsv($ix); + for ($i = 0; $i < @ixes; $i++) { + printf "xcv_%s %d\n", lc($subfield_names[$i]), $ixes[$i]; + } + printf "xcv_depth %d\nxcv_flags 0x%x\n", $cv->DEPTH, $cv->FLAGS; + # Now save all the subfields (except for CvROOT which was handled + # above) and CvSTART (now the initial element of @subfields). + shift @subfields; # bye-bye CvSTART + my $sv; + foreach $sv (@subfields) { + $sv->bytecode; + } +} + +sub B::IO::bytecode { + my $io = shift; + return if saved($io); + my $ix = $io->objix; + my $top_gv = $io->TOP_GV; + my $top_gvix = $top_gv->objix; + my $fmt_gv = $io->FMT_GV; + my $fmt_gvix = $fmt_gv->objix; + my $bottom_gv = $io->BOTTOM_GV; + my $bottom_gvix = $bottom_gv->objix; + + $io->B::PVMG::bytecode; + ldsv($ix); + print "xio_top_gv $top_gvix\n"; + print "xio_fmt_gv $fmt_gvix\n"; + print "xio_bottom_gv $bottom_gvix\n"; + my $field; + foreach $field (qw(TOP_NAME FMT_NAME BOTTOM_NAME)) { + printf "newpv %s\nxio_%s\n", pvstring($io->$field()), lc($field); + } + foreach $field (qw(LINES PAGE PAGE_LEN LINES_LEFT SUBPROCESS)) { + printf "xio_%s %d\n", lc($field), $io->$field(); + } + printf "xio_type %s\nxio_flags 0x%x\n", cstring($io->IoTYPE), $io->IoFLAGS; + $top_gv->bytecode; + $fmt_gv->bytecode; + $bottom_gv->bytecode; +} + +sub B::SPECIAL::bytecode { + # nothing extra needs doing +} + +sub bytecompile_object { + my $sv; + foreach $sv (@_) { + svref_2object($sv)->bytecode; + } +} + +sub B::GV::bytecodecv { + my $gv = shift; + my $cv = $gv->CV; + if ($$cv && !saved($cv)) { + if ($debug_cv) { + warn sprintf("saving extra CV &%s::%s (0x%x) from GV 0x%x\n", + $gv->STASH->NAME, $gv->NAME, $$cv, $$gv); + } + $gv->bytecode; + } +} + +sub bytecompile_main { + my $curpad = (comppadlist->ARRAY)[1]; + my $curpadix = $curpad->objix; + $curpad->bytecode; + walkoptree(main_root, "bytecode"); + warn "done main program, now walking symbol table\n" if $debug_bc; + my ($pack, %exclude); + foreach $pack (qw(B O AutoLoader DynaLoader Config DB VMS strict vars + FileHandle Exporter Carp UNIVERSAL IO Fcntl Symbol + SelectSaver blib Cwd)) + { + $exclude{$pack."::"} = 1; + } + no strict qw(vars refs); + walksymtable(\%{"main::"}, "bytecodecv", sub { + warn "considering $_[0]\n" if $debug_bc; + return !defined($exclude{$_[0]}); + }); + if (!$module_only) { + printf "main_root %d\n", main_root->objix; + printf "main_start %d\n", main_start->objix; + printf "curpad $curpadix\n"; + # XXX Do min_intro_pending and max_intro_pending matter? + } +} + +sub prepare_assemble { + my $newfh = IO::File->new_tmpfile; + select($newfh); + binmode $newfh; + return $newfh; +} + +sub do_assemble { + my $fh = shift; + seek($fh, 0, 0); # rewind the temporary file + assemble_fh($fh, sub { print OUT @_ }); +} + +sub compile { + my @options = @_; + my ($option, $opt, $arg); + open(OUT, ">&STDOUT"); + binmode OUT; + select(OUT); + OPTION: + while ($option = shift @options) { + if ($option =~ /^-(.)(.*)/) { + $opt = $1; + $arg = $2; + } else { + unshift @options, $option; + last OPTION; + } + if ($opt eq "-" && $arg eq "-") { + shift @options; + last OPTION; + } elsif ($opt eq "o") { + $arg ||= shift @options; + open(OUT, ">$arg") or return "$arg: $!\n"; + binmode OUT; + } elsif ($opt eq "D") { + $arg ||= shift @options; + foreach $arg (split(//, $arg)) { + if ($arg eq "b") { + $| = 1; + debug(1); + } elsif ($arg eq "o") { + B->debug(1); + } elsif ($arg eq "a") { + B::Assembler::debug(1); + } elsif ($arg eq "C") { + $debug_cv = 1; + } + } + } elsif ($opt eq "v") { + $verbose = 1; + } elsif ($opt eq "m") { + $module_only = 1; + } elsif ($opt eq "S") { + $no_assemble = 1; + } elsif ($opt eq "f") { + $arg ||= shift @options; + my $value = $arg !~ s/^no-//; + $arg =~ s/-/_/g; + my $ref = $optimise{$arg}; + if (defined($ref)) { + $$ref = $value; + } else { + warn qq(ignoring unknown optimisation option "$arg"\n); + } + } elsif ($opt eq "O") { + $arg = 1 if $arg eq ""; + my $ref; + foreach $ref (values %optimise) { + $$ref = 0; + } + if ($arg >= 6) { + $strip_syntree = 1; + } + if ($arg >= 2) { + $bypass_nullops = 1; + } + if ($arg >= 1) { + $compress_nullops = 1; + $omit_seq = 1; + } + } + } + if (@options) { + return sub { + my $objname; + my $newfh; + $newfh = prepare_assemble() unless $no_assemble; + foreach $objname (@options) { + eval "bytecompile_object(\\$objname)"; + } + do_assemble($newfh) unless $no_assemble; + } + } else { + return sub { + my $newfh; + $newfh = prepare_assemble() unless $no_assemble; + bytecompile_main(); + do_assemble($newfh) unless $no_assemble; + } + } +} + +1; + +__END__ + +=head1 NAME + +B::Bytecode - Perl compiler's bytecode backend + +=head1 SYNOPSIS + + perl -MO=Bytecode[,OPTIONS] foo.pl + +=head1 DESCRIPTION + +This compiler backend takes Perl source and generates a +platform-independent bytecode encapsulating code to load the +internal structures perl uses to run your program. When the +generated bytecode is loaded in, your program is ready to run, +reducing the time which perl would have taken to load and parse +your program into its internal semi-compiled form. That means that +compiling with this backend will not help improve the runtime +execution speed of your program but may improve the start-up time. +Depending on the environment in which your program runs this may +or may not be a help. + +The resulting bytecode can be run with a special byteperl executable +or (for non-main programs) be loaded via the C<byteload_fh> function +in the F<B> module. + +=head1 OPTIONS + +If there are any non-option arguments, they are taken to be names of +objects to be saved (probably doesn't work properly yet). Without +extra arguments, it saves the main program. + +=over 4 + +=item B<-ofilename> + +Output to filename instead of STDOUT. + +=item B<--> + +Force end of options. + +=item B<-f> + +Force optimisations on or off one at a time. Each can be preceded +by B<no-> to turn the option off (e.g. B<-fno-compress-nullops>). + +=item B<-fcompress-nullops> + +Only fills in the necessary fields of ops which have +been optimised away by perl's internal compiler. + +=item B<-fomit-sequence-numbers> + +Leaves out code to fill in the op_seq field of all ops +which is only used by perl's internal compiler. + +=item B<-fbypass-nullops> + +If op->op_next ever points to a NULLOP, replaces the op_next field +with the first non-NULLOP in the path of execution. + +=item B<-fstrip-syntax-tree> + +Leaves out code to fill in the pointers which link the internal syntax +tree together. They're not needed at run-time but leaving them out +will make it impossible to recompile or disassemble the resulting +program. It will also stop C<goto label> statements from working. + +=item B<-On> + +Optimisation level (n = 0, 1, 2, ...). B<-O> means B<-O1>. +B<-O1> sets B<-fcompress-nullops> B<-fomit-sequence numbers>. +B<-O6> adds B<-fstrip-syntax-tree>. + +=item B<-D> + +Debug options (concatenated or separate flags like C<perl -D>). + +=item B<-Do> + +Prints each OP as it's processed. + +=item B<-Db> + +Print debugging information about bytecompiler progress. + +=item B<-Da> + +Tells the (bytecode) assembler to include source assembler lines +in its output as bytecode comments. + +=item B<-DC> + +Prints each CV taken from the final symbol tree walk. + +=item B<-S> + +Output (bytecode) assembler source rather than piping it +through the assembler and outputting bytecode. + +=item B<-m> + +Compile as a module rather than a standalone program. Currently this +just means that the bytecodes for initialising C<main_start>, +C<main_root> and C<curpad> are omitted. + +=back + +=head1 EXAMPLES + + perl -MO=Bytecode,-O6,-o,foo.plc foo.pl + + perl -MO=Bytecode,-S foo.pl > foo.S + assemble foo.S > foo.plc + byteperl foo.plc + + perl -MO=Bytecode,-m,-oFoo.pmc Foo.pm + +=head1 BUGS + +Plenty. Current status: experimental. + +=head1 AUTHOR + +Malcolm Beattie, C<mbeattie@sable.ox.ac.uk> + +=cut diff --git a/gnu/usr.bin/perl/ext/B/B/C.pm b/gnu/usr.bin/perl/ext/B/B/C.pm new file mode 100644 index 00000000000..e695cc28767 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/B/C.pm @@ -0,0 +1,1378 @@ +# C.pm +# +# Copyright (c) 1996, 1997, 1998 Malcolm Beattie +# +# You may distribute under the terms of either the GNU General Public +# License or the Artistic License, as specified in the README file. +# +package B::C; +use Exporter (); +@ISA = qw(Exporter); +@EXPORT_OK = qw(output_all output_boilerplate output_main + init_sections set_callback save_unused_subs objsym); + +use B qw(minus_c sv_undef walkoptree walksymtable main_root main_start peekop + class cstring cchar svref_2object compile_stats comppadlist hash + threadsv_names main_cv init_av); +use B::Asmdata qw(@specialsv_name); + +use FileHandle; +use Carp; +use strict; + +my $hv_index = 0; +my $gv_index = 0; +my $re_index = 0; +my $pv_index = 0; +my $anonsub_index = 0; + +my %symtable; +my $warn_undefined_syms; +my $verbose; +my @unused_sub_packages; +my $nullop_count; +my $pv_copy_on_grow; +my ($debug_cops, $debug_av, $debug_cv, $debug_mg); + +my @threadsv_names; +BEGIN { + @threadsv_names = threadsv_names(); +} + +# Code sections +my ($init, $decl, $symsect, $binopsect, $condopsect, $copsect, $cvopsect, + $gvopsect, $listopsect, $logopsect, $loopsect, $opsect, $pmopsect, + $pvopsect, $svopsect, $unopsect, $svsect, $xpvsect, $xpvavsect, + $xpvhvsect, $xpvcvsect, $xpvivsect, $xpvnvsect, $xpvmgsect, $xpvlvsect, + $xrvsect, $xpvbmsect, $xpviosect, $bootstrap); + +sub walk_and_save_optree; +my $saveoptree_callback = \&walk_and_save_optree; +sub set_callback { $saveoptree_callback = shift } +sub saveoptree { &$saveoptree_callback(@_) } + +sub walk_and_save_optree { + my ($name, $root, $start) = @_; + walkoptree($root, "save"); + return objsym($start); +} + +# Current workaround/fix for op_free() trying to free statically +# defined OPs is to set op_seq = -1 and check for that in op_free(). +# Instead of hardwiring -1 in place of $op->seq, we use $op_seq +# so that it can be changed back easily if necessary. In fact, to +# stop compilers from moaning about a U16 being initialised with an +# uncast -1 (the printf format is %d so we can't tweak it), we have +# to "know" that op_seq is a U16 and use 65535. Ugh. +my $op_seq = 65535; + +sub AVf_REAL () { 1 } + +# XXX This shouldn't really be hardcoded here but it saves +# looking up the name of every BASEOP in B::OP +sub OP_THREADSV () { 345 } + +sub savesym { + my ($obj, $value) = @_; + my $sym = sprintf("s\\_%x", $$obj); + $symtable{$sym} = $value; +} + +sub objsym { + my $obj = shift; + return $symtable{sprintf("s\\_%x", $$obj)}; +} + +sub getsym { + my $sym = shift; + my $value; + + return 0 if $sym eq "sym_0"; # special case + $value = $symtable{$sym}; + if (defined($value)) { + return $value; + } else { + warn "warning: undefined symbol $sym\n" if $warn_undefined_syms; + return "UNUSED"; + } +} + +sub savepv { + my $pv = shift; + my $pvsym = 0; + my $pvmax = 0; + if ($pv_copy_on_grow) { + my $cstring = cstring($pv); + if ($cstring ne "0") { # sic + $pvsym = sprintf("pv%d", $pv_index++); + $decl->add(sprintf("static char %s[] = %s;", $pvsym, $cstring)); + } + } else { + $pvmax = length($pv) + 1; + } + return ($pvsym, $pvmax); +} + +sub B::OP::save { + my ($op, $level) = @_; + my $type = $op->type; + $nullop_count++ unless $type; + if ($type == OP_THREADSV) { + # saves looking up ppaddr but it's a bit naughty to hard code this + $init->add(sprintf("(void)find_threadsv(%s);", + cstring($threadsv_names[$op->targ]))); + } + $opsect->add(sprintf("s\\_%x, s\\_%x, %s, %u, %u, %u, 0x%x, 0x%x", + ${$op->next}, ${$op->sibling}, $op->ppaddr, $op->targ, + $type, $op_seq, $op->flags, $op->private)); + savesym($op, sprintf("&op_list[%d]", $opsect->index)); +} + +sub B::FAKEOP::new { + my ($class, %objdata) = @_; + bless \%objdata, $class; +} + +sub B::FAKEOP::save { + my ($op, $level) = @_; + $opsect->add(sprintf("%s, %s, %s, %u, %u, %u, 0x%x, 0x%x", + $op->next, $op->sibling, $op->ppaddr, $op->targ, + $op->type, $op_seq, $op->flags, $op->private)); + return sprintf("&op_list[%d]", $opsect->index); +} + +sub B::FAKEOP::next { $_[0]->{"next"} || 0 } +sub B::FAKEOP::type { $_[0]->{type} || 0} +sub B::FAKEOP::sibling { $_[0]->{sibling} || 0 } +sub B::FAKEOP::ppaddr { $_[0]->{ppaddr} || 0 } +sub B::FAKEOP::targ { $_[0]->{targ} || 0 } +sub B::FAKEOP::flags { $_[0]->{flags} || 0 } +sub B::FAKEOP::private { $_[0]->{private} || 0 } + +sub B::UNOP::save { + my ($op, $level) = @_; + $unopsect->add(sprintf("s\\_%x, s\\_%x, %s, %u, %u, %u, 0x%x, 0x%x, s\\_%x", + ${$op->next}, ${$op->sibling}, $op->ppaddr, + $op->targ, $op->type, $op_seq, $op->flags, + $op->private, ${$op->first})); + savesym($op, sprintf("(OP*)&unop_list[%d]", $unopsect->index)); +} + +sub B::BINOP::save { + my ($op, $level) = @_; + $binopsect->add(sprintf("s\\_%x, s\\_%x, %s, %u, %u, %u, 0x%x, 0x%x, s\\_%x, s\\_%x", + ${$op->next}, ${$op->sibling}, $op->ppaddr, + $op->targ, $op->type, $op_seq, $op->flags, + $op->private, ${$op->first}, ${$op->last})); + savesym($op, sprintf("(OP*)&binop_list[%d]", $binopsect->index)); +} + +sub B::LISTOP::save { + my ($op, $level) = @_; + $listopsect->add(sprintf("s\\_%x, s\\_%x, %s, %u, %u, %u, 0x%x, 0x%x, s\\_%x, s\\_%x, %u", + ${$op->next}, ${$op->sibling}, $op->ppaddr, + $op->targ, $op->type, $op_seq, $op->flags, + $op->private, ${$op->first}, ${$op->last}, + $op->children)); + savesym($op, sprintf("(OP*)&listop_list[%d]", $listopsect->index)); +} + +sub B::LOGOP::save { + my ($op, $level) = @_; + $logopsect->add(sprintf("s\\_%x, s\\_%x, %s, %u, %u, %u, 0x%x, 0x%x, s\\_%x, s\\_%x", + ${$op->next}, ${$op->sibling}, $op->ppaddr, + $op->targ, $op->type, $op_seq, $op->flags, + $op->private, ${$op->first}, ${$op->other})); + savesym($op, sprintf("(OP*)&logop_list[%d]", $logopsect->index)); +} + +sub B::CONDOP::save { + my ($op, $level) = @_; + $condopsect->add(sprintf("s\\_%x, s\\_%x, %s, %u, %u, %u, 0x%x, 0x%x, s\\_%x, s\\_%x, s\\_%x", + ${$op->next}, ${$op->sibling}, $op->ppaddr, + $op->targ, $op->type, $op_seq, $op->flags, + $op->private, ${$op->first}, ${$op->true}, + ${$op->false})); + savesym($op, sprintf("(OP*)&condop_list[%d]", $condopsect->index)); +} + +sub B::LOOP::save { + my ($op, $level) = @_; + #warn sprintf("LOOP: redoop %s, nextop %s, lastop %s\n", + # peekop($op->redoop), peekop($op->nextop), + # peekop($op->lastop)); # debug + $loopsect->add(sprintf("s\\_%x, s\\_%x, %s, %u, %u, %u, 0x%x, 0x%x, s\\_%x, s\\_%x, %u, s\\_%x, s\\_%x, s\\_%x", + ${$op->next}, ${$op->sibling}, $op->ppaddr, + $op->targ, $op->type, $op_seq, $op->flags, + $op->private, ${$op->first}, ${$op->last}, + $op->children, ${$op->redoop}, ${$op->nextop}, + ${$op->lastop})); + savesym($op, sprintf("(OP*)&loop_list[%d]", $loopsect->index)); +} + +sub B::PVOP::save { + my ($op, $level) = @_; + $pvopsect->add(sprintf("s\\_%x, s\\_%x, %s, %u, %u, %u, 0x%x, 0x%x, %s", + ${$op->next}, ${$op->sibling}, $op->ppaddr, + $op->targ, $op->type, $op_seq, $op->flags, + $op->private, cstring($op->pv))); + savesym($op, sprintf("(OP*)&pvop_list[%d]", $pvopsect->index)); +} + +sub B::SVOP::save { + my ($op, $level) = @_; + my $svsym = $op->sv->save; + $svopsect->add(sprintf("s\\_%x, s\\_%x, %s, %u, %u, %u, 0x%x, 0x%x, %s", + ${$op->next}, ${$op->sibling}, $op->ppaddr, + $op->targ, $op->type, $op_seq, $op->flags, + $op->private, "(SV*)$svsym")); + savesym($op, sprintf("(OP*)&svop_list[%d]", $svopsect->index)); +} + +sub B::GVOP::save { + my ($op, $level) = @_; + my $gvsym = $op->gv->save; + $gvopsect->add(sprintf("s\\_%x, s\\_%x, %s, %u, %u, %u, 0x%x, 0x%x, Nullgv", + ${$op->next}, ${$op->sibling}, $op->ppaddr, + $op->targ, $op->type, $op_seq, $op->flags, + $op->private)); + $init->add(sprintf("gvop_list[%d].op_gv = %s;", $gvopsect->index, $gvsym)); + savesym($op, sprintf("(OP*)&gvop_list[%d]", $gvopsect->index)); +} + +sub B::COP::save { + my ($op, $level) = @_; + my $gvsym = $op->filegv->save; + my $stashsym = $op->stash->save; + warn sprintf("COP: line %d file %s\n", $op->line, $op->filegv->SV->PV) + if $debug_cops; + $copsect->add(sprintf("s\\_%x, s\\_%x, %s, %u, %u, %u, 0x%x, 0x%x, %s, Nullhv, Nullgv, %u, %d, %u", + ${$op->next}, ${$op->sibling}, $op->ppaddr, + $op->targ, $op->type, $op_seq, $op->flags, + $op->private, cstring($op->label), $op->cop_seq, + $op->arybase, $op->line)); + my $copix = $copsect->index; + $init->add(sprintf("cop_list[%d].cop_filegv = %s;", $copix, $gvsym), + sprintf("cop_list[%d].cop_stash = %s;", $copix, $stashsym)); + savesym($op, "(OP*)&cop_list[$copix]"); +} + +sub B::PMOP::save { + my ($op, $level) = @_; + my $replroot = $op->pmreplroot; + my $replstart = $op->pmreplstart; + my $replrootfield = sprintf("s\\_%x", $$replroot); + my $replstartfield = sprintf("s\\_%x", $$replstart); + my $gvsym; + my $ppaddr = $op->ppaddr; + if ($$replroot) { + # OP_PUSHRE (a mutated version of OP_MATCH for the regexp + # argument to a split) stores a GV in op_pmreplroot instead + # of a substitution syntax tree. We don't want to walk that... + if ($ppaddr eq "pp_pushre") { + $gvsym = $replroot->save; +# warn "PMOP::save saving a pp_pushre with GV $gvsym\n"; # debug + $replrootfield = 0; + } else { + $replstartfield = saveoptree("*ignore*", $replroot, $replstart); + } + } + # pmnext handling is broken in perl itself, I think. Bad op_pmnext + # fields aren't noticed in perl's runtime (unless you try reset) but we + # segfault when trying to dereference it to find op->op_pmnext->op_type + $pmopsect->add(sprintf("s\\_%x, s\\_%x, %s, %u, %u, %u, 0x%x, 0x%x, s\\_%x, s\\_%x, %u, %s, %s, 0, 0, 0x%x, 0x%x", + ${$op->next}, ${$op->sibling}, $ppaddr, $op->targ, + $op->type, $op_seq, $op->flags, $op->private, + ${$op->first}, ${$op->last}, $op->children, + $replrootfield, $replstartfield, + $op->pmflags, $op->pmpermflags,)); + my $pm = sprintf("pmop_list[%d]", $pmopsect->index); + my $re = $op->precomp; + if (defined($re)) { + my $resym = sprintf("re%d", $re_index++); + $decl->add(sprintf("static char *$resym = %s;", cstring($re))); + $init->add(sprintf("$pm.op_pmregexp = pregcomp($resym, $resym + %u, &$pm);", + length($re))); + } + if ($gvsym) { + $init->add("$pm.op_pmreplroot = (OP*)$gvsym;"); + } + savesym($op, sprintf("(OP*)&pmop_list[%d]", $pmopsect->index)); +} + +sub B::SPECIAL::save { + my ($sv) = @_; + # special case: $$sv is not the address but an index into specialsv_list +# warn "SPECIAL::save specialsv $$sv\n"; # debug + my $sym = $specialsv_name[$$sv]; + if (!defined($sym)) { + confess "unknown specialsv index $$sv passed to B::SPECIAL::save"; + } + return $sym; +} + +sub B::OBJECT::save {} + +sub B::NULL::save { + my ($sv) = @_; + my $sym = objsym($sv); + return $sym if defined $sym; +# warn "Saving SVt_NULL SV\n"; # debug + # debug + #if ($$sv == 0) { + # warn "NULL::save for sv = 0 called from @{[(caller(1))[3]]}\n"; + #} + $svsect->add(sprintf("0, %u, 0x%x", $sv->REFCNT + 1, $sv->FLAGS)); + return savesym($sv, sprintf("&sv_list[%d]", $svsect->index)); +} + +sub B::IV::save { + my ($sv) = @_; + my $sym = objsym($sv); + return $sym if defined $sym; + $xpvivsect->add(sprintf("0, 0, 0, %d", $sv->IVX)); + $svsect->add(sprintf("&xpviv_list[%d], %lu, 0x%x", + $xpvivsect->index, $sv->REFCNT + 1, $sv->FLAGS)); + return savesym($sv, sprintf("&sv_list[%d]", $svsect->index)); +} + +sub B::NV::save { + my ($sv) = @_; + my $sym = objsym($sv); + return $sym if defined $sym; + $xpvnvsect->add(sprintf("0, 0, 0, %d, %s", $sv->IVX, $sv->NVX)); + $svsect->add(sprintf("&xpvnv_list[%d], %lu, 0x%x", + $xpvnvsect->index, $sv->REFCNT + 1, $sv->FLAGS)); + return savesym($sv, sprintf("&sv_list[%d]", $svsect->index)); +} + +sub B::PVLV::save { + my ($sv) = @_; + my $sym = objsym($sv); + return $sym if defined $sym; + my $pv = $sv->PV; + my $len = length($pv); + my ($pvsym, $pvmax) = savepv($pv); + my ($lvtarg, $lvtarg_sym); + $xpvlvsect->add(sprintf("%s, %u, %u, %d, %g, 0, 0, %u, %u, 0, %s", + $pvsym, $len, $pvmax, $sv->IVX, $sv->NVX, + $sv->TARGOFF, $sv->TARGLEN, cchar($sv->TYPE))); + $svsect->add(sprintf("&xpvlv_list[%d], %lu, 0x%x", + $xpvlvsect->index, $sv->REFCNT + 1, $sv->FLAGS)); + if (!$pv_copy_on_grow) { + $init->add(sprintf("xpvlv_list[%d].xpv_pv = savepvn(%s, %u);", + $xpvlvsect->index, cstring($pv), $len)); + } + $sv->save_magic; + return savesym($sv, sprintf("&sv_list[%d]", $svsect->index)); +} + +sub B::PVIV::save { + my ($sv) = @_; + my $sym = objsym($sv); + return $sym if defined $sym; + my $pv = $sv->PV; + my $len = length($pv); + my ($pvsym, $pvmax) = savepv($pv); + $xpvivsect->add(sprintf("%s, %u, %u, %d", $pvsym, $len, $pvmax, $sv->IVX)); + $svsect->add(sprintf("&xpviv_list[%d], %u, 0x%x", + $xpvivsect->index, $sv->REFCNT + 1, $sv->FLAGS)); + if (!$pv_copy_on_grow) { + $init->add(sprintf("xpviv_list[%d].xpv_pv = savepvn(%s, %u);", + $xpvivsect->index, cstring($pv), $len)); + } + return savesym($sv, sprintf("&sv_list[%d]", $svsect->index)); +} + +sub B::PVNV::save { + my ($sv) = @_; + my $sym = objsym($sv); + return $sym if defined $sym; + my $pv = $sv->PV; + my $len = length($pv); + my ($pvsym, $pvmax) = savepv($pv); + $xpvnvsect->add(sprintf("%s, %u, %u, %d, %s", + $pvsym, $len, $pvmax, $sv->IVX, $sv->NVX)); + $svsect->add(sprintf("&xpvnv_list[%d], %lu, 0x%x", + $xpvnvsect->index, $sv->REFCNT + 1, $sv->FLAGS)); + if (!$pv_copy_on_grow) { + $init->add(sprintf("xpvnv_list[%d].xpv_pv = savepvn(%s,%u);", + $xpvnvsect->index, cstring($pv), $len)); + } + return savesym($sv, sprintf("&sv_list[%d]", $svsect->index)); +} + +sub B::BM::save { + my ($sv) = @_; + my $sym = objsym($sv); + return $sym if defined $sym; + my $pv = $sv->PV . "\0" . $sv->TABLE; + my $len = length($pv); + $xpvbmsect->add(sprintf("0, %u, %u, %d, %s, 0, 0, %d, %u, 0x%x", + $len, $len + 258, $sv->IVX, $sv->NVX, + $sv->USEFUL, $sv->PREVIOUS, $sv->RARE)); + $svsect->add(sprintf("&xpvbm_list[%d], %lu, 0x%x", + $xpvbmsect->index, $sv->REFCNT + 1, $sv->FLAGS)); + $sv->save_magic; + $init->add(sprintf("xpvbm_list[%d].xpv_pv = savepvn(%s, %u);", + $xpvbmsect->index, cstring($pv), $len), + sprintf("xpvbm_list[%d].xpv_cur = %u;", + $xpvbmsect->index, $len - 257)); + return savesym($sv, sprintf("&sv_list[%d]", $svsect->index)); +} + +sub B::PV::save { + my ($sv) = @_; + my $sym = objsym($sv); + return $sym if defined $sym; + my $pv = $sv->PV; + my $len = length($pv); + my ($pvsym, $pvmax) = savepv($pv); + $xpvsect->add(sprintf("%s, %u, %u", $pvsym, $len, $pvmax)); + $svsect->add(sprintf("&xpv_list[%d], %lu, 0x%x", + $xpvsect->index, $sv->REFCNT + 1, $sv->FLAGS)); + if (!$pv_copy_on_grow) { + $init->add(sprintf("xpv_list[%d].xpv_pv = savepvn(%s, %u);", + $xpvsect->index, cstring($pv), $len)); + } + return savesym($sv, sprintf("&sv_list[%d]", $svsect->index)); +} + +sub B::PVMG::save { + my ($sv) = @_; + my $sym = objsym($sv); + return $sym if defined $sym; + my $pv = $sv->PV; + my $len = length($pv); + my ($pvsym, $pvmax) = savepv($pv); + $xpvmgsect->add(sprintf("%s, %u, %u, %d, %s, 0, 0", + $pvsym, $len, $pvmax, $sv->IVX, $sv->NVX)); + $svsect->add(sprintf("&xpvmg_list[%d], %lu, 0x%x", + $xpvmgsect->index, $sv->REFCNT + 1, $sv->FLAGS)); + if (!$pv_copy_on_grow) { + $init->add(sprintf("xpvmg_list[%d].xpv_pv = savepvn(%s, %u);", + $xpvmgsect->index, cstring($pv), $len)); + } + $sym = savesym($sv, sprintf("&sv_list[%d]", $svsect->index)); + $sv->save_magic; + return $sym; +} + +sub B::PVMG::save_magic { + my ($sv) = @_; + #warn sprintf("saving magic for %s (0x%x)\n", class($sv), $$sv); # debug + my $stash = $sv->SvSTASH; + if ($$stash) { + warn sprintf("xmg_stash = %s (0x%x)\n", $stash->NAME, $$stash) + if $debug_mg; + # XXX Hope stash is already going to be saved. + $init->add(sprintf("SvSTASH(s\\_%x) = s\\_%x;", $$sv, $$stash)); + } + my @mgchain = $sv->MAGIC; + my ($mg, $type, $obj, $ptr); + foreach $mg (@mgchain) { + $type = $mg->TYPE; + $obj = $mg->OBJ; + $ptr = $mg->PTR; + my $len = defined($ptr) ? length($ptr) : 0; + if ($debug_mg) { + warn sprintf("magic %s (0x%x), obj %s (0x%x), type %s, ptr %s\n", + class($sv), $$sv, class($obj), $$obj, + cchar($type), cstring($ptr)); + } + $init->add(sprintf("sv_magic((SV*)s\\_%x, (SV*)s\\_%x, %s, %s, %d);", + $$sv, $$obj, cchar($type),cstring($ptr),$len)); + } +} + +sub B::RV::save { + my ($sv) = @_; + my $sym = objsym($sv); + return $sym if defined $sym; + $xrvsect->add($sv->RV->save); + $svsect->add(sprintf("&xrv_list[%d], %lu, 0x%x", + $xrvsect->index, $sv->REFCNT + 1, $sv->FLAGS)); + return savesym($sv, sprintf("&sv_list[%d]", $svsect->index)); +} + +sub try_autoload { + my ($cvstashname, $cvname) = @_; + warn sprintf("No definition for sub %s::%s\n", $cvstashname, $cvname); + # Handle AutoLoader classes explicitly. Any more general AUTOLOAD + # use should be handled by the class itself. + no strict 'refs'; + my $isa = \@{"$cvstashname\::ISA"}; + if (grep($_ eq "AutoLoader", @$isa)) { + warn "Forcing immediate load of sub derived from AutoLoader\n"; + # Tweaked version of AutoLoader::AUTOLOAD + my $dir = $cvstashname; + $dir =~ s(::)(/)g; + eval { require "auto/$dir/$cvname.al" }; + if ($@) { + warn qq(failed require "auto/$dir/$cvname.al": $@\n); + return 0; + } else { + return 1; + } + } +} + +sub B::CV::save { + my ($cv) = @_; + my $sym = objsym($cv); + if (defined($sym)) { +# warn sprintf("CV 0x%x already saved as $sym\n", $$cv); # debug + return $sym; + } + # Reserve a place in svsect and xpvcvsect and record indices + my $sv_ix = $svsect->index + 1; + $svsect->add("svix$sv_ix"); + my $xpvcv_ix = $xpvcvsect->index + 1; + $xpvcvsect->add("xpvcvix$xpvcv_ix"); + # Save symbol now so that GvCV() doesn't recurse back to us via CvGV() + $sym = savesym($cv, "&sv_list[$sv_ix]"); + warn sprintf("saving CV 0x%x as $sym\n", $$cv) if $debug_cv; + my $gv = $cv->GV; + my $cvstashname = $gv->STASH->NAME; + my $cvname = $gv->NAME; + my $root = $cv->ROOT; + my $cvxsub = $cv->XSUB; + if (!$$root && !$cvxsub) { + if (try_autoload($cvstashname, $cvname)) { + # Recalculate root and xsub + $root = $cv->ROOT; + $cvxsub = $cv->XSUB; + if ($$root || $cvxsub) { + warn "Successful forced autoload\n"; + } + } + } + my $startfield = 0; + my $padlist = $cv->PADLIST; + my $pv = $cv->PV; + my $xsub = 0; + my $xsubany = "Nullany"; + if ($$root) { + warn sprintf("saving op tree for CV 0x%x, root = 0x%x\n", + $$cv, $$root) if $debug_cv; + my $ppname = ""; + if ($$gv) { + my $stashname = $gv->STASH->NAME; + my $gvname = $gv->NAME; + if ($gvname ne "__ANON__") { + $ppname = (${$gv->FORM} == $$cv) ? "pp_form_" : "pp_sub_"; + $ppname .= ($stashname eq "main") ? + $gvname : "$stashname\::$gvname"; + $ppname =~ s/::/__/g; + } + } + if (!$ppname) { + $ppname = "pp_anonsub_$anonsub_index"; + $anonsub_index++; + } + $startfield = saveoptree($ppname, $root, $cv->START, $padlist->ARRAY); + warn sprintf("done saving op tree for CV 0x%x, name %s, root 0x%x\n", + $$cv, $ppname, $$root) if $debug_cv; + if ($$padlist) { + warn sprintf("saving PADLIST 0x%x for CV 0x%x\n", + $$padlist, $$cv) if $debug_cv; + $padlist->save; + warn sprintf("done saving PADLIST 0x%x for CV 0x%x\n", + $$padlist, $$cv) if $debug_cv; + } + } + elsif ($cvxsub) { + $xsubany = sprintf("ANYINIT((void*)0x%x)", $cv->XSUBANY); + # Try to find out canonical name of XSUB function from EGV. + # XXX Doesn't work for XSUBs with PREFIX set (or anyone who + # calls newXS() manually with weird arguments). + my $egv = $gv->EGV; + my $stashname = $egv->STASH->NAME; + $stashname =~ s/::/__/g; + $xsub = sprintf("XS_%s_%s", $stashname, $egv->NAME); + $decl->add("void $xsub _((CV*));"); + } + else { + warn sprintf("No definition for sub %s::%s (unable to autoload)\n", + $cvstashname, $cvname); # debug + } + $symsect->add(sprintf("xpvcvix%d\t%s, %u, 0, %d, %s, 0, Nullhv, Nullhv, %s, s\\_%x, $xsub, $xsubany, Nullgv, Nullgv, %d, s\\_%x, (CV*)s\\_%x, 0x%x", + $xpvcv_ix, cstring($pv), length($pv), $cv->IVX, + $cv->NVX, $startfield, ${$cv->ROOT}, $cv->DEPTH, + $$padlist, ${$cv->OUTSIDE}, $cv->CvFLAGS)); + + if (${$cv->OUTSIDE} == ${main_cv()}){ + $init->add(sprintf("CvOUTSIDE(s\\_%x)=PL_main_cv;",$$cv)); + } + + if ($$gv) { + $gv->save; + $init->add(sprintf("CvGV(s\\_%x) = s\\_%x;",$$cv,$$gv)); + warn sprintf("done saving GV 0x%x for CV 0x%x\n", + $$gv, $$cv) if $debug_cv; + } + my $filegv = $cv->FILEGV; + if ($$filegv) { + $filegv->save; + $init->add(sprintf("CvFILEGV(s\\_%x) = s\\_%x;", $$cv, $$filegv)); + warn sprintf("done saving FILEGV 0x%x for CV 0x%x\n", + $$filegv, $$cv) if $debug_cv; + } + my $stash = $cv->STASH; + if ($$stash) { + $stash->save; + $init->add(sprintf("CvSTASH(s\\_%x) = s\\_%x;", $$cv, $$stash)); + warn sprintf("done saving STASH 0x%x for CV 0x%x\n", + $$stash, $$cv) if $debug_cv; + } + $symsect->add(sprintf("svix%d\t(XPVCV*)&xpvcv_list[%u], %lu, 0x%x", + $sv_ix, $xpvcv_ix, $cv->REFCNT + 1, $cv->FLAGS)); + return $sym; +} + +sub B::GV::save { + my ($gv) = @_; + my $sym = objsym($gv); + if (defined($sym)) { + #warn sprintf("GV 0x%x already saved as $sym\n", $$gv); # debug + return $sym; + } else { + my $ix = $gv_index++; + $sym = savesym($gv, "gv_list[$ix]"); + #warn sprintf("Saving GV 0x%x as $sym\n", $$gv); # debug + } + my $gvname = $gv->NAME; + my $name = cstring($gv->STASH->NAME . "::" . $gvname); + #warn "GV name is $name\n"; # debug + my $egv = $gv->EGV; + my $egvsym; + if ($$gv != $$egv) { + #warn(sprintf("EGV name is %s, saving it now\n", + # $egv->STASH->NAME . "::" . $egv->NAME)); # debug + $egvsym = $egv->save; + } + $init->add(qq[$sym = gv_fetchpv($name, TRUE, SVt_PV);], + sprintf("SvFLAGS($sym) = 0x%x;", $gv->FLAGS), + sprintf("GvFLAGS($sym) = 0x%x;", $gv->GvFLAGS), + sprintf("GvLINE($sym) = %u;", $gv->LINE)); + # Shouldn't need to do save_magic since gv_fetchpv handles that + #$gv->save_magic; + my $refcnt = $gv->REFCNT + 1; + $init->add(sprintf("SvREFCNT($sym) += %u;", $refcnt - 1)) if $refcnt > 1; + my $gvrefcnt = $gv->GvREFCNT; + if ($gvrefcnt > 1) { + $init->add(sprintf("GvREFCNT($sym) += %u;", $gvrefcnt - 1)); + } + if (defined($egvsym)) { + # Shared glob *foo = *bar + $init->add("gp_free($sym);", + "GvGP($sym) = GvGP($egvsym);"); + } elsif ($gvname !~ /^([^A-Za-z]|STDIN|STDOUT|STDERR|ARGV|SIG|ENV)$/) { + # Don't save subfields of special GVs (*_, *1, *# and so on) +# warn "GV::save saving subfields\n"; # debug + my $gvsv = $gv->SV; + if ($$gvsv) { + $init->add(sprintf("GvSV($sym) = s\\_%x;", $$gvsv)); +# warn "GV::save \$$name\n"; # debug + $gvsv->save; + } + my $gvav = $gv->AV; + if ($$gvav) { + $init->add(sprintf("GvAV($sym) = s\\_%x;", $$gvav)); +# warn "GV::save \@$name\n"; # debug + $gvav->save; + } + my $gvhv = $gv->HV; + if ($$gvhv) { + $init->add(sprintf("GvHV($sym) = s\\_%x;", $$gvhv)); +# warn "GV::save \%$name\n"; # debug + $gvhv->save; + } + my $gvcv = $gv->CV; + if ($$gvcv) { + $init->add(sprintf("GvCV($sym) = (CV*)s\\_%x;", $$gvcv)); +# warn "GV::save &$name\n"; # debug + $gvcv->save; + } + my $gvfilegv = $gv->FILEGV; + if ($$gvfilegv) { + $init->add(sprintf("GvFILEGV($sym) = (GV*)s\\_%x;",$$gvfilegv)); +# warn "GV::save GvFILEGV(*$name)\n"; # debug + $gvfilegv->save; + } + my $gvform = $gv->FORM; + if ($$gvform) { + $init->add(sprintf("GvFORM($sym) = (CV*)s\\_%x;", $$gvform)); +# warn "GV::save GvFORM(*$name)\n"; # debug + $gvform->save; + } + my $gvio = $gv->IO; + if ($$gvio) { + $init->add(sprintf("GvIOp($sym) = s\\_%x;", $$gvio)); +# warn "GV::save GvIO(*$name)\n"; # debug + $gvio->save; + } + } + return $sym; +} +sub B::AV::save { + my ($av) = @_; + my $sym = objsym($av); + return $sym if defined $sym; + my $avflags = $av->AvFLAGS; + $xpvavsect->add(sprintf("0, -1, -1, 0, 0.0, 0, Nullhv, 0, 0, 0x%x", + $avflags)); + $svsect->add(sprintf("&xpvav_list[%d], %lu, 0x%x", + $xpvavsect->index, $av->REFCNT + 1, $av->FLAGS)); + my $sv_list_index = $svsect->index; + my $fill = $av->FILL; + $av->save_magic; + warn sprintf("saving AV 0x%x FILL=$fill AvFLAGS=0x%x", $$av, $avflags) + if $debug_av; + # XXX AVf_REAL is wrong test: need to save comppadlist but not stack + #if ($fill > -1 && ($avflags & AVf_REAL)) { + if ($fill > -1) { + my @array = $av->ARRAY; + if ($debug_av) { + my $el; + my $i = 0; + foreach $el (@array) { + warn sprintf("AV 0x%x[%d] = %s 0x%x\n", + $$av, $i++, class($el), $$el); + } + } + my @names = map($_->save, @array); + # XXX Better ways to write loop? + # Perhaps svp[0] = ...; svp[1] = ...; svp[2] = ...; + # Perhaps I32 i = 0; svp[i++] = ...; svp[i++] = ...; svp[i++] = ...; + $init->add("{", + "\tSV **svp;", + "\tAV *av = (AV*)&sv_list[$sv_list_index];", + "\tav_extend(av, $fill);", + "\tsvp = AvARRAY(av);", + map("\t*svp++ = (SV*)$_;", @names), + "\tAvFILLp(av) = $fill;", + "}"); + } else { + my $max = $av->MAX; + $init->add("av_extend((AV*)&sv_list[$sv_list_index], $max);") + if $max > -1; + } + return savesym($av, "(AV*)&sv_list[$sv_list_index]"); +} + +sub B::HV::save { + my ($hv) = @_; + my $sym = objsym($hv); + return $sym if defined $sym; + my $name = $hv->NAME; + if ($name) { + # It's a stash + + # A perl bug means HvPMROOT isn't altered when a PMOP is freed. Usually + # the only symptom is that sv_reset tries to reset the PMf_USED flag of + # a trashed op but we look at the trashed op_type and segfault. + #my $adpmroot = ${$hv->PMROOT}; + my $adpmroot = 0; + $decl->add("static HV *hv$hv_index;"); + # XXX Beware of weird package names containing double-quotes, \n, ...? + $init->add(qq[hv$hv_index = gv_stashpv("$name", TRUE);]); + if ($adpmroot) { + $init->add(sprintf("HvPMROOT(hv$hv_index) = (PMOP*)s\\_%x;", + $adpmroot)); + } + $sym = savesym($hv, "hv$hv_index"); + $hv_index++; + return $sym; + } + # It's just an ordinary HV + $xpvhvsect->add(sprintf("0, 0, %d, 0, 0.0, 0, Nullhv, %d, 0, 0, 0", + $hv->MAX, $hv->RITER)); + $svsect->add(sprintf("&xpvhv_list[%d], %lu, 0x%x", + $xpvhvsect->index, $hv->REFCNT + 1, $hv->FLAGS)); + my $sv_list_index = $svsect->index; + my @contents = $hv->ARRAY; + if (@contents) { + my $i; + for ($i = 1; $i < @contents; $i += 2) { + $contents[$i] = $contents[$i]->save; + } + $init->add("{", "\tHV *hv = (HV*)&sv_list[$sv_list_index];"); + while (@contents) { + my ($key, $value) = splice(@contents, 0, 2); + $init->add(sprintf("\thv_store(hv, %s, %u, %s, %s);", + cstring($key),length($key),$value, hash($key))); + } + $init->add("}"); + } + return savesym($hv, "(HV*)&sv_list[$sv_list_index]"); +} + +sub B::IO::save { + my ($io) = @_; + my $sym = objsym($io); + return $sym if defined $sym; + my $pv = $io->PV; + my $len = length($pv); + $xpviosect->add(sprintf("0, %u, %u, %d, %s, 0, 0, 0, 0, 0, %d, %d, %d, %d, %s, Nullgv, %s, Nullgv, %s, Nullgv, %d, %s, 0x%x", + $len, $len+1, $io->IVX, $io->NVX, $io->LINES, + $io->PAGE, $io->PAGE_LEN, $io->LINES_LEFT, + cstring($io->TOP_NAME), cstring($io->FMT_NAME), + cstring($io->BOTTOM_NAME), $io->SUBPROCESS, + cchar($io->IoTYPE), $io->IoFLAGS)); + $svsect->add(sprintf("&xpvio_list[%d], %lu, 0x%x", + $xpviosect->index, $io->REFCNT + 1, $io->FLAGS)); + $sym = savesym($io, sprintf("(IO*)&sv_list[%d]", $svsect->index)); + my ($field, $fsym); + foreach $field (qw(TOP_GV FMT_GV BOTTOM_GV)) { + $fsym = $io->$field(); + if ($$fsym) { + $init->add(sprintf("Io$field($sym) = (GV*)s\\_%x;", $$fsym)); + $fsym->save; + } + } + $io->save_magic; + return $sym; +} + +sub B::SV::save { + my $sv = shift; + # This is where we catch an honest-to-goodness Nullsv (which gets + # blessed into B::SV explicitly) and any stray erroneous SVs. + return 0 unless $$sv; + confess sprintf("cannot save that type of SV: %s (0x%x)\n", + class($sv), $$sv); +} + +sub output_all { + my $init_name = shift; + my $section; + my @sections = ($opsect, $unopsect, $binopsect, $logopsect, $condopsect, + $listopsect, $pmopsect, $svopsect, $gvopsect, $pvopsect, + $cvopsect, $loopsect, $copsect, $svsect, $xpvsect, + $xpvavsect, $xpvhvsect, $xpvcvsect, $xpvivsect, $xpvnvsect, + $xpvmgsect, $xpvlvsect, $xrvsect, $xpvbmsect, $xpviosect); + $bootstrap->output(\*STDOUT, "/* bootstrap %s */\n"); + $symsect->output(\*STDOUT, "#define %s\n"); + print "\n"; + output_declarations(); + foreach $section (@sections) { + my $lines = $section->index + 1; + if ($lines) { + my $name = $section->name; + my $typename = ($name eq "xpvcv") ? "XPVCV_or_similar" : uc($name); + print "Static $typename ${name}_list[$lines];\n"; + } + } + $decl->output(\*STDOUT, "%s\n"); + print "\n"; + foreach $section (@sections) { + my $lines = $section->index + 1; + if ($lines) { + my $name = $section->name; + my $typename = ($name eq "xpvcv") ? "XPVCV_or_similar" : uc($name); + printf "static %s %s_list[%u] = {\n", $typename, $name, $lines; + $section->output(\*STDOUT, "\t{ %s },\n"); + print "};\n\n"; + } + } + + print <<"EOT"; +static int $init_name() +{ + dTHR; +EOT + $init->output(\*STDOUT, "\t%s\n"); + print "\treturn 0;\n}\n"; + if ($verbose) { + warn compile_stats(); + warn "NULLOP count: $nullop_count\n"; + } +} + +sub output_declarations { + print <<'EOT'; +#ifdef BROKEN_STATIC_REDECL +#define Static extern +#else +#define Static static +#endif /* BROKEN_STATIC_REDECL */ + +#ifdef BROKEN_UNION_INIT +/* + * Cribbed from cv.h with ANY (a union) replaced by void*. + * Some pre-Standard compilers can't cope with initialising unions. Ho hum. + */ +typedef struct { + char * xpv_pv; /* pointer to malloced string */ + STRLEN xpv_cur; /* length of xp_pv as a C string */ + STRLEN xpv_len; /* allocated size */ + IV xof_off; /* integer value */ + double xnv_nv; /* numeric value, if any */ + MAGIC* xmg_magic; /* magic for scalar array */ + HV* xmg_stash; /* class package */ + + HV * xcv_stash; + OP * xcv_start; + OP * xcv_root; + void (*xcv_xsub) _((CV*)); + void * xcv_xsubany; + GV * xcv_gv; + GV * xcv_filegv; + long xcv_depth; /* >= 2 indicates recursive call */ + AV * xcv_padlist; + CV * xcv_outside; +#ifdef USE_THREADS + perl_mutex *xcv_mutexp; + struct perl_thread *xcv_owner; /* current owner thread */ +#endif /* USE_THREADS */ + U8 xcv_flags; +} XPVCV_or_similar; +#define ANYINIT(i) i +#else +#define XPVCV_or_similar XPVCV +#define ANYINIT(i) {i} +#endif /* BROKEN_UNION_INIT */ +#define Nullany ANYINIT(0) + +#define UNUSED 0 +#define sym_0 0 + +EOT + print "static GV *gv_list[$gv_index];\n" if $gv_index; + print "\n"; +} + + +sub output_boilerplate { + print <<'EOT'; +#include "EXTERN.h" +#include "perl.h" +#ifndef PATCHLEVEL +#include "patchlevel.h" +#endif + +/* Workaround for mapstart: the only op which needs a different ppaddr */ +#undef pp_mapstart +#define pp_mapstart pp_grepstart + +static void xs_init _((void)); +static PerlInterpreter *my_perl; +EOT +} + +sub output_main { + print <<'EOT'; +int +#ifndef CAN_PROTOTYPE +main(argc, argv, env) +int argc; +char **argv; +char **env; +#else /* def(CAN_PROTOTYPE) */ +main(int argc, char **argv, char **env) +#endif /* def(CAN_PROTOTYPE) */ +{ + int exitstatus; + int i; + char **fakeargv; + + PERL_SYS_INIT(&argc,&argv); + + perl_init_i18nl10n(1); + + if (!PL_do_undump) { + my_perl = perl_alloc(); + if (!my_perl) + exit(1); + perl_construct( my_perl ); + } + +#ifdef CSH + if (!PL_cshlen) + PL_cshlen = strlen(PL_cshname); +#endif + +#ifdef ALLOW_PERL_OPTIONS +#define EXTRA_OPTIONS 2 +#else +#define EXTRA_OPTIONS 3 +#endif /* ALLOW_PERL_OPTIONS */ + New(666, fakeargv, argc + EXTRA_OPTIONS + 1, char *); + fakeargv[0] = argv[0]; + fakeargv[1] = "-e"; + fakeargv[2] = ""; +#ifndef ALLOW_PERL_OPTIONS + fakeargv[3] = "--"; +#endif /* ALLOW_PERL_OPTIONS */ + for (i = 1; i < argc; i++) + fakeargv[i + EXTRA_OPTIONS] = argv[i]; + fakeargv[argc + EXTRA_OPTIONS] = 0; + + exitstatus = perl_parse(my_perl, xs_init, argc + EXTRA_OPTIONS, + fakeargv, NULL); + if (exitstatus) + exit( exitstatus ); + + sv_setpv(GvSV(gv_fetchpv("0", TRUE, SVt_PV)), argv[0]); + PL_main_cv = PL_compcv; + PL_compcv = 0; + + exitstatus = perl_init(); + if (exitstatus) + exit( exitstatus ); + + exitstatus = perl_run( my_perl ); + + perl_destruct( my_perl ); + perl_free( my_perl ); + + exit( exitstatus ); +} + +static void +xs_init() +{ +} +EOT +} + +sub dump_symtable { + # For debugging + my ($sym, $val); + warn "----Symbol table:\n"; + while (($sym, $val) = each %symtable) { + warn "$sym => $val\n"; + } + warn "---End of symbol table\n"; +} + +sub save_object { + my $sv; + foreach $sv (@_) { + svref_2object($sv)->save; + } +} + +sub Dummy_BootStrap { } + +sub B::GV::savecv { + my $gv = shift; + my $cv = $gv->CV; + my $name = $gv->NAME; + if ($$cv) { + if ($name eq "bootstrap" && $cv->XSUB) { + my $file = $cv->FILEGV->SV->PV; + $bootstrap->add($file); + my $name = $gv->STASH->NAME.'::'.$name; + no strict 'refs'; + *{$name} = \&Dummy_BootStrap; + $cv = $gv->CV; + } + if ($debug_cv) { + warn sprintf("saving extra CV &%s::%s (0x%x) from GV 0x%x\n", + $gv->STASH->NAME, $name, $$cv, $$gv); + } + my $package=$gv->STASH->NAME; + # This seems to undo all the ->isa and prefix stuff we do below + # so disable again for now + if (0 && ! grep(/^$package$/,@unused_sub_packages)){ + warn sprintf("omitting cv in superclass %s", $gv->STASH->NAME) + if $debug_cv; + return ; + } + $gv->save; + } + elsif ($name eq 'ISA') + { + $gv->save; + } + +} + + + +sub save_unused_subs { + my %search_pack; + map { $search_pack{$_} = 1 } @_; + @unused_sub_packages=@_; + no strict qw(vars refs); + walksymtable(\%{"main::"}, "savecv", sub { + my $package = shift; + $package =~ s/::$//; + return 0 if ($package =~ /::::/); # skip ::::ISA::CACHE etc. + #warn "Considering $package\n";#debug + return 1 if exists $search_pack{$package}; + #sub try for a partial match + if (grep(/^$package\:\:/,@unused_sub_packages)){ + return 1; + } + #warn " (nothing explicit)\n";#debug + # Omit the packages which we use (and which cause grief + # because of fancy "goto &$AUTOLOAD" stuff). + # XXX Surely there must be a nicer way to do this. + if ($package eq "FileHandle" + || $package eq "Config" + || $package eq "SelectSaver") { + return 0; + } + foreach my $u (keys %search_pack) { + if ($package =~ /^${u}::/) { + warn "$package starts with $u\n"; + return 1 + } + if ($package->isa($u)) { + warn "$package isa $u\n"; + return 1 + } + return 1 if $package->isa($u); + } + foreach my $m (qw(new DESTROY TIESCALAR TIEARRAY TIEHASH)) { + if (defined(&{$package."::$m"})) { + warn "$package has method $m: -u$package assumed\n";#debug + push @unused_sub_package, $package; + return 1; + } + } + return 0; + }); +} + +sub save_main { + warn "Walking tree\n"; + my $curpad_nam = (comppadlist->ARRAY)[0]->save; + my $curpad_sym = (comppadlist->ARRAY)[1]->save; + my $init_av = init_av->save; + my $inc_hv = svref_2object(\%INC)->save; + my $inc_av = svref_2object(\@INC)->save; + walkoptree(main_root, "save"); + warn "done main optree, walking symtable for extras\n" if $debug_cv; + save_unused_subs(@unused_sub_packages); + + $init->add(sprintf("PL_main_root = s\\_%x;", ${main_root()}), + sprintf("PL_main_start = s\\_%x;", ${main_start()}), + "PL_curpad = AvARRAY($curpad_sym);", + "PL_initav = $init_av;", + "GvHV(PL_incgv) = $inc_hv;", + "GvAV(PL_incgv) = $inc_av;", + "av_store(CvPADLIST(PL_main_cv),0,SvREFCNT_inc($curpad_nam));", + "av_store(CvPADLIST(PL_main_cv),1,SvREFCNT_inc($curpad_sym));"); + warn "Writing output\n"; + output_boilerplate(); + print "\n"; + output_all("perl_init"); + print "\n"; + output_main(); +} + +sub init_sections { + my @sections = (init => \$init, decl => \$decl, sym => \$symsect, + binop => \$binopsect, condop => \$condopsect, + cop => \$copsect, cvop => \$cvopsect, gvop => \$gvopsect, + listop => \$listopsect, logop => \$logopsect, + loop => \$loopsect, op => \$opsect, pmop => \$pmopsect, + pvop => \$pvopsect, svop => \$svopsect, unop => \$unopsect, + sv => \$svsect, xpv => \$xpvsect, xpvav => \$xpvavsect, + xpvhv => \$xpvhvsect, xpvcv => \$xpvcvsect, + xpviv => \$xpvivsect, xpvnv => \$xpvnvsect, + xpvmg => \$xpvmgsect, xpvlv => \$xpvlvsect, + xrv => \$xrvsect, xpvbm => \$xpvbmsect, + xpvio => \$xpviosect, bootstrap => \$bootstrap); + my ($name, $sectref); + while (($name, $sectref) = splice(@sections, 0, 2)) { + $$sectref = new B::Section $name, \%symtable, 0; + } +} + +sub compile { + my @options = @_; + my ($option, $opt, $arg); + OPTION: + while ($option = shift @options) { + if ($option =~ /^-(.)(.*)/) { + $opt = $1; + $arg = $2; + } else { + unshift @options, $option; + last OPTION; + } + if ($opt eq "-" && $arg eq "-") { + shift @options; + last OPTION; + } + if ($opt eq "w") { + $warn_undefined_syms = 1; + } elsif ($opt eq "D") { + $arg ||= shift @options; + foreach $arg (split(//, $arg)) { + if ($arg eq "o") { + B->debug(1); + } elsif ($arg eq "c") { + $debug_cops = 1; + } elsif ($arg eq "A") { + $debug_av = 1; + } elsif ($arg eq "C") { + $debug_cv = 1; + } elsif ($arg eq "M") { + $debug_mg = 1; + } else { + warn "ignoring unknown debug option: $arg\n"; + } + } + } elsif ($opt eq "o") { + $arg ||= shift @options; + open(STDOUT, ">$arg") or return "$arg: $!\n"; + } elsif ($opt eq "v") { + $verbose = 1; + } elsif ($opt eq "u") { + $arg ||= shift @options; + push(@unused_sub_packages, $arg); + } elsif ($opt eq "f") { + $arg ||= shift @options; + if ($arg eq "cog") { + $pv_copy_on_grow = 1; + } elsif ($arg eq "no-cog") { + $pv_copy_on_grow = 0; + } + } elsif ($opt eq "O") { + $arg = 1 if $arg eq ""; + $pv_copy_on_grow = 0; + if ($arg >= 1) { + # Optimisations for -O1 + $pv_copy_on_grow = 1; + } + } + } + init_sections(); + if (@options) { + return sub { + my $objname; + foreach $objname (@options) { + eval "save_object(\\$objname)"; + } + output_all(); + } + } else { + return sub { save_main() }; + } +} + +1; + +__END__ + +=head1 NAME + +B::C - Perl compiler's C backend + +=head1 SYNOPSIS + + perl -MO=C[,OPTIONS] foo.pl + +=head1 DESCRIPTION + +This compiler backend takes Perl source and generates C source code +corresponding to the internal structures that perl uses to run +your program. When the generated C source is compiled and run, it +cuts out the time which perl would have taken to load and parse +your program into its internal semi-compiled form. That means that +compiling with this backend will not help improve the runtime +execution speed of your program but may improve the start-up time. +Depending on the environment in which your program runs this may be +either a help or a hindrance. + +=head1 OPTIONS + +If there are any non-option arguments, they are taken to be +names of objects to be saved (probably doesn't work properly yet). +Without extra arguments, it saves the main program. + +=over 4 + +=item B<-ofilename> + +Output to filename instead of STDOUT + +=item B<-v> + +Verbose compilation (currently gives a few compilation statistics). + +=item B<--> + +Force end of options + +=item B<-uPackname> + +Force apparently unused subs from package Packname to be compiled. +This allows programs to use eval "foo()" even when sub foo is never +seen to be used at compile time. The down side is that any subs which +really are never used also have code generated. This option is +necessary, for example, if you have a signal handler foo which you +initialise with C<$SIG{BAR} = "foo">. A better fix, though, is just +to change it to C<$SIG{BAR} = \&foo>. You can have multiple B<-u> +options. The compiler tries to figure out which packages may possibly +have subs in which need compiling but the current version doesn't do +it very well. In particular, it is confused by nested packages (i.e. +of the form C<A::B>) where package C<A> does not contain any subs. + +=item B<-D> + +Debug options (concatenated or separate flags like C<perl -D>). + +=item B<-Do> + +OPs, prints each OP as it's processed + +=item B<-Dc> + +COPs, prints COPs as processed (incl. file & line num) + +=item B<-DA> + +prints AV information on saving + +=item B<-DC> + +prints CV information on saving + +=item B<-DM> + +prints MAGIC information on saving + +=item B<-f> + +Force optimisations on or off one at a time. + +=item B<-fcog> + +Copy-on-grow: PVs declared and initialised statically. + +=item B<-fno-cog> + +No copy-on-grow. + +=item B<-On> + +Optimisation level (n = 0, 1, 2, ...). B<-O> means B<-O1>. Currently, +B<-O1> and higher set B<-fcog>. + +=head1 EXAMPLES + + perl -MO=C,-ofoo.c foo.pl + perl cc_harness -o foo foo.c + +Note that C<cc_harness> lives in the C<B> subdirectory of your perl +library directory. The utility called C<perlcc> may also be used to +help make use of this compiler. + + perl -MO=C,-v,-DcA bar.pl > /dev/null + +=head1 BUGS + +Plenty. Current status: experimental. + +=head1 AUTHOR + +Malcolm Beattie, C<mbeattie@sable.ox.ac.uk> + +=cut diff --git a/gnu/usr.bin/perl/ext/B/B/CC.pm b/gnu/usr.bin/perl/ext/B/B/CC.pm new file mode 100644 index 00000000000..d200d70f1ae --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/B/CC.pm @@ -0,0 +1,1749 @@ +# CC.pm +# +# Copyright (c) 1996, 1997, 1998 Malcolm Beattie +# +# You may distribute under the terms of either the GNU General Public +# License or the Artistic License, as specified in the README file. +# +package B::CC; +use strict; +use B qw(main_start main_root class comppadlist peekop svref_2object + timing_info); +use B::C qw(save_unused_subs objsym init_sections + output_all output_boilerplate output_main); +use B::Bblock qw(find_leaders); +use B::Stackobj qw(:types :flags); + +# These should probably be elsewhere +# Flags for $op->flags +sub OPf_LIST () { 1 } +sub OPf_KNOW () { 2 } +sub OPf_MOD () { 32 } +sub OPf_STACKED () { 64 } +sub OPf_SPECIAL () { 128 } +# op-specific flags for $op->private +sub OPpASSIGN_BACKWARDS () { 64 } +sub OPpLVAL_INTRO () { 128 } +sub OPpDEREF_AV () { 32 } +sub OPpDEREF_HV () { 64 } +sub OPpDEREF () { OPpDEREF_AV|OPpDEREF_HV } +sub OPpFLIP_LINENUM () { 64 } +sub G_ARRAY () { 1 } +# cop.h +sub CXt_NULL () { 0 } +sub CXt_SUB () { 1 } +sub CXt_EVAL () { 2 } +sub CXt_LOOP () { 3 } +sub CXt_SUBST () { 4 } +sub CXt_BLOCK () { 5 } + +my $module; # module name (when compiled with -m) +my %done; # hash keyed by $$op of leaders of basic blocks + # which have already been done. +my $leaders; # ref to hash of basic block leaders. Keys are $$op + # addresses, values are the $op objects themselves. +my @bblock_todo; # list of leaders of basic blocks that need visiting + # sometime. +my @cc_todo; # list of tuples defining what PP code needs to be + # saved (e.g. CV, main or PMOP repl code). Each tuple + # is [$name, $root, $start, @padlist]. PMOP repl code + # tuples inherit padlist. +my @stack; # shadows perl's stack when contents are known. + # Values are objects derived from class B::Stackobj +my @pad; # Lexicals in current pad as Stackobj-derived objects +my @padlist; # Copy of current padlist so PMOP repl code can find it +my @cxstack; # Shadows the (compile-time) cxstack for next,last,redo +my $jmpbuf_ix = 0; # Next free index for dynamically allocated jmpbufs +my %constobj; # OP_CONST constants as Stackobj-derived objects + # keyed by $$sv. +my $need_freetmps = 0; # We may postpone FREETMPS to the end of each basic + # block or even to the end of each loop of blocks, + # depending on optimisation options. +my $know_op = 0; # Set when C variable op already holds the right op + # (from an immediately preceding DOOP(ppname)). +my $errors = 0; # Number of errors encountered +my %skip_stack; # Hash of PP names which don't need write_back_stack +my %skip_lexicals; # Hash of PP names which don't need write_back_lexicals +my %skip_invalidate; # Hash of PP names which don't need invalidate_lexicals +my %ignore_op; # Hash of ops which do nothing except returning op_next + +BEGIN { + foreach (qw(pp_scalar pp_regcmaybe pp_lineseq pp_scope pp_null)) { + $ignore_op{$_} = 1; + } +} + +my @unused_sub_packages; # list of packages (given by -u options) to search + # explicitly and save every sub we find there, even + # if apparently unused (could be only referenced from + # an eval "" or from a $SIG{FOO} = "bar"). + +my ($module_name); +my ($debug_op, $debug_stack, $debug_cxstack, $debug_pad, $debug_runtime, + $debug_shadow, $debug_queue, $debug_lineno, $debug_timings); + +# Optimisation options. On the command line, use hyphens instead of +# underscores for compatibility with gcc-style options. We use +# underscores here because they are OK in (strict) barewords. +my ($freetmps_each_bblock, $freetmps_each_loop, $omit_taint); +my %optimise = (freetmps_each_bblock => \$freetmps_each_bblock, + freetmps_each_loop => \$freetmps_each_loop, + omit_taint => \$omit_taint); +# perl patchlevel to generate code for (defaults to current patchlevel) +my $patchlevel = int(0.5 + 1000 * ($] - 5)); + +# Could rewrite push_runtime() and output_runtime() to use a +# temporary file if memory is at a premium. +my $ppname; # name of current fake PP function +my $runtime_list_ref; +my $declare_ref; # Hash ref keyed by C variable type of declarations. + +my @pp_list; # list of [$ppname, $runtime_list_ref, $declare_ref] + # tuples to be written out. + +my ($init, $decl); + +sub init_hash { map { $_ => 1 } @_ } + +# +# Initialise the hashes for the default PP functions where we can avoid +# either write_back_stack, write_back_lexicals or invalidate_lexicals. +# +%skip_lexicals = init_hash qw(pp_enter pp_enterloop); +%skip_invalidate = init_hash qw(pp_enter pp_enterloop); + +sub debug { + if ($debug_runtime) { + warn(@_); + } else { + runtime(map { chomp; "/* $_ */"} @_); + } +} + +sub declare { + my ($type, $var) = @_; + push(@{$declare_ref->{$type}}, $var); +} + +sub push_runtime { + push(@$runtime_list_ref, @_); + warn join("\n", @_) . "\n" if $debug_runtime; +} + +sub save_runtime { + push(@pp_list, [$ppname, $runtime_list_ref, $declare_ref]); +} + +sub output_runtime { + my $ppdata; + print qq(#include "cc_runtime.h"\n); + foreach $ppdata (@pp_list) { + my ($name, $runtime, $declare) = @$ppdata; + print "\nstatic\nPP($name)\n{\n"; + my ($type, $varlist, $line); + while (($type, $varlist) = each %$declare) { + print "\t$type ", join(", ", @$varlist), ";\n"; + } + foreach $line (@$runtime) { + print $line, "\n"; + } + print "}\n"; + } +} + +sub runtime { + my $line; + foreach $line (@_) { + push_runtime("\t$line"); + } +} + +sub init_pp { + $ppname = shift; + $runtime_list_ref = []; + $declare_ref = {}; + runtime("djSP;"); + declare("I32", "oldsave"); + declare("SV", "**svp"); + map { declare("SV", "*$_") } qw(sv src dst left right); + declare("MAGIC", "*mg"); + $decl->add("static OP * $ppname _((ARGSproto));"); + debug "init_pp: $ppname\n" if $debug_queue; +} + +# Initialise runtime_callback function for Stackobj class +BEGIN { B::Stackobj::set_callback(\&runtime) } + +# Initialise saveoptree_callback for B::C class +sub cc_queue { + my ($name, $root, $start, @pl) = @_; + debug "cc_queue: name $name, root $root, start $start, padlist (@pl)\n" + if $debug_queue; + if ($name eq "*ignore*") { + $name = 0; + } else { + push(@cc_todo, [$name, $root, $start, (@pl ? @pl : @padlist)]); + } + my $fakeop = new B::FAKEOP ("next" => 0, sibling => 0, ppaddr => $name); + $start = $fakeop->save; + debug "cc_queue: name $name returns $start\n" if $debug_queue; + return $start; +} +BEGIN { B::C::set_callback(\&cc_queue) } + +sub valid_int { $_[0]->{flags} & VALID_INT } +sub valid_double { $_[0]->{flags} & VALID_DOUBLE } +sub valid_numeric { $_[0]->{flags} & (VALID_INT | VALID_DOUBLE) } +sub valid_sv { $_[0]->{flags} & VALID_SV } + +sub top_int { @stack ? $stack[-1]->as_int : "TOPi" } +sub top_double { @stack ? $stack[-1]->as_double : "TOPn" } +sub top_numeric { @stack ? $stack[-1]->as_numeric : "TOPn" } +sub top_sv { @stack ? $stack[-1]->as_sv : "TOPs" } +sub top_bool { @stack ? $stack[-1]->as_numeric : "SvTRUE(TOPs)" } + +sub pop_int { @stack ? (pop @stack)->as_int : "POPi" } +sub pop_double { @stack ? (pop @stack)->as_double : "POPn" } +sub pop_numeric { @stack ? (pop @stack)->as_numeric : "POPn" } +sub pop_sv { @stack ? (pop @stack)->as_sv : "POPs" } +sub pop_bool { + if (@stack) { + return ((pop @stack)->as_numeric); + } else { + # Careful: POPs has an auto-decrement and SvTRUE evaluates + # its argument more than once. + runtime("sv = POPs;"); + return "SvTRUE(sv)"; + } +} + +sub write_back_lexicals { + my $avoid = shift || 0; + debug "write_back_lexicals($avoid) called from @{[(caller(1))[3]]}\n" + if $debug_shadow; + my $lex; + foreach $lex (@pad) { + next unless ref($lex); + $lex->write_back unless $lex->{flags} & $avoid; + } +} + +sub write_back_stack { + my $obj; + return unless @stack; + runtime(sprintf("EXTEND(sp, %d);", scalar(@stack))); + foreach $obj (@stack) { + runtime(sprintf("PUSHs((SV*)%s);", $obj->as_sv)); + } + @stack = (); +} + +sub invalidate_lexicals { + my $avoid = shift || 0; + debug "invalidate_lexicals($avoid) called from @{[(caller(1))[3]]}\n" + if $debug_shadow; + my $lex; + foreach $lex (@pad) { + next unless ref($lex); + $lex->invalidate unless $lex->{flags} & $avoid; + } +} + +sub reload_lexicals { + my $lex; + foreach $lex (@pad) { + next unless ref($lex); + my $type = $lex->{type}; + if ($type == T_INT) { + $lex->as_int; + } elsif ($type == T_DOUBLE) { + $lex->as_double; + } else { + $lex->as_sv; + } + } +} + +{ + package B::Pseudoreg; + # + # This class allocates pseudo-registers (OK, so they're C variables). + # + my %alloc; # Keyed by variable name. A value of 1 means the + # variable has been declared. A value of 2 means + # it's in use. + + sub new_scope { %alloc = () } + + sub new ($$$) { + my ($class, $type, $prefix) = @_; + my ($ptr, $i, $varname, $status, $obj); + $prefix =~ s/^(\**)//; + $ptr = $1; + $i = 0; + do { + $varname = "$prefix$i"; + $status = $alloc{$varname}; + } while $status == 2; + if ($status != 1) { + # Not declared yet + B::CC::declare($type, "$ptr$varname"); + $alloc{$varname} = 2; # declared and in use + } + $obj = bless \$varname, $class; + return $obj; + } + sub DESTROY { + my $obj = shift; + $alloc{$$obj} = 1; # no longer in use but still declared + } +} +{ + package B::Shadow; + # + # This class gives a standard API for a perl object to shadow a + # C variable and only generate reloads/write-backs when necessary. + # + # Use $obj->load($foo) instead of runtime("shadowed_c_var = foo"). + # Use $obj->write_back whenever shadowed_c_var needs to be up to date. + # Use $obj->invalidate whenever an unknown function may have + # set shadow itself. + + sub new { + my ($class, $write_back) = @_; + # Object fields are perl shadow variable, validity flag + # (for *C* variable) and callback sub for write_back + # (passed perl shadow variable as argument). + bless [undef, 1, $write_back], $class; + } + sub load { + my ($obj, $newval) = @_; + $obj->[1] = 0; # C variable no longer valid + $obj->[0] = $newval; + } + sub write_back { + my $obj = shift; + if (!($obj->[1])) { + $obj->[1] = 1; # C variable will now be valid + &{$obj->[2]}($obj->[0]); + } + } + sub invalidate { $_[0]->[1] = 0 } # force C variable to be invalid +} +my $curcop = new B::Shadow (sub { + my $opsym = shift->save; + runtime("PL_curcop = (COP*)$opsym;"); +}); + +# +# Context stack shadowing. Mimics stuff in pp_ctl.c, cop.h and so on. +# +sub dopoptoloop { + my $cxix = $#cxstack; + while ($cxix >= 0 && $cxstack[$cxix]->{type} != CXt_LOOP) { + $cxix--; + } + debug "dopoptoloop: returning $cxix" if $debug_cxstack; + return $cxix; +} + +sub dopoptolabel { + my $label = shift; + my $cxix = $#cxstack; + while ($cxix >= 0 && $cxstack[$cxix]->{type} != CXt_LOOP + && $cxstack[$cxix]->{label} ne $label) { + $cxix--; + } + debug "dopoptolabel: returning $cxix" if $debug_cxstack; + return $cxix; +} + +sub error { + my $format = shift; + my $file = $curcop->[0]->filegv->SV->PV; + my $line = $curcop->[0]->line; + $errors++; + if (@_) { + warn sprintf("%s:%d: $format\n", $file, $line, @_); + } else { + warn sprintf("%s:%d: %s\n", $file, $line, $format); + } +} + +# +# Load pad takes (the elements of) a PADLIST as arguments and loads +# up @pad with Stackobj-derived objects which represent those lexicals. +# If/when perl itself can generate type information (my int $foo) then +# we'll take advantage of that here. Until then, we'll use various hacks +# to tell the compiler when we want a lexical to be a particular type +# or to be a register. +# +sub load_pad { + my ($namelistav, $valuelistav) = @_; + @padlist = @_; + my @namelist = $namelistav->ARRAY; + my @valuelist = $valuelistav->ARRAY; + my $ix; + @pad = (); + debug "load_pad: $#namelist names, $#valuelist values\n" if $debug_pad; + # Temporary lexicals don't get named so it's possible for @valuelist + # to be strictly longer than @namelist. We count $ix up to the end of + # @valuelist but index into @namelist for the name. Any temporaries which + # run off the end of @namelist will make $namesv undefined and we treat + # that the same as having an explicit SPECIAL sv_undef object in @namelist. + # [XXX If/when @_ becomes a lexical, we must start at 0 here.] + for ($ix = 1; $ix < @valuelist; $ix++) { + my $namesv = $namelist[$ix]; + my $type = T_UNKNOWN; + my $flags = 0; + my $name = "tmp$ix"; + my $class = class($namesv); + if (!defined($namesv) || $class eq "SPECIAL") { + # temporaries have &PL_sv_undef instead of a PVNV for a name + $flags = VALID_SV|TEMPORARY|REGISTER; + } else { + if ($namesv->PV =~ /^\$(.*)_([di])(r?)$/) { + $name = $1; + if ($2 eq "i") { + $type = T_INT; + $flags = VALID_SV|VALID_INT; + } elsif ($2 eq "d") { + $type = T_DOUBLE; + $flags = VALID_SV|VALID_DOUBLE; + } + $flags |= REGISTER if $3; + } + } + $pad[$ix] = new B::Stackobj::Padsv ($type, $flags, $ix, + "i_$name", "d_$name"); + declare("IV", $type == T_INT ? "i_$name = 0" : "i_$name"); + declare("double", $type == T_DOUBLE ? "d_$name = 0" : "d_$name"); + debug sprintf("PL_curpad[$ix] = %s\n", $pad[$ix]->peek) if $debug_pad; + } +} + +# +# Debugging stuff +# +sub peek_stack { sprintf "stack = %s\n", join(" ", map($_->minipeek, @stack)) } + +# +# OP stuff +# + +sub label { + my $op = shift; + # XXX Preserve original label name for "real" labels? + return sprintf("lab_%x", $$op); +} + +sub write_label { + my $op = shift; + push_runtime(sprintf(" %s:", label($op))); +} + +sub loadop { + my $op = shift; + my $opsym = $op->save; + runtime("PL_op = $opsym;") unless $know_op; + return $opsym; +} + +sub doop { + my $op = shift; + my $ppname = $op->ppaddr; + my $sym = loadop($op); + runtime("DOOP($ppname);"); + $know_op = 1; + return $sym; +} + +sub gimme { + my $op = shift; + my $flags = $op->flags; + return (($flags & OPf_KNOW) ? ($flags & OPf_LIST) : "dowantarray()"); +} + +# +# Code generation for PP code +# + +sub pp_null { + my $op = shift; + return $op->next; +} + +sub pp_stub { + my $op = shift; + my $gimme = gimme($op); + if ($gimme != 1) { + # XXX Change to push a constant sv_undef Stackobj onto @stack + write_back_stack(); + runtime("if ($gimme != G_ARRAY) XPUSHs(&PL_sv_undef);"); + } + return $op->next; +} + +sub pp_unstack { + my $op = shift; + @stack = (); + runtime("PP_UNSTACK;"); + return $op->next; +} + +sub pp_and { + my $op = shift; + my $next = $op->next; + reload_lexicals(); + unshift(@bblock_todo, $next); + if (@stack >= 1) { + my $bool = pop_bool(); + write_back_stack(); + runtime(sprintf("if (!$bool) goto %s;", label($next))); + } else { + runtime(sprintf("if (!%s) goto %s;", top_bool(), label($next)), + "*sp--;"); + } + return $op->other; +} + +sub pp_or { + my $op = shift; + my $next = $op->next; + reload_lexicals(); + unshift(@bblock_todo, $next); + if (@stack >= 1) { + my $obj = pop @stack; + write_back_stack(); + runtime(sprintf("if (%s) { XPUSHs(%s); goto %s; }", + $obj->as_numeric, $obj->as_sv, label($next))); + } else { + runtime(sprintf("if (%s) goto %s;", top_bool(), label($next)), + "*sp--;"); + } + return $op->other; +} + +sub pp_cond_expr { + my $op = shift; + my $false = $op->false; + unshift(@bblock_todo, $false); + reload_lexicals(); + my $bool = pop_bool(); + write_back_stack(); + runtime(sprintf("if (!$bool) goto %s;", label($false))); + return $op->true; +} + +sub pp_padsv { + my $op = shift; + my $ix = $op->targ; + push(@stack, $pad[$ix]); + if ($op->flags & OPf_MOD) { + my $private = $op->private; + if ($private & OPpLVAL_INTRO) { + runtime("SAVECLEARSV(PL_curpad[$ix]);"); + } elsif ($private & OPpDEREF) { + runtime(sprintf("vivify_ref(PL_curpad[%d], %d);", + $ix, $private & OPpDEREF)); + $pad[$ix]->invalidate; + } + } + return $op->next; +} + +sub pp_const { + my $op = shift; + my $sv = $op->sv; + my $obj = $constobj{$$sv}; + if (!defined($obj)) { + $obj = $constobj{$$sv} = new B::Stackobj::Const ($sv); + } + push(@stack, $obj); + return $op->next; +} + +sub pp_nextstate { + my $op = shift; + $curcop->load($op); + @stack = (); + debug(sprintf("%s:%d\n", $op->filegv->SV->PV, $op->line)) if $debug_lineno; + runtime("TAINT_NOT;") unless $omit_taint; + runtime("sp = PL_stack_base + cxstack[cxstack_ix].blk_oldsp;"); + if ($freetmps_each_bblock || $freetmps_each_loop) { + $need_freetmps = 1; + } else { + runtime("FREETMPS;"); + } + return $op->next; +} + +sub pp_dbstate { + my $op = shift; + $curcop->invalidate; # XXX? + return default_pp($op); +} + +sub pp_rv2gv { $curcop->write_back; default_pp(@_) } +sub pp_bless { $curcop->write_back; default_pp(@_) } +sub pp_repeat { $curcop->write_back; default_pp(@_) } +# The following subs need $curcop->write_back if we decide to support arybase: +# pp_pos, pp_substr, pp_index, pp_rindex, pp_aslice, pp_lslice, pp_splice +sub pp_sort { $curcop->write_back; default_pp(@_) } +sub pp_caller { $curcop->write_back; default_pp(@_) } +sub pp_reset { $curcop->write_back; default_pp(@_) } + +sub pp_gv { + my $op = shift; + my $gvsym = $op->gv->save; + write_back_stack(); + runtime("XPUSHs((SV*)$gvsym);"); + return $op->next; +} + +sub pp_gvsv { + my $op = shift; + my $gvsym = $op->gv->save; + write_back_stack(); + if ($op->private & OPpLVAL_INTRO) { + runtime("XPUSHs(save_scalar($gvsym));"); + } else { + runtime("XPUSHs(GvSV($gvsym));"); + } + return $op->next; +} + +sub pp_aelemfast { + my $op = shift; + my $gvsym = $op->gv->save; + my $ix = $op->private; + my $flag = $op->flags & OPf_MOD; + write_back_stack(); + runtime("svp = av_fetch(GvAV($gvsym), $ix, $flag);", + "PUSHs(svp ? *svp : &PL_sv_undef);"); + return $op->next; +} + +sub int_binop { + my ($op, $operator) = @_; + if ($op->flags & OPf_STACKED) { + my $right = pop_int(); + if (@stack >= 1) { + my $left = top_int(); + $stack[-1]->set_int(&$operator($left, $right)); + } else { + runtime(sprintf("sv_setiv(TOPs, %s);",&$operator("TOPi", $right))); + } + } else { + my $targ = $pad[$op->targ]; + my $right = new B::Pseudoreg ("IV", "riv"); + my $left = new B::Pseudoreg ("IV", "liv"); + runtime(sprintf("$$right = %s; $$left = %s;", pop_int(), pop_int)); + $targ->set_int(&$operator($$left, $$right)); + push(@stack, $targ); + } + return $op->next; +} + +sub INTS_CLOSED () { 0x1 } +sub INT_RESULT () { 0x2 } +sub NUMERIC_RESULT () { 0x4 } + +sub numeric_binop { + my ($op, $operator, $flags) = @_; + my $force_int = 0; + $force_int ||= ($flags & INT_RESULT); + $force_int ||= ($flags & INTS_CLOSED && @stack >= 2 + && valid_int($stack[-2]) && valid_int($stack[-1])); + if ($op->flags & OPf_STACKED) { + my $right = pop_numeric(); + if (@stack >= 1) { + my $left = top_numeric(); + if ($force_int) { + $stack[-1]->set_int(&$operator($left, $right)); + } else { + $stack[-1]->set_numeric(&$operator($left, $right)); + } + } else { + if ($force_int) { + runtime(sprintf("sv_setiv(TOPs, %s);", + &$operator("TOPi", $right))); + } else { + runtime(sprintf("sv_setnv(TOPs, %s);", + &$operator("TOPn", $right))); + } + } + } else { + my $targ = $pad[$op->targ]; + $force_int ||= ($targ->{type} == T_INT); + if ($force_int) { + my $right = new B::Pseudoreg ("IV", "riv"); + my $left = new B::Pseudoreg ("IV", "liv"); + runtime(sprintf("$$right = %s; $$left = %s;", + pop_numeric(), pop_numeric)); + $targ->set_int(&$operator($$left, $$right)); + } else { + my $right = new B::Pseudoreg ("double", "rnv"); + my $left = new B::Pseudoreg ("double", "lnv"); + runtime(sprintf("$$right = %s; $$left = %s;", + pop_numeric(), pop_numeric)); + $targ->set_numeric(&$operator($$left, $$right)); + } + push(@stack, $targ); + } + return $op->next; +} + +sub sv_binop { + my ($op, $operator, $flags) = @_; + if ($op->flags & OPf_STACKED) { + my $right = pop_sv(); + if (@stack >= 1) { + my $left = top_sv(); + if ($flags & INT_RESULT) { + $stack[-1]->set_int(&$operator($left, $right)); + } elsif ($flags & NUMERIC_RESULT) { + $stack[-1]->set_numeric(&$operator($left, $right)); + } else { + # XXX Does this work? + runtime(sprintf("sv_setsv($left, %s);", + &$operator($left, $right))); + $stack[-1]->invalidate; + } + } else { + my $f; + if ($flags & INT_RESULT) { + $f = "sv_setiv"; + } elsif ($flags & NUMERIC_RESULT) { + $f = "sv_setnv"; + } else { + $f = "sv_setsv"; + } + runtime(sprintf("%s(TOPs, %s);", $f, &$operator("TOPs", $right))); + } + } else { + my $targ = $pad[$op->targ]; + runtime(sprintf("right = %s; left = %s;", pop_sv(), pop_sv)); + if ($flags & INT_RESULT) { + $targ->set_int(&$operator("left", "right")); + } elsif ($flags & NUMERIC_RESULT) { + $targ->set_numeric(&$operator("left", "right")); + } else { + # XXX Does this work? + runtime(sprintf("sv_setsv(%s, %s);", + $targ->as_sv, &$operator("left", "right"))); + $targ->invalidate; + } + push(@stack, $targ); + } + return $op->next; +} + +sub bool_int_binop { + my ($op, $operator) = @_; + my $right = new B::Pseudoreg ("IV", "riv"); + my $left = new B::Pseudoreg ("IV", "liv"); + runtime(sprintf("$$right = %s; $$left = %s;", pop_int(), pop_int())); + my $bool = new B::Stackobj::Bool (new B::Pseudoreg ("int", "b")); + $bool->set_int(&$operator($$left, $$right)); + push(@stack, $bool); + return $op->next; +} + +sub bool_numeric_binop { + my ($op, $operator) = @_; + my $right = new B::Pseudoreg ("double", "rnv"); + my $left = new B::Pseudoreg ("double", "lnv"); + runtime(sprintf("$$right = %s; $$left = %s;", + pop_numeric(), pop_numeric())); + my $bool = new B::Stackobj::Bool (new B::Pseudoreg ("int", "b")); + $bool->set_numeric(&$operator($$left, $$right)); + push(@stack, $bool); + return $op->next; +} + +sub bool_sv_binop { + my ($op, $operator) = @_; + runtime(sprintf("right = %s; left = %s;", pop_sv(), pop_sv())); + my $bool = new B::Stackobj::Bool (new B::Pseudoreg ("int", "b")); + $bool->set_numeric(&$operator("left", "right")); + push(@stack, $bool); + return $op->next; +} + +sub infix_op { + my $opname = shift; + return sub { "$_[0] $opname $_[1]" } +} + +sub prefix_op { + my $opname = shift; + return sub { sprintf("%s(%s)", $opname, join(", ", @_)) } +} + +BEGIN { + my $plus_op = infix_op("+"); + my $minus_op = infix_op("-"); + my $multiply_op = infix_op("*"); + my $divide_op = infix_op("/"); + my $modulo_op = infix_op("%"); + my $lshift_op = infix_op("<<"); + my $rshift_op = infix_op(">>"); + my $ncmp_op = sub { "($_[0] > $_[1] ? 1 : ($_[0] < $_[1]) ? -1 : 0)" }; + my $scmp_op = prefix_op("sv_cmp"); + my $seq_op = prefix_op("sv_eq"); + my $sne_op = prefix_op("!sv_eq"); + my $slt_op = sub { "sv_cmp($_[0], $_[1]) < 0" }; + my $sgt_op = sub { "sv_cmp($_[0], $_[1]) > 0" }; + my $sle_op = sub { "sv_cmp($_[0], $_[1]) <= 0" }; + my $sge_op = sub { "sv_cmp($_[0], $_[1]) >= 0" }; + my $eq_op = infix_op("=="); + my $ne_op = infix_op("!="); + my $lt_op = infix_op("<"); + my $gt_op = infix_op(">"); + my $le_op = infix_op("<="); + my $ge_op = infix_op(">="); + + # + # XXX The standard perl PP code has extra handling for + # some special case arguments of these operators. + # + sub pp_add { numeric_binop($_[0], $plus_op, INTS_CLOSED) } + sub pp_subtract { numeric_binop($_[0], $minus_op, INTS_CLOSED) } + sub pp_multiply { numeric_binop($_[0], $multiply_op, INTS_CLOSED) } + sub pp_divide { numeric_binop($_[0], $divide_op) } + sub pp_modulo { int_binop($_[0], $modulo_op) } # differs from perl's + sub pp_ncmp { numeric_binop($_[0], $ncmp_op, INT_RESULT) } + + sub pp_left_shift { int_binop($_[0], $lshift_op) } + sub pp_right_shift { int_binop($_[0], $rshift_op) } + sub pp_i_add { int_binop($_[0], $plus_op) } + sub pp_i_subtract { int_binop($_[0], $minus_op) } + sub pp_i_multiply { int_binop($_[0], $multiply_op) } + sub pp_i_divide { int_binop($_[0], $divide_op) } + sub pp_i_modulo { int_binop($_[0], $modulo_op) } + + sub pp_eq { bool_numeric_binop($_[0], $eq_op) } + sub pp_ne { bool_numeric_binop($_[0], $ne_op) } + sub pp_lt { bool_numeric_binop($_[0], $lt_op) } + sub pp_gt { bool_numeric_binop($_[0], $gt_op) } + sub pp_le { bool_numeric_binop($_[0], $le_op) } + sub pp_ge { bool_numeric_binop($_[0], $ge_op) } + + sub pp_i_eq { bool_int_binop($_[0], $eq_op) } + sub pp_i_ne { bool_int_binop($_[0], $ne_op) } + sub pp_i_lt { bool_int_binop($_[0], $lt_op) } + sub pp_i_gt { bool_int_binop($_[0], $gt_op) } + sub pp_i_le { bool_int_binop($_[0], $le_op) } + sub pp_i_ge { bool_int_binop($_[0], $ge_op) } + + sub pp_scmp { sv_binop($_[0], $scmp_op, INT_RESULT) } + sub pp_slt { bool_sv_binop($_[0], $slt_op) } + sub pp_sgt { bool_sv_binop($_[0], $sgt_op) } + sub pp_sle { bool_sv_binop($_[0], $sle_op) } + sub pp_sge { bool_sv_binop($_[0], $sge_op) } + sub pp_seq { bool_sv_binop($_[0], $seq_op) } + sub pp_sne { bool_sv_binop($_[0], $sne_op) } +} + + +sub pp_sassign { + my $op = shift; + my $backwards = $op->private & OPpASSIGN_BACKWARDS; + my ($dst, $src); + if (@stack >= 2) { + $dst = pop @stack; + $src = pop @stack; + ($src, $dst) = ($dst, $src) if $backwards; + my $type = $src->{type}; + if ($type == T_INT) { + $dst->set_int($src->as_int); + } elsif ($type == T_DOUBLE) { + $dst->set_numeric($src->as_numeric); + } else { + $dst->set_sv($src->as_sv); + } + push(@stack, $dst); + } elsif (@stack == 1) { + if ($backwards) { + my $src = pop @stack; + my $type = $src->{type}; + runtime("if (PL_tainting && PL_tainted) TAINT_NOT;"); + if ($type == T_INT) { + runtime sprintf("sv_setiv(TOPs, %s);", $src->as_int); + } elsif ($type == T_DOUBLE) { + runtime sprintf("sv_setnv(TOPs, %s);", $src->as_double); + } else { + runtime sprintf("sv_setsv(TOPs, %s);", $src->as_sv); + } + runtime("SvSETMAGIC(TOPs);"); + } else { + my $dst = $stack[-1]; + my $type = $dst->{type}; + runtime("sv = POPs;"); + runtime("MAYBE_TAINT_SASSIGN_SRC(sv);"); + if ($type == T_INT) { + $dst->set_int("SvIV(sv)"); + } elsif ($type == T_DOUBLE) { + $dst->set_double("SvNV(sv)"); + } else { + runtime("SvSetSV($dst->{sv}, sv);"); + $dst->invalidate; + } + } + } else { + if ($backwards) { + runtime("src = POPs; dst = TOPs;"); + } else { + runtime("dst = POPs; src = TOPs;"); + } + runtime("MAYBE_TAINT_SASSIGN_SRC(src);", + "SvSetSV(dst, src);", + "SvSETMAGIC(dst);", + "SETs(dst);"); + } + return $op->next; +} + +sub pp_preinc { + my $op = shift; + if (@stack >= 1) { + my $obj = $stack[-1]; + my $type = $obj->{type}; + if ($type == T_INT || $type == T_DOUBLE) { + $obj->set_int($obj->as_int . " + 1"); + } else { + runtime sprintf("PP_PREINC(%s);", $obj->as_sv); + $obj->invalidate(); + } + } else { + runtime sprintf("PP_PREINC(TOPs);"); + } + return $op->next; +} + +sub pp_pushmark { + my $op = shift; + write_back_stack(); + runtime("PUSHMARK(sp);"); + return $op->next; +} + +sub pp_list { + my $op = shift; + write_back_stack(); + my $gimme = gimme($op); + if ($gimme == 1) { # sic + runtime("POPMARK;"); # need this even though not a "full" pp_list + } else { + runtime("PP_LIST($gimme);"); + } + return $op->next; +} + +sub pp_entersub { + my $op = shift; + write_back_lexicals(REGISTER|TEMPORARY); + write_back_stack(); + my $sym = doop($op); + runtime("while (PL_op != ($sym)->op_next && PL_op != (OP*)0 ){"); + runtime("PL_op = (*PL_op->op_ppaddr)(ARGS);"); + runtime("SPAGAIN;}"); + $know_op = 0; + invalidate_lexicals(REGISTER|TEMPORARY); + return $op->next; +} + +sub pp_goto{ + + my $op = shift; + my $ppname = $op->ppaddr; + write_back_lexicals() unless $skip_lexicals{$ppname}; + write_back_stack() unless $skip_stack{$ppname}; + my $sym=doop($op); + runtime("if (PL_op != ($sym)->op_next && PL_op != (OP*)0){return PL_op;}"); + invalidate_lexicals() unless $skip_invalidate{$ppname}; + return $op->next; +} +sub pp_enterwrite { + my $op = shift; + pp_entersub($op); +} + +sub pp_leavewrite { + my $op = shift; + write_back_lexicals(REGISTER|TEMPORARY); + write_back_stack(); + my $sym = doop($op); + # XXX Is this the right way to distinguish between it returning + # CvSTART(cv) (via doform) and pop_return()? + runtime("if (PL_op) PL_op = (*PL_op->op_ppaddr)(ARGS);"); + runtime("SPAGAIN;"); + $know_op = 0; + invalidate_lexicals(REGISTER|TEMPORARY); + return $op->next; +} + +sub doeval { + my $op = shift; + $curcop->write_back; + write_back_lexicals(REGISTER|TEMPORARY); + write_back_stack(); + my $sym = loadop($op); + my $ppaddr = $op->ppaddr; + runtime("PP_EVAL($ppaddr, ($sym)->op_next);"); + $know_op = 1; + invalidate_lexicals(REGISTER|TEMPORARY); + return $op->next; +} + +sub pp_entereval { doeval(@_) } +sub pp_require { doeval(@_) } +sub pp_dofile { doeval(@_) } + +sub pp_entertry { + my $op = shift; + $curcop->write_back; + write_back_lexicals(REGISTER|TEMPORARY); + write_back_stack(); + my $sym = doop($op); + my $jmpbuf = sprintf("jmpbuf%d", $jmpbuf_ix++); + declare("Sigjmp_buf", $jmpbuf); + runtime(sprintf("PP_ENTERTRY(%s,%s);", $jmpbuf, label($op->other->next))); + invalidate_lexicals(REGISTER|TEMPORARY); + return $op->next; +} + +sub pp_grepstart { + my $op = shift; + if ($need_freetmps && $freetmps_each_loop) { + runtime("FREETMPS;"); # otherwise the grepwhile loop messes things up + $need_freetmps = 0; + } + write_back_stack(); + doop($op); + return $op->next->other; +} + +sub pp_mapstart { + my $op = shift; + if ($need_freetmps && $freetmps_each_loop) { + runtime("FREETMPS;"); # otherwise the mapwhile loop messes things up + $need_freetmps = 0; + } + write_back_stack(); + doop($op); + return $op->next->other; +} + +sub pp_grepwhile { + my $op = shift; + my $next = $op->next; + unshift(@bblock_todo, $next); + write_back_lexicals(); + write_back_stack(); + my $sym = doop($op); + # pp_grepwhile can return either op_next or op_other and we need to + # be able to distinguish the two at runtime. Since it's possible for + # both ops to be "inlined", the fields could both be zero. To get + # around that, we hack op_next to be our own op (purely because we + # know it's a non-NULL pointer and can't be the same as op_other). + $init->add("((LOGOP*)$sym)->op_next = $sym;"); + runtime(sprintf("if (PL_op == ($sym)->op_next) goto %s;", label($next))); + $know_op = 0; + return $op->other; +} + +sub pp_mapwhile { + pp_grepwhile(@_); +} + +sub pp_return { + my $op = shift; + write_back_lexicals(REGISTER|TEMPORARY); + write_back_stack(); + doop($op); + runtime("PUTBACK;", "return (PL_op)?PL_op->op_next:0;"); + $know_op = 0; + return $op->next; +} + +sub nyi { + my $op = shift; + warn sprintf("%s not yet implemented properly\n", $op->ppaddr); + return default_pp($op); +} + +sub pp_range { + my $op = shift; + my $flags = $op->flags; + if (!($flags & OPf_KNOW)) { + error("context of range unknown at compile-time"); + } + write_back_lexicals(); + write_back_stack(); + if (!($flags & OPf_LIST)) { + # We need to save our UNOP structure since pp_flop uses + # it to find and adjust out targ. We don't need it ourselves. + $op->save; + runtime sprintf("if (SvTRUE(PL_curpad[%d])) goto %s;", + $op->targ, label($op->false)); + unshift(@bblock_todo, $op->false); + } + return $op->true; +} + +sub pp_flip { + my $op = shift; + my $flags = $op->flags; + if (!($flags & OPf_KNOW)) { + error("context of flip unknown at compile-time"); + } + if ($flags & OPf_LIST) { + return $op->first->false; + } + write_back_lexicals(); + write_back_stack(); + # We need to save our UNOP structure since pp_flop uses + # it to find and adjust out targ. We don't need it ourselves. + $op->save; + my $ix = $op->targ; + my $rangeix = $op->first->targ; + runtime(($op->private & OPpFLIP_LINENUM) ? + "if (PL_last_in_gv && SvIV(TOPs) == IoLINES(GvIOp(PL_last_in_gv))) {" + : "if (SvTRUE(TOPs)) {"); + runtime("\tsv_setiv(PL_curpad[$rangeix], 1);"); + if ($op->flags & OPf_SPECIAL) { + runtime("sv_setiv(PL_curpad[$ix], 1);"); + } else { + runtime("\tsv_setiv(PL_curpad[$ix], 0);", + "\tsp--;", + sprintf("\tgoto %s;", label($op->first->false))); + } + runtime("}", + qq{sv_setpv(PL_curpad[$ix], "");}, + "SETs(PL_curpad[$ix]);"); + $know_op = 0; + return $op->next; +} + +sub pp_flop { + my $op = shift; + default_pp($op); + $know_op = 0; + return $op->next; +} + +sub enterloop { + my $op = shift; + my $nextop = $op->nextop; + my $lastop = $op->lastop; + my $redoop = $op->redoop; + $curcop->write_back; + debug "enterloop: pushing on cxstack" if $debug_cxstack; + push(@cxstack, { + type => CXt_LOOP, + op => $op, + "label" => $curcop->[0]->label, + nextop => $nextop, + lastop => $lastop, + redoop => $redoop + }); + $nextop->save; + $lastop->save; + $redoop->save; + return default_pp($op); +} + +sub pp_enterloop { enterloop(@_) } +sub pp_enteriter { enterloop(@_) } + +sub pp_leaveloop { + my $op = shift; + if (!@cxstack) { + die "panic: leaveloop"; + } + debug "leaveloop: popping from cxstack" if $debug_cxstack; + pop(@cxstack); + return default_pp($op); +} + +sub pp_next { + my $op = shift; + my $cxix; + if ($op->flags & OPf_SPECIAL) { + $cxix = dopoptoloop(); + if ($cxix < 0) { + error('"next" used outside loop'); + return $op->next; # ignore the op + } + } else { + $cxix = dopoptolabel($op->pv); + if ($cxix < 0) { + error('Label not found at compile time for "next %s"', $op->pv); + return $op->next; # ignore the op + } + } + default_pp($op); + my $nextop = $cxstack[$cxix]->{nextop}; + push(@bblock_todo, $nextop); + runtime(sprintf("goto %s;", label($nextop))); + return $op->next; +} + +sub pp_redo { + my $op = shift; + my $cxix; + if ($op->flags & OPf_SPECIAL) { + $cxix = dopoptoloop(); + if ($cxix < 0) { + error('"redo" used outside loop'); + return $op->next; # ignore the op + } + } else { + $cxix = dopoptolabel($op->pv); + if ($cxix < 0) { + error('Label not found at compile time for "redo %s"', $op->pv); + return $op->next; # ignore the op + } + } + default_pp($op); + my $redoop = $cxstack[$cxix]->{redoop}; + push(@bblock_todo, $redoop); + runtime(sprintf("goto %s;", label($redoop))); + return $op->next; +} + +sub pp_last { + my $op = shift; + my $cxix; + if ($op->flags & OPf_SPECIAL) { + $cxix = dopoptoloop(); + if ($cxix < 0) { + error('"last" used outside loop'); + return $op->next; # ignore the op + } + } else { + $cxix = dopoptolabel($op->pv); + if ($cxix < 0) { + error('Label not found at compile time for "last %s"', $op->pv); + return $op->next; # ignore the op + } + # XXX Add support for "last" to leave non-loop blocks + if ($cxstack[$cxix]->{type} != CXt_LOOP) { + error('Use of "last" for non-loop blocks is not yet implemented'); + return $op->next; # ignore the op + } + } + default_pp($op); + my $lastop = $cxstack[$cxix]->{lastop}->next; + push(@bblock_todo, $lastop); + runtime(sprintf("goto %s;", label($lastop))); + return $op->next; +} + +sub pp_subst { + my $op = shift; + write_back_lexicals(); + write_back_stack(); + my $sym = doop($op); + my $replroot = $op->pmreplroot; + if ($$replroot) { + runtime sprintf("if (PL_op == ((PMOP*)(%s))->op_pmreplroot) goto %s;", + $sym, label($replroot)); + $op->pmreplstart->save; + push(@bblock_todo, $replroot); + } + invalidate_lexicals(); + return $op->next; +} + +sub pp_substcont { + my $op = shift; + write_back_lexicals(); + write_back_stack(); + doop($op); + my $pmop = $op->other; + warn sprintf("substcont: op = %s, pmop = %s\n", + peekop($op), peekop($pmop));#debug +# my $pmopsym = objsym($pmop); + my $pmopsym = $pmop->save; # XXX can this recurse? + warn "pmopsym = $pmopsym\n";#debug + runtime sprintf("if (PL_op == ((PMOP*)(%s))->op_pmreplstart) goto %s;", + $pmopsym, label($pmop->pmreplstart)); + invalidate_lexicals(); + return $pmop->next; +} + +sub default_pp { + my $op = shift; + my $ppname = $op->ppaddr; + write_back_lexicals() unless $skip_lexicals{$ppname}; + write_back_stack() unless $skip_stack{$ppname}; + doop($op); + # XXX If the only way that ops can write to a TEMPORARY lexical is + # when it's named in $op->targ then we could call + # invalidate_lexicals(TEMPORARY) and avoid having to write back all + # the temporaries. For now, we'll play it safe and write back the lot. + invalidate_lexicals() unless $skip_invalidate{$ppname}; + return $op->next; +} + +sub compile_op { + my $op = shift; + my $ppname = $op->ppaddr; + if (exists $ignore_op{$ppname}) { + return $op->next; + } + debug peek_stack() if $debug_stack; + if ($debug_op) { + debug sprintf("%s [%s]\n", + peekop($op), + $op->flags & OPf_STACKED ? "OPf_STACKED" : $op->targ); + } + no strict 'refs'; + if (defined(&$ppname)) { + $know_op = 0; + return &$ppname($op); + } else { + return default_pp($op); + } +} + +sub compile_bblock { + my $op = shift; + #warn "compile_bblock: ", peekop($op), "\n"; # debug + write_label($op); + $know_op = 0; + do { + $op = compile_op($op); + } while (defined($op) && $$op && !exists($leaders->{$$op})); + write_back_stack(); # boo hoo: big loss + reload_lexicals(); + return $op; +} + +sub cc { + my ($name, $root, $start, @padlist) = @_; + my $op; + init_pp($name); + load_pad(@padlist); + B::Pseudoreg->new_scope; + @cxstack = (); + if ($debug_timings) { + warn sprintf("Basic block analysis at %s\n", timing_info); + } + $leaders = find_leaders($root, $start); + @bblock_todo = ($start, values %$leaders); + if ($debug_timings) { + warn sprintf("Compilation at %s\n", timing_info); + } + while (@bblock_todo) { + $op = shift @bblock_todo; + #warn sprintf("Considering basic block %s\n", peekop($op)); # debug + next if !defined($op) || !$$op || $done{$$op}; + #warn "...compiling it\n"; # debug + do { + $done{$$op} = 1; + $op = compile_bblock($op); + if ($need_freetmps && $freetmps_each_bblock) { + runtime("FREETMPS;"); + $need_freetmps = 0; + } + } while defined($op) && $$op && !$done{$$op}; + if ($need_freetmps && $freetmps_each_loop) { + runtime("FREETMPS;"); + $need_freetmps = 0; + } + if (!$$op) { + runtime("PUTBACK;","return (PL_op)?PL_op->op_next:0;"); + } elsif ($done{$$op}) { + runtime(sprintf("goto %s;", label($op))); + } + } + if ($debug_timings) { + warn sprintf("Saving runtime at %s\n", timing_info); + } + save_runtime(); +} + +sub cc_recurse { + my $ccinfo; + my $start; + $start = cc_queue(@_) if @_; + while ($ccinfo = shift @cc_todo) { + cc(@$ccinfo); + } + return $start; +} + +sub cc_obj { + my ($name, $cvref) = @_; + my $cv = svref_2object($cvref); + my @padlist = $cv->PADLIST->ARRAY; + my $curpad_sym = $padlist[1]->save; + cc_recurse($name, $cv->ROOT, $cv->START, @padlist); +} + +sub cc_main { + my @comppadlist = comppadlist->ARRAY; + my $curpad_nam = $comppadlist[0]->save; + my $curpad_sym = $comppadlist[1]->save; + my $start = cc_recurse("pp_main", main_root, main_start, @comppadlist); + save_unused_subs(@unused_sub_packages); + cc_recurse(); + + return if $errors; + if (!defined($module)) { + $init->add(sprintf("PL_main_root = s\\_%x;", ${main_root()}), + "PL_main_start = $start;", + "PL_curpad = AvARRAY($curpad_sym);", + "av_store(CvPADLIST(PL_main_cv),0,SvREFCNT_inc($curpad_nam));", + "av_store(CvPADLIST(PL_main_cv),1,SvREFCNT_inc($curpad_sym));"); + } + output_boilerplate(); + print "\n"; + output_all("perl_init"); + output_runtime(); + print "\n"; + output_main(); + if (defined($module)) { + my $cmodule = $module; + $cmodule =~ s/::/__/g; + print <<"EOT"; + +#include "XSUB.h" +XS(boot_$cmodule) +{ + dXSARGS; + perl_init(); + ENTER; + SAVETMPS; + SAVESPTR(PL_curpad); + SAVESPTR(PL_op); + PL_curpad = AvARRAY($curpad_sym); + PL_op = $start; + pp_main(ARGS); + FREETMPS; + LEAVE; + ST(0) = &PL_sv_yes; + XSRETURN(1); +} +EOT + } + if ($debug_timings) { + warn sprintf("Done at %s\n", timing_info); + } +} + +sub compile { + my @options = @_; + my ($option, $opt, $arg); + OPTION: + while ($option = shift @options) { + if ($option =~ /^-(.)(.*)/) { + $opt = $1; + $arg = $2; + } else { + unshift @options, $option; + last OPTION; + } + if ($opt eq "-" && $arg eq "-") { + shift @options; + last OPTION; + } elsif ($opt eq "o") { + $arg ||= shift @options; + open(STDOUT, ">$arg") or return "open '>$arg': $!\n"; + } elsif ($opt eq "n") { + $arg ||= shift @options; + $module_name = $arg; + } elsif ($opt eq "u") { + $arg ||= shift @options; + push(@unused_sub_packages, $arg); + } elsif ($opt eq "f") { + $arg ||= shift @options; + my $value = $arg !~ s/^no-//; + $arg =~ s/-/_/g; + my $ref = $optimise{$arg}; + if (defined($ref)) { + $$ref = $value; + } else { + warn qq(ignoring unknown optimisation option "$arg"\n); + } + } elsif ($opt eq "O") { + $arg = 1 if $arg eq ""; + my $ref; + foreach $ref (values %optimise) { + $$ref = 0; + } + if ($arg >= 2) { + $freetmps_each_loop = 1; + } + if ($arg >= 1) { + $freetmps_each_bblock = 1 unless $freetmps_each_loop; + } + } elsif ($opt eq "m") { + $arg ||= shift @options; + $module = $arg; + push(@unused_sub_packages, $arg); + } elsif ($opt eq "p") { + $arg ||= shift @options; + $patchlevel = $arg; + } elsif ($opt eq "D") { + $arg ||= shift @options; + foreach $arg (split(//, $arg)) { + if ($arg eq "o") { + B->debug(1); + } elsif ($arg eq "O") { + $debug_op = 1; + } elsif ($arg eq "s") { + $debug_stack = 1; + } elsif ($arg eq "c") { + $debug_cxstack = 1; + } elsif ($arg eq "p") { + $debug_pad = 1; + } elsif ($arg eq "r") { + $debug_runtime = 1; + } elsif ($arg eq "S") { + $debug_shadow = 1; + } elsif ($arg eq "q") { + $debug_queue = 1; + } elsif ($arg eq "l") { + $debug_lineno = 1; + } elsif ($arg eq "t") { + $debug_timings = 1; + } + } + } + } + init_sections(); + $init = B::Section->get("init"); + $decl = B::Section->get("decl"); + + if (@options) { + return sub { + my ($objname, $ppname); + foreach $objname (@options) { + $objname = "main::$objname" unless $objname =~ /::/; + ($ppname = $objname) =~ s/^.*?:://; + eval "cc_obj(qq(pp_sub_$ppname), \\&$objname)"; + die "cc_obj(qq(pp_sub_$ppname, \\&$objname) failed: $@" if $@; + return if $errors; + } + output_boilerplate(); + print "\n"; + output_all($module_name || "init_module"); + output_runtime(); + } + } else { + return sub { cc_main() }; + } +} + +1; + +__END__ + +=head1 NAME + +B::CC - Perl compiler's optimized C translation backend + +=head1 SYNOPSIS + + perl -MO=CC[,OPTIONS] foo.pl + +=head1 DESCRIPTION + +This compiler backend takes Perl source and generates C source code +corresponding to the flow of your program. In other words, this +backend is somewhat a "real" compiler in the sense that many people +think about compilers. Note however that, currently, it is a very +poor compiler in that although it generates (mostly, or at least +sometimes) correct code, it performs relatively few optimisations. +This will change as the compiler develops. The result is that +running an executable compiled with this backend may start up more +quickly than running the original Perl program (a feature shared +by the B<C> compiler backend--see F<B::C>) and may also execute +slightly faster. This is by no means a good optimising compiler--yet. + +=head1 OPTIONS + +If there are any non-option arguments, they are taken to be +names of objects to be saved (probably doesn't work properly yet). +Without extra arguments, it saves the main program. + +=over 4 + +=item B<-ofilename> + +Output to filename instead of STDOUT + +=item B<-v> + +Verbose compilation (currently gives a few compilation statistics). + +=item B<--> + +Force end of options + +=item B<-uPackname> + +Force apparently unused subs from package Packname to be compiled. +This allows programs to use eval "foo()" even when sub foo is never +seen to be used at compile time. The down side is that any subs which +really are never used also have code generated. This option is +necessary, for example, if you have a signal handler foo which you +initialise with C<$SIG{BAR} = "foo">. A better fix, though, is just +to change it to C<$SIG{BAR} = \&foo>. You can have multiple B<-u> +options. The compiler tries to figure out which packages may possibly +have subs in which need compiling but the current version doesn't do +it very well. In particular, it is confused by nested packages (i.e. +of the form C<A::B>) where package C<A> does not contain any subs. + +=item B<-mModulename> + +Instead of generating source for a runnable executable, generate +source for an XSUB module. The boot_Modulename function (which +DynaLoader can look for) does the appropriate initialisation and runs +the main part of the Perl source that is being compiled. + + +=item B<-D> + +Debug options (concatenated or separate flags like C<perl -D>). + +=item B<-Dr> + +Writes debugging output to STDERR just as it's about to write to the +program's runtime (otherwise writes debugging info as comments in +its C output). + +=item B<-DO> + +Outputs each OP as it's compiled + +=item B<-Ds> + +Outputs the contents of the shadow stack at each OP + +=item B<-Dp> + +Outputs the contents of the shadow pad of lexicals as it's loaded for +each sub or the main program. + +=item B<-Dq> + +Outputs the name of each fake PP function in the queue as it's about +to process it. + +=item B<-Dl> + +Output the filename and line number of each original line of Perl +code as it's processed (C<pp_nextstate>). + +=item B<-Dt> + +Outputs timing information of compilation stages. + +=item B<-f> + +Force optimisations on or off one at a time. + +=item B<-ffreetmps-each-bblock> + +Delays FREETMPS from the end of each statement to the end of the each +basic block. + +=item B<-ffreetmps-each-loop> + +Delays FREETMPS from the end of each statement to the end of the group +of basic blocks forming a loop. At most one of the freetmps-each-* +options can be used. + +=item B<-fomit-taint> + +Omits generating code for handling perl's tainting mechanism. + +=item B<-On> + +Optimisation level (n = 0, 1, 2, ...). B<-O> means B<-O1>. +Currently, B<-O1> sets B<-ffreetmps-each-bblock> and B<-O2> +sets B<-ffreetmps-each-loop>. + +=back + +=head1 EXAMPLES + + perl -MO=CC,-O2,-ofoo.c foo.pl + perl cc_harness -o foo foo.c + +Note that C<cc_harness> lives in the C<B> subdirectory of your perl +library directory. The utility called C<perlcc> may also be used to +help make use of this compiler. + + perl -MO=CC,-mFoo,-oFoo.c Foo.pm + perl cc_harness -shared -c -o Foo.so Foo.c + +=head1 BUGS + +Plenty. Current status: experimental. + +=head1 DIFFERENCES + +These aren't really bugs but they are constructs which are heavily +tied to perl's compile-and-go implementation and with which this +compiler backend cannot cope. + +=head2 Loops + +Standard perl calculates the target of "next", "last", and "redo" +at run-time. The compiler calculates the targets at compile-time. +For example, the program + + sub skip_on_odd { next NUMBER if $_[0] % 2 } + NUMBER: for ($i = 0; $i < 5; $i++) { + skip_on_odd($i); + print $i; + } + +produces the output + + 024 + +with standard perl but gives a compile-time error with the compiler. + +=head2 Context of ".." + +The context (scalar or array) of the ".." operator determines whether +it behaves as a range or a flip/flop. Standard perl delays until +runtime the decision of which context it is in but the compiler needs +to know the context at compile-time. For example, + + @a = (4,6,1,0,0,1); + sub range { (shift @a)..(shift @a) } + print range(); + while (@a) { print scalar(range()) } + +generates the output + + 456123E0 + +with standard Perl but gives a compile-time error with compiled Perl. + +=head2 Arithmetic + +Compiled Perl programs use native C arithemtic much more frequently +than standard perl. Operations on large numbers or on boundary +cases may produce different behaviour. + +=head2 Deprecated features + +Features of standard perl such as C<$[> which have been deprecated +in standard perl since Perl5 was released have not been implemented +in the compiler. + +=head1 AUTHOR + +Malcolm Beattie, C<mbeattie@sable.ox.ac.uk> + +=cut diff --git a/gnu/usr.bin/perl/ext/B/B/Debug.pm b/gnu/usr.bin/perl/ext/B/B/Debug.pm new file mode 100644 index 00000000000..7754a5a8079 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/B/Debug.pm @@ -0,0 +1,283 @@ +package B::Debug; +use strict; +use B qw(peekop class walkoptree walkoptree_exec + main_start main_root cstring sv_undef); +use B::Asmdata qw(@specialsv_name); + +my %done_gv; + +sub B::OP::debug { + my ($op) = @_; + printf <<'EOT', class($op), $$op, ${$op->next}, ${$op->sibling}, $op->ppaddr, $op->targ, $op->type, $op->seq, $op->flags, $op->private; +%s (0x%lx) + op_next 0x%x + op_sibling 0x%x + op_ppaddr %s + op_targ %d + op_type %d + op_seq %d + op_flags %d + op_private %d +EOT +} + +sub B::UNOP::debug { + my ($op) = @_; + $op->B::OP::debug(); + printf "\top_first\t0x%x\n", ${$op->first}; +} + +sub B::BINOP::debug { + my ($op) = @_; + $op->B::UNOP::debug(); + printf "\top_last\t\t0x%x\n", ${$op->last}; +} + +sub B::LOGOP::debug { + my ($op) = @_; + $op->B::UNOP::debug(); + printf "\top_other\t0x%x\n", ${$op->other}; +} + +sub B::CONDOP::debug { + my ($op) = @_; + $op->B::UNOP::debug(); + printf "\top_true\t0x%x\n", ${$op->true}; + printf "\top_false\t0x%x\n", ${$op->false}; +} + +sub B::LISTOP::debug { + my ($op) = @_; + $op->B::BINOP::debug(); + printf "\top_children\t%d\n", $op->children; +} + +sub B::PMOP::debug { + my ($op) = @_; + $op->B::LISTOP::debug(); + printf "\top_pmreplroot\t0x%x\n", ${$op->pmreplroot}; + printf "\top_pmreplstart\t0x%x\n", ${$op->pmreplstart}; + printf "\top_pmnext\t0x%x\n", ${$op->pmnext}; + printf "\top_pmregexp->precomp\t%s\n", cstring($op->precomp); + printf "\top_pmflags\t0x%x\n", $op->pmflags; + $op->pmshort->debug; + $op->pmreplroot->debug; +} + +sub B::COP::debug { + my ($op) = @_; + $op->B::OP::debug(); + my ($filegv) = $op->filegv; + printf <<'EOT', $op->label, ${$op->stash}, $$filegv, $op->seq, $op->arybase, $op->line; + cop_label %s + cop_stash 0x%x + cop_filegv 0x%x + cop_seq %d + cop_arybase %d + cop_line %d +EOT + $filegv->debug; +} + +sub B::SVOP::debug { + my ($op) = @_; + $op->B::OP::debug(); + printf "\top_sv\t\t0x%x\n", ${$op->sv}; + $op->sv->debug; +} + +sub B::PVOP::debug { + my ($op) = @_; + $op->B::OP::debug(); + printf "\top_pv\t\t0x%x\n", $op->pv; +} + +sub B::GVOP::debug { + my ($op) = @_; + $op->B::OP::debug(); + printf "\top_gv\t\t0x%x\n", ${$op->gv}; + $op->gv->debug; +} + +sub B::CVOP::debug { + my ($op) = @_; + $op->B::OP::debug(); + printf "\top_cv\t\t0x%x\n", ${$op->cv}; +} + +sub B::NULL::debug { + my ($sv) = @_; + if ($$sv == ${sv_undef()}) { + print "&sv_undef\n"; + } else { + printf "NULL (0x%x)\n", $$sv; + } +} + +sub B::SV::debug { + my ($sv) = @_; + if (!$$sv) { + print class($sv), " = NULL\n"; + return; + } + printf <<'EOT', class($sv), $$sv, $sv->REFCNT, $sv->FLAGS; +%s (0x%x) + REFCNT %d + FLAGS 0x%x +EOT +} + +sub B::PV::debug { + my ($sv) = @_; + $sv->B::SV::debug(); + my $pv = $sv->PV(); + printf <<'EOT', cstring($pv), length($pv); + xpv_pv %s + xpv_cur %d +EOT +} + +sub B::IV::debug { + my ($sv) = @_; + $sv->B::SV::debug(); + printf "\txiv_iv\t\t%d\n", $sv->IV; +} + +sub B::NV::debug { + my ($sv) = @_; + $sv->B::IV::debug(); + printf "\txnv_nv\t\t%s\n", $sv->NV; +} + +sub B::PVIV::debug { + my ($sv) = @_; + $sv->B::PV::debug(); + printf "\txiv_iv\t\t%d\n", $sv->IV; +} + +sub B::PVNV::debug { + my ($sv) = @_; + $sv->B::PVIV::debug(); + printf "\txnv_nv\t\t%s\n", $sv->NV; +} + +sub B::PVLV::debug { + my ($sv) = @_; + $sv->B::PVNV::debug(); + printf "\txlv_targoff\t%d\n", $sv->TARGOFF; + printf "\txlv_targlen\t%u\n", $sv->TARGLEN; + printf "\txlv_type\t%s\n", cstring(chr($sv->TYPE)); +} + +sub B::BM::debug { + my ($sv) = @_; + $sv->B::PVNV::debug(); + printf "\txbm_useful\t%d\n", $sv->USEFUL; + printf "\txbm_previous\t%u\n", $sv->PREVIOUS; + printf "\txbm_rare\t%s\n", cstring(chr($sv->RARE)); +} + +sub B::CV::debug { + my ($sv) = @_; + $sv->B::PVNV::debug(); + my ($stash) = $sv->STASH; + my ($start) = $sv->START; + my ($root) = $sv->ROOT; + my ($padlist) = $sv->PADLIST; + my ($gv) = $sv->GV; + my ($filegv) = $sv->FILEGV; + printf <<'EOT', $$stash, $$start, $$root, $$gv, $$filegv, $sv->DEPTH, $padlist, ${$sv->OUTSIDE}; + STASH 0x%x + START 0x%x + ROOT 0x%x + GV 0x%x + FILEGV 0x%x + DEPTH %d + PADLIST 0x%x + OUTSIDE 0x%x +EOT + $start->debug if $start; + $root->debug if $root; + $gv->debug if $gv; + $filegv->debug if $filegv; + $padlist->debug if $padlist; +} + +sub B::AV::debug { + my ($av) = @_; + $av->B::SV::debug; + my(@array) = $av->ARRAY; + print "\tARRAY\t\t(", join(", ", map("0x" . $$_, @array)), ")\n"; + printf <<'EOT', scalar(@array), $av->MAX, $av->OFF, $av->AvFLAGS; + FILL %d + MAX %d + OFF %d + AvFLAGS %d +EOT +} + +sub B::GV::debug { + my ($gv) = @_; + if ($done_gv{$$gv}++) { + printf "GV %s::%s\n", $gv->STASH->NAME, $gv->NAME; + return; + } + my ($sv) = $gv->SV; + my ($av) = $gv->AV; + my ($cv) = $gv->CV; + $gv->B::SV::debug; + printf <<'EOT', $gv->NAME, $gv->STASH->NAME, $gv->STASH, $$sv, $gv->GvREFCNT, $gv->FORM, $$av, ${$gv->HV}, ${$gv->EGV}, $$cv, $gv->CVGEN, $gv->LINE, $gv->FILEGV, $gv->GvFLAGS; + NAME %s + STASH %s (0x%x) + SV 0x%x + GvREFCNT %d + FORM 0x%x + AV 0x%x + HV 0x%x + EGV 0x%x + CV 0x%x + CVGEN %d + LINE %d + FILEGV 0x%x + GvFLAGS 0x%x +EOT + $sv->debug if $sv; + $av->debug if $av; + $cv->debug if $cv; +} + +sub B::SPECIAL::debug { + my $sv = shift; + print $specialsv_name[$$sv], "\n"; +} + +sub compile { + my $order = shift; + if ($order eq "exec") { + return sub { walkoptree_exec(main_start, "debug") } + } else { + return sub { walkoptree(main_root, "debug") } + } +} + +1; + +__END__ + +=head1 NAME + +B::Debug - Walk Perl syntax tree, printing debug info about ops + +=head1 SYNOPSIS + + perl -MO=Debug[,OPTIONS] foo.pl + +=head1 DESCRIPTION + +See F<ext/B/README>. + +=head1 AUTHOR + +Malcolm Beattie, C<mbeattie@sable.ox.ac.uk> + +=cut diff --git a/gnu/usr.bin/perl/ext/B/B/Deparse.pm b/gnu/usr.bin/perl/ext/B/B/Deparse.pm new file mode 100644 index 00000000000..5e0bd1d3de7 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/B/Deparse.pm @@ -0,0 +1,2670 @@ +# B::Deparse.pm +# Copyright (c) 1998 Stephen McCamant. All rights reserved. +# This module is free software; you can redistribute and/or modify +# it under the same terms as Perl itself. + +# This is based on the module of the same name by Malcolm Beattie, +# but essentially none of his code remains. + +package B::Deparse; +use Carp 'cluck'; +use B qw(class main_root main_start main_cv svref_2object); +$VERSION = 0.56; +use strict; + +# Changes between 0.50 and 0.51: +# - fixed nulled leave with live enter in sort { } +# - fixed reference constants (\"str") +# - handle empty programs gracefully +# - handle infinte loops (for (;;) {}, while (1) {}) +# - differentiate between `for my $x ...' and `my $x; for $x ...' +# - various minor cleanups +# - moved globals into an object +# - added `-u', like B::C +# - package declarations using cop_stash +# - subs, formats and code sorted by cop_seq +# Changes between 0.51 and 0.52: +# - added pp_threadsv (special variables under USE_THREADS) +# - added documentation +# Changes between 0.52 and 0.53 +# - many changes adding precedence contexts and associativity +# - added `-p' and `-s' output style options +# - various other minor fixes +# Changes between 0.53 and 0.54 +# - added support for new `for (1..100)' optimization, +# thanks to Gisle Aas +# Changes between 0.54 and 0.55 +# - added support for new qr// construct +# - added support for new pp_regcreset OP +# Changes between 0.55 and 0.56 +# - tested on base/*.t, cmd/*.t, comp/*.t, io/*.t +# - fixed $# on non-lexicals broken in last big rewrite +# - added temporary fix for change in opcode of OP_STRINGIFY +# - fixed problem in 0.54's for() patch in `for (@ary)' +# - fixed precedence in conditional of ?: +# - tweaked list paren elimination in `my($x) = @_' +# - made continue-block detection trickier wrt. null ops +# - fixed various prototype problems in pp_entersub +# - added support for sub prototypes that never get GVs +# - added unquoting for special filehandle first arg in truncate +# - print doubled rv2gv (a bug) as `*{*GV}' instead of illegal `**GV' +# - added semicolons at the ends of blocks +# - added -l `#line' declaration option -- fixes cmd/subval.t 27,28 + +# Todo: +# - {} around variables in strings ("${var}letters") +# base/lex.t 25-27 +# comp/term.t 11 +# - generate symbolic constants directly from core source +# - left/right context +# - avoid semis in one-statement blocks +# - associativity of &&=, ||=, ?: +# - ',' => '=>' (auto-unquote?) +# - break long lines ("\r" as discretionary break?) +# - include values of variables (e.g. set in BEGIN) +# - coordinate with Data::Dumper (both directions? see previous) +# - version using op_next instead of op_first/sibling? +# - avoid string copies (pass arrays, one big join?) +# - auto-apply `-u'? +# - while{} with one-statement continue => for(; XXX; XXX) {}? +# - -uPackage:: descend recursively? +# - here-docs? +# - <DATA>? + +# Tests that will always fail: +# comp/redef.t -- all (redefinition happens at compile time) + +# Object fields (were globals): +# +# avoid_local: +# (local($a), local($b)) and local($a, $b) have the same internal +# representation but the short form looks better. We notice we can +# use a large-scale local when checking the list, but need to prevent +# individual locals too. This hash holds the addresses of OPs that +# have already had their local-ness accounted for. The same thing +# is done with my(). +# +# curcv: +# CV for current sub (or main program) being deparsed +# +# curstash: +# name of the current package for deparsed code +# +# subs_todo: +# array of [cop_seq, GV, is_format?] for subs and formats we still +# want to deparse +# +# protos_todo: +# as above, but [name, prototype] for subs that never got a GV +# +# subs_done, forms_done: +# keys are addresses of GVs for subs and formats we've already +# deparsed (or at least put into subs_todo) +# +# parens: -p +# linenums: -l +# cuddle: ` ' or `\n', depending on -sC + +# A little explanation of how precedence contexts and associativity +# work: +# +# deparse() calls each per-op subroutine with an argument $cx (short +# for context, but not the same as the cx* in the perl core), which is +# a number describing the op's parents in terms of precedence, whether +# they're inside an expression or at statement level, etc. (see +# chart below). When ops with children call deparse on them, they pass +# along their precedence. Fractional values are used to implement +# associativity (`($x + $y) + $z' => `$x + $y + $y') and related +# parentheses hacks. The major disadvantage of this scheme is that +# it doesn't know about right sides and left sides, so say if you +# assign a listop to a variable, it can't tell it's allowed to leave +# the parens off the listop. + +# Precedences: +# 26 [TODO] inside interpolation context ("") +# 25 left terms and list operators (leftward) +# 24 left -> +# 23 nonassoc ++ -- +# 22 right ** +# 21 right ! ~ \ and unary + and - +# 20 left =~ !~ +# 19 left * / % x +# 18 left + - . +# 17 left << >> +# 16 nonassoc named unary operators +# 15 nonassoc < > <= >= lt gt le ge +# 14 nonassoc == != <=> eq ne cmp +# 13 left & +# 12 left | ^ +# 11 left && +# 10 left || +# 9 nonassoc .. ... +# 8 right ?: +# 7 right = += -= *= etc. +# 6 left , => +# 5 nonassoc list operators (rightward) +# 4 right not +# 3 left and +# 2 left or xor +# 1 statement modifiers +# 0 statement level + +# Nonprinting characters with special meaning: +# \cS - steal parens (see maybe_parens_unop) +# \n - newline and indent +# \t - increase indent +# \b - decrease indent (`outdent') +# \f - flush left (no indent) +# \cK - kill following semicolon, if any + +sub null { + my $op = shift; + return class($op) eq "NULL"; +} + +sub todo { + my $self = shift; + my($gv, $cv, $is_form) = @_; + my $seq; + if (!null($cv->START) and is_state($cv->START)) { + $seq = $cv->START->cop_seq; + } else { + $seq = 0; + } + push @{$self->{'subs_todo'}}, [$seq, $gv, $is_form]; +} + +sub next_todo { + my $self = shift; + my $ent = shift @{$self->{'subs_todo'}}; + my $name = $self->gv_name($ent->[1]); + if ($ent->[2]) { + return "format $name =\n" + . $self->deparse_format($ent->[1]->FORM). "\n"; + } else { + return "sub $name " . + $self->deparse_sub($ent->[1]->CV); + } +} + +sub OPf_KIDS () { 4 } + +sub walk_tree { + my($op, $sub) = @_; + $sub->($op); + if ($op->flags & OPf_KIDS) { + my $kid; + for ($kid = $op->first; not null $kid; $kid = $kid->sibling) { + walk_tree($kid, $sub); + } + } +} + +sub walk_sub { + my $self = shift; + my $cv = shift; + my $op = $cv->ROOT; + $op = shift if null $op; + return if !$op or null $op; + walk_tree($op, sub { + my $op = shift; + if ($op->ppaddr eq "pp_gv") { + if ($op->next->ppaddr eq "pp_entersub") { + next if $self->{'subs_done'}{$ {$op->gv}}++; + next if class($op->gv->CV) eq "SPECIAL"; + $self->todo($op->gv, $op->gv->CV, 0); + $self->walk_sub($op->gv->CV); + } elsif ($op->next->ppaddr eq "pp_enterwrite" + or ($op->next->ppaddr eq "pp_rv2gv" + and $op->next->next->ppaddr eq "pp_enterwrite")) { + next if $self->{'forms_done'}{$ {$op->gv}}++; + next if class($op->gv->FORM) eq "SPECIAL"; + $self->todo($op->gv, $op->gv->FORM, 1); + $self->walk_sub($op->gv->FORM); + } + } + }); +} + +sub stash_subs { + my $self = shift; + my $pack = shift; + my(%stash, @ret); + { no strict 'refs'; %stash = svref_2object(\%{$pack . "::"})->ARRAY } + if ($pack eq "main") { + $pack = ""; + } else { + $pack = $pack . "::"; + } + my($key, $val); + while (($key, $val) = each %stash) { + my $class = class($val); + if ($class eq "PV") { + # Just a prototype + push @{$self->{'protos_todo'}}, [$pack . $key, $val->PV]; + } elsif ($class eq "IV") { + # Just a name + push @{$self->{'protos_todo'}}, [$pack . $key, undef]; + } elsif ($class eq "GV") { + if (class($val->CV) ne "SPECIAL") { + next if $self->{'subs_done'}{$$val}++; + $self->todo($val, $val->CV, 0); + $self->walk_sub($val->CV); + } + if (class($val->FORM) ne "SPECIAL") { + next if $self->{'forms_done'}{$$val}++; + $self->todo($val, $val->FORM, 1); + $self->walk_sub($val->FORM); + } + } + } +} + +sub print_protos { + my $self = shift; + my $ar; + my @ret; + foreach $ar (@{$self->{'protos_todo'}}) { + my $proto = (defined $ar->[1] ? " (". $ar->[1] . ")" : ""); + push @ret, "sub " . $ar->[0] . "$proto;\n"; + } + delete $self->{'protos_todo'}; + return @ret; +} + +sub style_opts { + my $self = shift; + my $opts = shift; + my $opt; + while (length($opt = substr($opts, 0, 1))) { + if ($opt eq "C") { + $self->{'cuddle'} = " "; + } + $opts = substr($opts, 1); + } +} + +sub compile { + my(@args) = @_; + return sub { + my $self = bless {}; + my $arg; + $self->{'subs_todo'} = []; + $self->stash_subs("main"); + $self->{'curcv'} = main_cv; + $self->{'curstash'} = "main"; + $self->{'cuddle'} = "\n"; + while ($arg = shift @args) { + if (substr($arg, 0, 2) eq "-u") { + $self->stash_subs(substr($arg, 2)); + } elsif ($arg eq "-p") { + $self->{'parens'} = 1; + } elsif ($arg eq "-l") { + $self->{'linenums'} = 1; + } elsif (substr($arg, 0, 2) eq "-s") { + $self->style_opts(substr $arg, 2); + } + } + $self->walk_sub(main_cv, main_start); + print $self->print_protos; + @{$self->{'subs_todo'}} = + sort {$a->[0] <=> $b->[0]} @{$self->{'subs_todo'}}; + print indent($self->deparse(main_root, 0)), "\n" unless null main_root; + my @text; + while (scalar(@{$self->{'subs_todo'}})) { + push @text, $self->next_todo; + } + print indent(join("", @text)), "\n" if @text; + } +} + +sub deparse { + my $self = shift; + my($op, $cx) = @_; +# cluck if class($op) eq "NULL"; + my $meth = $op->ppaddr; + return $self->$meth($op, $cx); +} + +sub indent { + my $txt = shift; + my @lines = split(/\n/, $txt); + my $leader = ""; + my $line; + for $line (@lines) { + if (substr($line, 0, 1) eq "\t") { + $leader = $leader . " "; + $line = substr($line, 1); + } elsif (substr($line, 0, 1) eq "\b") { + $leader = substr($leader, 0, length($leader) - 4); + $line = substr($line, 1); + } + if (substr($line, 0, 1) eq "\f") { + $line = substr($line, 1); # no indent + } else { + $line = $leader . $line; + } + $line =~ s/\cK;?//g; + } + return join("\n", @lines); +} + +sub SVf_POK () {0x40000} + +sub deparse_sub { + my $self = shift; + my $cv = shift; + my $proto = ""; + if ($cv->FLAGS & SVf_POK) { + $proto = "(". $cv->PV . ") "; + } + local($self->{'curcv'}) = $cv; + local($self->{'curstash'}) = $self->{'curstash'}; + if (not null $cv->ROOT) { + # skip leavesub + return $proto . "{\n\t" . + $self->deparse($cv->ROOT->first, 0) . "\n\b}\n"; + } else { # XSUB? + return $proto . "{}\n"; + } +} + +sub deparse_format { + my $self = shift; + my $form = shift; + my @text; + local($self->{'curcv'}) = $form; + local($self->{'curstash'}) = $self->{'curstash'}; + my $op = $form->ROOT; + my $kid; + $op = $op->first->first; # skip leavewrite, lineseq + while (not null $op) { + $op = $op->sibling; # skip nextstate + my @exprs; + $kid = $op->first->sibling; # skip pushmark + push @text, $kid->sv->PV; + $kid = $kid->sibling; + for (; not null $kid; $kid = $kid->sibling) { + push @exprs, $self->deparse($kid, 0); + } + push @text, join(", ", @exprs)."\n" if @exprs; + $op = $op->sibling; + } + return join("", @text) . "."; +} + +# the aassign in-common check messes up SvCUR (always setting it +# to a value >= 100), but it's probably safe to assume there +# won't be any NULs in the names of my() variables. (with +# stash variables, I wouldn't be so sure) +sub padname_fix { + my $str = shift; + $str = substr($str, 0, index($str, "\0")) if index($str, "\0") != -1; + return $str; +} + +sub is_scope { + my $op = shift; + return $op->ppaddr eq "pp_leave" || $op->ppaddr eq "pp_scope" + || $op->ppaddr eq "pp_lineseq" + || ($op->ppaddr eq "pp_null" && class($op) eq "UNOP" + && (is_scope($op->first) || $op->first->ppaddr eq "pp_enter")); +} + +sub is_state { + my $name = $_[0]->ppaddr; + return $name eq "pp_nextstate" || $name eq "pp_dbstate"; +} + +sub is_miniwhile { # check for one-line loop (`foo() while $y--') + my $op = shift; + return (!null($op) and null($op->sibling) + and $op->ppaddr eq "pp_null" and class($op) eq "UNOP" + and (($op->first->ppaddr =~ /^pp_(and|or)$/ + and $op->first->first->sibling->ppaddr eq "pp_lineseq") + or ($op->first->ppaddr eq "pp_lineseq" + and not null $op->first->first->sibling + and $op->first->first->sibling->ppaddr eq "pp_unstack") + )); +} + +sub is_scalar { + my $op = shift; + return ($op->ppaddr eq "pp_rv2sv" or + $op->ppaddr eq "pp_padsv" or + $op->ppaddr eq "pp_gv" or # only in array/hash constructs + !null($op->first) && $op->first->ppaddr eq "pp_gvsv"); +} + +sub maybe_parens { + my $self = shift; + my($text, $cx, $prec) = @_; + if ($prec < $cx # unary ops nest just fine + or $prec == $cx and $cx != 4 and $cx != 16 and $cx != 21 + or $self->{'parens'}) + { + $text = "($text)"; + # In a unop, let parent reuse our parens; see maybe_parens_unop + $text = "\cS" . $text if $cx == 16; + return $text; + } else { + return $text; + } +} + +# same as above, but get around the `if it looks like a function' rule +sub maybe_parens_unop { + my $self = shift; + my($name, $kid, $cx) = @_; + if ($cx > 16 or $self->{'parens'}) { + return "$name(" . $self->deparse($kid, 1) . ")"; + } else { + $kid = $self->deparse($kid, 16); + if (substr($kid, 0, 1) eq "\cS") { + # use kid's parens + return $name . substr($kid, 1); + } elsif (substr($kid, 0, 1) eq "(") { + # avoid looks-like-a-function trap with extra parens + # (`+' can lead to ambiguities) + return "$name(" . $kid . ")"; + } else { + return "$name $kid"; + } + } +} + +sub maybe_parens_func { + my $self = shift; + my($func, $text, $cx, $prec) = @_; + if ($prec <= $cx or substr($text, 0, 1) eq "(" or $self->{'parens'}) { + return "$func($text)"; + } else { + return "$func $text"; + } +} + +sub OPp_LVAL_INTRO () { 128 } + +sub maybe_local { + my $self = shift; + my($op, $cx, $text) = @_; + if ($op->private & OPp_LVAL_INTRO and not $self->{'avoid_local'}{$$op}) { + return $self->maybe_parens_func("local", $text, $cx, 16); + } else { + return $text; + } +} + +sub padname_sv { + my $self = shift; + my $targ = shift; + return (($self->{'curcv'}->PADLIST->ARRAY)[0]->ARRAY)[$targ]; +} + +sub maybe_my { + my $self = shift; + my($op, $cx, $text) = @_; + if ($op->private & OPp_LVAL_INTRO and not $self->{'avoid_local'}{$$op}) { + return $self->maybe_parens_func("my", $text, $cx, 16); + } else { + return $text; + } +} + +# The following OPs don't have functions: + +# pp_padany -- does not exist after parsing +# pp_rcatline -- does not exist + +sub pp_enter { # see also leave + cluck "unexpected OP_ENTER"; + return "XXX"; +} + +sub pp_pushmark { # see also list + cluck "unexpected OP_PUSHMARK"; + return "XXX"; +} + +sub pp_leavesub { # see also deparse_sub + cluck "unexpected OP_LEAVESUB"; + return "XXX"; +} + +sub pp_leavewrite { # see also deparse_format + cluck "unexpected OP_LEAVEWRITE"; + return "XXX"; +} + +sub pp_method { # see also entersub + cluck "unexpected OP_METHOD"; + return "XXX"; +} + +sub pp_regcmaybe { # see also regcomp + cluck "unexpected OP_REGCMAYBE"; + return "XXX"; +} + +sub pp_regcreset { # see also regcomp + cluck "unexpected OP_REGCRESET"; + return "XXX"; +} + +sub pp_substcont { # see also subst + cluck "unexpected OP_SUBSTCONT"; + return "XXX"; +} + +sub pp_grepstart { # see also grepwhile + cluck "unexpected OP_GREPSTART"; + return "XXX"; +} + +sub pp_mapstart { # see also mapwhile + cluck "unexpected OP_MAPSTART"; + return "XXX"; +} + +sub pp_flip { # see also flop + cluck "unexpected OP_FLIP"; + return "XXX"; +} + +sub pp_iter { # see also leaveloop + cluck "unexpected OP_ITER"; + return "XXX"; +} + +sub pp_enteriter { # see also leaveloop + cluck "unexpected OP_ENTERITER"; + return "XXX"; +} + +sub pp_enterloop { # see also leaveloop + cluck "unexpected OP_ENTERLOOP"; + return "XXX"; +} + +sub pp_leaveeval { # see also entereval + cluck "unexpected OP_LEAVEEVAL"; + return "XXX"; +} + +sub pp_entertry { # see also leavetry + cluck "unexpected OP_ENTERTRY"; + return "XXX"; +} + +# leave and scope/lineseq should probably share code +sub pp_leave { + my $self = shift; + my($op, $cx) = @_; + my ($kid, $expr); + my @exprs; + local($self->{'curstash'}) = $self->{'curstash'}; + $kid = $op->first->sibling; # skip enter + if (is_miniwhile($kid)) { + my $top = $kid->first; + my $name = $top->ppaddr; + if ($name eq "pp_and") { + $name = "while"; + } elsif ($name eq "pp_or") { + $name = "until"; + } else { # no conditional -> while 1 or until 0 + return $self->deparse($top->first, 1) . " while 1"; + } + my $cond = $top->first; + my $body = $cond->sibling->first; # skip lineseq + $cond = $self->deparse($cond, 1); + $body = $self->deparse($body, 1); + return "$body $name $cond"; + } + for (; !null($kid); $kid = $kid->sibling) { + $expr = ""; + if (is_state $kid) { + $expr = $self->deparse($kid, 0); + $kid = $kid->sibling; + last if null $kid; + } + $expr .= $self->deparse($kid, 0); + push @exprs, $expr if $expr; + } + if ($cx > 0) { # inside an expression + return "do { " . join(";\n", @exprs) . " }"; + } else { + return join(";\n", @exprs) . ";"; + } +} + +sub pp_scope { + my $self = shift; + my($op, $cx) = @_; + my ($kid, $expr); + my @exprs; + for ($kid = $op->first; !null($kid); $kid = $kid->sibling) { + $expr = ""; + if (is_state $kid) { + $expr = $self->deparse($kid, 0); + $kid = $kid->sibling; + last if null $kid; + } + $expr .= $self->deparse($kid, 0); + push @exprs, $expr if $expr; + } + if ($cx > 0) { # inside an expression, (a do {} while for lineseq) + return "do { " . join(";\n", @exprs) . " }"; + } else { + return join(";\n", @exprs) . ";"; + } +} + +sub pp_lineseq { pp_scope(@_) } + +# The BEGIN {} is used here because otherwise this code isn't executed +# when you run B::Deparse on itself. +my %globalnames; +BEGIN { map($globalnames{$_}++, "SIG", "STDIN", "STDOUT", "STDERR", "INC", + "ENV", "ARGV", "ARGVOUT", "_"); } + +sub gv_name { + my $self = shift; + my $gv = shift; + my $stash = $gv->STASH->NAME; + my $name = $gv->NAME; + if ($stash eq $self->{'curstash'} or $globalnames{$name} + or $name =~ /^[^A-Za-z_]/) + { + $stash = ""; + } else { + $stash = $stash . "::"; + } + if ($name =~ /^([\cA-\cZ])$/) { + $name = "^" . chr(64 + ord($1)); + } + return $stash . $name; +} + +# Notice how subs and formats are inserted between statements here +sub pp_nextstate { + my $self = shift; + my($op, $cx) = @_; + my @text; + @text = $op->label . ": " if $op->label; + my $seq = $op->cop_seq; + while (scalar(@{$self->{'subs_todo'}}) + and $seq > $self->{'subs_todo'}[0][0]) { + push @text, $self->next_todo; + } + my $stash = $op->stash->NAME; + if ($stash ne $self->{'curstash'}) { + push @text, "package $stash;\n"; + $self->{'curstash'} = $stash; + } + if ($self->{'linenums'}) { + push @text, "\f#line " . $op->line . + ' "' . substr($op->filegv->NAME, 2), qq'"\n'; + } + return join("", @text); +} + +sub pp_dbstate { pp_nextstate(@_) } + +sub pp_unstack { return "" } # see also leaveloop + +sub baseop { + my $self = shift; + my($op, $cx, $name) = @_; + return $name; +} + +sub pp_stub { baseop(@_, "()") } +sub pp_wantarray { baseop(@_, "wantarray") } +sub pp_fork { baseop(@_, "fork") } +sub pp_wait { baseop(@_, "wait") } +sub pp_getppid { baseop(@_, "getppid") } +sub pp_time { baseop(@_, "time") } +sub pp_tms { baseop(@_, "times") } +sub pp_ghostent { baseop(@_, "gethostent") } +sub pp_gnetent { baseop(@_, "getnetent") } +sub pp_gprotoent { baseop(@_, "getprotoent") } +sub pp_gservent { baseop(@_, "getservent") } +sub pp_ehostent { baseop(@_, "endhostent") } +sub pp_enetent { baseop(@_, "endnetent") } +sub pp_eprotoent { baseop(@_, "endprotoent") } +sub pp_eservent { baseop(@_, "endservent") } +sub pp_gpwent { baseop(@_, "getpwent") } +sub pp_spwent { baseop(@_, "setpwent") } +sub pp_epwent { baseop(@_, "endpwent") } +sub pp_ggrent { baseop(@_, "getgrent") } +sub pp_sgrent { baseop(@_, "setgrent") } +sub pp_egrent { baseop(@_, "endgrent") } +sub pp_getlogin { baseop(@_, "getlogin") } + +sub POSTFIX () { 1 } + +# I couldn't think of a good short name, but this is the category of +# symbolic unary operators with interesting precedence + +sub pfixop { + my $self = shift; + my($op, $cx, $name, $prec, $flags) = (@_, 0); + my $kid = $op->first; + $kid = $self->deparse($kid, $prec); + return $self->maybe_parens(($flags & POSTFIX) ? "$kid$name" : "$name$kid", + $cx, $prec); +} + +sub pp_preinc { pfixop(@_, "++", 23) } +sub pp_predec { pfixop(@_, "--", 23) } +sub pp_postinc { pfixop(@_, "++", 23, POSTFIX) } +sub pp_postdec { pfixop(@_, "--", 23, POSTFIX) } +sub pp_i_preinc { pfixop(@_, "++", 23) } +sub pp_i_predec { pfixop(@_, "--", 23) } +sub pp_i_postinc { pfixop(@_, "++", 23, POSTFIX) } +sub pp_i_postdec { pfixop(@_, "--", 23, POSTFIX) } +sub pp_complement { pfixop(@_, "~", 21) } + +sub pp_negate { + my $self = shift; + my($op, $cx) = @_; + if ($op->first->ppaddr =~ /^pp_(i_)?negate$/) { + # avoid --$x + $self->pfixop($op, $cx, "-", 21.5); + } else { + $self->pfixop($op, $cx, "-", 21); + } +} +sub pp_i_negate { pp_negate(@_) } + +sub pp_not { + my $self = shift; + my($op, $cx) = @_; + if ($cx <= 4) { + $self->pfixop($op, $cx, "not ", 4); + } else { + $self->pfixop($op, $cx, "!", 21); + } +} + +sub OPf_SPECIAL () { 128 } + +sub unop { + my $self = shift; + my($op, $cx, $name, $prec, $flags) = (@_, 0, 0); + my $kid; + if ($op->flags & OPf_KIDS) { + $kid = $op->first; + return $self->maybe_parens_unop($name, $kid, $cx); + } else { + return $name . ($op->flags & OPf_SPECIAL ? "()" : ""); + } +} + +sub pp_chop { unop(@_, "chop") } +sub pp_chomp { unop(@_, "chomp") } +sub pp_schop { unop(@_, "chop") } +sub pp_schomp { unop(@_, "chomp") } +sub pp_defined { unop(@_, "defined") } +sub pp_undef { unop(@_, "undef") } +sub pp_study { unop(@_, "study") } +sub pp_ref { unop(@_, "ref") } +sub pp_pos { maybe_local(@_, unop(@_, "pos")) } + +sub pp_sin { unop(@_, "sin") } +sub pp_cos { unop(@_, "cos") } +sub pp_rand { unop(@_, "rand") } +sub pp_srand { unop(@_, "srand") } +sub pp_exp { unop(@_, "exp") } +sub pp_log { unop(@_, "log") } +sub pp_sqrt { unop(@_, "sqrt") } +sub pp_int { unop(@_, "int") } +sub pp_hex { unop(@_, "hex") } +sub pp_oct { unop(@_, "oct") } +sub pp_abs { unop(@_, "abs") } + +sub pp_length { unop(@_, "length") } +sub pp_ord { unop(@_, "ord") } +sub pp_chr { unop(@_, "chr") } +sub pp_ucfirst { unop(@_, "ucfirst") } +sub pp_lcfirst { unop(@_, "lcfirst") } +sub pp_uc { unop(@_, "uc") } +sub pp_lc { unop(@_, "lc") } +sub pp_quotemeta { unop(@_, "quotemeta") } + +sub pp_each { unop(@_, "each") } +sub pp_values { unop(@_, "values") } +sub pp_keys { unop(@_, "keys") } +sub pp_pop { unop(@_, "pop") } +sub pp_shift { unop(@_, "shift") } + +sub pp_caller { unop(@_, "caller") } +sub pp_reset { unop(@_, "reset") } +sub pp_exit { unop(@_, "exit") } +sub pp_prototype { unop(@_, "prototype") } + +sub pp_close { unop(@_, "close") } +sub pp_fileno { unop(@_, "fileno") } +sub pp_umask { unop(@_, "umask") } +sub pp_binmode { unop(@_, "binmode") } +sub pp_untie { unop(@_, "untie") } +sub pp_tied { unop(@_, "tied") } +sub pp_dbmclose { unop(@_, "dbmclose") } +sub pp_getc { unop(@_, "getc") } +sub pp_eof { unop(@_, "eof") } +sub pp_tell { unop(@_, "tell") } +sub pp_getsockname { unop(@_, "getsockname") } +sub pp_getpeername { unop(@_, "getpeername") } + +sub pp_chdir { unop(@_, "chdir") } +sub pp_chroot { unop(@_, "chroot") } +sub pp_readlink { unop(@_, "readlink") } +sub pp_rmdir { unop(@_, "rmdir") } +sub pp_readdir { unop(@_, "readdir") } +sub pp_telldir { unop(@_, "telldir") } +sub pp_rewinddir { unop(@_, "rewinddir") } +sub pp_closedir { unop(@_, "closedir") } +sub pp_getpgrp { unop(@_, "getpgrp") } +sub pp_localtime { unop(@_, "localtime") } +sub pp_gmtime { unop(@_, "gmtime") } +sub pp_alarm { unop(@_, "alarm") } +sub pp_sleep { unop(@_, "sleep") } + +sub pp_dofile { unop(@_, "do") } +sub pp_entereval { unop(@_, "eval") } + +sub pp_ghbyname { unop(@_, "gethostbyname") } +sub pp_gnbyname { unop(@_, "getnetbyname") } +sub pp_gpbyname { unop(@_, "getprotobyname") } +sub pp_shostent { unop(@_, "sethostent") } +sub pp_snetent { unop(@_, "setnetent") } +sub pp_sprotoent { unop(@_, "setprotoent") } +sub pp_sservent { unop(@_, "setservent") } +sub pp_gpwnam { unop(@_, "getpwnam") } +sub pp_gpwuid { unop(@_, "getpwuid") } +sub pp_ggrnam { unop(@_, "getgrnam") } +sub pp_ggrgid { unop(@_, "getgrgid") } + +sub pp_lock { unop(@_, "lock") } + +sub pp_exists { + my $self = shift; + my($op, $cx) = @_; + return $self->maybe_parens_func("exists", $self->pp_helem($op->first, 16), + $cx, 16); +} + +sub OPpSLICE () { 64 } + +sub pp_delete { + my $self = shift; + my($op, $cx) = @_; + my $arg; + if ($op->private & OPpSLICE) { + return $self->maybe_parens_func("delete", + $self->pp_hslice($op->first, 16), + $cx, 16); + } else { + return $self->maybe_parens_func("delete", + $self->pp_helem($op->first, 16), + $cx, 16); + } +} + +sub OPp_CONST_BARE () { 64 } + +sub pp_require { + my $self = shift; + my($op, $cx) = @_; + if (class($op) eq "UNOP" and $op->first->ppaddr eq "pp_const" + and $op->first->private & OPp_CONST_BARE) + { + my $name = $op->first->sv->PV; + $name =~ s[/][::]g; + $name =~ s/\.pm//g; + return "require($name)"; + } else { + $self->unop($op, $cx, "require"); + } +} + +sub pp_scalar { + my $self = shift; + my($op, $cv) = @_; + my $kid = $op->first; + if (not null $kid->sibling) { + # XXX Was a here-doc + return $self->dquote($op); + } + $self->unop(@_, "scalar"); +} + + +sub padval { + my $self = shift; + my $targ = shift; + return (($self->{'curcv'}->PADLIST->ARRAY)[1]->ARRAY)[$targ]; +} + +sub OPf_REF () { 16 } + +sub pp_refgen { + my $self = shift; + my($op, $cx) = @_; + my $kid = $op->first; + if ($kid->ppaddr eq "pp_null") { + $kid = $kid->first; + if ($kid->ppaddr eq "pp_anonlist" || $kid->ppaddr eq "pp_anonhash") { + my($pre, $post) = @{{"pp_anonlist" => ["[","]"], + "pp_anonhash" => ["{","}"]}->{$kid->ppaddr}}; + my($expr, @exprs); + $kid = $kid->first->sibling; # skip pushmark + for (; !null($kid); $kid = $kid->sibling) { + $expr = $self->deparse($kid, 6); + push @exprs, $expr; + } + return $pre . join(", ", @exprs) . $post; + } elsif (!null($kid->sibling) and + $kid->sibling->ppaddr eq "pp_anoncode") { + return "sub " . + $self->deparse_sub($self->padval($kid->sibling->targ)); + } elsif ($kid->ppaddr eq "pp_pushmark" + and $kid->sibling->ppaddr =~ /^pp_(pad|rv2)[ah]v$/ + and not $kid->sibling->flags & OPf_REF) { + # The @a in \(@a) isn't in ref context, but only when the + # parens are there. + return "\\(" . $self->deparse($kid->sibling, 1) . ")"; + } + } + $self->pfixop($op, $cx, "\\", 20); +} + +sub pp_srefgen { pp_refgen(@_) } + +sub pp_readline { + my $self = shift; + my($op, $cx) = @_; + my $kid = $op->first; + $kid = $kid->first if $kid->ppaddr eq "pp_rv2gv"; # <$fh> + if ($kid->ppaddr eq "pp_rv2gv") { + $kid = $kid->first; + } + return "<" . $self->deparse($kid, 1) . ">"; +} + +sub loopex { + my $self = shift; + my ($op, $cx, $name) = @_; + if (class($op) eq "PVOP") { + return "$name " . $op->pv; + } elsif (class($op) eq "OP") { + return $name; + } elsif (class($op) eq "UNOP") { + # Note -- loop exits are actually exempt from the + # looks-like-a-func rule, but a few extra parens won't hurt + return $self->maybe_parens_unop($name, $op->first, $cx); + } +} + +sub pp_last { loopex(@_, "last") } +sub pp_next { loopex(@_, "next") } +sub pp_redo { loopex(@_, "redo") } +sub pp_goto { loopex(@_, "goto") } +sub pp_dump { loopex(@_, "dump") } + +sub ftst { + my $self = shift; + my($op, $cx, $name) = @_; + if (class($op) eq "UNOP") { + # Genuine `-X' filetests are exempt from the LLAFR, but not + # l?stat(); for the sake of clarity, give'em all parens + return $self->maybe_parens_unop($name, $op->first, $cx); + } elsif (class($op) eq "GVOP") { + return $self->maybe_parens_func($name, $self->pp_gv($op, 1), $cx, 16); + } else { # I don't think baseop filetests ever survive ck_ftst, but... + return $name; + } +} + +sub pp_lstat { ftst(@_, "lstat") } +sub pp_stat { ftst(@_, "stat") } +sub pp_ftrread { ftst(@_, "-R") } +sub pp_ftrwrite { ftst(@_, "-W") } +sub pp_ftrexec { ftst(@_, "-X") } +sub pp_fteread { ftst(@_, "-r") } +sub pp_ftewrite { ftst(@_, "-r") } +sub pp_fteexec { ftst(@_, "-r") } +sub pp_ftis { ftst(@_, "-e") } +sub pp_fteowned { ftst(@_, "-O") } +sub pp_ftrowned { ftst(@_, "-o") } +sub pp_ftzero { ftst(@_, "-z") } +sub pp_ftsize { ftst(@_, "-s") } +sub pp_ftmtime { ftst(@_, "-M") } +sub pp_ftatime { ftst(@_, "-A") } +sub pp_ftctime { ftst(@_, "-C") } +sub pp_ftsock { ftst(@_, "-S") } +sub pp_ftchr { ftst(@_, "-c") } +sub pp_ftblk { ftst(@_, "-b") } +sub pp_ftfile { ftst(@_, "-f") } +sub pp_ftdir { ftst(@_, "-d") } +sub pp_ftpipe { ftst(@_, "-p") } +sub pp_ftlink { ftst(@_, "-l") } +sub pp_ftsuid { ftst(@_, "-u") } +sub pp_ftsgid { ftst(@_, "-g") } +sub pp_ftsvtx { ftst(@_, "-k") } +sub pp_fttty { ftst(@_, "-t") } +sub pp_fttext { ftst(@_, "-T") } +sub pp_ftbinary { ftst(@_, "-B") } + +sub SWAP_CHILDREN () { 1 } +sub ASSIGN () { 2 } # has OP= variant + +sub OPf_STACKED () { 64 } + +my(%left, %right); + +sub assoc_class { + my $op = shift; + my $name = $op->ppaddr; + if ($name eq "pp_concat" and $op->first->ppaddr eq "pp_concat") { + # avoid spurious `=' -- see comment in pp_concat + return "pp_concat"; + } + if ($name eq "pp_null" and class($op) eq "UNOP" + and $op->first->ppaddr =~ /^pp_(and|x?or)$/ + and null $op->first->sibling) + { + # Like all conditional constructs, OP_ANDs and OP_ORs are topped + # with a null that's used as the common end point of the two + # flows of control. For precedence purposes, ignore it. + # (COND_EXPRs have these too, but we don't bother with + # their associativity). + return assoc_class($op->first); + } + return $name . ($op->flags & OPf_STACKED ? "=" : ""); +} + +# Left associative operators, like `+', for which +# $a + $b + $c is equivalent to ($a + $b) + $c + +BEGIN { + %left = ('pp_multiply' => 19, 'pp_i_multiply' => 19, + 'pp_divide' => 19, 'pp_i_divide' => 19, + 'pp_modulo' => 19, 'pp_i_modulo' => 19, + 'pp_repeat' => 19, + 'pp_add' => 18, 'pp_i_add' => 18, + 'pp_subtract' => 18, 'pp_i_subtract' => 18, + 'pp_concat' => 18, + 'pp_left_shift' => 17, 'pp_right_shift' => 17, + 'pp_bit_and' => 13, + 'pp_bit_or' => 12, 'pp_bit_xor' => 12, + 'pp_and' => 3, + 'pp_or' => 2, 'pp_xor' => 2, + ); +} + +sub deparse_binop_left { + my $self = shift; + my($op, $left, $prec) = @_; + if ($left{assoc_class($op)} + and $left{assoc_class($op)} == $left{assoc_class($left)}) + { + return $self->deparse($left, $prec - .00001); + } else { + return $self->deparse($left, $prec); + } +} + +# Right associative operators, like `=', for which +# $a = $b = $c is equivalent to $a = ($b = $c) + +BEGIN { + %right = ('pp_pow' => 22, + 'pp_sassign=' => 7, 'pp_aassign=' => 7, + 'pp_multiply=' => 7, 'pp_i_multiply=' => 7, + 'pp_divide=' => 7, 'pp_i_divide=' => 7, + 'pp_modulo=' => 7, 'pp_i_modulo=' => 7, + 'pp_repeat=' => 7, + 'pp_add=' => 7, 'pp_i_add=' => 7, + 'pp_subtract=' => 7, 'pp_i_subtract=' => 7, + 'pp_concat=' => 7, + 'pp_left_shift=' => 7, 'pp_right_shift=' => 7, + 'pp_bit_and=' => 7, + 'pp_bit_or=' => 7, 'pp_bit_xor=' => 7, + 'pp_andassign' => 7, + 'pp_orassign' => 7, + ); +} + +sub deparse_binop_right { + my $self = shift; + my($op, $right, $prec) = @_; + if ($right{assoc_class($op)} + and $right{assoc_class($op)} == $right{assoc_class($right)}) + { + return $self->deparse($right, $prec - .00001); + } else { + return $self->deparse($right, $prec); + } +} + +sub binop { + my $self = shift; + my ($op, $cx, $opname, $prec, $flags) = (@_, 0); + my $left = $op->first; + my $right = $op->last; + my $eq = ""; + if ($op->flags & OPf_STACKED && $flags & ASSIGN) { + $eq = "="; + $prec = 7; + } + if ($flags & SWAP_CHILDREN) { + ($left, $right) = ($right, $left); + } + $left = $self->deparse_binop_left($op, $left, $prec); + $right = $self->deparse_binop_right($op, $right, $prec); + return $self->maybe_parens("$left $opname$eq $right", $cx, $prec); +} + +sub pp_add { binop(@_, "+", 18, ASSIGN) } +sub pp_multiply { binop(@_, "*", 19, ASSIGN) } +sub pp_subtract { binop(@_, "-",18, ASSIGN) } +sub pp_divide { binop(@_, "/", 19, ASSIGN) } +sub pp_modulo { binop(@_, "%", 19, ASSIGN) } +sub pp_i_add { binop(@_, "+", 18, ASSIGN) } +sub pp_i_multiply { binop(@_, "*", 19, ASSIGN) } +sub pp_i_subtract { binop(@_, "-", 18, ASSIGN) } +sub pp_i_divide { binop(@_, "/", 19, ASSIGN) } +sub pp_i_modulo { binop(@_, "%", 19, ASSIGN) } +sub pp_pow { binop(@_, "**", 22, ASSIGN) } + +sub pp_left_shift { binop(@_, "<<", 17, ASSIGN) } +sub pp_right_shift { binop(@_, ">>", 17, ASSIGN) } +sub pp_bit_and { binop(@_, "&", 13, ASSIGN) } +sub pp_bit_or { binop(@_, "|", 12, ASSIGN) } +sub pp_bit_xor { binop(@_, "^", 12, ASSIGN) } + +sub pp_eq { binop(@_, "==", 14) } +sub pp_ne { binop(@_, "!=", 14) } +sub pp_lt { binop(@_, "<", 15) } +sub pp_gt { binop(@_, ">", 15) } +sub pp_ge { binop(@_, ">=", 15) } +sub pp_le { binop(@_, "<=", 15) } +sub pp_ncmp { binop(@_, "<=>", 14) } +sub pp_i_eq { binop(@_, "==", 14) } +sub pp_i_ne { binop(@_, "!=", 14) } +sub pp_i_lt { binop(@_, "<", 15) } +sub pp_i_gt { binop(@_, ">", 15) } +sub pp_i_ge { binop(@_, ">=", 15) } +sub pp_i_le { binop(@_, "<=", 15) } +sub pp_i_ncmp { binop(@_, "<=>", 14) } + +sub pp_seq { binop(@_, "eq", 14) } +sub pp_sne { binop(@_, "ne", 14) } +sub pp_slt { binop(@_, "lt", 15) } +sub pp_sgt { binop(@_, "gt", 15) } +sub pp_sge { binop(@_, "ge", 15) } +sub pp_sle { binop(@_, "le", 15) } +sub pp_scmp { binop(@_, "cmp", 14) } + +sub pp_sassign { binop(@_, "=", 7, SWAP_CHILDREN) } +sub pp_aassign { binop(@_, "=", 7, SWAP_CHILDREN) } + +# `.' is special because concats-of-concats are optimized to save copying +# by making all but the first concat stacked. The effect is as if the +# programmer had written `($a . $b) .= $c', except legal. +sub pp_concat { + my $self = shift; + my($op, $cx) = @_; + my $left = $op->first; + my $right = $op->last; + my $eq = ""; + my $prec = 18; + if ($op->flags & OPf_STACKED and $op->first->ppaddr ne "pp_concat") { + $eq = "="; + $prec = 7; + } + $left = $self->deparse_binop_left($op, $left, $prec); + $right = $self->deparse_binop_right($op, $right, $prec); + return $self->maybe_parens("$left .$eq $right", $cx, $prec); +} + +# `x' is weird when the left arg is a list +sub pp_repeat { + my $self = shift; + my($op, $cx) = @_; + my $left = $op->first; + my $right = $op->last; + my $eq = ""; + my $prec = 19; + if ($op->flags & OPf_STACKED) { + $eq = "="; + $prec = 7; + } + if (null($right)) { # list repeat; count is inside left-side ex-list + my $kid = $left->first->sibling; # skip pushmark + my @exprs; + for (; !null($kid->sibling); $kid = $kid->sibling) { + push @exprs, $self->deparse($kid, 6); + } + $right = $kid; + $left = "(" . join(", ", @exprs). ")"; + } else { + $left = $self->deparse_binop_left($op, $left, $prec); + } + $right = $self->deparse_binop_right($op, $right, $prec); + return $self->maybe_parens("$left x$eq $right", $cx, $prec); +} + +sub range { + my $self = shift; + my ($op, $cx, $type) = @_; + my $left = $op->first; + my $right = $left->sibling; + $left = $self->deparse($left, 9); + $right = $self->deparse($right, 9); + return $self->maybe_parens("$left $type $right", $cx, 9); +} + +sub pp_flop { + my $self = shift; + my($op, $cx) = @_; + my $flip = $op->first; + my $type = ($flip->flags & OPf_SPECIAL) ? "..." : ".."; + return $self->range($flip->first, $cx, $type); +} + +# one-line while/until is handled in pp_leave + +sub logop { + my $self = shift; + my ($op, $cx, $lowop, $lowprec, $highop, $highprec, $blockname) = @_; + my $left = $op->first; + my $right = $op->first->sibling; + if ($cx == 0 and is_scope($right) and $blockname) { # if ($a) {$b} + $left = $self->deparse($left, 1); + $right = $self->deparse($right, 0); + return "$blockname ($left) {\n\t$right\n\b}\cK"; + } elsif ($cx == 0 and $blockname and not $self->{'parens'}) { # $b if $a + $right = $self->deparse($right, 1); + $left = $self->deparse($left, 1); + return "$right $blockname $left"; + } elsif ($cx > $lowprec and $highop) { # $a && $b + $left = $self->deparse_binop_left($op, $left, $highprec); + $right = $self->deparse_binop_right($op, $right, $highprec); + return $self->maybe_parens("$left $highop $right", $cx, $highprec); + } else { # $a and $b + $left = $self->deparse_binop_left($op, $left, $lowprec); + $right = $self->deparse_binop_right($op, $right, $lowprec); + return $self->maybe_parens("$left $lowop $right", $cx, $lowprec); + } +} + +sub pp_and { logop(@_, "and", 3, "&&", 11, "if") } +sub pp_or { logop(@_, "or", 2, "||", 10, "unless") } +sub pp_xor { logop(@_, "xor", 2, "", 0, "") } + +sub logassignop { + my $self = shift; + my ($op, $cx, $opname) = @_; + my $left = $op->first; + my $right = $op->first->sibling->first; # skip sassign + $left = $self->deparse($left, 7); + $right = $self->deparse($right, 7); + return $self->maybe_parens("$left $opname $right", $cx, 7); +} + +sub pp_andassign { logassignop(@_, "&&=") } +sub pp_orassign { logassignop(@_, "||=") } + +sub listop { + my $self = shift; + my($op, $cx, $name) = @_; + my(@exprs); + my $parens = ($cx >= 5) || $self->{'parens'}; + my $kid = $op->first->sibling; + return $name if null $kid; + my $first = $self->deparse($kid, 6); + $first = "+$first" if not $parens and substr($first, 0, 1) eq "("; + push @exprs, $first; + $kid = $kid->sibling; + for (; !null($kid); $kid = $kid->sibling) { + push @exprs, $self->deparse($kid, 6); + } + if ($parens) { + return "$name(" . join(", ", @exprs) . ")"; + } else { + return "$name " . join(", ", @exprs); + } +} + +sub pp_bless { listop(@_, "bless") } +sub pp_atan2 { listop(@_, "atan2") } +sub pp_substr { maybe_local(@_, listop(@_, "substr")) } +sub pp_vec { maybe_local(@_, listop(@_, "vec")) } +sub pp_index { listop(@_, "index") } +sub pp_rindex { listop(@_, "rindex") } +sub pp_sprintf { listop(@_, "sprintf") } +sub pp_formline { listop(@_, "formline") } # see also deparse_format +sub pp_crypt { listop(@_, "crypt") } +sub pp_unpack { listop(@_, "unpack") } +sub pp_pack { listop(@_, "pack") } +sub pp_join { listop(@_, "join") } +sub pp_splice { listop(@_, "splice") } +sub pp_push { listop(@_, "push") } +sub pp_unshift { listop(@_, "unshift") } +sub pp_reverse { listop(@_, "reverse") } +sub pp_warn { listop(@_, "warn") } +sub pp_die { listop(@_, "die") } +# Actually, return is exempt from the LLAFR (see examples in this very +# module!), but for consistency's sake, ignore that fact +sub pp_return { listop(@_, "return") } +sub pp_open { listop(@_, "open") } +sub pp_pipe_op { listop(@_, "pipe") } +sub pp_tie { listop(@_, "tie") } +sub pp_dbmopen { listop(@_, "dbmopen") } +sub pp_sselect { listop(@_, "select") } +sub pp_select { listop(@_, "select") } +sub pp_read { listop(@_, "read") } +sub pp_sysopen { listop(@_, "sysopen") } +sub pp_sysseek { listop(@_, "sysseek") } +sub pp_sysread { listop(@_, "sysread") } +sub pp_syswrite { listop(@_, "syswrite") } +sub pp_send { listop(@_, "send") } +sub pp_recv { listop(@_, "recv") } +sub pp_seek { listop(@_, "seek") } +sub pp_fcntl { listop(@_, "fcntl") } +sub pp_ioctl { listop(@_, "ioctl") } +sub pp_flock { listop(@_, "flock") } +sub pp_socket { listop(@_, "socket") } +sub pp_sockpair { listop(@_, "sockpair") } +sub pp_bind { listop(@_, "bind") } +sub pp_connect { listop(@_, "connect") } +sub pp_listen { listop(@_, "listen") } +sub pp_accept { listop(@_, "accept") } +sub pp_shutdown { listop(@_, "shutdown") } +sub pp_gsockopt { listop(@_, "getsockopt") } +sub pp_ssockopt { listop(@_, "setsockopt") } +sub pp_chown { listop(@_, "chown") } +sub pp_unlink { listop(@_, "unlink") } +sub pp_chmod { listop(@_, "chmod") } +sub pp_utime { listop(@_, "utime") } +sub pp_rename { listop(@_, "rename") } +sub pp_link { listop(@_, "link") } +sub pp_symlink { listop(@_, "symlink") } +sub pp_mkdir { listop(@_, "mkdir") } +sub pp_open_dir { listop(@_, "opendir") } +sub pp_seekdir { listop(@_, "seekdir") } +sub pp_waitpid { listop(@_, "waitpid") } +sub pp_system { listop(@_, "system") } +sub pp_exec { listop(@_, "exec") } +sub pp_kill { listop(@_, "kill") } +sub pp_setpgrp { listop(@_, "setpgrp") } +sub pp_getpriority { listop(@_, "getpriority") } +sub pp_setpriority { listop(@_, "setpriority") } +sub pp_shmget { listop(@_, "shmget") } +sub pp_shmctl { listop(@_, "shmctl") } +sub pp_shmread { listop(@_, "shmread") } +sub pp_shmwrite { listop(@_, "shmwrite") } +sub pp_msgget { listop(@_, "msgget") } +sub pp_msgctl { listop(@_, "msgctl") } +sub pp_msgsnd { listop(@_, "msgsnd") } +sub pp_msgrcv { listop(@_, "msgrcv") } +sub pp_semget { listop(@_, "semget") } +sub pp_semctl { listop(@_, "semctl") } +sub pp_semop { listop(@_, "semop") } +sub pp_ghbyaddr { listop(@_, "gethostbyaddr") } +sub pp_gnbyaddr { listop(@_, "getnetbyaddr") } +sub pp_gpbynumber { listop(@_, "getprotobynumber") } +sub pp_gsbyname { listop(@_, "getservbyname") } +sub pp_gsbyport { listop(@_, "getservbyport") } +sub pp_syscall { listop(@_, "syscall") } + +sub pp_glob { + my $self = shift; + my($op, $cx) = @_; + my $text = $self->dq($op->first->sibling); # skip pushmark + if ($text =~ /^\$?(\w|::|\`)+$/ # could look like a readline + or $text =~ /[<>]/) { + return 'glob(' . single_delim('qq', '"', $text) . ')'; + } else { + return '<' . $text . '>'; + } +} + +# Truncate is special because OPf_SPECIAL makes a bareword first arg +# be a filehandle. This could probably be better fixed in the core +# by moving the GV lookup into ck_truc. + +sub pp_truncate { + my $self = shift; + my($op, $cx) = @_; + my(@exprs); + my $parens = ($cx >= 5) || $self->{'parens'}; + my $kid = $op->first->sibling; + my($fh, $len); + if ($op->flags & OPf_SPECIAL) { + # $kid is an OP_CONST + $fh = $kid->sv->PV; + } else { + $fh = $self->deparse($kid, 6); + $fh = "+$fh" if not $parens and substr($fh, 0, 1) eq "("; + } + my $len = $self->deparse($kid->sibling, 6); + if ($parens) { + return "truncate($fh, $len)"; + } else { + return "truncate $fh, $len"; + } + +} + +sub indirop { + my $self = shift; + my($op, $cx, $name) = @_; + my($expr, @exprs); + my $kid = $op->first->sibling; + my $indir = ""; + if ($op->flags & OPf_STACKED) { + $indir = $kid; + $indir = $indir->first; # skip rv2gv + if (is_scope($indir)) { + $indir = "{" . $self->deparse($indir, 0) . "}"; + } else { + $indir = $self->deparse($indir, 24); + } + $indir = $indir . " "; + $kid = $kid->sibling; + } + for (; !null($kid); $kid = $kid->sibling) { + $expr = $self->deparse($kid, 6); + push @exprs, $expr; + } + return $self->maybe_parens_func($name, + $indir . join(", ", @exprs), + $cx, 5); +} + +sub pp_prtf { indirop(@_, "printf") } +sub pp_print { indirop(@_, "print") } +sub pp_sort { indirop(@_, "sort") } + +sub mapop { + my $self = shift; + my($op, $cx, $name) = @_; + my($expr, @exprs); + my $kid = $op->first; # this is the (map|grep)start + $kid = $kid->first->sibling; # skip a pushmark + my $code = $kid->first; # skip a null + if (is_scope $code) { + $code = "{" . $self->deparse($code, 1) . "} "; + } else { + $code = $self->deparse($code, 24) . ", "; + } + $kid = $kid->sibling; + for (; !null($kid); $kid = $kid->sibling) { + $expr = $self->deparse($kid, 6); + push @exprs, $expr if $expr; + } + return $self->maybe_parens_func($name, $code . join(", ", @exprs), $cx, 5); +} + +sub pp_mapwhile { mapop(@_, "map") } +sub pp_grepwhile { mapop(@_, "grep") } + +sub pp_list { + my $self = shift; + my($op, $cx) = @_; + my($expr, @exprs); + my $kid = $op->first->sibling; # skip pushmark + my $lop; + my $local = "either"; # could be local(...) or my(...) + for ($lop = $kid; !null($lop); $lop = $lop->sibling) { + # This assumes that no other private flags equal 128, and that + # OPs that store things other than flags in their op_private, + # like OP_AELEMFAST, won't be immediate children of a list. + unless ($lop->private & OPp_LVAL_INTRO or $lop->ppaddr eq "pp_undef") + { + $local = ""; # or not + last; + } + if ($lop->ppaddr =~ /^pp_pad[ash]v$/) { # my() + ($local = "", last) if $local eq "local"; + $local = "my"; + } elsif ($lop->ppaddr ne "pp_undef") { # local() + ($local = "", last) if $local eq "my"; + $local = "local"; + } + } + $local = "" if $local eq "either"; # no point if it's all undefs + return $self->deparse($kid, $cx) if null $kid->sibling and not $local; + for (; !null($kid); $kid = $kid->sibling) { + if ($local) { + if (class($kid) eq "UNOP" and $kid->first->ppaddr eq "pp_gvsv") { + $lop = $kid->first; + } else { + $lop = $kid; + } + $self->{'avoid_local'}{$$lop}++; + $expr = $self->deparse($kid, 6); + delete $self->{'avoid_local'}{$$lop}; + } else { + $expr = $self->deparse($kid, 6); + } + push @exprs, $expr; + } + if ($local) { + return "$local(" . join(", ", @exprs) . ")"; + } else { + return $self->maybe_parens( join(", ", @exprs), $cx, 6); + } +} + +sub pp_cond_expr { + my $self = shift; + my($op, $cx) = @_; + my $cond = $op->first; + my $true = $cond->sibling; + my $false = $true->sibling; + my $cuddle = $self->{'cuddle'}; + unless ($cx == 0 and is_scope($true) and is_scope($false)) { + $cond = $self->deparse($cond, 8); + $true = $self->deparse($true, 8); + $false = $self->deparse($false, 8); + return $self->maybe_parens("$cond ? $true : $false", $cx, 8); + } + $cond = $self->deparse($cond, 1); + $true = $self->deparse($true, 0); + if ($false->ppaddr eq "pp_lineseq") { # braces w/o scope => elsif + my $head = "if ($cond) {\n\t$true\n\b}"; + my @elsifs; + while (!null($false) and $false->ppaddr eq "pp_lineseq") { + my $newop = $false->first->sibling->first; + my $newcond = $newop->first; + my $newtrue = $newcond->sibling; + $false = $newtrue->sibling; # last in chain is OP_AND => no else + $newcond = $self->deparse($newcond, 1); + $newtrue = $self->deparse($newtrue, 0); + push @elsifs, "elsif ($newcond) {\n\t$newtrue\n\b}"; + } + if (!null($false)) { + $false = $cuddle . "else {\n\t" . + $self->deparse($false, 0) . "\n\b}\cK"; + } else { + $false = "\cK"; + } + return $head . join($cuddle, "", @elsifs) . $false; + } + $false = $self->deparse($false, 0); + return "if ($cond) {\n\t$true\n\b}${cuddle}else {\n\t$false\n\b}\cK"; +} + +sub pp_leaveloop { + my $self = shift; + my($op, $cx) = @_; + my $enter = $op->first; + my $kid = $enter->sibling; + local($self->{'curstash'}) = $self->{'curstash'}; + my $head = ""; + my $bare = 0; + if ($kid->ppaddr eq "pp_lineseq") { # bare or infinite loop + if (is_state $kid->last) { # infinite + $head = "for (;;) "; # shorter than while (1) + } else { + $bare = 1; + } + } elsif ($enter->ppaddr eq "pp_enteriter") { # foreach + my $ary = $enter->first->sibling; # first was pushmark + my $var = $ary->sibling; + if ($enter->flags & OPf_STACKED + and not null $ary->first->sibling->sibling) + { + $ary = $self->deparse($ary->first->sibling, 9) . " .. " . + $self->deparse($ary->first->sibling->sibling, 9); + } else { + $ary = $self->deparse($ary, 1); + } + if (null $var) { + if ($enter->flags & OPf_SPECIAL) { # thread special var + $var = $self->pp_threadsv($enter, 1); + } else { # regular my() variable + $var = $self->pp_padsv($enter, 1); + if ($self->padname_sv($enter->targ)->IVX == + $kid->first->first->sibling->last->cop_seq) + { + # If the scope of this variable closes at the last + # statement of the loop, it must have been + # declared here. + $var = "my " . $var; + } + } + } elsif ($var->ppaddr eq "pp_rv2gv") { + $var = $self->pp_rv2sv($var, 1); + } elsif ($var->ppaddr eq "pp_gv") { + $var = "\$" . $self->deparse($var, 1); + } + $head = "foreach $var ($ary) "; + $kid = $kid->first->first->sibling; # skip OP_AND and OP_ITER + } elsif ($kid->ppaddr eq "pp_null") { # while/until + $kid = $kid->first; + my $name = {"pp_and" => "while", "pp_or" => "until"} + ->{$kid->ppaddr}; + $head = "$name (" . $self->deparse($kid->first, 1) . ") "; + $kid = $kid->first->sibling; + } elsif ($kid->ppaddr eq "pp_stub") { # bare and empty + return "{;}"; # {} could be a hashref + } + # The third-to-last kid is the continue block if the pointer used + # by `next BLOCK' points to its first OP, which happens to be the + # the op_next of the head of the _previous_ statement. + # Unless it's a bare loop, in which case it's last, since there's + # no unstack or extra nextstate. + # Except if the previous head isn't null but the first kid is + # (because it's a nulled out nextstate in a scope), in which + # case the head's next is advanced past the null but the nextop's + # isn't, so we need to try nextop->next. + my($cont, $precont); + if ($bare) { + $cont = $kid->first; + while (!null($cont->sibling)) { + $precont = $cont; + $cont = $cont->sibling; + } + } else { + $cont = $kid->first; + while (!null($cont->sibling->sibling->sibling)) { + $precont = $cont; + $cont = $cont->sibling; + } + } + if ($precont and $ {$precont->next} == $ {$enter->nextop} + || $ {$precont->next} == $ {$enter->nextop->next} ) + { + my $state = $kid->first; + my $cuddle = $self->{'cuddle'}; + my($expr, @exprs); + for (; $$state != $$cont; $state = $state->sibling) { + $expr = ""; + if (is_state $state) { + $expr = $self->deparse($state, 0); + $state = $state->sibling; + last if null $kid; + } + $expr .= $self->deparse($state, 0); + push @exprs, $expr if $expr; + } + $kid = join(";\n", @exprs); + $cont = $cuddle . "continue {\n\t" . + $self->deparse($cont, 0) . "\n\b}\cK"; + } else { + $cont = "\cK"; + $kid = $self->deparse($kid, 0); + } + return $head . "{\n\t" . $kid . "\n\b}" . $cont; +} + +sub pp_leavetry { + my $self = shift; + return "eval {\n\t" . $self->pp_leave(@_) . "\n\b}"; +} + +sub OP_CONST () { 5 } + +# XXX need a better way to do this +sub OP_STRINGIFY () { $] > 5.004_72 ? 67 : 65 } + +sub pp_null { + my $self = shift; + my($op, $cx) = @_; + if (class($op) eq "OP") { + return "'???'" if $op->targ == OP_CONST; # old value is lost + } elsif ($op->first->ppaddr eq "pp_pushmark") { + return $self->pp_list($op, $cx); + } elsif ($op->first->ppaddr eq "pp_enter") { + return $self->pp_leave($op, $cx); + } elsif ($op->targ == OP_STRINGIFY) { + return $self->dquote($op); + } elsif (!null($op->first->sibling) and + $op->first->sibling->ppaddr eq "pp_readline" and + $op->first->sibling->flags & OPf_STACKED) { + return $self->maybe_parens($self->deparse($op->first, 7) . " = " + . $self->deparse($op->first->sibling, 7), + $cx, 7); + } elsif (!null($op->first->sibling) and + $op->first->sibling->ppaddr eq "pp_trans" and + $op->first->sibling->flags & OPf_STACKED) { + return $self->maybe_parens($self->deparse($op->first, 20) . " =~ " + . $self->deparse($op->first->sibling, 20), + $cx, 20); + } else { + return $self->deparse($op->first, $cx); + } +} + +sub padname { + my $self = shift; + my $targ = shift; + my $str = $self->padname_sv($targ)->PV; + return padname_fix($str); +} + +sub padany { + my $self = shift; + my $op = shift; + return substr($self->padname($op->targ), 1); # skip $/@/% +} + +sub pp_padsv { + my $self = shift; + my($op, $cx) = @_; + return $self->maybe_my($op, $cx, $self->padname($op->targ)); +} + +sub pp_padav { pp_padsv(@_) } +sub pp_padhv { pp_padsv(@_) } + +my @threadsv_names; + +BEGIN { + @threadsv_names = ("_", "1", "2", "3", "4", "5", "6", "7", "8", "9", + "&", "`", "'", "+", "/", ".", ",", "\\", '"', ";", + "^", "-", "%", "=", "|", "~", ":", "^A", "^E", + "!", "@"); +} + +sub pp_threadsv { + my $self = shift; + my($op, $cx) = @_; + return $self->maybe_local($op, $cx, "\$" . $threadsv_names[$op->targ]); +} + +sub pp_gvsv { + my $self = shift; + my($op, $cx) = @_; + return $self->maybe_local($op, $cx, "\$" . $self->gv_name($op->gv)); +} + +sub pp_gv { + my $self = shift; + my($op, $cx) = @_; + return $self->gv_name($op->gv); +} + +sub pp_aelemfast { + my $self = shift; + my($op, $cx) = @_; + my $gv = $op->gv; + return "\$" . $self->gv_name($gv) . "[" . $op->private . "]"; +} + +sub rv2x { + my $self = shift; + my($op, $cx, $type) = @_; + my $kid = $op->first; + my $str = $self->deparse($kid, 0); + return $type . (is_scalar($kid) ? $str : "{$str}"); +} + +sub pp_rv2sv { maybe_local(@_, rv2x(@_, "\$")) } +sub pp_rv2hv { maybe_local(@_, rv2x(@_, "%")) } +sub pp_rv2gv { maybe_local(@_, rv2x(@_, "*")) } + +# skip rv2av +sub pp_av2arylen { + my $self = shift; + my($op, $cx) = @_; + if ($op->first->ppaddr eq "pp_padav") { + return $self->maybe_local($op, $cx, '$#' . $self->padany($op->first)); + } else { + return $self->maybe_local($op, $cx, + $self->rv2x($op->first, $cx, '$#')); + } +} + +# skip down to the old, ex-rv2cv +sub pp_rv2cv { $_[0]->rv2x($_[1]->first->first->sibling, $_[2], "&") } + +sub pp_rv2av { + my $self = shift; + my($op, $cx) = @_; + my $kid = $op->first; + if ($kid->ppaddr eq "pp_const") { # constant list + my $av = $kid->sv; + return "(" . join(", ", map(const($_), $av->ARRAY)) . ")"; + } else { + return $self->maybe_local($op, $cx, $self->rv2x($op, $cx, "\@")); + } + } + + +sub elem { + my $self = shift; + my ($op, $cx, $left, $right, $padname) = @_; + my($array, $idx) = ($op->first, $op->first->sibling); + unless ($array->ppaddr eq $padname) { # Maybe this has been fixed + $array = $array->first; # skip rv2av (or ex-rv2av in _53+) + } + if ($array->ppaddr eq $padname) { + $array = $self->padany($array); + } elsif (is_scope($array)) { # ${expr}[0] + $array = "{" . $self->deparse($array, 0) . "}"; + } elsif (is_scalar $array) { # $x[0], $$x[0], ... + $array = $self->deparse($array, 24); + } else { + # $x[20][3]{hi} or expr->[20] + my $arrow; + $arrow = "->" if $array->ppaddr !~ /^pp_[ah]elem$/; + return $self->deparse($array, 24) . $arrow . + $left . $self->deparse($idx, 1) . $right; + } + $idx = $self->deparse($idx, 1); + return "\$" . $array . $left . $idx . $right; +} + +sub pp_aelem { maybe_local(@_, elem(@_, "[", "]", "pp_padav")) } +sub pp_helem { maybe_local(@_, elem(@_, "{", "}", "pp_padhv")) } + +sub pp_gelem { + my $self = shift; + my($op, $cx) = @_; + my($glob, $part) = ($op->first, $op->last); + $glob = $glob->first; # skip rv2gv + $glob = $glob->first if $glob->ppaddr eq "pp_rv2gv"; # this one's a bug + my $scope = is_scope($glob); + $glob = $self->deparse($glob, 0); + $part = $self->deparse($part, 1); + return "*" . ($scope ? "{$glob}" : $glob) . "{$part}"; +} + +sub slice { + my $self = shift; + my ($op, $cx, $left, $right, $regname, $padname) = @_; + my $last; + my(@elems, $kid, $array, $list); + if (class($op) eq "LISTOP") { + $last = $op->last; + } else { # ex-hslice inside delete() + for ($kid = $op->first; !null $kid->sibling; $kid = $kid->sibling) {} + $last = $kid; + } + $array = $last; + $array = $array->first + if $array->ppaddr eq $regname or $array->ppaddr eq "pp_null"; + if (is_scope($array)) { + $array = "{" . $self->deparse($array, 0) . "}"; + } elsif ($array->ppaddr eq $padname) { + $array = $self->padany($array); + } else { + $array = $self->deparse($array, 24); + } + $kid = $op->first->sibling; # skip pushmark + if ($kid->ppaddr eq "pp_list") { + $kid = $kid->first->sibling; # skip list, pushmark + for (; !null $kid; $kid = $kid->sibling) { + push @elems, $self->deparse($kid, 6); + } + $list = join(", ", @elems); + } else { + $list = $self->deparse($kid, 1); + } + return "\@" . $array . $left . $list . $right; +} + +sub pp_aslice { maybe_local(@_, slice(@_, "[", "]", + "pp_rv2av", "pp_padav")) } +sub pp_hslice { maybe_local(@_, slice(@_, "{", "}", + "pp_rv2hv", "pp_padhv")) } + +sub pp_lslice { + my $self = shift; + my($op, $cx) = @_; + my $idx = $op->first; + my $list = $op->last; + my(@elems, $kid); + $list = $self->deparse($list, 1); + $idx = $self->deparse($idx, 1); + return "($list)" . "[$idx]"; +} + +sub OPpENTERSUB_AMPER () { 8 } + +sub OPf_WANT () { 3 } +sub OPf_WANT_VOID () { 1 } +sub OPf_WANT_SCALAR () { 2 } +sub OPf_WANT_LIST () { 2 } + +sub want_scalar { + my $op = shift; + return ($op->flags & OPf_WANT) == OPf_WANT_SCALAR; +} + +sub pp_entersub { + my $self = shift; + my($op, $cx) = @_; + my $prefix = ""; + my $amper = ""; + my $proto = undef; + my $simple = 0; + my($kid, $args, @exprs); + if (not null $op->first->sibling) { # method + $kid = $op->first->sibling; # skip pushmark + my $obj = $self->deparse($kid, 24); + $kid = $kid->sibling; + for (; not null $kid->sibling; $kid = $kid->sibling) { + push @exprs, $self->deparse($kid, 6); + } + my $meth = $kid->first; + if ($meth->ppaddr eq "pp_const") { + $meth = $meth->sv->PV; # needs to be bare + } else { + $meth = $self->deparse($meth, 1); + } + $args = join(", ", @exprs); + $kid = $obj . "->" . $meth; + if ($args) { + return $kid . "(" . $args . ")"; # parens mandatory + } else { + return $kid; # toke.c fakes parens + } + } + # else, not a method + if ($op->flags & OPf_SPECIAL) { + $prefix = "do "; + } elsif ($op->private & OPpENTERSUB_AMPER) { + $amper = "&"; + } + $kid = $op->first; + $kid = $kid->first->sibling; # skip ex-list, pushmark + for (; not null $kid->sibling; $kid = $kid->sibling) { + push @exprs, $kid; + } + if (is_scope($kid)) { + $amper = "&"; + $kid = "{" . $self->deparse($kid, 0) . "}"; + } elsif ($kid->first->ppaddr eq "pp_gv") { + my $gv = $kid->first->gv; + if (class($gv->CV) ne "SPECIAL") { + $proto = $gv->CV->PV if $gv->CV->FLAGS & SVf_POK; + } + $simple = 1; + $kid = $self->deparse($kid, 24); + } elsif (is_scalar $kid->first) { + $amper = "&"; + $kid = $self->deparse($kid, 24); + } else { + $prefix = ""; + $kid = $self->deparse($kid, 24) . "->"; + } + if (defined $proto and not $amper) { + my($arg, $real); + my $doneok = 0; + my @args = @exprs; + my @reals; + my $p = $proto; + $p =~ s/([^\\]|^)([@%])(.*)$/$1$2/; + while ($p) { + $p =~ s/^ *([\\]?[\$\@&%*]|;)//; + my $chr = $1; + if ($chr eq "") { + undef $proto if @args; + } elsif ($chr eq ";") { + $doneok = 1; + } elsif ($chr eq "@" or $chr eq "%") { + push @reals, map($self->deparse($_, 6), @args); + @args = (); + } else { + $arg = shift @args; + last unless $arg; + if ($chr eq "\$") { + if (want_scalar $arg) { + push @reals, $self->deparse($arg, 6); + } else { + undef $proto; + } + } elsif ($chr eq "&") { + if ($arg->ppaddr =~ /pp_(s?refgen|undef)/) { + push @reals, $self->deparse($arg, 6); + } else { + undef $proto; + } + } elsif ($chr eq "*") { + if ($arg->ppaddr =~ /^pp_s?refgen$/ + and $arg->first->first->ppaddr eq "pp_rv2gv") + { + $real = $arg->first->first; # skip refgen, null + if ($real->first->ppaddr eq "pp_gv") { + push @reals, $self->deparse($real, 6); + } else { + push @reals, $self->deparse($real->first, 6); + } + } else { + undef $proto; + } + } elsif (substr($chr, 0, 1) eq "\\") { + $chr = substr($chr, 1); + if ($arg->ppaddr =~ /^pp_s?refgen$/ and + !null($real = $arg->first) and + ($chr eq "\$" && is_scalar($real->first) + or ($chr eq "\@" + && $real->first->sibling->ppaddr + =~ /^pp_(rv2|pad)av$/) + or ($chr eq "%" + && $real->first->sibling->ppaddr + =~ /^pp_(rv2|pad)hv$/) + #or ($chr eq "&" # This doesn't work + # && $real->first->ppaddr eq "pp_rv2cv") + or ($chr eq "*" + && $real->first->ppaddr eq "pp_rv2gv"))) + { + push @reals, $self->deparse($real, 6); + } else { + undef $proto; + } + } + } + } + undef $proto if $p and !$doneok; + undef $proto if @args; + $args = join(", ", @reals); + $amper = ""; + unless (defined $proto) { + $amper = "&"; + $args = join(", ", map($self->deparse($_, 6), @exprs)); + } + } else { + $args = join(", ", map($self->deparse($_, 6), @exprs)); + } + if ($prefix or $amper) { + if ($op->flags & OPf_STACKED) { + return $prefix . $amper . $kid . "(" . $args . ")"; + } else { + return $prefix . $amper. $kid; + } + } else { + if (defined $proto and $proto eq "") { + return $kid; + } elsif ($proto eq "\$") { + return $self->maybe_parens_func($kid, $args, $cx, 16); + } elsif ($proto or $simple) { + return $self->maybe_parens_func($kid, $args, $cx, 5); + } else { + return "$kid(" . $args . ")"; + } + } +} + +sub pp_enterwrite { unop(@_, "write") } + +# escape things that cause interpolation in double quotes, +# but not character escapes +sub uninterp { + my($str) = @_; + $str =~ s/(^|[^\\])([\$\@]|\\[uUlLQE])/$1\\$2/g; + return $str; +} + +# the same, but treat $|, $), and $ at the end of the string differently +sub re_uninterp { + my($str) = @_; + $str =~ s/(^|[^\\])(\@|\\[uUlLQE])/$1\\$2/g; + $str =~ s/(^|[^\\])(\$[^)|])/$1\\$2/g; + return $str; +} + +# character escapes, but not delimiters that might need to be escaped +sub escape_str { # ASCII + my($str) = @_; + $str =~ s/\a/\\a/g; +# $str =~ s/\cH/\\b/g; # \b means someting different in a regex + $str =~ s/\t/\\t/g; + $str =~ s/\n/\\n/g; + $str =~ s/\e/\\e/g; + $str =~ s/\f/\\f/g; + $str =~ s/\r/\\r/g; + $str =~ s/([\cA-\cZ])/'\\c' . chr(ord('@') + ord($1))/ge; + $str =~ s/([\0\033-\037\177-\377])/'\\' . sprintf("%03o", ord($1))/ge; + return $str; +} + +# Don't do this for regexen +sub unback { + my($str) = @_; + $str =~ s/\\/\\\\/g; + return $str; +} + +sub balanced_delim { + my($str) = @_; + my @str = split //, $str; + my($ar, $open, $close, $fail, $c, $cnt); + for $ar (['[',']'], ['(',')'], ['<','>'], ['{','}']) { + ($open, $close) = @$ar; + $fail = 0; $cnt = 0; + for $c (@str) { + if ($c eq $open) { + $cnt++; + } elsif ($c eq $close) { + $cnt--; + if ($cnt < 0) { + $fail = 1; + last; + } + } + } + $fail = 1 if $cnt != 0; + return ($open, "$open$str$close") if not $fail; + } + return ("", $str); +} + +sub single_delim { + my($q, $default, $str) = @_; + return "$default$str$default" if $default and index($str, $default) == -1; + my($succeed, $delim); + ($succeed, $str) = balanced_delim($str); + return "$q$str" if $succeed; + for $delim ('/', '"', '#') { + return "$q$delim" . $str . $delim if index($str, $delim) == -1; + } + if ($default) { + $str =~ s/$default/\\$default/g; + return "$default$str$default"; + } else { + $str =~ s[/][\\/]g; + return "$q/$str/"; + } +} + +sub SVf_IOK () {0x10000} +sub SVf_NOK () {0x20000} +sub SVf_ROK () {0x80000} + +sub const { + my $sv = shift; + if (class($sv) eq "SPECIAL") { + return ('undef', '1', '0')[$$sv-1]; + } elsif ($sv->FLAGS & SVf_IOK) { + return $sv->IV; + } elsif ($sv->FLAGS & SVf_NOK) { + return $sv->NV; + } elsif ($sv->FLAGS & SVf_ROK) { + return "\\(" . const($sv->RV) . ")"; # constant folded + } else { + my $str = $sv->PV; + if ($str =~ /[^ -~]/) { # ASCII + return single_delim("qq", '"', uninterp escape_str unback $str); + } else { + $str =~ s/\\/\\\\/g; + return single_delim("q", "'", $str); + } + } +} + +sub pp_const { + my $self = shift; + my($op, $cx) = @_; +# if ($op->private & OPp_CONST_BARE) { # trouble with `=>' autoquoting +# return $op->sv->PV; +# } + return const($op->sv); +} + +sub dq { + my $self = shift; + my $op = shift; + my $type = $op->ppaddr; + if ($type eq "pp_const") { + return uninterp(escape_str(unback($op->sv->PV))); + } elsif ($type eq "pp_concat") { + return $self->dq($op->first) . $self->dq($op->last); + } elsif ($type eq "pp_uc") { + return '\U' . $self->dq($op->first->sibling) . '\E'; + } elsif ($type eq "pp_lc") { + return '\L' . $self->dq($op->first->sibling) . '\E'; + } elsif ($type eq "pp_ucfirst") { + return '\u' . $self->dq($op->first->sibling); + } elsif ($type eq "pp_lcfirst") { + return '\l' . $self->dq($op->first->sibling); + } elsif ($type eq "pp_quotemeta") { + return '\Q' . $self->dq($op->first->sibling) . '\E'; + } elsif ($type eq "pp_join") { + return $self->deparse($op->last, 26); # was join($", @ary) + } else { + return $self->deparse($op, 26); + } +} + +sub pp_backtick { + my $self = shift; + my($op, $cx) = @_; + # skip pushmark + return single_delim("qx", '`', $self->dq($op->first->sibling)); +} + +sub dquote { + my $self = shift; + my $op = shift; + # skip ex-stringify, pushmark + return single_delim("qq", '"', $self->dq($op->first->sibling)); +} + +# OP_STRINGIFY is a listop, but it only ever has one arg (?) +sub pp_stringify { dquote(@_) } + +# tr/// and s/// (and tr[][], tr[]//, tr###, etc) +# note that tr(from)/to/ is OK, but not tr/from/(to) +sub double_delim { + my($from, $to) = @_; + my($succeed, $delim); + if ($from !~ m[/] and $to !~ m[/]) { + return "/$from/$to/"; + } elsif (($succeed, $from) = balanced_delim($from) and $succeed) { + if (($succeed, $to) = balanced_delim($to) and $succeed) { + return "$from$to"; + } else { + for $delim ('/', '"', '#') { # note no `'' -- s''' is special + return "$from$delim$to$delim" if index($to, $delim) == -1; + } + $to =~ s[/][\\/]g; + return "$from/$to/"; + } + } else { + for $delim ('/', '"', '#') { # note no ' + return "$delim$from$delim$to$delim" + if index($to . $from, $delim) == -1; + } + $from =~ s[/][\\/]g; + $to =~ s[/][\\/]g; + return "/$from/$to/"; + } +} + +sub pchr { # ASCII + my($n) = @_; + if ($n == ord '\\') { + return '\\\\'; + } elsif ($n >= ord(' ') and $n <= ord('~')) { + return chr($n); + } elsif ($n == ord "\a") { + return '\\a'; + } elsif ($n == ord "\b") { + return '\\b'; + } elsif ($n == ord "\t") { + return '\\t'; + } elsif ($n == ord "\n") { + return '\\n'; + } elsif ($n == ord "\e") { + return '\\e'; + } elsif ($n == ord "\f") { + return '\\f'; + } elsif ($n == ord "\r") { + return '\\r'; + } elsif ($n >= ord("\cA") and $n <= ord("\cZ")) { + return '\\c' . chr(ord("@") + $n); + } else { +# return '\x' . sprintf("%02x", $n); + return '\\' . sprintf("%03o", $n); + } +} + +sub collapse { + my(@chars) = @_; + my($c, $str, $tr); + for ($c = 0; $c < @chars; $c++) { + $tr = $chars[$c]; + $str .= pchr($tr); + if ($c <= $#chars - 2 and $chars[$c + 1] == $tr + 1 and + $chars[$c + 2] == $tr + 2) + { + for (; $c <= $#chars and $chars[$c + 1] == $chars[$c] + 1; $c++) {} + $str .= "-"; + $str .= pchr($chars[$c]); + } + } + return $str; +} + +sub OPpTRANS_SQUASH () { 16 } +sub OPpTRANS_DELETE () { 32 } +sub OPpTRANS_COMPLEMENT () { 64 } + +sub pp_trans { + my $self = shift; + my($op, $cx) = @_; + my(@table) = unpack("s256", $op->pv); + my($c, $tr, @from, @to, @delfrom, $delhyphen); + if ($table[ord "-"] != -1 and + $table[ord("-") - 1] == -1 || $table[ord("-") + 1] == -1) + { + $tr = $table[ord "-"]; + $table[ord "-"] = -1; + if ($tr >= 0) { + @from = ord("-"); + @to = $tr; + } else { # -2 ==> delete + $delhyphen = 1; + } + } + for ($c = 0; $c < 256; $c++) { + $tr = $table[$c]; + if ($tr >= 0) { + push @from, $c; push @to, $tr; + } elsif ($tr == -2) { + push @delfrom, $c; + } + } + my $flags; + @from = (@from, @delfrom); + if ($op->private & OPpTRANS_COMPLEMENT) { + $flags .= "c"; + my @newfrom = (); + my %from; + @from{@from} = (1) x @from; + for ($c = 0; $c < 256; $c++) { + push @newfrom, $c unless $from{$c}; + } + @from = @newfrom; + } + if ($op->private & OPpTRANS_DELETE) { + $flags .= "d"; + } else { + pop @to while $#to and $to[$#to] == $to[$#to -1]; + } + $flags .= "s" if $op->private & OPpTRANS_SQUASH; + my($from, $to); + $from = collapse(@from); + $to = collapse(@to); + $from .= "-" if $delhyphen; + return "tr" . double_delim($from, $to) . $flags; +} + +# Like dq(), but different +sub re_dq { + my $self = shift; + my $op = shift; + my $type = $op->ppaddr; + if ($type eq "pp_const") { + return uninterp($op->sv->PV); + } elsif ($type eq "pp_concat") { + return $self->re_dq($op->first) . $self->re_dq($op->last); + } elsif ($type eq "pp_uc") { + return '\U' . $self->re_dq($op->first->sibling) . '\E'; + } elsif ($type eq "pp_lc") { + return '\L' . $self->re_dq($op->first->sibling) . '\E'; + } elsif ($type eq "pp_ucfirst") { + return '\u' . $self->re_dq($op->first->sibling); + } elsif ($type eq "pp_lcfirst") { + return '\l' . $self->re_dq($op->first->sibling); + } elsif ($type eq "pp_quotemeta") { + return '\Q' . $self->re_dq($op->first->sibling) . '\E'; + } elsif ($type eq "pp_join") { + return $self->deparse($op->last, 26); # was join($", @ary) + } else { + return $self->deparse($op, 26); + } +} + +sub pp_regcomp { + my $self = shift; + my($op, $cx) = @_; + my $kid = $op->first; + $kid = $kid->first if $kid->ppaddr eq "pp_regcmaybe"; + $kid = $kid->first if $kid->ppaddr eq "pp_regcreset"; + return $self->re_dq($kid); +} + +sub OPp_RUNTIME () { 64 } + +sub PMf_ONCE () { 0x2 } +sub PMf_SKIPWHITE () { 0x10 } +sub PMf_CONST () { 0x40 } +sub PMf_KEEP () { 0x80 } +sub PMf_GLOBAL () { 0x100 } +sub PMf_CONTINUE () { 0x200 } +sub PMf_EVAL () { 0x400 } +sub PMf_LOCALE () { 0x800 } +sub PMf_MULTILINE () { 0x1000 } +sub PMf_SINGLELINE () { 0x2000 } +sub PMf_FOLD () { 0x4000 } +sub PMf_EXTENDED () { 0x8000 } + +# osmic acid -- see osmium tetroxide + +my %matchwords; +map($matchwords{join "", sort split //, $_} = $_, 'cig', 'cog', 'cos', 'cogs', + 'cox', 'go', 'is', 'ism', 'iso', 'mig', 'mix', 'osmic', 'ox', 'sic', + 'sig', 'six', 'smog', 'so', 'soc', 'sog', 'xi'); + +sub matchop { + my $self = shift; + my($op, $cx, $name, $delim) = @_; + my $kid = $op->first; + my ($binop, $var, $re) = ("", "", ""); + if ($op->flags & OPf_STACKED) { + $binop = 1; + $var = $self->deparse($kid, 20); + $kid = $kid->sibling; + } + if (null $kid) { + $re = re_uninterp(escape_str($op->precomp)); + } else { + $re = $self->deparse($kid, 1); + } + my $flags = ""; + $flags .= "c" if $op->pmflags & PMf_CONTINUE; + $flags .= "g" if $op->pmflags & PMf_GLOBAL; + $flags .= "i" if $op->pmflags & PMf_FOLD; + $flags .= "m" if $op->pmflags & PMf_MULTILINE; + $flags .= "o" if $op->pmflags & PMf_KEEP; + $flags .= "s" if $op->pmflags & PMf_SINGLELINE; + $flags .= "x" if $op->pmflags & PMf_EXTENDED; + $flags = $matchwords{$flags} if $matchwords{$flags}; + if ($op->pmflags & PMf_ONCE) { # only one kind of delimiter works here + $re =~ s/\?/\\?/g; + $re = "?$re?"; + } else { + $re = single_delim($name, $delim, $re); + } + $re = $re . $flags; + if ($binop) { + return $self->maybe_parens("$var =~ $re", $cx, 20); + } else { + return $re; + } +} + +sub pp_match { matchop(@_, "m", "/") } +sub pp_pushre { matchop(@_, "m", "/") } +sub pp_qr { matchop(@_, "qr", "") } + +sub pp_split { + my $self = shift; + my($op, $cx) = @_; + my($kid, @exprs, $ary, $expr); + $kid = $op->first; + if ($ {$kid->pmreplroot}) { + $ary = '@' . $self->gv_name($kid->pmreplroot); + } + for (; !null($kid); $kid = $kid->sibling) { + push @exprs, $self->deparse($kid, 6); + } + $expr = "split(" . join(", ", @exprs) . ")"; + if ($ary) { + return $self->maybe_parens("$ary = $expr", $cx, 7); + } else { + return $expr; + } +} + +# oxime -- any of various compounds obtained chiefly by the action of +# hydroxylamine on aldehydes and ketones and characterized by the +# bivalent grouping C=NOH [Webster's Tenth] + +my %substwords; +map($substwords{join "", sort split //, $_} = $_, 'ego', 'egoism', 'em', + 'es', 'ex', 'exes', 'gee', 'go', 'goes', 'ie', 'ism', 'iso', 'me', + 'meese', 'meso', 'mig', 'mix', 'os', 'ox', 'oxime', 'see', 'seem', + 'seg', 'sex', 'sig', 'six', 'smog', 'sog', 'some', 'xi'); + +sub pp_subst { + my $self = shift; + my($op, $cx) = @_; + my $kid = $op->first; + my($binop, $var, $re, $repl) = ("", "", "", ""); + if ($op->flags & OPf_STACKED) { + $binop = 1; + $var = $self->deparse($kid, 20); + $kid = $kid->sibling; + } + my $flags = ""; + if (null($op->pmreplroot)) { + $repl = $self->dq($kid); + $kid = $kid->sibling; + } else { + $repl = $op->pmreplroot->first; # skip substcont + while ($repl->ppaddr eq "pp_entereval") { + $repl = $repl->first; + $flags .= "e"; + } + $repl = $self->dq($repl); + } + if (null $kid) { + $re = re_uninterp(escape_str($op->precomp)); + } else { + $re = $self->deparse($kid, 1); + } + $flags .= "e" if $op->pmflags & PMf_EVAL; + $flags .= "g" if $op->pmflags & PMf_GLOBAL; + $flags .= "i" if $op->pmflags & PMf_FOLD; + $flags .= "m" if $op->pmflags & PMf_MULTILINE; + $flags .= "o" if $op->pmflags & PMf_KEEP; + $flags .= "s" if $op->pmflags & PMf_SINGLELINE; + $flags .= "x" if $op->pmflags & PMf_EXTENDED; + $flags = $substwords{$flags} if $substwords{$flags}; + if ($binop) { + return $self->maybe_parens("$var =~ s" + . double_delim($re, $repl) . $flags, + $cx, 20); + } else { + return "s". double_delim($re, $repl) . $flags; + } +} + +1; +__END__ + +=head1 NAME + +B::Deparse - Perl compiler backend to produce perl code + +=head1 SYNOPSIS + +B<perl> B<-MO=Deparse>[B<,-u>I<PACKAGE>][B<,-p>][B<,-l>][B<,-s>I<LETTERS>] I<prog.pl> + +=head1 DESCRIPTION + +B::Deparse is a backend module for the Perl compiler that generates +perl source code, based on the internal compiled structure that perl +itself creates after parsing a program. The output of B::Deparse won't +be exactly the same as the original source, since perl doesn't keep +track of comments or whitespace, and there isn't a one-to-one +correspondence between perl's syntactical constructions and their +compiled form, but it will often be close. When you use the B<-p> +option, the output also includes parentheses even when they are not +required by precedence, which can make it easy to see if perl is +parsing your expressions the way you intended. + +Please note that this module is mainly new and untested code and is +still under development, so it may change in the future. + +=head1 OPTIONS + +As with all compiler backend options, these must follow directly after +the '-MO=Deparse', separated by a comma but not any white space. + +=over 4 + +=item B<-p> + +Print extra parentheses. Without this option, B::Deparse includes +parentheses in its output only when they are needed, based on the +structure of your program. With B<-p>, it uses parentheses (almost) +whenever they would be legal. This can be useful if you are used to +LISP, or if you want to see how perl parses your input. If you say + + if ($var & 0x7f == 65) {print "Gimme an A!"} + print ($which ? $a : $b), "\n"; + $name = $ENV{USER} or "Bob"; + +C<B::Deparse,-p> will print + + if (($var & 0)) { + print('Gimme an A!') + }; + (print(($which ? $a : $b)), '???'); + (($name = $ENV{'USER'}) or '???') + +which probably isn't what you intended (the C<'???'> is a sign that +perl optimized away a constant value). + +=item B<-u>I<PACKAGE> + +Normally, B::Deparse deparses the main code of a program, all the subs +called by the main program (and all the subs called by them, +recursively), and any other subs in the main:: package. To include +subs in other packages that aren't called directly, such as AUTOLOAD, +DESTROY, other subs called automatically by perl, and methods, which +aren't resolved to subs until runtime, use the B<-u> option. The +argument to B<-u> is the name of a package, and should follow directly +after the 'u'. Multiple B<-u> options may be given, separated by +commas. Note that unlike some other backends, B::Deparse doesn't +(yet) try to guess automatically when B<-u> is needed -- you must +invoke it yourself. + +=item B<-l> + +Add '#line' declarations to the output based on the line and file +locations of the original code. + +=item B<-s>I<LETTERS> + +Tweak the style of B::Deparse's output. At the moment, only one style +option is implemented: + +=over 4 + +=item B<C> + +Cuddle C<elsif>, C<else>, and C<continue> blocks. For example, print + + if (...) { + ... + } else { + ... + } + +instead of + + if (...) { + ... + } + else { + ... + } + +The default is not to cuddle. + +=back + +=back + +=head1 BUGS + +See the 'to do' list at the beginning of the module file. + +=head1 AUTHOR + +Stephen McCamant <alias@mcs.com>, based on an earlier version by +Malcolm Beattie <mbeattie@sable.ox.ac.uk>. + +=cut diff --git a/gnu/usr.bin/perl/ext/B/B/Disassembler.pm b/gnu/usr.bin/perl/ext/B/B/Disassembler.pm new file mode 100644 index 00000000000..4a008a3750c --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/B/Disassembler.pm @@ -0,0 +1,164 @@ +# Disassembler.pm +# +# Copyright (c) 1996 Malcolm Beattie +# +# You may distribute under the terms of either the GNU General Public +# License or the Artistic License, as specified in the README file. +package B::Disassembler::BytecodeStream; +use FileHandle; +use Carp; +use B qw(cstring cast_I32); +@ISA = qw(FileHandle); +sub readn { + my ($fh, $len) = @_; + my $data; + read($fh, $data, $len); + croak "reached EOF while reading $len bytes" unless length($data) == $len; + return $data; +} + +sub GET_U8 { + my $fh = shift; + my $c = $fh->getc; + croak "reached EOF while reading U8" unless defined($c); + return ord($c); +} + +sub GET_U16 { + my $fh = shift; + my $str = $fh->readn(2); + croak "reached EOF while reading U16" unless length($str) == 2; + return unpack("n", $str); +} + +sub GET_U32 { + my $fh = shift; + my $str = $fh->readn(4); + croak "reached EOF while reading U32" unless length($str) == 4; + return unpack("N", $str); +} + +sub GET_I32 { + my $fh = shift; + my $str = $fh->readn(4); + croak "reached EOF while reading I32" unless length($str) == 4; + return cast_I32(unpack("N", $str)); +} + +sub GET_objindex { + my $fh = shift; + my $str = $fh->readn(4); + croak "reached EOF while reading objindex" unless length($str) == 4; + return unpack("N", $str); +} + +sub GET_strconst { + my $fh = shift; + my ($str, $c); + while (defined($c = $fh->getc) && $c ne "\0") { + $str .= $c; + } + croak "reached EOF while reading strconst" unless defined($c); + return cstring($str); +} + +sub GET_pvcontents {} + +sub GET_PV { + my $fh = shift; + my $str; + my $len = $fh->GET_U32; + if ($len) { + read($fh, $str, $len); + croak "reached EOF while reading PV" unless length($str) == $len; + return cstring($str); + } else { + return '""'; + } +} + +sub GET_comment_t { + my $fh = shift; + my ($str, $c); + while (defined($c = $fh->getc) && $c ne "\n") { + $str .= $c; + } + croak "reached EOF while reading comment" unless defined($c); + return cstring($str); +} + +sub GET_double { + my $fh = shift; + my ($str, $c); + while (defined($c = $fh->getc) && $c ne "\0") { + $str .= $c; + } + croak "reached EOF while reading double" unless defined($c); + return $str; +} + +sub GET_none {} + +sub GET_op_tr_array { + my $fh = shift; + my @ary = unpack("n256", $fh->readn(256 * 2)); + return join(",", @ary); +} + +sub GET_IV64 { + my $fh = shift; + my ($hi, $lo) = unpack("NN", $fh->readn(8)); + return sprintf("0x%4x%04x", $hi, $lo); # cheat +} + +package B::Disassembler; +use Exporter; +@ISA = qw(Exporter); +@EXPORT_OK = qw(disassemble_fh); +use Carp; +use strict; + +use B::Asmdata qw(%insn_data @insn_name); + +sub disassemble_fh { + my ($fh, $out) = @_; + my ($c, $getmeth, $insn, $arg); + bless $fh, "B::Disassembler::BytecodeStream"; + while (defined($c = $fh->getc)) { + $c = ord($c); + $insn = $insn_name[$c]; + if (!defined($insn) || $insn eq "unused") { + my $pos = $fh->tell - 1; + die "Illegal instruction code $c at stream offset $pos\n"; + } + $getmeth = $insn_data{$insn}->[2]; + $arg = $fh->$getmeth(); + if (defined($arg)) { + &$out($insn, $arg); + } else { + &$out($insn); + } + } +} + +1; + +__END__ + +=head1 NAME + +B::Disassembler - Disassemble Perl bytecode + +=head1 SYNOPSIS + + use Disassembler; + +=head1 DESCRIPTION + +See F<ext/B/B/Disassembler.pm>. + +=head1 AUTHOR + +Malcolm Beattie, C<mbeattie@sable.ox.ac.uk> + +=cut diff --git a/gnu/usr.bin/perl/ext/B/B/Lint.pm b/gnu/usr.bin/perl/ext/B/B/Lint.pm new file mode 100644 index 00000000000..d34bd7792bc --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/B/Lint.pm @@ -0,0 +1,367 @@ +package B::Lint; + +=head1 NAME + +B::Lint - Perl lint + +=head1 SYNOPSIS + +perl -MO=Lint[,OPTIONS] foo.pl + +=head1 DESCRIPTION + +The B::Lint module is equivalent to an extended version of the B<-w> +option of B<perl>. It is named after the program B<lint> which carries +out a similar process for C programs. + +=head1 OPTIONS AND LINT CHECKS + +Option words are separated by commas (not whitespace) and follow the +usual conventions of compiler backend options. Following any options +(indicated by a leading B<->) come lint check arguments. Each such +argument (apart from the special B<all> and B<none> options) is a +word representing one possible lint check (turning on that check) or +is B<no-foo> (turning off that check). Before processing the check +arguments, a standard list of checks is turned on. Later options +override earlier ones. Available options are: + +=over 8 + +=item B<context> + +Produces a warning whenever an array is used in an implicit scalar +context. For example, both of the lines + + $foo = length(@bar); + $foo = @bar; +will elicit a warning. Using an explicit B<scalar()> silences the +warning. For example, + + $foo = scalar(@bar); + +=item B<implicit-read> and B<implicit-write> + +These options produce a warning whenever an operation implicitly +reads or (respectively) writes to one of Perl's special variables. +For example, B<implicit-read> will warn about these: + + /foo/; + +and B<implicit-write> will warn about these: + + s/foo/bar/; + +Both B<implicit-read> and B<implicit-write> warn about this: + + for (@a) { ... } + +=item B<dollar-underscore> + +This option warns whenever $_ is used either explicitly anywhere or +as the implicit argument of a B<print> statement. + +=item B<private-names> + +This option warns on each use of any variable, subroutine or +method name that lives in a non-current package but begins with +an underscore ("_"). Warnings aren't issued for the special case +of the single character name "_" by itself (e.g. $_ and @_). + +=item B<undefined-subs> + +This option warns whenever an undefined subroutine is invoked. +This option will only catch explicitly invoked subroutines such +as C<foo()> and not indirect invocations such as C<&$subref()> +or C<$obj-E<gt>meth()>. Note that some programs or modules delay +definition of subs until runtime by means of the AUTOLOAD +mechanism. + +=item B<regexp-variables> + +This option warns whenever one of the regexp variables $', $& or +$' is used. Any occurrence of any of these variables in your +program can slow your whole program down. See L<perlre> for +details. + +=item B<all> + +Turn all warnings on. + +=item B<none> + +Turn all warnings off. + +=back + +=head1 NON LINT-CHECK OPTIONS + +=over 8 + +=item B<-u Package> + +Normally, Lint only checks the main code of the program together +with all subs defined in package main. The B<-u> option lets you +include other package names whose subs are then checked by Lint. + +=back + +=head1 BUGS + +This is only a very preliminary version. + +=head1 AUTHOR + +Malcolm Beattie, mbeattie@sable.ox.ac.uk. + +=cut + +use strict; +use B qw(walkoptree_slow main_root walksymtable svref_2object parents); + +# Constants (should probably be elsewhere) +sub G_ARRAY () { 1 } +sub OPf_LIST () { 1 } +sub OPf_KNOW () { 2 } +sub OPf_STACKED () { 64 } + +my $file = "unknown"; # shadows current filename +my $line = 0; # shadows current line number +my $curstash = "main"; # shadows current stash + +# Lint checks +my %check; +my %implies_ok_context; +BEGIN { + map($implies_ok_context{$_}++, + qw(pp_scalar pp_av2arylen pp_aelem pp_aslice pp_helem pp_hslice + pp_keys pp_values pp_hslice pp_defined pp_undef pp_delete)); +} + +# Lint checks turned on by default +my @default_checks = qw(context); + +my %valid_check; +# All valid checks +BEGIN { + map($valid_check{$_}++, + qw(context implicit_read implicit_write dollar_underscore + private_names undefined_subs regexp_variables)); +} + +# Debugging options +my ($debug_op); + +my %done_cv; # used to mark which subs have already been linted +my @extra_packages; # Lint checks mainline code and all subs which are + # in main:: or in one of these packages. + +sub warning { + my $format = (@_ < 2) ? "%s" : shift; + warn sprintf("$format at %s line %d\n", @_, $file, $line); +} + +# This gimme can't cope with context that's only determined +# at runtime via dowantarray(). +sub gimme { + my $op = shift; + my $flags = $op->flags; + if ($flags & OPf_KNOW) { + return(($flags & OPf_LIST) ? 1 : 0); + } + return undef; +} + +sub B::OP::lint {} + +sub B::COP::lint { + my $op = shift; + if ($op->ppaddr eq "pp_nextstate") { + $file = $op->filegv->SV->PV; + $line = $op->line; + $curstash = $op->stash->NAME; + } +} + +sub B::UNOP::lint { + my $op = shift; + my $ppaddr = $op->ppaddr; + if ($check{context} && ($ppaddr eq "pp_rv2av" || $ppaddr eq "pp_rv2hv")) { + my $parent = parents->[0]; + my $pname = $parent->ppaddr; + return if gimme($op) || $implies_ok_context{$pname}; + # Two special cases to deal with: "foreach (@foo)" and "delete $a{$b}" + # null out the parent so we have to check for a parent of pp_null and + # a grandparent of pp_enteriter or pp_delete + if ($pname eq "pp_null") { + my $gpname = parents->[1]->ppaddr; + return if $gpname eq "pp_enteriter" || $gpname eq "pp_delete"; + } + warning("Implicit scalar context for %s in %s", + $ppaddr eq "pp_rv2av" ? "array" : "hash", $parent->desc); + } + if ($check{private_names} && $ppaddr eq "pp_method") { + my $methop = $op->first; + if ($methop->ppaddr eq "pp_const") { + my $method = $methop->sv->PV; + if ($method =~ /^_/ && !defined(&{"$curstash\::$method"})) { + warning("Illegal reference to private method name $method"); + } + } + } +} + +sub B::PMOP::lint { + my $op = shift; + if ($check{implicit_read}) { + my $ppaddr = $op->ppaddr; + if ($ppaddr eq "pp_match" && !($op->flags & OPf_STACKED)) { + warning('Implicit match on $_'); + } + } + if ($check{implicit_write}) { + my $ppaddr = $op->ppaddr; + if ($ppaddr eq "pp_subst" && !($op->flags & OPf_STACKED)) { + warning('Implicit substitution on $_'); + } + } +} + +sub B::LOOP::lint { + my $op = shift; + if ($check{implicit_read} || $check{implicit_write}) { + my $ppaddr = $op->ppaddr; + if ($ppaddr eq "pp_enteriter") { + my $last = $op->last; + if ($last->ppaddr eq "pp_gv" && $last->gv->NAME eq "_") { + warning('Implicit use of $_ in foreach'); + } + } + } +} + +sub B::GVOP::lint { + my $op = shift; + if ($check{dollar_underscore} && $op->ppaddr eq "pp_gvsv" + && $op->gv->NAME eq "_") + { + warning('Use of $_'); + } + if ($check{private_names}) { + my $ppaddr = $op->ppaddr; + my $gv = $op->gv; + if (($ppaddr eq "pp_gv" || $ppaddr eq "pp_gvsv") + && $gv->NAME =~ /^_./ && $gv->STASH->NAME ne $curstash) + { + warning('Illegal reference to private name %s', $gv->NAME); + } + } + if ($check{undefined_subs}) { + if ($op->ppaddr eq "pp_gv" && $op->next->ppaddr eq "pp_entersub") { + my $gv = $op->gv; + my $subname = $gv->STASH->NAME . "::" . $gv->NAME; + no strict 'refs'; + if (!defined(&$subname)) { + $subname =~ s/^main:://; + warning('Undefined subroutine %s called', $subname); + } + } + } + if ($check{regexp_variables} && $op->ppaddr eq "pp_gvsv") { + my $name = $op->gv->NAME; + if ($name =~ /^[&'`]$/) { + warning('Use of regexp variable $%s', $name); + } + } +} + +sub B::GV::lintcv { + my $gv = shift; + my $cv = $gv->CV; + #warn sprintf("lintcv: %s::%s (done=%d)\n", + # $gv->STASH->NAME, $gv->NAME, $done_cv{$$cv});#debug + return if !$$cv || $done_cv{$$cv}++; + my $root = $cv->ROOT; + #warn " root = $root (0x$$root)\n";#debug + walkoptree_slow($root, "lint") if $$root; +} + +sub do_lint { + my %search_pack; + walkoptree_slow(main_root, "lint") if ${main_root()}; + + # Now do subs in main + no strict qw(vars refs); + my $sym; + local(*glob); + while (($sym, *glob) = each %{"main::"}) { + #warn "Trying $sym\n";#debug + svref_2object(\*glob)->EGV->lintcv unless $sym =~ /::$/; + } + + # Now do subs in non-main packages given by -u options + map { $search_pack{$_} = 1 } @extra_packages; + walksymtable(\%{"main::"}, "lintcv", sub { + my $package = shift; + $package =~ s/::$//; + #warn "Considering $package\n";#debug + return exists $search_pack{$package}; + }); +} + +sub compile { + my @options = @_; + my ($option, $opt, $arg); + # Turn on default lint checks + for $opt (@default_checks) { + $check{$opt} = 1; + } + OPTION: + while ($option = shift @options) { + if ($option =~ /^-(.)(.*)/) { + $opt = $1; + $arg = $2; + } else { + unshift @options, $option; + last OPTION; + } + if ($opt eq "-" && $arg eq "-") { + shift @options; + last OPTION; + } elsif ($opt eq "D") { + $arg ||= shift @options; + foreach $arg (split(//, $arg)) { + if ($arg eq "o") { + B->debug(1); + } elsif ($arg eq "O") { + $debug_op = 1; + } + } + } elsif ($opt eq "u") { + $arg ||= shift @options; + push(@extra_packages, $arg); + } + } + foreach $opt (@default_checks, @options) { + $opt =~ tr/-/_/; + if ($opt eq "all") { + %check = %valid_check; + } + elsif ($opt eq "none") { + %check = (); + } + else { + if ($opt =~ s/^no-//) { + $check{$opt} = 0; + } + else { + $check{$opt} = 1; + } + warn "No such check: $opt\n" unless defined $valid_check{$opt}; + } + } + # Remaining arguments are things to check + + return \&do_lint; +} + +1; diff --git a/gnu/usr.bin/perl/ext/B/B/Showlex.pm b/gnu/usr.bin/perl/ext/B/B/Showlex.pm new file mode 100644 index 00000000000..648f95dcc0a --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/B/Showlex.pm @@ -0,0 +1,80 @@ +package B::Showlex; +use strict; +use B qw(svref_2object comppadlist class); +use B::Terse (); + +# +# Invoke as +# perl -MO=Showlex,foo bar.pl +# to see the names of lexical variables used by &foo +# or as +# perl -MO=Showlex bar.pl +# to see the names of file scope lexicals used by bar.pl +# + +sub showarray { + my ($name, $av) = @_; + my @els = $av->ARRAY; + my $count = @els; + my $i; + print "$name has $count entries\n"; + for ($i = 0; $i < $count; $i++) { + print "$i: "; + $els[$i]->terse; + } +} + +sub showlex { + my ($objname, $namesav, $valsav) = @_; + showarray("Pad of lexical names for $objname", $namesav); + showarray("Pad of lexical values for $objname", $valsav); +} + +sub showlex_obj { + my ($objname, $obj) = @_; + $objname =~ s/^&main::/&/; + showlex($objname, svref_2object($obj)->PADLIST->ARRAY); +} + +sub showlex_main { + showlex("comppadlist", comppadlist->ARRAY); +} + +sub compile { + my @options = @_; + if (@options) { + return sub { + my $objname; + foreach $objname (@options) { + $objname = "main::$objname" unless $objname =~ /::/; + eval "showlex_obj('&$objname', \\&$objname)"; + } + } + } else { + return \&showlex_main; + } +} + +1; + +__END__ + +=head1 NAME + +B::Showlex - Show lexical variables used in functions or files + +=head1 SYNOPSIS + + perl -MO=Showlex[,SUBROUTINE] foo.pl + +=head1 DESCRIPTION + +When a subroutine name is provided in OPTIONS, prints the lexical +variables used in that subroutine. Otherwise, prints the file-scope +lexicals in the file. + +=head1 AUTHOR + +Malcolm Beattie, C<mbeattie@sable.ox.ac.uk> + +=cut diff --git a/gnu/usr.bin/perl/ext/B/B/Stackobj.pm b/gnu/usr.bin/perl/ext/B/B/Stackobj.pm new file mode 100644 index 00000000000..eea966ceb6b --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/B/Stackobj.pm @@ -0,0 +1,301 @@ +# Stackobj.pm +# +# Copyright (c) 1996 Malcolm Beattie +# +# You may distribute under the terms of either the GNU General Public +# License or the Artistic License, as specified in the README file. +# +package B::Stackobj; +use Exporter (); +@ISA = qw(Exporter); +@EXPORT_OK = qw(set_callback T_UNKNOWN T_DOUBLE T_INT + VALID_INT VALID_DOUBLE VALID_SV REGISTER TEMPORARY); +%EXPORT_TAGS = (types => [qw(T_UNKNOWN T_DOUBLE T_INT)], + flags => [qw(VALID_INT VALID_DOUBLE VALID_SV + REGISTER TEMPORARY)]); + +use Carp qw(confess); +use strict; +use B qw(class); + +# Perl internal constants that I should probably define elsewhere. +sub SVf_IOK () { 0x10000 } +sub SVf_NOK () { 0x20000 } + +# Types +sub T_UNKNOWN () { 0 } +sub T_DOUBLE () { 1 } +sub T_INT () { 2 } + +# Flags +sub VALID_INT () { 0x01 } +sub VALID_DOUBLE () { 0x02 } +sub VALID_SV () { 0x04 } +sub REGISTER () { 0x08 } # no implicit write-back when calling subs +sub TEMPORARY () { 0x10 } # no implicit write-back needed at all + +# +# Callback for runtime code generation +# +my $runtime_callback = sub { confess "set_callback not yet called" }; +sub set_callback (&) { $runtime_callback = shift } +sub runtime { &$runtime_callback(@_) } + +# +# Methods +# + +sub write_back { confess "stack object does not implement write_back" } + +sub invalidate { shift->{flags} &= ~(VALID_INT | VALID_DOUBLE) } + +sub as_sv { + my $obj = shift; + if (!($obj->{flags} & VALID_SV)) { + $obj->write_back; + $obj->{flags} |= VALID_SV; + } + return $obj->{sv}; +} + +sub as_int { + my $obj = shift; + if (!($obj->{flags} & VALID_INT)) { + $obj->load_int; + $obj->{flags} |= VALID_INT; + } + return $obj->{iv}; +} + +sub as_double { + my $obj = shift; + if (!($obj->{flags} & VALID_DOUBLE)) { + $obj->load_double; + $obj->{flags} |= VALID_DOUBLE; + } + return $obj->{nv}; +} + +sub as_numeric { + my $obj = shift; + return $obj->{type} == T_INT ? $obj->as_int : $obj->as_double; +} + +# +# Debugging methods +# +sub peek { + my $obj = shift; + my $type = $obj->{type}; + my $flags = $obj->{flags}; + my @flags; + if ($type == T_UNKNOWN) { + $type = "T_UNKNOWN"; + } elsif ($type == T_INT) { + $type = "T_INT"; + } elsif ($type == T_DOUBLE) { + $type = "T_DOUBLE"; + } else { + $type = "(illegal type $type)"; + } + push(@flags, "VALID_INT") if $flags & VALID_INT; + push(@flags, "VALID_DOUBLE") if $flags & VALID_DOUBLE; + push(@flags, "VALID_SV") if $flags & VALID_SV; + push(@flags, "REGISTER") if $flags & REGISTER; + push(@flags, "TEMPORARY") if $flags & TEMPORARY; + @flags = ("none") unless @flags; + return sprintf("%s type=$type flags=%s sv=$obj->{sv}", + class($obj), join("|", @flags)); +} + +sub minipeek { + my $obj = shift; + my $type = $obj->{type}; + my $flags = $obj->{flags}; + if ($type == T_INT || $flags & VALID_INT) { + return $obj->{iv}; + } elsif ($type == T_DOUBLE || $flags & VALID_DOUBLE) { + return $obj->{nv}; + } else { + return $obj->{sv}; + } +} + +# +# Caller needs to ensure that set_int, set_double, +# set_numeric and set_sv are only invoked on legal lvalues. +# +sub set_int { + my ($obj, $expr) = @_; + runtime("$obj->{iv} = $expr;"); + $obj->{flags} &= ~(VALID_SV | VALID_DOUBLE); + $obj->{flags} |= VALID_INT; +} + +sub set_double { + my ($obj, $expr) = @_; + runtime("$obj->{nv} = $expr;"); + $obj->{flags} &= ~(VALID_SV | VALID_INT); + $obj->{flags} |= VALID_DOUBLE; +} + +sub set_numeric { + my ($obj, $expr) = @_; + if ($obj->{type} == T_INT) { + $obj->set_int($expr); + } else { + $obj->set_double($expr); + } +} + +sub set_sv { + my ($obj, $expr) = @_; + runtime("SvSetSV($obj->{sv}, $expr);"); + $obj->invalidate; + $obj->{flags} |= VALID_SV; +} + +# +# Stackobj::Padsv +# + +@B::Stackobj::Padsv::ISA = 'B::Stackobj'; +sub B::Stackobj::Padsv::new { + my ($class, $type, $extra_flags, $ix, $iname, $dname) = @_; + bless { + type => $type, + flags => VALID_SV | $extra_flags, + sv => "PL_curpad[$ix]", + iv => "$iname", + nv => "$dname" + }, $class; +} + +sub B::Stackobj::Padsv::load_int { + my $obj = shift; + if ($obj->{flags} & VALID_DOUBLE) { + runtime("$obj->{iv} = $obj->{nv};"); + } else { + runtime("$obj->{iv} = SvIV($obj->{sv});"); + } + $obj->{flags} |= VALID_INT; +} + +sub B::Stackobj::Padsv::load_double { + my $obj = shift; + $obj->write_back; + runtime("$obj->{nv} = SvNV($obj->{sv});"); + $obj->{flags} |= VALID_DOUBLE; +} + +sub B::Stackobj::Padsv::write_back { + my $obj = shift; + my $flags = $obj->{flags}; + return if $flags & VALID_SV; + if ($flags & VALID_INT) { + runtime("sv_setiv($obj->{sv}, $obj->{iv});"); + } elsif ($flags & VALID_DOUBLE) { + runtime("sv_setnv($obj->{sv}, $obj->{nv});"); + } else { + confess "write_back failed for lexical @{[$obj->peek]}\n"; + } + $obj->{flags} |= VALID_SV; +} + +# +# Stackobj::Const +# + +@B::Stackobj::Const::ISA = 'B::Stackobj'; +sub B::Stackobj::Const::new { + my ($class, $sv) = @_; + my $obj = bless { + flags => 0, + sv => $sv # holds the SV object until write_back happens + }, $class; + my $svflags = $sv->FLAGS; + if ($svflags & SVf_IOK) { + $obj->{flags} = VALID_INT|VALID_DOUBLE; + $obj->{type} = T_INT; + $obj->{nv} = $obj->{iv} = $sv->IV; + } elsif ($svflags & SVf_NOK) { + $obj->{flags} = VALID_INT|VALID_DOUBLE; + $obj->{type} = T_DOUBLE; + $obj->{iv} = $obj->{nv} = $sv->NV; + } else { + $obj->{type} = T_UNKNOWN; + } + return $obj; +} + +sub B::Stackobj::Const::write_back { + my $obj = shift; + return if $obj->{flags} & VALID_SV; + # Save the SV object and replace $obj->{sv} by its C source code name + $obj->{sv} = $obj->{sv}->save; + $obj->{flags} |= VALID_SV|VALID_INT|VALID_DOUBLE; +} + +sub B::Stackobj::Const::load_int { + my $obj = shift; + $obj->{iv} = int($obj->{sv}->PV); + $obj->{flags} |= VALID_INT; +} + +sub B::Stackobj::Const::load_double { + my $obj = shift; + $obj->{nv} = $obj->{sv}->PV + 0.0; + $obj->{flags} |= VALID_DOUBLE; +} + +sub B::Stackobj::Const::invalidate {} + +# +# Stackobj::Bool +# + +@B::Stackobj::Bool::ISA = 'B::Stackobj'; +sub B::Stackobj::Bool::new { + my ($class, $preg) = @_; + my $obj = bless { + type => T_INT, + flags => VALID_INT|VALID_DOUBLE, + iv => $$preg, + nv => $$preg, + preg => $preg # this holds our ref to the pseudo-reg + }, $class; + return $obj; +} + +sub B::Stackobj::Bool::write_back { + my $obj = shift; + return if $obj->{flags} & VALID_SV; + $obj->{sv} = "($obj->{iv} ? &PL_sv_yes : &PL_sv_no)"; + $obj->{flags} |= VALID_SV; +} + +# XXX Might want to handle as_double/set_double/load_double? + +sub B::Stackobj::Bool::invalidate {} + +1; + +__END__ + +=head1 NAME + +B::Stackobj - Helper module for CC backend + +=head1 SYNOPSIS + + use B::Stackobj; + +=head1 DESCRIPTION + +See F<ext/B/README>. + +=head1 AUTHOR + +Malcolm Beattie, C<mbeattie@sable.ox.ac.uk> + +=cut diff --git a/gnu/usr.bin/perl/ext/B/B/Terse.pm b/gnu/usr.bin/perl/ext/B/B/Terse.pm new file mode 100644 index 00000000000..93757f34ce8 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/B/Terse.pm @@ -0,0 +1,152 @@ +package B::Terse; +use strict; +use B qw(peekop class walkoptree_slow walkoptree_exec + main_start main_root cstring svref_2object); +use B::Asmdata qw(@specialsv_name); + +sub terse { + my ($order, $cvref) = @_; + my $cv = svref_2object($cvref); + if ($order eq "exec") { + walkoptree_exec($cv->START, "terse"); + } else { + walkoptree_slow($cv->ROOT, "terse"); + } +} + +sub compile { + my $order = shift; + my @options = @_; + if (@options) { + return sub { + my $objname; + foreach $objname (@options) { + $objname = "main::$objname" unless $objname =~ /::/; + eval "terse(\$order, \\&$objname)"; + die "terse($order, \\&$objname) failed: $@" if $@; + } + } + } else { + if ($order eq "exec") { + return sub { walkoptree_exec(main_start, "terse") } + } else { + return sub { walkoptree_slow(main_root, "terse") } + } + } +} + +sub indent { + my $level = shift; + return " " x $level; +} + +sub B::OP::terse { + my ($op, $level) = @_; + my $targ = $op->targ; + $targ = ($targ > 0) ? " [$targ]" : ""; + print indent($level), peekop($op), $targ, "\n"; +} + +sub B::SVOP::terse { + my ($op, $level) = @_; + print indent($level), peekop($op), " "; + $op->sv->terse(0); +} + +sub B::GVOP::terse { + my ($op, $level) = @_; + print indent($level), peekop($op), " "; + $op->gv->terse(0); +} + +sub B::PMOP::terse { + my ($op, $level) = @_; + my $precomp = $op->precomp; + print indent($level), peekop($op), + defined($precomp) ? " /$precomp/\n" : " (regexp not compiled)\n"; + +} + +sub B::PVOP::terse { + my ($op, $level) = @_; + print indent($level), peekop($op), " ", cstring($op->pv), "\n"; +} + +sub B::COP::terse { + my ($op, $level) = @_; + my $label = $op->label; + if ($label) { + $label = " label ".cstring($label); + } + print indent($level), peekop($op), $label, "\n"; +} + +sub B::PV::terse { + my ($sv, $level) = @_; + print indent($level); + printf "%s (0x%lx) %s\n", class($sv), $$sv, cstring($sv->PV); +} + +sub B::AV::terse { + my ($sv, $level) = @_; + print indent($level); + printf "%s (0x%lx) FILL %d\n", class($sv), $$sv, $sv->FILL; +} + +sub B::GV::terse { + my ($gv, $level) = @_; + my $stash = $gv->STASH->NAME; + if ($stash eq "main") { + $stash = ""; + } else { + $stash = $stash . "::"; + } + print indent($level); + printf "%s (0x%lx) *%s%s\n", class($gv), $$gv, $stash, $gv->NAME; +} + +sub B::IV::terse { + my ($sv, $level) = @_; + print indent($level); + printf "%s (0x%lx) %d\n", class($sv), $$sv, $sv->IV; +} + +sub B::NV::terse { + my ($sv, $level) = @_; + print indent($level); + printf "%s (0x%lx) %s\n", class($sv), $$sv, $sv->NV; +} + +sub B::NULL::terse { + my ($sv, $level) = @_; + print indent($level); + printf "%s (0x%lx)\n", class($sv), $$sv; +} + +sub B::SPECIAL::terse { + my ($sv, $level) = @_; + print indent($level); + printf "%s #%d %s\n", class($sv), $$sv, $specialsv_name[$$sv]; +} + +1; + +__END__ + +=head1 NAME + +B::Terse - Walk Perl syntax tree, printing terse info about ops + +=head1 SYNOPSIS + + perl -MO=Terse[,OPTIONS] foo.pl + +=head1 DESCRIPTION + +See F<ext/B/README>. + +=head1 AUTHOR + +Malcolm Beattie, C<mbeattie@sable.ox.ac.uk> + +=cut diff --git a/gnu/usr.bin/perl/ext/B/B/Xref.pm b/gnu/usr.bin/perl/ext/B/B/Xref.pm new file mode 100644 index 00000000000..0102856919a --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/B/Xref.pm @@ -0,0 +1,392 @@ +package B::Xref; + +=head1 NAME + +B::Xref - Generates cross reference reports for Perl programs + +=head1 SYNOPSIS + +perl -MO=Xref[,OPTIONS] foo.pl + +=head1 DESCRIPTION + +The B::Xref module is used to generate a cross reference listing of all +definitions and uses of variables, subroutines and formats in a Perl program. +It is implemented as a backend for the Perl compiler. + +The report generated is in the following format: + + File filename1 + Subroutine subname1 + Package package1 + object1 C<line numbers> + object2 C<line numbers> + ... + Package package2 + ... + +Each B<File> section reports on a single file. Each B<Subroutine> section +reports on a single subroutine apart from the special cases +"(definitions)" and "(main)". These report, respectively, on subroutine +definitions found by the initial symbol table walk and on the main part of +the program or module external to all subroutines. + +The report is then grouped by the B<Package> of each variable, +subroutine or format with the special case "(lexicals)" meaning +lexical variables. Each B<object> name (implicitly qualified by its +containing B<Package>) includes its type character(s) at the beginning +where possible. Lexical variables are easier to track and even +included dereferencing information where possible. + +The C<line numbers> are a comma separated list of line numbers (some +preceded by code letters) where that object is used in some way. +Simple uses aren't preceded by a code letter. Introductions (such as +where a lexical is first defined with C<my>) are indicated with the +letter "i". Subroutine and method calls are indicated by the character +"&". Subroutine definitions are indicated by "s" and format +definitions by "f". + +=head1 OPTIONS + +Option words are separated by commas (not whitespace) and follow the +usual conventions of compiler backend options. + +=over 8 + +=item C<-oFILENAME> + +Directs output to C<FILENAME> instead of standard output. + +=item C<-r> + +Raw output. Instead of producing a human-readable report, outputs a line +in machine-readable form for each definition/use of a variable/sub/format. + +=item C<-D[tO]> + +(Internal) debug options, probably only useful if C<-r> included. +The C<t> option prints the object on the top of the stack as it's +being tracked. The C<O> option prints each operator as it's being +processed in the execution order of the program. + +=back + +=head1 BUGS + +Non-lexical variables are quite difficult to track through a program. +Sometimes the type of a non-lexical variable's use is impossible to +determine. Introductions of non-lexical non-scalars don't seem to be +reported properly. + +=head1 AUTHOR + +Malcolm Beattie, mbeattie@sable.ox.ac.uk. + +=cut + +use strict; +use B qw(peekop class comppadlist main_start svref_2object walksymtable); + +# Constants (should probably be elsewhere) +sub OPpLVAL_INTRO () { 128 } +sub SVf_POK () { 0x40000 } + +sub UNKNOWN { ["?", "?", "?"] } + +my @pad; # lexicals in current pad + # as ["(lexical)", type, name] +my %done; # keyed by $$op: set when each $op is done +my $top = UNKNOWN; # shadows top element of stack as + # [pack, type, name] (pack can be "(lexical)") +my $file; # shadows current filename +my $line; # shadows current line number +my $subname; # shadows current sub name +my %table; # Multi-level hash to record all uses etc. +my @todo = (); # List of CVs that need processing + +my %code = (intro => "i", used => "", + subdef => "s", subused => "&", + formdef => "f", meth => "->"); + + +# Options +my ($debug_op, $debug_top, $nodefs, $raw); + +sub process { + my ($var, $event) = @_; + my ($pack, $type, $name) = @$var; + if ($type eq "*") { + if ($event eq "used") { + return; + } elsif ($event eq "subused") { + $type = "&"; + } + } + $type =~ s/(.)\*$/$1/g; + if ($raw) { + printf "%-16s %-12s %5d %-12s %4s %-16s %s\n", + $file, $subname, $line, $pack, $type, $name, $event; + } else { + # Wheee + push(@{$table{$file}->{$subname}->{$pack}->{$type.$name}->{$event}}, + $line); + } +} + +sub load_pad { + my $padlist = shift; + my ($namelistav, @namelist, $ix); + @pad = (); + return if class($padlist) eq "SPECIAL"; + ($namelistav) = $padlist->ARRAY; + @namelist = $namelistav->ARRAY; + for ($ix = 1; $ix < @namelist; $ix++) { + my $namesv = $namelist[$ix]; + next if class($namesv) eq "SPECIAL"; + my ($type, $name) = $namesv->PV =~ /^(.)(.*)$/; + $pad[$ix] = ["(lexical)", $type, $name]; + } +} + +sub xref { + my $start = shift; + my $op; + for ($op = $start; $$op; $op = $op->next) { + last if $done{$$op}++; + warn sprintf("top = [%s, %s, %s]\n", @$top) if $debug_top; + warn peekop($op), "\n" if $debug_op; + my $ppname = $op->ppaddr; + if ($ppname =~ /^pp_(or|and|mapwhile|grepwhile)$/) { + xref($op->other); + } elsif ($ppname eq "pp_match" || $ppname eq "pp_subst") { + xref($op->pmreplstart); + } elsif ($ppname eq "pp_substcont") { + xref($op->other->pmreplstart); + $op = $op->other; + redo; + } elsif ($ppname eq "pp_cond_expr") { + # pp_cond_expr never returns op_next + xref($op->true); + $op = $op->false; + redo; + } elsif ($ppname eq "pp_enterloop") { + xref($op->redoop); + xref($op->nextop); + xref($op->lastop); + } elsif ($ppname eq "pp_subst") { + xref($op->pmreplstart); + } else { + no strict 'refs'; + &$ppname($op) if defined(&$ppname); + } + } +} + +sub xref_cv { + my $cv = shift; + my $pack = $cv->GV->STASH->NAME; + $subname = ($pack eq "main" ? "" : "$pack\::") . $cv->GV->NAME; + load_pad($cv->PADLIST); + xref($cv->START); + $subname = "(main)"; +} + +sub xref_object { + my $cvref = shift; + xref_cv(svref_2object($cvref)); +} + +sub xref_main { + $subname = "(main)"; + load_pad(comppadlist); + xref(main_start); + while (@todo) { + xref_cv(shift @todo); + } +} + +sub pp_nextstate { + my $op = shift; + $file = $op->filegv->SV->PV; + $line = $op->line; + $top = UNKNOWN; +} + +sub pp_padsv { + my $op = shift; + $top = $pad[$op->targ]; + process($top, $op->private & OPpLVAL_INTRO ? "intro" : "used"); +} + +sub pp_padav { pp_padsv(@_) } +sub pp_padhv { pp_padsv(@_) } + +sub deref { + my ($var, $as) = @_; + $var->[1] = $as . $var->[1]; + process($var, "used"); +} + +sub pp_rv2cv { deref($top, "&"); } +sub pp_rv2hv { deref($top, "%"); } +sub pp_rv2sv { deref($top, "\$"); } +sub pp_rv2av { deref($top, "\@"); } +sub pp_rv2gv { deref($top, "*"); } + +sub pp_gvsv { + my $op = shift; + my $gv = $op->gv; + $top = [$gv->STASH->NAME, '$', $gv->NAME]; + process($top, $op->private & OPpLVAL_INTRO ? "intro" : "used"); +} + +sub pp_gv { + my $op = shift; + my $gv = $op->gv; + $top = [$gv->STASH->NAME, "*", $gv->NAME]; + process($top, $op->private & OPpLVAL_INTRO ? "intro" : "used"); +} + +sub pp_const { + my $op = shift; + my $sv = $op->sv; + $top = ["?", "", + (class($sv) ne "SPECIAL" && $sv->FLAGS & SVf_POK) ? $sv->PV : "?"]; +} + +sub pp_method { + my $op = shift; + $top = ["(method)", "->".$top->[1], $top->[2]]; +} + +sub pp_entersub { + my $op = shift; + if ($top->[1] eq "m") { + process($top, "meth"); + } else { + process($top, "subused"); + } + $top = UNKNOWN; +} + +# +# Stuff for cross referencing definitions of variables and subs +# + +sub B::GV::xref { + my $gv = shift; + my $cv = $gv->CV; + if ($$cv) { + #return if $done{$$cv}++; + $file = $gv->FILEGV->SV->PV; + $line = $gv->LINE; + process([$gv->STASH->NAME, "&", $gv->NAME], "subdef"); + push(@todo, $cv); + } + my $form = $gv->FORM; + if ($$form) { + return if $done{$$form}++; + $file = $gv->FILEGV->SV->PV; + $line = $gv->LINE; + process([$gv->STASH->NAME, "", $gv->NAME], "formdef"); + } +} + +sub xref_definitions { + my ($pack, %exclude); + return if $nodefs; + $subname = "(definitions)"; + foreach $pack (qw(B O AutoLoader DynaLoader Config DB VMS + strict vars FileHandle Exporter Carp)) { + $exclude{$pack."::"} = 1; + } + no strict qw(vars refs); + walksymtable(\%{"main::"}, "xref", sub { !defined($exclude{$_[0]}) }); +} + +sub output { + return if $raw; + my ($file, $subname, $pack, $name, $ev, $perfile, $persubname, + $perpack, $pername, $perev); + foreach $file (sort(keys(%table))) { + $perfile = $table{$file}; + print "File $file\n"; + foreach $subname (sort(keys(%$perfile))) { + $persubname = $perfile->{$subname}; + print " Subroutine $subname\n"; + foreach $pack (sort(keys(%$persubname))) { + $perpack = $persubname->{$pack}; + print " Package $pack\n"; + foreach $name (sort(keys(%$perpack))) { + $pername = $perpack->{$name}; + my @lines; + foreach $ev (qw(intro formdef subdef meth subused used)) { + $perev = $pername->{$ev}; + if (defined($perev) && @$perev) { + my $code = $code{$ev}; + push(@lines, map("$code$_", @$perev)); + } + } + printf " %-16s %s\n", $name, join(", ", @lines); + } + } + } + } +} + +sub compile { + my @options = @_; + my ($option, $opt, $arg); + OPTION: + while ($option = shift @options) { + if ($option =~ /^-(.)(.*)/) { + $opt = $1; + $arg = $2; + } else { + unshift @options, $option; + last OPTION; + } + if ($opt eq "-" && $arg eq "-") { + shift @options; + last OPTION; + } elsif ($opt eq "o") { + $arg ||= shift @options; + open(STDOUT, ">$arg") or return "$arg: $!\n"; + } elsif ($opt eq "d") { + $nodefs = 1; + } elsif ($opt eq "r") { + $raw = 1; + } elsif ($opt eq "D") { + $arg ||= shift @options; + foreach $arg (split(//, $arg)) { + if ($arg eq "o") { + B->debug(1); + } elsif ($arg eq "O") { + $debug_op = 1; + } elsif ($arg eq "t") { + $debug_top = 1; + } + } + } + } + if (@options) { + return sub { + my $objname; + xref_definitions(); + foreach $objname (@options) { + $objname = "main::$objname" unless $objname =~ /::/; + eval "xref_object(\\&$objname)"; + die "xref_object(\\&$objname) failed: $@" if $@; + } + output(); + } + } else { + return sub { + xref_definitions(); + xref_main(); + output(); + } + } +} + +1; diff --git a/gnu/usr.bin/perl/ext/B/B/assemble b/gnu/usr.bin/perl/ext/B/B/assemble new file mode 100644 index 00000000000..43cc5bc4b33 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/B/assemble @@ -0,0 +1,30 @@ +use B::Assembler qw(assemble_fh); +use FileHandle; + +my ($filename, $fh, $out); + +if ($ARGV[0] eq "-d") { + B::Assembler::debug(1); + shift; +} + +$out = \*STDOUT; + +if (@ARGV == 0) { + $fh = \*STDIN; + $filename = "-"; +} elsif (@ARGV == 1) { + $filename = $ARGV[0]; + $fh = new FileHandle "<$filename"; +} elsif (@ARGV == 2) { + $filename = $ARGV[0]; + $fh = new FileHandle "<$filename"; + $out = new FileHandle ">$ARGV[1]"; +} else { + die "Usage: assemble [filename] [outfilename]\n"; +} + +binmode $out; +$SIG{__WARN__} = sub { warn "$filename:@_" }; +$SIG{__DIE__} = sub { die "$filename: @_" }; +assemble_fh($fh, sub { print $out @_ }); diff --git a/gnu/usr.bin/perl/ext/B/B/cc_harness b/gnu/usr.bin/perl/ext/B/B/cc_harness new file mode 100644 index 00000000000..79f8727a8f0 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/B/cc_harness @@ -0,0 +1,12 @@ +use Config; + +$libdir = $ENV{PERL_SRC} || "$Config{installarchlib}/CORE"; + +if (!grep(/^-[cS]$/, @ARGV)) { + $linkargs = sprintf("%s $libdir/$Config{libperl} %s", + @Config{qw(ldflags libs)}); +} + +$cccmd = "$Config{cc} $Config{ccflags} -I$libdir @ARGV $linkargs"; +print "$cccmd\n"; +exec $cccmd; diff --git a/gnu/usr.bin/perl/ext/B/B/disassemble b/gnu/usr.bin/perl/ext/B/B/disassemble new file mode 100644 index 00000000000..6530b809502 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/B/disassemble @@ -0,0 +1,22 @@ +use B::Disassembler qw(disassemble_fh); +use FileHandle; + +my $fh; +if (@ARGV == 0) { + $fh = \*STDIN; +} elsif (@ARGV == 1) { + $fh = new FileHandle "<$ARGV[0]"; +} else { + die "Usage: disassemble [filename]\n"; +} + +sub print_insn { + my ($insn, $arg) = @_; + if (defined($arg)) { + printf "%s %s\n", $insn, $arg; + } else { + print $insn, "\n"; + } +} + +disassemble_fh($fh, \&print_insn); diff --git a/gnu/usr.bin/perl/ext/B/B/makeliblinks b/gnu/usr.bin/perl/ext/B/B/makeliblinks new file mode 100644 index 00000000000..82560783c01 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/B/makeliblinks @@ -0,0 +1,54 @@ +use File::Find; +use Config; + +if (@ARGV != 2) { + warn <<"EOT"; +Usage: makeliblinks libautodir targetdir +where libautodir is the architecture-dependent auto directory +(e.g. $Config::Config{archlib}/auto). +EOT + exit 2; +} + +my ($libautodir, $targetdir) = @ARGV; + +# Calculate relative path prefix from $targetdir to $libautodir +sub relprefix { + my ($to, $from) = @_; + my $up; + for ($up = 0; substr($to, 0, length($from)) ne $from; $up++) { + $from =~ s( + [^/]+ (?# a group of non-slashes) + /* (?# maybe with some trailing slashes) + $ (?# at the end of the path) + )()x; + } + return (("../" x $up) . substr($to, length($from))); +} + +my $relprefix = relprefix($libautodir, $targetdir); + +my ($dlext, $lib_ext) = @Config::Config{qw(dlext lib_ext)}; + +sub link_if_library { + if (/\.($dlext|$lib_ext)$/o) { + my $ext = $1; + my $name = $File::Find::name; + if (substr($name, 0, length($libautodir) + 1) ne "$libautodir/") { + die "directory of $name doesn't match $libautodir\n"; + } + substr($name, 0, length($libautodir) + 1) = ''; + my @parts = split(m(/), $name); + if ($parts[-1] ne "$parts[-2].$ext") { + die "module name $_ doesn't match its directory $libautodir\n"; + } + pop @parts; + my $libpath = "$targetdir/lib" . join("__", @parts) . ".$ext"; + print "$libpath -> $relprefix/$name\n"; + symlink("$relprefix/$name", $libpath) + or warn "above link failed with error: $!\n"; + } +} + +find(\&link_if_library, $libautodir); +exit 0; diff --git a/gnu/usr.bin/perl/ext/B/Makefile.PL b/gnu/usr.bin/perl/ext/B/Makefile.PL new file mode 100644 index 00000000000..80e5e1b905d --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/Makefile.PL @@ -0,0 +1,46 @@ +use ExtUtils::MakeMaker; +use Config; + +my $e = $Config{'exe_ext'}; +my $o = $Config{'obj_ext'}; +my $exeout_flag = '-o '; +if ($^O eq 'MSWin32') { + if ($Config{'cc'} =~ /^cl/i) { + $exeout_flag = '-Fe'; + } + elsif ($Config{'cc'} =~ /^bcc/i) { + $exeout_flag = '-e'; + } +} + +WriteMakefile( + NAME => "B", + VERSION => "a5", + MAN3PODS => {}, + clean => { + FILES => "perl$e byteperl$e *$o B.c *~" + } +); + +sub MY::post_constants { + "\nLIBS = $Config{libs}\n" +} + +# Leave out doing byteperl for now. Probably should be built in the +# core directory or somewhere else rather than here +#sub MY::top_targets { +# my $self = shift; +# my $targets = $self->MM::top_targets(); +# $targets =~ s/^(all ::.*)$/$1 byteperl$e/m; +# return <<"EOT" . $targets; + +# +# byteperl is *not* a standard perl+XSUB executable. It's a special +# program for running standalone bytecode executables. It isn't an XSUB +# at the moment because a standlone Perl program needs to set up curpad +# which is overwritten on exit from an XSUB. +# +#byteperl$e : byteperl$o B$o \$(PERL_SRC)/byterun$o +# \$(CC) ${exeout_flag}byteperl$e byteperl$o B$o byterun$o \$(LDFLAGS) \$(PERL_ARCHLIB)/CORE/$Config{libperl} \$(LIBS) +#EOT +#} diff --git a/gnu/usr.bin/perl/ext/B/NOTES b/gnu/usr.bin/perl/ext/B/NOTES new file mode 100644 index 00000000000..ee10ba03e97 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/NOTES @@ -0,0 +1,168 @@ +C backend invocation + If there are any non-option arguments, they are taken to be + names of objects to be saved (probably doesn't work properly yet). + Without extra arguments, it saves the main program. + -ofilename Output to filename instead of STDOUT + -v Verbose (currently gives a few compilation statistics) + -- Force end of options + -uPackname Force apparently unused subs from package Packname to + be compiled. This allows programs to use eval "foo()" + even when sub foo is never seen to be used at compile + time. The down side is that any subs which really are + never used also have code generated. This option is + necessary, for example, if you have a signal handler + foo which you initialise with $SIG{BAR} = "foo". + A better fix, though, is just to change it to + $SIG{BAR} = \&foo. You can have multiple -u options. + -D Debug options (concat or separate flags like perl -D) + o OPs, prints each OP as it's processed + c COPs, prints COPs as processed (incl. file & line num) + A prints AV information on saving + C prints CV information on saving + M prints MAGIC information on saving + -f Force optimisations on or off one at a time. + cog Copy-on-grow: PVs declared and initialised statically + no-cog No copy-on-grow + -On Optimisation level (n = 0, 1, 2, ...). -O means -O1. + Currently, -O1 and higher set -fcog. + +Examples + perl -MO=C foo.pl > foo.c + perl cc_harness -o foo foo.c + + perl -MO=C,-v,-DcA bar.pl > /dev/null + +CC backend invocation + If there are any non-option arguments, they are taken to be names of + subs to be saved. Without extra arguments, it saves the main program. + -ofilename Output to filename instead of STDOUT + -- Force end of options + -uPackname Force apparently unused subs from package Packname to + be compiled. This allows programs to use eval "foo()" + even when sub foo is never seen to be used at compile + time. The down side is that any subs which really are + never used also have code generated. This option is + necessary, for example, if you have a signal handler + foo which you initialise with $SIG{BAR} = "foo". + A better fix, though, is just to change it to + $SIG{BAR} = \&foo. You can have multiple -u options. + -mModulename Instead of generating source for a runnable executable, + generate source for an XSUB module. The + boot_Modulename function (which DynaLoader can look + for) does the appropriate initialisation and runs the + main part of the Perl source that is being compiled. + -pn Generate code for perl patchlevel n (e.g. 3 or 4). + The default is to generate C code which will link + with the currently executing version of perl. + running the perl compiler. + -D Debug options (concat or separate flags like perl -D) + r Writes debugging output to STDERR just as it's about + to write to the program's runtime (otherwise writes + debugging info as comments in its C output). + O Outputs each OP as it's compiled + s Outputs the contents of the shadow stack at each OP + p Outputs the contents of the shadow pad of lexicals as + it's loaded for each sub or the main program. + q Outputs the name of each fake PP function in the queue + as it's about to processes. + l Output the filename and line number of each original + line of Perl code as it's processed (pp_nextstate). + t Outputs timing information of compilation stages + -f Force optimisations on or off one at a time. + [ + cog Copy-on-grow: PVs declared and initialised statically + no-cog No copy-on-grow + These two not in CC yet. + ] + freetmps-each-bblock Delays FREETMPS from the end of each + statement to the end of the each basic + block. + freetmps-each-loop Delays FREETMPS from the end of each + statement to the end of the group of + basic blocks forming a loop. At most + one of the freetmps-each-* options can + be used. + omit-taint Omits generating code for handling + perl's tainting mechanism. + -On Optimisation level (n = 0, 1, 2, ...). -O means -O1. + Currently, -O1 sets -ffreetmps-each-bblock and -O2 + sets -ffreetmps-each-loop. + +Example + perl -MO=CC,-O2,-ofoo.c foo.pl + perl cc_harness -o foo foo.c + + perl -MO=CC,-mFoo,-oFoo.c Foo.pm + perl cc_harness -shared -c -o Foo.so Foo.c + + +Bytecode backend invocation + + If there are any non-option arguments, they are taken to be + names of objects to be saved (probably doesn't work properly yet). + Without extra arguments, it saves the main program. + -ofilename Output to filename instead of STDOUT. + -- Force end of options. + -f Force optimisations on or off one at a time. + Each can be preceded by no- to turn the option off. + compress-nullops + Only fills in the necessary fields of ops which have + been optimised away by perl's internal compiler. + omit-sequence-numbers + Leaves out code to fill in the op_seq field of all ops + which is only used by perl's internal compiler. + bypass-nullops + If op->op_next ever points to a NULLOP, replaces the + op_next field with the first non-NULLOP in the path + of execution. + strip-syntax-tree + Leaves out code to fill in the pointers which link the + internal syntax tree together. They're not needed at + run-time but leaving them out will make it impossible + to recompile or disassemble the resulting program. + It will also stop "goto label" statements from working. + -On Optimisation level (n = 0, 1, 2, ...). -O means -O1. + -O1 sets -fcompress-nullops -fomit-sequence numbers. + -O6 adds -fstrip-syntax-tree. + -D Debug options (concat or separate flags like perl -D) + o OPs, prints each OP as it's processed. + b print debugging information about bytecompiler progress + a tells the assembler to include source assembler lines + in its output as bytecode comments. + C prints each CV taken from the final symbol tree walk. + -S Output assembler source rather than piping it + through the assembler and outputting bytecode. + -m Compile as a module rather than a standalone program. + Currently this just means that the bytecodes for + initialising main_start, main_root and curpad are + omitted. + +Example + perl -MO=Bytecode,-O6,-o,foo.plc foo.pl + + perl -MO=Bytecode,-S foo.pl > foo.S + assemble foo.S > foo.plc + byteperl foo.plc + + perl -MO=Bytecode,-m,-oFoo.pmc Foo.pm + +Backends for debugging + perl -MO=Terse,exec foo.pl + perl -MO=Debug bar.pl + +O module + Used with "perl -MO=Backend,foo,bar prog.pl" to invoke the backend + B::Backend with options foo and bar. O invokes the sub + B::Backend::compile() with arguments foo and bar at BEGIN time. + That compile() sub must do any inital argument processing replied. + If unsuccessful, it should return a string which O arranges to be + printed as an error message followed by a clean error exit. In the + normal case where any option processing in compile() is successful, + it should return a sub ref (usually a closure) to perform the + actual compilation. When O regains control, it ensures that the + "-c" option is forced (so that the program being compiled doesn't + end up running) and registers an END block to call back the sub ref + returned from the backend's compile(). Perl then continues by + parsing prog.pl (just as it would with "perl -c prog.pl") and after + doing so, assuming there are no parse-time errors, the END block + of O gets called and the actual backend compilation happens. Phew. diff --git a/gnu/usr.bin/perl/ext/B/O.pm b/gnu/usr.bin/perl/ext/B/O.pm new file mode 100644 index 00000000000..ad391a3f4a4 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/O.pm @@ -0,0 +1,85 @@ +package O; +use B qw(minus_c); +use Carp; + +sub import { + my ($class, $backend, @options) = @_; + eval "use B::$backend ()"; + if ($@) { + croak "use of backend $backend failed: $@"; + } + my $compilesub = &{"B::${backend}::compile"}(@options); + if (ref($compilesub) eq "CODE") { + minus_c; + eval 'END { &$compilesub() }'; + } else { + die $compilesub; + } +} + +1; + +__END__ + +=head1 NAME + +O - Generic interface to Perl Compiler backends + +=head1 SYNOPSIS + + perl -MO=Backend[,OPTIONS] foo.pl + +=head1 DESCRIPTION + +This is the module that is used as a frontend to the Perl Compiler. + +=head1 CONVENTIONS + +Most compiler backends use the following conventions: OPTIONS +consists of a comma-separated list of words (no white-space). +The C<-v> option usually puts the backend into verbose mode. +The C<-ofile> option generates output to B<file> instead of +stdout. The C<-D> option followed by various letters turns on +various internal debugging flags. See the documentation for the +desired backend (named C<B::Backend> for the example above) to +find out about that backend. + +=head1 IMPLEMENTATION + +This section is only necessary for those who want to write a +compiler backend module that can be used via this module. + +The command-line mentioned in the SYNOPSIS section corresponds to +the Perl code + + use O ("Backend", OPTIONS); + +The C<import> function which that calls loads in the appropriate +C<B::Backend> module and calls the C<compile> function in that +package, passing it OPTIONS. That function is expected to return +a sub reference which we'll call CALLBACK. Next, the "compile-only" +flag is switched on (equivalent to the command-line option C<-c>) +and an END block is registered which calls CALLBACK. Thus the main +Perl program mentioned on the command-line is read in, parsed and +compiled into internal syntax tree form. Since the C<-c> flag is +set, the program does not start running (excepting BEGIN blocks of +course) but the CALLBACK function registered by the compiler +backend is called. + +In summary, a compiler backend module should be called "B::Foo" +for some foo and live in the appropriate directory for that name. +It should define a function called C<compile>. When the user types + + perl -MO=Foo,OPTIONS foo.pl + +that function is called and is passed those OPTIONS (split on +commas). It should return a sub ref to the main compilation function. +After the user's program is loaded and parsed, that returned sub ref +is invoked which can then go ahead and do the compilation, usually by +making use of the C<B> module's functionality. + +=head1 AUTHOR + +Malcolm Beattie, C<mbeattie@sable.ox.ac.uk> + +=cut diff --git a/gnu/usr.bin/perl/ext/B/README b/gnu/usr.bin/perl/ext/B/README new file mode 100644 index 00000000000..fa3f085a986 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/README @@ -0,0 +1,325 @@ + Perl Compiler Kit, Version alpha4 + + Copyright (c) 1996, 1997, Malcolm Beattie + + This program is free software; you can redistribute it and/or modify + it under the terms of either: + + a) the GNU General Public License as published by the Free + Software Foundation; either version 1, or (at your option) any + later version, or + + b) the "Artistic License" which comes with this kit. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See either + the GNU General Public License or the Artistic License for more details. + + You should have received a copy of the Artistic License with this kit, + in the file named "Artistic". If not, you can get one from the Perl + distribution. You should also have received a copy of the GNU General + Public License, in the file named "Copying". If not, you can get one + from the Perl distribution or else write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + +CHANGES + +New since alpha3 + Anonymous subs work properly with C and CC. + Heuristics for forcing compilation of apparently unused subs/methods. + Subs which use the AutoLoader module are forcibly loaded at compile-time. + Slightly faster compilation. + Handles slightly more complex code within a BEGIN { }. + Minor bug fixes. + +New since alpha2 + CC backend now supports ".." and s//e. + Xref backend generates cross-reference reports + Cleanups to fix benign but irritating "-w" warnings + Minor cxstack fix +New since alpha1 + Working CC backend + Shared globs and pre-initialised hash support + Some XSUB support + Assorted bug fixes + +INSTALLATION + +(1) You need perl5.002 or later. + +(2) If you want to compile and run programs with the C or CC backends +which undefine (or redefine) subroutines, then you need to apply a +one-line patch to perl itself. One or two of the programs in perl's +own test suite do this. The patch is in file op.patch. It prevents +perl from calling free() on OPs with the magic sequence number (U16)-1. +The compiler declares all OPs as static structures and uses that magic +sequence number. + +(3) Type + perl Makefile.PL +to write a personalised Makefile for your system. If you want the +bytecode modules to support reading bytecode from strings (instead of +just from files) then add the option + -DINDIRECT_BGET_MACROS +into the middle of the definition of the CCCMD macro in the Makefile. +Your C compiler may need to be able to cope with Standard C for this. +I haven't tested this option yet with an old pre-Standard compiler. + +(4) If your platform supports dynamic loading then just type + make +and you can then use + perl -Iblib/arch -MO=foo bar +to use the compiler modules (see later for details). +If you need/want instead to make a statically linked perl which +contains the appropriate modules, then type + make perl + make byteperl +and you can then use + ./perl -MO=foo bar +to use the compiler modules. +In both cases, the byteperl executable is required for running standalone +bytecode programs. It is *not* a standard perl+XSUB perl executable. + +USAGE + +As of the alpha3 release, the Bytecode, C and CC backends are now all +functional enough to compile almost the whole of the main perl test +suite. In the case of the CC backend, any failures are all due to +differences and/or known bugs documented below. See the file TESTS. +In the following examples, you'll need to replace "perl" by + perl -Iblib/arch +if you have built the extensions for a dynamic loading platform but +haven't installed the extensions completely. You'll need to replace +"perl" by + ./perl +if you have built the extensions into a statically linked perl binary. + +(1) To compile perl program foo.pl with the C backend, do + perl -MO=C,-ofoo.c foo.pl +Then use the cc_harness perl program to compile the resulting C source: + perl cc_harness -O2 -o foo foo.c + +If you are using a non-ANSI pre-Standard C compiler that can't handle +pre-declaring static arrays, then add -DBROKEN_STATIC_REDECL to the +options you use: + perl cc_harness -O2 -o foo -DBROKEN_STATIC_REDECL foo.c +If you are using a non-ANSI pre-Standard C compiler that can't handle +static initialisation of structures with union members then add +-DBROKEN_UNION_INIT to the options you use. If you want command line +arguments passed to your executable to be interpreted by perl (e.g. -Dx) +then compile foo.c with -DALLOW_PERL_OPTIONS. Otherwise, all command line +arguments passed to foo will appear directly in @ARGV. The resulting +executable foo is the compiled version of foo.pl. See the file NOTES for +extra options you can pass to -MO=C. + +There are some constraints on the contents on foo.pl if you want to be +able to compile it successfully. Some problems can be fixed fairly easily +by altering foo.pl; some problems with the compiler are known to be +straightforward to solve and I'll do so soon. The file Todo lists a +number of known problems. See the XSUB section lower down for information +about compiling programs which use XSUBs. + +(2) To compile foo.pl with the CC backend (which generates actual +optimised C code for the execution path of your perl program), use + perl -MO=CC,-ofoo.c foo.pl + +and proceed just as with the C backend. You should almost certainly +use an option such as -O2 with the subsequent cc_harness invocation +so that your C compiler uses optimisation. The C code generated by +the Perl compiler's CC backend looks ugly to humans but is easily +optimised by C compilers. + +To make the most of this compiler backend, you need to tell the +compiler when you're using int or double variables so that it can +optimise appropriately (although this part of the compiler is the most +buggy). You currently do that by naming lexical variables ending in +"_i" for ints, "_d" for doubles, "_ir" for int "register" variables or +"_dr" for double "register" variables. Here "register" is a promise +that you won't pass a reference to the variable into a sub which then +modifies the variable. The compiler ought to catch attempts to use +"\$i" just as C compilers catch attempts to do "&i" for a register int +i but it doesn't at the moment. Bugs in the CC backend may make your +program fail in mysterious ways and give wrong answers rather than just +crash in boring ways. But, hey, this is an alpha release so you knew +that anyway. See the XSUB section lower down for information about +compiling programs which use XSUBs. + +If your program uses classes which define methods (or other subs which +are not exported and not apparently used until runtime) then you'll +need to use -u compile-time options (see the NOTES file) to force the +subs to be compiled. Future releases will probably default the other +way, do more auto-detection and provide more fine-grained control. + +Since compiled executables need linking with libperl, you may want +to turn libperl.a into a shared library if your platform supports +it. For example, with Digital UNIX, do something like + ld -shared -o libperl.so -all libperl.a -none -lc +and with Linux/ELF, rebuild the perl .c files with -fPIC (and I +also suggest -fomit-frame-pointer for Linux on Intel architetcures), +do "make libperl.a" and then do + gcc -shared -Wl,-soname,libperl.so.5 -o libperl.so.5.3 `ar t libperl.a` +and then + # cp libperl.so.5.3 /usr/lib + # cd /usr/lib + # ln -s libperl.so.5.3 libperl.so.5 + # ln -s libperl.so.5 libperl.so + # ldconfig +When you compile perl executables with cc_harness, append -L/usr/lib +otherwise the -L for the perl source directory will override it. For +example, + perl -Iblib/arch -MO=CC,-O2,-ofoo3.c foo3.bench + perl cc_harness -o foo3 -O2 foo3.c -L/usr/lib + ls -l foo3 + -rwxr-xr-x 1 mbeattie xzdg 11218 Jul 1 15:28 foo3 +You'll probably also want to link your main perl executable against +libperl.so; it's nice having an 11K perl executable. + +(3) To compile foo.pl into bytecode do + perl -MO=Bytecode,-ofoo foo.pl +To run the resulting bytecode file foo as a standalone program, you +use the program byteperl which should have been built along with the +extensions. + ./byteperl foo +Any extra arguments are passed in as @ARGV; they are not interpreted +as perl options. If you want to load chunks of bytecode into an already +running perl program then use the -m option and investigate the +byteload_fh and byteload_string functions exported by the B module. +See the NOTES file for details of these and other options (including +optimisation options and ways of getting at the intermediate "assembler" +code that the Bytecode backend uses). + +(3) There are little Bourne shell scripts and perl programs to aid with +some common operations: assemble, disassemble, run_bytecode_test, +run_test, cc_harness, test_harness, test_harness_bytecode. + +(4) Walk the op tree in execution order printing terse info about each op + perl -MO=Terse,exec foo.pl + +(5) Walk the op tree in syntax order printing lengthier debug info about +each op. You can also append ",exec" to walk in execution order, but the +formatting is designed to look nice with Terse rather than Debug. + perl -MO=Debug foo.pl + +(6) Produce a cross-reference report of the line numbers at which all +variables, subs and formats are defined and used. + perl -MO=Xref foo.pl + +XSUBS + +The C and CC backends can successfully compile some perl programs which +make use of XSUB extensions. [I'll add more detail to this section in a +later release.] As a prerequisite, such extensions must not need to do +anything in their BOOT: section which needs to be done at runtime rather +than compile time. Normally, the only code in the boot_Foo() function is +a list of newXS() calls which xsubpp puts there and the compiler handles +saving those XS subs itself. For each XSUB used, the C and CC compiler +will generate an initialiser in their C output which refers to the name +of the relevant C function (XS_Foo_somesub). What is not yet automated +is the necessary commands and cc command-line options (e.g. via +"perl cc_harness") which link against the extension libraries. For now, +you need the XSUB extension to have installed files in the right format +for using as C libraries (e.g. Foo.a or Foo.so). As the Foo.so files (or +your platform's version) aren't suitable for linking against, you will +have to reget the extension source and rebuild it as a static extension +to force the generation of a suitable Foo.a file. Then you need to make +a symlink (or copy or rename) of that file into a libFoo.a suitable for +cc linking. Then add the appropriate -L and -l options to your +"perl cc_harness" command line to find and link against those libraries. +You may also need to fix up some platform-dependent environment variable +to ensure that linked-against .so files are found at runtime too. + +DIFFERENCES + +The result of running a compiled Perl program can sometimes be different +from running the same program with standard perl. Think of the compiler +as having a slightly different implementation of the language Perl. +Unfortunately, since Perl has had a single implementation until now, +there are no formal standards or documents defining what behaviour is +guaranteed of Perl the language and what just "happens to work". +Some of the differences below are almost impossible to change because of +the way the compiler works. Others can be changed to produce "standard" +perl behaviour if it's deemed proper and the resulting performance hit +is accepted. I'll use "standard perl" to mean the result of running a +Perl program using the perl executable from the perl distribution. +I'll use "compiled Perl program" to mean running an executable produced +by this compiler kit ("the compiler") with the CC backend. + +Loops + Standard perl calculates the target of "next", "last", and "redo" + at run-time. The compiler calculates the targets at compile-time. + For example, the program + + sub skip_on_odd { next NUMBER if $_[0] % 2 } + NUMBER: for ($i = 0; $i < 5; $i++) { + skip_on_odd($i); + print $i; + } + + produces the output + 024 + with standard perl but gives a compile-time error with the compiler. + +Context of ".." + The context (scalar or array) of the ".." operator determines whether + it behaves as a range or a flip/flop. Standard perl delays until + runtime the decision of which context it is in but the compiler needs + to know the context at compile-time. For example, + @a = (4,6,1,0,0,1); + sub range { (shift @a)..(shift @a) } + print range(); + while (@a) { print scalar(range()) } + generates the output + 456123E0 + with standard Perl but gives a compile-time error with compiled Perl. + +Arithmetic + Compiled Perl programs use native C arithemtic much more frequently + than standard perl. Operations on large numbers or on boundary + cases may produce different behaviour. + +Deprecated features + Features of standard perl such as $[ which have been deprecated + in standard perl since version 5 was released have not been + implemented in the compiler. + +Others + I'll add to this list as I remember what they are. + +BUGS + +Here are some things which may cause the compiler problems. + +The following render the compiler useless (without serious hacking): +* Use of the DATA filehandle (via __END__ or __DATA__ tokens) +* Operator overloading with %OVERLOAD +* The (deprecated) magic array-offset variable $[ does not work +* The following operators are not yet implemented for CC + goto + sort with a non-default comparison (i.e. a named sub or inline block) +* You can't use "last" to exit from a non-loop block. + +The following may give significant problems: +* BEGIN blocks containing complex initialisation code +* Code which is only ever referred to at runtime (e.g. via eval "..." or + via method calls): see the -u option for the C and CC backends. +* Run-time lookups of lexical variables in "outside" closures + +The following may cause problems (not thoroughly tested): +* Dependencies on whether values of some "magic" Perl variables are + determined at compile-time or runtime. +* For the C and CC backends: compile-time strings which are longer than + your C compiler can cope with in a single line or definition. +* Reliance on intimate details of global destruction +* For the Bytecode backend: high -On optimisation numbers with code + that has complex flow of control. +* Any "-w" option in the first line of your perl program is seen and + acted on by perl itself before the compiler starts. The compiler + itself then runs with warnings turned on. This may cause perl to + print out warnings about the compiler itself since I haven't tested + it thoroughly with warnings turned on. + +There is a terser but more complete list in the Todo file. + +Malcolm Beattie +2 September 1996 diff --git a/gnu/usr.bin/perl/ext/B/TESTS b/gnu/usr.bin/perl/ext/B/TESTS new file mode 100644 index 00000000000..e050f6cfddb --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/TESTS @@ -0,0 +1,78 @@ +Test results from compiling t/*/*.t + C Bytecode CC + +base/cond.t OK ok OK +base/if.t OK ok OK +base/lex.t OK ok OK +base/pat.t OK ok OK +base/term.t OK ok OK +cmd/elsif.t OK ok OK +cmd/for.t OK ok ok 1, 2, 3, panic: pp_iter +cmd/mod.t OK ok ok +cmd/subval.t OK ok 1..34, not ok 27,28 (simply + because filename changes). +cmd/switch.t OK ok ok +cmd/while.t OK ok ok +io/argv.t OK ok ok +io/dup.t OK ok ok +io/fs.t OK ok ok +io/inplace.t OK ok ok +io/pipe.t OK ok ok with -umain +io/print.t OK ok ok +io/tell.t OK ok ok +op/append.t OK ok OK +op/array.t OK ok 1..36, not ok 7,10 (no $[) +op/auto.t OK ok OK +op/chop.t OK ok OK +op/cond.t OK ok OK +op/delete.t OK ok OK +op/do.t OK ok OK +op/each.t OK ok OK +op/eval.t OK ok ok 1-6 of 16 then exits +op/exec.t OK ok OK +op/exp.t OK ok OK +op/flip.t OK ok OK +op/fork.t OK ok OK +op/glob.t OK ok OK +op/goto.t OK ok 1..9, Can't find label label1. +op/groups.t OK (s/ucb/bin/ under Linux) OK 1..0 for now. +op/index.t OK ok OK +op/int.t OK ok OK +op/join.t OK ok OK +op/list.t OK ok OK +op/local.t OK ok OK +op/magic.t OK ok OK +op/misc.t no DATA filehandle so succeeds trivially with 1..0 +op/mkdir.t OK ok OK +op/my.t OK ok OK +op/oct.t OK ok OK (C large const warnings) +op/ord.t OK ok OK +op/overload.t Mostly not ok Mostly not ok C errors. +op/pack.t OK ok OK +op/pat.t omit 26 (reset) ok [lots of memory for compile] +op/push.t OK ok OK +op/quotemeta.t OK ok OK +op/rand.t OK ok +op/range.t OK ok OK +op/read.t OK ok OK +op/readdir.t OK ok OK (substcont works too) +op/ref.t omits "ok 40" (lex destruction) ok (Bytecode) + CC: need -u for OBJ,BASEOBJ, + UNIVERSAL,WHATEVER,main. + 1..41, ok1-33,36-38, + then ok 41, ok 39.DESTROY probs +op/regexp.t OK ok ok (trivially all eval'd) +op/repeat.t OK ok ok +op/sleep.t OK ok ok +op/sort.t OK ok 1..10, ok 1, Out of memory! +op/split.t OK ok ok +op/sprintf.t OK ok ok +op/stat.t OK ok ok +op/study.t OK ok ok +op/subst.t OK ok ok +op/substr.t OK ok ok1-22 except 7-9,11 (all $[) +op/time.t OK ok ok +op/undef.t omit 21 ok ok +op/unshift.t OK ok ok +op/vec.t OK ok ok +op/write.t not ok 3 (no CvOUTSIDE lex from runtime eval). CC: 1..3, hang diff --git a/gnu/usr.bin/perl/ext/B/Todo b/gnu/usr.bin/perl/ext/B/Todo new file mode 100644 index 00000000000..495be2ef3d1 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/Todo @@ -0,0 +1,37 @@ +* Fixes + +CC backend: goto, sort with non-default comparison. last for non-loop blocks. +Version checking +improve XSUB handling (both static and dynamic) +sv_magic can do SvREFCNT_inc(obj) which messes up precalculated refcounts +allocation of XPV[INAHC]V structures needs fixing: Perl tries to free +them whereas the compiler expects them to be linked to a xpv[inahc]v_root +list the same as X[IPR]V structures. +ref counts +perl_parse replacement +fix cstring for long strings +compile-time initialisation of AvARRAYs +signed/unsigned problems with NV (and IV?) initialisation and elsewhere? +CvOUTSIDE for ordinary subs +DATA filehandle for standalone Bytecode program (easy) +DATA filehandle for multiple bytecode-compiled modules (harder) +DATA filehandle for C-compiled program (yet harder) + +* Features + +type checking +compile time v. runtime initialisation +save PMOPs in compiled form +selection of what to dump +options for cutting out line info etc. +comment output +shared constants +module dependencies + +* Optimisations +collapse LISTOPs to UNOPs or BASEOPs +compile-time qw(), constant subs +global analysis of variables, type hints etc. +demand-loaded bytecode (leader of each basic block replaced by an op +which loads in bytecode for its block) +fast sub calls for CC backend diff --git a/gnu/usr.bin/perl/ext/B/byteperl.c b/gnu/usr.bin/perl/ext/B/byteperl.c new file mode 100644 index 00000000000..6b53e3b174a --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/byteperl.c @@ -0,0 +1,110 @@ +#include "EXTERN.h" +#include "perl.h" +#ifndef PATCHLEVEL +#include "patchlevel.h" +#endif + +static void xs_init _((void)); +static PerlInterpreter *my_perl; + +int +#ifndef CAN_PROTOTYPE +main(argc, argv, env) +int argc; +char **argv; +char **env; +#else /* def(CAN_PROTOTYPE) */ +main(int argc, char **argv, char **env) +#endif /* def(CAN_PROTOTYPE) */ +{ + int exitstatus; + int i; + char **fakeargv; + FILE *fp; +#ifdef INDIRECT_BGET_MACROS + struct bytestream bs; +#endif /* INDIRECT_BGET_MACROS */ + + INIT_SPECIALSV_LIST; + PERL_SYS_INIT(&argc,&argv); + +#if PATCHLEVEL > 3 || (PATCHLEVEL == 3 && SUBVERSION >= 1) + perl_init_i18nl10n(1); +#else + perl_init_i18nl14n(1); +#endif + + if (!PL_do_undump) { + my_perl = perl_alloc(); + if (!my_perl) +#ifdef VMS + exit(vaxc$errno); +#else + exit(1); +#endif + perl_construct( my_perl ); + } + +#ifdef CSH + if (!PL_cshlen) + PL_cshlen = strlen(PL_cshname); +#endif + + if (argc < 2) + fp = stdin; + else { +#ifdef WIN32 + fp = fopen(argv[1], "rb"); +#else + fp = fopen(argv[1], "r"); +#endif + if (!fp) { + perror(argv[1]); +#ifdef VMS + exit(vaxc$errno); +#else + exit(1); +#endif + } + argv++; + argc--; + } + New(666, fakeargv, argc + 4, char *); + fakeargv[0] = argv[0]; + fakeargv[1] = "-e"; + fakeargv[2] = ""; + fakeargv[3] = "--"; + for (i = 1; i < argc; i++) + fakeargv[i + 3] = argv[i]; + fakeargv[argc + 3] = 0; + + exitstatus = perl_parse(my_perl, xs_init, argc + 3, fakeargv, NULL); + if (exitstatus) + exit( exitstatus ); + + sv_setpv(GvSV(gv_fetchpv("0", TRUE, SVt_PV)), argv[0]); + PL_main_cv = PL_compcv; + PL_compcv = 0; + +#ifdef INDIRECT_BGET_MACROS + bs.data = fp; + bs.fgetc = (int(*) _((void*)))fgetc; + bs.fread = (int(*) _((char*,size_t,size_t,void*)))fread; + bs.freadpv = freadpv; + byterun(bs); +#else + byterun(fp); +#endif /* INDIRECT_BGET_MACROS */ + + exitstatus = perl_run( my_perl ); + + perl_destruct( my_perl ); + perl_free( my_perl ); + + exit( exitstatus ); +} + +static void +xs_init() +{ +} diff --git a/gnu/usr.bin/perl/ext/B/ramblings/cc.notes b/gnu/usr.bin/perl/ext/B/ramblings/cc.notes new file mode 100644 index 00000000000..47bd65a09d8 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/ramblings/cc.notes @@ -0,0 +1,32 @@ +At entry to each basic block, the following can be assumed (and hence +must be forced where necessary at the end of each basic block): + +The shadow stack @stack is empty. +For each lexical object in @pad, VALID_IV holds for each T_INT, +VALID_DOUBLE holds for each T_DOUBLE and VALID_SV holds otherwise. +The C shadow variable sp holds the stack pointer (not necessarily stack_sp). + +write_back_stack + Writes the contents of the shadow stack @stack back to the real stack. + A write-back of each object in the stack is forced so that its + backing SV contains the right value and that SV is then pushed onto the + real stack. On return, @stack is empty. + +write_back_lexicals + Forces a write-back (i.e. achieves VALID_SV), where necessary, for each + lexical object in @pad. Objects with the TEMPORARY flag are skipped. If + write_back_lexicals is called with an (optional) argument, then it is + taken to be a bitmask of more flags: any lexical object with one of those + flags set is also skipped and not written back to its SV. + +invalidate_lexicals($avoid) + The VALID_INT and VALID_DOUBLE flags are turned off for each lexical + object in @pad whose flags field doesn't overlap with $avoid. + +reload_lexicals + For each necessary lexical object in @pad, makes sure that VALID_IV + holds for objects of type T_INT, VALID_DOUBLE holds for objects for + type T_DOUBLE, and VALID_SV holds for other objects. An object is + considered for reloading if its flags field does not overlap with the + (optional) argument passed to reload_lexicals. + diff --git a/gnu/usr.bin/perl/ext/B/ramblings/curcop.runtime b/gnu/usr.bin/perl/ext/B/ramblings/curcop.runtime new file mode 100644 index 00000000000..9b8b7d52e71 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/ramblings/curcop.runtime @@ -0,0 +1,39 @@ +PP code uses of curcop +---------------------- + +pp_rv2gv + when a new glob is created for an OPpLVAL_INTRO, + curcop->cop_line is stored as GvLINE() in the new GP. +pp_bless + curcop->cop_stash is used as the stash in the one-arg form of bless + +pp_repeat + tests (curcop != &compiling) to warn "Can't x= to readonly value" + +pp_pos +pp_substr +pp_index +pp_rindex +pp_aslice +pp_lslice +pp_splice + curcop->cop_arybase + +pp_sort + curcop->cop_stash used to determine whether to gv_fetchpv $a and $b + +pp_caller + tests (curcop->cop_stash == debstash) to determine whether + to set DB::args + +pp_reset + resets vars in curcop->cop_stash + +pp_dbstate + sets curcop = (COP*)op + +doeval + compiles into curcop->cop_stash + +pp_nextstate + sets curcop = (COP*)op diff --git a/gnu/usr.bin/perl/ext/B/ramblings/flip-flop b/gnu/usr.bin/perl/ext/B/ramblings/flip-flop new file mode 100644 index 00000000000..183d541b982 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/ramblings/flip-flop @@ -0,0 +1,51 @@ +PP(pp_range) +{ + if (GIMME == G_ARRAY) + return cCONDOP->op_true; + return SvTRUEx(PAD_SV(op->op_targ)) ? cCONDOP->op_false : cCONDOP->op_true; +} + +pp_range is a CONDOP. +In array context, it just returns op_true. +In scalar context it checks the truth of targ and returns +op_false if true, op_true if false. + +flip is an UNOP. +It "looks after" its child which is always a pp_range CONDOP. +In array context, it just returns the child's op_false. +In scalar context, there are three possible outcomes: + (1) set child's targ to 1, our targ to 1 and return op_next. + (2) set child's targ to 1, our targ to 0, sp-- and return child's op_false. + (3) Blank targ and TOPs and return op_next. +Case 1 happens for a "..." with a matching lineno... or true TOPs. +Case 2 happens for a ".." with a matching lineno... or true TOPs. +Case 3 happens for a non-matching lineno or false TOPs. + + $a = lhs..rhs; + + ,-------> range + ^ / \ + | true/ \false + | / \ + first| lhs rhs + | \ first / + ^--- flip <----- flop + \ / + \ / + sassign + + +/* range */ +if (SvTRUE(curpad[op->op_targ])) + goto label(op_false); +/* op_true */ +... +/* flip */ +/* For "..." returns op_next. For ".." returns op_next or op_first->op_false */ +/* end of basic block */ +goto out; +label(range op_false): +... +/* flop */ +out: +... diff --git a/gnu/usr.bin/perl/ext/B/ramblings/magic b/gnu/usr.bin/perl/ext/B/ramblings/magic new file mode 100644 index 00000000000..e41930a0f02 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/ramblings/magic @@ -0,0 +1,93 @@ +sv_magic() +---------- +av.c +av_store() + Storing a non-undef element into an SMAGICAL array, av, + assigns the equivalent lowercase form of magic (of the first + MAGIC in the chain) to the value (with obj = av, name = 0 and + namlen = array index). + +gv.c +gv_init() + Initialising gv assigns '*' magic to it with obj = gv, name = + GvNAME and namlen = GvNAMELEN. +gv_fetchpv() + @ISA gets 'I' magic with obj = gv, zero name and namlen. + %OVERLOAD gets 'A' magic with obj = gv, zero name and namlen. + $1 to $9, $&, $`, $', $+ get '\0' magic with obj = gv, + name = GvNAME and namlen = len ( = 1 presumably). +Gv_AMupdate() + Stashes for overload magic seem to get 'c' magic with obj = 0, + name = &amt and namlen = sizeof(amt). +hv_magic(hv, gv, how) + Gives magic how to hv with obj = gv and zero name and namlen. + +mg.c +mg_copy(sv, nsv, key, klen) + Traverses the magic chain of sv. Upper case forms of magic + (only) are copied across to nsv, preserving obj but using + name = key and namlen = klen. +magic_setpos() + LvTARG of a PVLV gets 'g' magic with obj = name = 0 and namlen = pos. + +op.c +mod() + PVLV operators give magic to their targs with + obj = name = namlen = 0. OP_POS gives '.', OP_VEC gives 'v' + and OP_SUBSTR gives 'x'. + +perl.c +magicname(sym, name, namlen) + Fetches/creates a GV with name sym and gives it '\0' magic + with obj = gv, name and namlen as passed. +init_postdump_symbols() + Elements of the environment get given SVs with 'e' magic. + obj = sv and name and namlen point to the actual string + within env. + +pp.c +pp_av2arylen() + $#foo gives '#' magic to the new SV with obj = av and + name = namlen = 0. +pp_study() + SV gets 'g' magic with obj = name = namlen = 0. +pp_substr() + PVLV gets 'x' magic with obj = name = namlen = 0. +pp_vec() + PVLV gets 'x' magic with obj = name = namlen = 0. + +pp_hot.c +pp_match() + m//g gets 'g' magic with obj = name = namlen = 0. + +pp_sys.c +pp_tie() + sv gets magic with obj = sv and name = namlen = 0. + If an HV or an AV, it gets 'P' magic, otherwise 'q' magic. +pp_dbmopen() + 'P' magic for the HV just as with pp_tie(). +pp_sysread() + If tainting, the buffer SV gets 't' magic with + obj = name = namlen = 0. + +sv.c +sv_setsv() + Doing sv_setsv(dstr, gv) gives '*' magic to dstr with + obj = dstr, name = GvNAME, namlen = GvNAMELEN. + +util.c +fbm_compile() + The PVBM gets 'B' magic with obj = name = namlen = 0 and SvVALID + is set to indicate that the Boyer-Moore table is valid. + magic_setbm() just clears the SvVALID flag. + +hv_magic() +---------- + +gv.c +gv_fetchfile() + With perldb, the HV of a gvfile gv gets 'L' magic with obj = gv. +gv_fetchpv() + %SIG gets 'S' magic with obj = siggv. +init_postdump_symbols() + %ENV gets 'E' magic with obj = envgv. diff --git a/gnu/usr.bin/perl/ext/B/ramblings/reg.alloc b/gnu/usr.bin/perl/ext/B/ramblings/reg.alloc new file mode 100644 index 00000000000..7fd69f2ebe5 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/ramblings/reg.alloc @@ -0,0 +1,32 @@ +while ($i--) { + foo(); +} +exit + + PP code if i an int register if i an int but not a + (i.e. can't be register (i.e. can be + implicitly invalidated) implicitly invalidated) + nextstate + enterloop + + + loop: + gvsv GV (0xe6078) *i validates i validates i + postdec invalidates $i invalidates $i + and if_false goto out; + i valid; $i invalid i valid; $i invalid + + i valid; $i invalid i valid; $i invalid + nextstate + pushmark + gv GV (0xe600c) *foo + entersub validates $i; invals i + + unstack + goto loop: + + i valid; $i invalid + out: + leaveloop + nextstate + exit diff --git a/gnu/usr.bin/perl/ext/B/ramblings/runtime.porting b/gnu/usr.bin/perl/ext/B/ramblings/runtime.porting new file mode 100644 index 00000000000..4699b255cf4 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/ramblings/runtime.porting @@ -0,0 +1,350 @@ +Notes on porting the perl runtime PP engine. +Importance: 1 = who cares?, 10 = vital +Difficulty: 1 = trivial, 10 = very difficult. Level assumes a +reasonable implementation of the SV and OP API already ported. + +OP Import Diff Comments +null 10 1 +stub 10 1 +scalar 10 1 +pushmark 10 1 PUSHMARK +wantarray 7 3 cxstack, dopoptosub +const 10 1 +gvsv 10 1 save_scalar +gv 10 1 +gelem 3 3 +padsv 10 2 SAVECLEARSV, provide_ref +padav 10 2 +padhv 10 2 +padany 1 1 +pushre 7 3 pushes an op. Blech. +rv2gv 6 5 +rv2sv 10 4 +av2arylen 7 3 sv_magic +rv2cv 8 5 sv_2cv +anoncode 7 6 cv_clone +prototype 4 4 sv_2cv +refgen 8 3 +srefgen 8 2 +ref 8 3 +bless 7 3 +backtick 5 4 +glob 5 2 do_readline +readline 8 2 do_readline +rcatline 8 2 +regcmaybe 8 1 +regcomp 8 9 pregcomp +match 8 10 +subst 8 10 +substcont 8 7 +trans 7 4 do_trans +sassign 10 3 mg_find, SvSETMAGIC +aassign 10 5 +chop 8 3 do_chop +schop 8 3 do_chop +chomp 8 3 do_chomp +schomp 8 3 do_chomp +defined 10 2 +undef 10 3 +study 4 5 +pos 8 3 PVLV, mg_find +preinc 10 2 sv_inc, SvSETMAGIC +i_preinc +predec 10 2 sv_dec, SvSETMAGIC +i_predec +postinc 10 2 sv_dec, SvSETMAGIC +i_postinc +postdec 10 2 sv_dec, SvSETMAGIC +i_postdec +pow 10 1 +multiply 10 1 +i_multiply 10 1 +divide 10 2 +i_divide 10 1 +modulo 10 2 +i_modulo 10 1 +repeat 6 4 +add 10 1 +i_add 10 1 +subtract 10 1 +i_subtract 10 1 +concat 10 2 mg_get +stringify 10 2 sv_setpvn +left_shift 10 1 +right_shift 10 1 +lt 10 1 +i_lt 10 1 +gt 10 1 +i_gt 10 1 +le 10 1 +i_le 10 1 +ge 10 1 +i_ge 10 1 +eq 10 1 +i_eq 10 1 +ne 10 1 +i_ne 10 1 +ncmp 10 1 +i_ncmp 10 1 +slt 10 2 +sgt 10 2 +sle 10 2 +sge 10 2 +seq 10 2 sv_eq +sne 10 2 +scmp 10 2 +bit_and 10 2 +bit_xor 10 2 +bit_or 10 2 +negate 10 3 +i_negate 10 1 +not 10 1 +complement 10 3 +atan2 6 1 +sin 6 1 +cos 6 1 +rand 5 2 +srand 5 2 +exp 6 1 +log 6 2 +sqrt 6 2 +int 10 2 +hex 9 2 +oct 9 2 +abs 10 1 +length 10 1 +substr 10 4 PVLV +vec 5 4 +index 9 3 +rindex 9 3 +sprintf 9 4 do_sprintf +formline 6 7 +ord 6 2 +chr 6 2 +crypt 3 2 +ucfirst 6 2 +lcfirst 6 2 +uc 6 2 +lc 6 2 +quotemeta 6 3 +rv2av 10 3 save_svref, mg_get, save_ary +aelemfast 10 2 av_fetch +aelem 10 3 +aslice 9 4 +each 10 3 hv_iternext +values 10 3 do_kv +keys 10 3 do_kv +delete 10 3 +exists 10 3 +rv2hv 10 3 save_svref, mg_get, save_ary, do_kv +helem 10 3 save_svref, provide_ref +hslice 9 4 +unpack 9 6 lengthy +pack 9 6 lengthy +split 9 9 +join 10 4 do_join +list 10 2 +lslice 9 4 +anonlist 10 2 +anonhash 10 3 +splice 9 6 +push 10 2 +pop 10 2 +shift 10 2 +unshift 10 2 +sort 6 7 +reverse 9 4 +grepstart 6 5 modifies flow of control +grepwhile 6 5 modifies flow of control +mapstart 1 1 +mapwhile 6 5 modifies flow of control +range 7 3 modifies flow of control +flip 7 4 modifies flow of control +flop 7 4 modifies flow of control +and 10 3 modifies flow of control +or 10 3 modifies flow of control +xor +cond_expr 10 3 modifies flow of control +andassign 7 3 modifies flow of control +orassign 7 3 modifies flow of control +method 8 5 +entersub 10 7 +leavesub 10 5 +caller 2 8 +warn 9 3 +die 9 3 +reset 2 2 +lineseq 1 1 +nextstate 10 1 Update stack_sp from cxstack. FREETMPS. +dbstate 3 7 +unstack +enter 10 3 cxstack, ENTER, SAVETMPS, PUSHBLOCK +leave 10 3 cxstack, SAVETMPS, LEAVE, POPBLOCK +scope 1 1 +enteriter 9 4 cxstack +iter 9 3 cxstack +enterloop 10 4 +leaveloop 10 4 +return 10 5 +last 9 6 +next 9 6 +redo 9 6 +dump 1 9 pp_goto +goto 6 9 +exit 9 2 my_exit +open 9 5 do_open +close 9 3 do_close +pipe_op 7 4 +fileno 9 2 +umask 4 2 +binmode 4 2 +tie 5 5 pp_entersub +untie 5 2 sv_unmagic +tied 5 2 +dbmopen 4 5 +dbmclose 4 2 +sselect 4 4 +select 7 3 +getc 7 2 +read 8 2 pp_sysread +enterwrite 4 4 doform +leavewrite 4 5 +prtf 4 4 do_sprintf +print 8 6 +sysopen 8 2 +sysread 8 4 +syswrite 8 4 pp_send +send 8 4 +recv 8 4 pp_sysread +eof 9 2 +tell 9 3 +seek 9 2 +truncate 8 3 +fcntl 8 4 pp_ioctl +ioctl 8 4 +flock 8 2 +socket 5 3 +sockpair 5 3 +bind 5 3 +connect 5 3 +listen 5 3 +accept 5 3 +shutdown 5 2 +gsockopt 5 3 pp_ssockopt +ssockopt 5 3 +getsockname 5 3 pp_getpeername +getpeername 5 3 +lstat 5 4 pp_stat +stat 5 4 lengthy +ftrread 5 2 cando +ftrwrite 5 2 cando +ftrexec 5 2 cando +fteread 5 2 cando +ftewrite 5 2 cando +fteexec 5 2 cando +ftis 5 2 cando +fteowned 5 2 cando +ftrowned 5 2 cando +ftzero 5 2 cando +ftsize 5 2 cando +ftmtime 5 2 cando +ftatime 5 2 cando +ftctime 5 2 cando +ftsock 5 2 cando +ftchr 5 2 cando +ftblk 5 2 cando +ftfile 5 2 cando +ftdir 5 2 cando +ftpipe 5 2 cando +ftlink 5 2 cando +ftsuid 5 2 cando +ftsgid 5 2 cando +ftsvtx 5 2 cando +fttty 5 2 cando +fttext 5 4 +ftbinary 5 4 fttext +chdir +chown +chroot +unlink +chmod +utime +rename +link +symlink +readlink +mkdir +rmdir +open_dir +readdir +telldir +seekdir +rewinddir +closedir +fork +wait +waitpid +system +exec +kill +getppid +getpgrp +setpgrp +getpriority +setpriority +time +tms +localtime +gmtime +alarm +sleep +shmget +shmctl +shmread +shmwrite +msgget +msgctl +msgsnd +msgrcv +semget +semctl +semop +require 6 9 doeval +dofile 6 9 doeval +entereval 6 9 doeval +leaveeval 6 5 +entertry 7 4 modifies flow of control +leavetry 7 3 +ghbyname +ghbyaddr +ghostent +gnbyname +gnbyaddr +gnetent +gpbyname +gpbynumber +gprotoent +gsbyname +gsbyport +gservent +shostent +snetent +sprotoent +sservent +ehostent +enetent +eprotoent +eservent +gpwnam +gpwuid +gpwent +spwent +epwent +ggrnam +ggrgid +ggrent +sgrent +egrent +getlogin +syscall +
\ No newline at end of file diff --git a/gnu/usr.bin/perl/ext/B/typemap b/gnu/usr.bin/perl/ext/B/typemap new file mode 100644 index 00000000000..7206a6a2e11 --- /dev/null +++ b/gnu/usr.bin/perl/ext/B/typemap @@ -0,0 +1,69 @@ +TYPEMAP + +B::OP T_OP_OBJ +B::UNOP T_OP_OBJ +B::BINOP T_OP_OBJ +B::LOGOP T_OP_OBJ +B::CONDOP T_OP_OBJ +B::LISTOP T_OP_OBJ +B::PMOP T_OP_OBJ +B::SVOP T_OP_OBJ +B::GVOP T_OP_OBJ +B::PVOP T_OP_OBJ +B::CVOP T_OP_OBJ +B::LOOP T_OP_OBJ +B::COP T_OP_OBJ + +B::SV T_SV_OBJ +B::PV T_SV_OBJ +B::IV T_SV_OBJ +B::NV T_SV_OBJ +B::PVMG T_SV_OBJ +B::PVLV T_SV_OBJ +B::BM T_SV_OBJ +B::RV T_SV_OBJ +B::GV T_SV_OBJ +B::CV T_SV_OBJ +B::HV T_SV_OBJ +B::AV T_SV_OBJ +B::IO T_SV_OBJ + +B::MAGIC T_MG_OBJ +SSize_t T_IV +STRLEN T_IV + +INPUT +T_OP_OBJ + if (SvROK($arg)) { + IV tmp = SvIV((SV*)SvRV($arg)); + $var = ($type) tmp; + } + else + croak(\"$var is not a reference\") + +T_SV_OBJ + if (SvROK($arg)) { + IV tmp = SvIV((SV*)SvRV($arg)); + $var = ($type) tmp; + } + else + croak(\"$var is not a reference\") + +T_MG_OBJ + if (SvROK($arg)) { + IV tmp = SvIV((SV*)SvRV($arg)); + $var = ($type) tmp; + } + else + croak(\"$var is not a reference\") + +OUTPUT +T_OP_OBJ + sv_setiv(newSVrv($arg, cc_opclassname((OP*)$var)), (IV)$var); + +T_SV_OBJ + make_sv_object(($arg), (SV*)($var)); + + +T_MG_OBJ + sv_setiv(newSVrv($arg, "B::MAGIC"), (IV)$var); diff --git a/gnu/usr.bin/perl/ext/DB_File/Changes b/gnu/usr.bin/perl/ext/DB_File/Changes new file mode 100644 index 00000000000..2fab9192290 --- /dev/null +++ b/gnu/usr.bin/perl/ext/DB_File/Changes @@ -0,0 +1,234 @@ + +0.1 + + First Release. + +0.2 + + When DB_File is opening a database file it no longer terminates the + process if dbopen returned an error. This allows file protection + errors to be caught at run time. Thanks to Judith Grass + <grass@cybercash.com> for spotting the bug. + +0.3 + + Added prototype support for multiple btree compare callbacks. + +1.0 + + DB_File has been in use for over a year. To reflect that, the + version number has been incremented to 1.0. + + Added complete support for multiple concurrent callbacks. + + Using the push method on an empty list didn't work properly. This + has been fixed. + +1.01 + + Fixed a core dump problem with SunOS. + + The return value from TIEHASH wasn't set to NULL when dbopen + returned an error. + +1.02 + + Merged OS/2 specific code into DB_File.xs + + Removed some redundant code in DB_File.xs. + + Documentation update. + + Allow negative subscripts with RECNO interface. + + Changed the default flags from O_RDWR to O_CREAT|O_RDWR. + + The example code which showed how to lock a database needed a call + to sync added. Without it the resultant database file was empty. + + Added get_dup method. + +1.03 + + Documentation update. + + DB_File now imports the constants (O_RDWR, O_CREAT etc.) from Fcntl + automatically. + + The standard hash function exists is now supported. + + Modified the behavior of get_dup. When it returns an associative + array, the value is the count of the number of matching BTREE + values. + +1.04 + + Minor documentation changes. + + Fixed a bug in hash_cb. Patches supplied by Dave Hammen, + <hammen@gothamcity.jsc.nasa.govt>. + + Fixed a bug with the constructors for DB_File::HASHINFO, + DB_File::BTREEINFO and DB_File::RECNOINFO. Also tidied up the + constructors to make them -w clean. + + Reworked part of the test harness to be more locale friendly. + +1.05 + + Made all scripts in the documentation strict and -w clean. + + Added logic to DB_File.xs to allow the module to be built after + Perl is installed. + +1.06 + + Minor namespace cleanup: Localized PrintBtree. + +1.07 + + Fixed bug with RECNO, where bval wasn't defaulting to "\n". + +1.08 + + Documented operation of bval. + +1.09 + + Minor bug fix in DB_File::HASHINFO, DB_File::RECNOINFO and + DB_File::BTREEINFO. + + Changed default mode to 0666. + +1.10 + + Fixed fd method so that it still returns -1 for in-memory files + when db 1.86 is used. + +1.11 + + Documented the untie gotcha. + +1.12 + + Documented the incompatibility with version 2 of Berkeley DB. + +1.13 + + Minor changes to DB_FIle.xs and DB_File.pm + +1.14 + + Made it illegal to tie an associative array to a RECNO database and + an ordinary array to a HASH or BTREE database. + +1.15 + + Patch from Gisle Aas <gisle@aas.no> to suppress "use of undefined + value" warning with db_get and db_seq. + + Patch from Gisle Aas <gisle@aas.no> to make DB_File export only the + O_* constants from Fcntl. + + Removed the DESTROY method from the DB_File::HASHINFO module. + + Previously DB_File hard-wired the class name of any object that it + created to "DB_File". This makes sub-classing difficult. Now + DB_File creats objects in the namespace of the package it has been + inherited into. + + +1.16 + + A harmless looking tab was causing Makefile.PL to fail on AIX 3.2.5 + + Small fix for the AIX strict C compiler XLC which doesn't like + __attribute__ being defined via proto.h and redefined via db.h. Fix + courtesy of Jarkko Hietaniemi. + +1.50 + + DB_File can now build with either DB 1.x or 2.x, but not both at + the same time. + +1.51 + + Fixed the test harness so that it doesn't expect DB_File to have + been installed by the main Perl build. + + + Fixed a bug in mapping 1.x O_RDONLY flag to 2.x DB_RDONLY equivalent + +1.52 + + Patch from Nick Ing-Simmons now allows DB_File to build on NT. + Merged 1.15 patch. + +1.53 + + Added DB_RENUMBER to flags for recno. + +1.54 + + Fixed a small bug in the test harness when run under win32 + The emulation of fd when useing DB 2.x was busted. + +1.55 + Merged 1.16 changes. + +1.56 + Documented the Solaris 2.5 mutex bug + +1.57 + If Perl has been compiled with Threads support,the symbol op will be + defined. This clashes with a field name in db.h, so it needs to be + #undef'ed before db.h is included. + +1.58 + Tied Array support was enhanced in Perl 5.004_57. DB_File now + supports PUSH,POP,SHIFT,UNSHIFT & STORESIZE. + + Fixed a problem with the use of sv_setpvn. When the size is + specified as 0, it does a strlen on the data. This was ok for DB + 1.x, but isn't for DB 2.x. + +1.59 + Updated the license section. + + Berkeley DB 2.4.10 disallows zero length keys. Tests 32 & 42 in + db-btree.t and test 27 in db-hash.t failed because of this change. + Those tests have been zapped. + + Added dbinfo to the distribution. + +1.60 + Changed the test to check for full tied array support + +1.61 19th November 1998 + + Added a note to README about how to build Berkeley DB 2.x when + using HP-UX. + Minor modifications to get the module to build with DB 2.5.x + Fixed a typo in the definition of O_RDONLY, courtesy of Mark Kettenis. + +1.62 30th November 1998 + + Added hints/dynixptx.pl. + Fixed typemap -- 1.61 used PL_na instead of na + +1.63 19th December 1998 + + * Fix to allow DB 2.6.x to build with DB_File + * Documentation updated to use push,pop etc in the RECNO example & + to include the find_dup & del_dup methods. + +1.64 21st February 1999 + + * Tidied the 1.x to 2.x flag mapping code. + * Added a patch from Mark Kettenis <kettenis@wins.uva.nl> to fix a flag + mapping problem with O_RDONLY on the Hurd + * Updated the message that db-recno.t prints when tests 51, 53 or 55 fail. + +1.65 6th March 1999 + * Fixed a bug in the recno PUSH logic. + * The BOOT version check now needs 2.3.4 when using Berkeley DB version 2 diff --git a/gnu/usr.bin/perl/ext/DB_File/dbinfo b/gnu/usr.bin/perl/ext/DB_File/dbinfo new file mode 100644 index 00000000000..24a794448f8 --- /dev/null +++ b/gnu/usr.bin/perl/ext/DB_File/dbinfo @@ -0,0 +1,96 @@ +#!/usr/local/bin/perl + +# Name: dbinfo -- identify berkeley DB version used to create +# a database file +# +# Author: Paul Marquess <Paul.Marquess@btinternet.com> +# Version: 1.01 +# Date 16th April 1998 +# +# Copyright (c) 1998 Paul Marquess. All rights reserved. +# This program is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. + +# Todo: Print more stats on a db file, e.g. no of records +# add log/txn/lock files + +use strict ; + +my %Data = + ( + 0x053162 => { + Type => "Btree", + Versions => + { + 1 => "Unknown (older than 1.71)", + 2 => "Unknown (older than 1.71)", + 3 => "1.71 -> 1.85, 1.86", + 4 => "Unknown", + 5 => "2.0.0 -> 2.3.0", + 6 => "2.3.1 or greater", + } + }, + 0x061561 => { + Type => "Hash", + Versions => + { + 1 => "Unknown (older than 1.71)", + 2 => "1.71 -> 1.85", + 3 => "1.86", + 4 => "2.0.0 -> 2.1.0", + 5 => "2.2.6 or greater", + } + }, + ) ; + +die "Usage: dbinfo file\n" unless @ARGV == 1 ; + +print "testing file $ARGV[0]...\n\n" ; +open (F, "<$ARGV[0]") or die "Cannot open file $ARGV[0]: $!\n" ; + +my $buff ; +read F, $buff, 20 ; + +my (@info) = unpack("NNNNN", $buff) ; +my (@info1) = unpack("VVVVV", $buff) ; +my ($magic, $version, $endian) ; + +if ($Data{$info[0]}) # first try DB 1.x format +{ + $magic = $info[0] ; + $version = $info[1] ; + $endian = "Unknown" ; +} +elsif ($Data{$info[3]}) # next DB 2.x big endian +{ + $magic = $info[3] ; + $version = $info[4] ; + $endian = "Big Endian" ; +} +elsif ($Data{$info1[3]}) # next DB 2.x little endian +{ + $magic = $info1[3] ; + $version = $info1[4] ; + $endian = "Little Endian" ; +} +else + { die "not a Berkeley DB database file.\n" } + +my $type = $Data{$magic} ; +my $magic = sprintf "%06X", $magic ; + +my $ver_string = "Unknown" ; +$ver_string = $type->{Versions}{$version} + if defined $type->{Versions}{$version} ; + +print <<EOM ; +File Type: Berkeley DB $type->{Type} file. +File Version ID: $version +Built with Berkeley DB: $ver_string +Byte Order: $endian +Magic: $magic +EOM + +close F ; + +exit ; diff --git a/gnu/usr.bin/perl/ext/DB_File/hints/dynixptx.pl b/gnu/usr.bin/perl/ext/DB_File/hints/dynixptx.pl new file mode 100644 index 00000000000..bb5ffa56e6b --- /dev/null +++ b/gnu/usr.bin/perl/ext/DB_File/hints/dynixptx.pl @@ -0,0 +1,3 @@ +# Need to add an extra '-lc' to the end to work around a DYNIX/ptx bug + +$self->{LIBS} = ['-lm -lc']; diff --git a/gnu/usr.bin/perl/ext/Data/Dumper/Changes b/gnu/usr.bin/perl/ext/Data/Dumper/Changes new file mode 100644 index 00000000000..9a96edab8dd --- /dev/null +++ b/gnu/usr.bin/perl/ext/Data/Dumper/Changes @@ -0,0 +1,178 @@ +=head1 NAME + +HISTORY - public release history for Data::Dumper + +=head1 DESCRIPTION + +=over 8 + +=item 2.10 (31 Oct 1998) + +Bugfixes for dumping related undef values, globs, and better double +quoting: three patches suggested by Gisle Aas <gisle@aas.no>. + +Escaping of single quotes in the XS version could get tripped up +by the presence of nulls in the string. Fix suggested by +Slaven Rezic <eserte@cs.tu-berlin.de>. + +Rather large scale reworking of the logic in how seen values +are stashed. Anonymous scalars that may be encountered while +traversing the structure are properly tracked, in case they become +used in data dumped in a later pass. There used to be a problem +with the previous logic that prevented such structures from being +dumped correctly. + +Various additions to the testsuite. + +=item 2.09 (9 July 1998) + +Implement $Data::Dumper::Bless, suggested by Mark Daku <daku@nortel.ca>. + +=item 2.081 (15 January 1998) + +Minor release to fix Makefile.PL not accepting MakeMaker args. + +=item 2.08 (7 December 1997) + +Glob dumps don't output superflous 'undef' anymore. + +Fixes from Gisle Aas <gisle@aas.no> to make Dumper() work with +overloaded strings in recent perls, and his new testsuite. + +require 5.004. + +A separate flag to always quote hash keys (on by default). + +Recreating known CODE refs is now better supported. + +Changed flawed constant SCALAR bless workaround. + +=item 2.07 (7 December 1996) + +Dumpxs output is now exactly the same as Dump. It still doesn't +honor C<Useqq> though. + +Regression tests test for identical output and C<eval>-ability. + +Bug in *GLOB{THING} output fixed. + +Other small enhancements. + +=item 2.06 (2 December 1996) + +Bugfix that was serious enough for new release--the bug cripples +MLDBM. Problem was "Attempt to modify readonly value..." failures +that stemmed for a misguided SvPV_force() instead of a SvPV().) + +=item 2.05 (2 December 1996) + +Fixed the type mismatch that was causing Dumpxs test to fail +on 64-bit platforms. + +GLOB elements are dumped now when C<Purity> is set (using the +*GLOB{THING} syntax). + +The C<Freezer> option can be set to a method name to call +before probing objects for dumping. Some applications: objects with +external data, can re-bless themselves into a transitional package; +Objects the maintain ephemeral state (like open files) can put +additional information in the object to facilitate persistence. + +The corresponding C<Toaster> option, if set, specifies +the method call that will revive the frozen object. + +The C<Deepcopy> flag has been added to do just that. + +Dumper does more aggressive cataloging of SCALARs encountered +within ARRAY/HASH structures. Thanks to Norman Gaywood +<norm@godel.une.edu.au> for reporting the problem. + +Objects that C<overload> the '""' operator are now handled +properly by the C<Dump> method. + +Significant additions to the testsuite. + +More documentation. + +=item 2.04beta (28 August 1996) + +Made dump of glob names respect C<Useqq> setting. + +[@$%] are now escaped now when in double quotes. + +=item 2.03beta (26 August 1996) + +Fixed Dumpxs. It was appending trailing nulls to globnames. +(reported by Randal Schwartz <merlyn@teleport.com>). + +Calling the C<Indent()> method on a dumper object now correctly +resets the internal separator (reported by Curt Tilmes +<curt@ltpmail.gsfc.nasa.gov>). + +New C<Terse> option to suppress the 'C<VARI<n> = >' prefix +introduced. If the option is set, they are output only when +absolutely essential. + +The C<Useqq> flag is supported (but not by the XSUB version +yet). + +Embedded nulls in keys are now handled properly by Dumpxs. + +Dumper.xs now use various integer types in perl.h (should +make it compile without noises on 64 bit platforms, although +I haven't been able to test this). + +All the dump methods now return a list of strings in a list +context. + + +=item 2.02beta (13 April 1996) + +Non portable sprintf usage in XS code fixed (thanks to +Ulrich Pfeifer <pfeifer@charly.informatik.uni-dortmund.de>). + + +=item 2.01beta (10 April 1996) + +Minor bugfix (single digit numbers were always getting quoted). + + +=item 2.00beta (9 April 1996) + +C<Dumpxs> is now the exact XSUB equivalent of C<Dump>. The XS version +is 4-5 times faster. + +C<require 5.002>. + +MLDBM example removed (as its own module, it has a separate CPAN +reality now). + +Fixed bugs in handling keys with wierd characters. Perl can be +tripped up in its implicit quoting of the word before '=>'. The +fix: C<Data::Dumper::Purity>, when set, always triggers quotes +around hash keys. + +Andreas Koenig <k@anna.in-berlin.de> pointed out that handling octals +is busted. His patch added. + +Dead code removed, other minor documentation fixes. + + +=item 1.23 (3 Dec 1995) + +MLDBM example added. + +Several folks pointed out that quoting of ticks and backslashes +in strings is missing. Added. + +Ian Phillips <ian@pipex.net> pointed out that numerics may lose +precision without quotes. Fixed. + + +=item 1.21 (20 Nov 1995) + +Last stable version I can remember. + +=back + +=cut diff --git a/gnu/usr.bin/perl/ext/Data/Dumper/Dumper.pm b/gnu/usr.bin/perl/ext/Data/Dumper/Dumper.pm new file mode 100644 index 00000000000..b1fd2b790ae --- /dev/null +++ b/gnu/usr.bin/perl/ext/Data/Dumper/Dumper.pm @@ -0,0 +1,1000 @@ +# +# Data/Dumper.pm +# +# convert perl data structures into perl syntax suitable for both printing +# and eval +# +# Documentation at the __END__ +# + +package Data::Dumper; + +$VERSION = $VERSION = '2.101'; + +#$| = 1; + +require 5.004; +require Exporter; +require DynaLoader; +require overload; + +use Carp; + +@ISA = qw(Exporter DynaLoader); +@EXPORT = qw(Dumper); +@EXPORT_OK = qw(DumperX); + +bootstrap Data::Dumper; + +# module vars and their defaults +$Indent = 2 unless defined $Indent; +$Purity = 0 unless defined $Purity; +$Pad = "" unless defined $Pad; +$Varname = "VAR" unless defined $Varname; +$Useqq = 0 unless defined $Useqq; +$Terse = 0 unless defined $Terse; +$Freezer = "" unless defined $Freezer; +$Toaster = "" unless defined $Toaster; +$Deepcopy = 0 unless defined $Deepcopy; +$Quotekeys = 1 unless defined $Quotekeys; +$Bless = "bless" unless defined $Bless; +#$Expdepth = 0 unless defined $Expdepth; +#$Maxdepth = 0 unless defined $Maxdepth; + +# +# expects an arrayref of values to be dumped. +# can optionally pass an arrayref of names for the values. +# names must have leading $ sign stripped. begin the name with * +# to cause output of arrays and hashes rather than refs. +# +sub new { + my($c, $v, $n) = @_; + + croak "Usage: PACKAGE->new(ARRAYREF, [ARRAYREF])" + unless (defined($v) && (ref($v) eq 'ARRAY')); + $n = [] unless (defined($n) && (ref($v) eq 'ARRAY')); + + my($s) = { + level => 0, # current recursive depth + indent => $Indent, # various styles of indenting + pad => $Pad, # all lines prefixed by this string + xpad => "", # padding-per-level + apad => "", # added padding for hash keys n such + sep => "", # list separator + seen => {}, # local (nested) refs (id => [name, val]) + todump => $v, # values to dump [] + names => $n, # optional names for values [] + varname => $Varname, # prefix to use for tagging nameless ones + purity => $Purity, # degree to which output is evalable + useqq => $Useqq, # use "" for strings (backslashitis ensues) + terse => $Terse, # avoid name output (where feasible) + freezer => $Freezer, # name of Freezer method for objects + toaster => $Toaster, # name of method to revive objects + deepcopy => $Deepcopy, # dont cross-ref, except to stop recursion + quotekeys => $Quotekeys, # quote hash keys + 'bless' => $Bless, # keyword to use for "bless" +# expdepth => $Expdepth, # cutoff depth for explicit dumping +# maxdepth => $Maxdepth, # depth beyond which we give up + }; + + if ($Indent > 0) { + $s->{xpad} = " "; + $s->{sep} = "\n"; + } + return bless($s, $c); +} + +# +# add-to or query the table of already seen references +# +sub Seen { + my($s, $g) = @_; + if (defined($g) && (ref($g) eq 'HASH')) { + my($k, $v, $id); + while (($k, $v) = each %$g) { + if (defined $v and ref $v) { + ($id) = (overload::StrVal($v) =~ /\((.*)\)$/); + if ($k =~ /^[*](.*)$/) { + $k = (ref $v eq 'ARRAY') ? ( "\\\@" . $1 ) : + (ref $v eq 'HASH') ? ( "\\\%" . $1 ) : + (ref $v eq 'CODE') ? ( "\\\&" . $1 ) : + ( "\$" . $1 ) ; + } + elsif ($k !~ /^\$/) { + $k = "\$" . $k; + } + $s->{seen}{$id} = [$k, $v]; + } + else { + carp "Only refs supported, ignoring non-ref item \$$k"; + } + } + return $s; + } + else { + return map { @$_ } values %{$s->{seen}}; + } +} + +# +# set or query the values to be dumped +# +sub Values { + my($s, $v) = @_; + if (defined($v) && (ref($v) eq 'ARRAY')) { + $s->{todump} = [@$v]; # make a copy + return $s; + } + else { + return @{$s->{todump}}; + } +} + +# +# set or query the names of the values to be dumped +# +sub Names { + my($s, $n) = @_; + if (defined($n) && (ref($n) eq 'ARRAY')) { + $s->{names} = [@$n]; # make a copy + return $s; + } + else { + return @{$s->{names}}; + } +} + +sub DESTROY {} + +# +# dump the refs in the current dumper object. +# expects same args as new() if called via package name. +# +sub Dump { + my($s) = shift; + my(@out, $val, $name); + my($i) = 0; + local(@post); + + $s = $s->new(@_) unless ref $s; + + for $val (@{$s->{todump}}) { + my $out = ""; + @post = (); + $name = $s->{names}[$i++]; + if (defined $name) { + if ($name =~ /^[*](.*)$/) { + if (defined $val) { + $name = (ref $val eq 'ARRAY') ? ( "\@" . $1 ) : + (ref $val eq 'HASH') ? ( "\%" . $1 ) : + (ref $val eq 'CODE') ? ( "\*" . $1 ) : + ( "\$" . $1 ) ; + } + else { + $name = "\$" . $1; + } + } + elsif ($name !~ /^\$/) { + $name = "\$" . $name; + } + } + else { + $name = "\$" . $s->{varname} . $i; + } + + my $valstr; + { + local($s->{apad}) = $s->{apad}; + $s->{apad} .= ' ' x (length($name) + 3) if $s->{indent} >= 2; + $valstr = $s->_dump($val, $name); + } + + $valstr = "$name = " . $valstr . ';' if @post or !$s->{terse}; + $out .= $s->{pad} . $valstr . $s->{sep}; + $out .= $s->{pad} . join(';' . $s->{sep} . $s->{pad}, @post) + . ';' . $s->{sep} if @post; + + push @out, $out; + } + return wantarray ? @out : join('', @out); +} + +# +# twist, toil and turn; +# and recurse, of course. +# +sub _dump { + my($s, $val, $name) = @_; + my($sname); + my($out, $realpack, $realtype, $type, $ipad, $id, $blesspad); + + $type = ref $val; + $out = ""; + + if ($type) { + + # prep it, if it looks like an object + if ($type =~ /[a-z_:]/) { + my $freezer = $s->{freezer}; + $val->$freezer() if $freezer && UNIVERSAL::can($val, $freezer); + } + + ($realpack, $realtype, $id) = + (overload::StrVal($val) =~ /^(?:(.*)\=)?([^=]*)\(([^\(]*)\)$/); + + # if it has a name, we need to either look it up, or keep a tab + # on it so we know when we hit it later + if (defined($name) and length($name)) { + # keep a tab on it so that we dont fall into recursive pit + if (exists $s->{seen}{$id}) { +# if ($s->{expdepth} < $s->{level}) { + if ($s->{purity} and $s->{level} > 0) { + $out = ($realtype eq 'HASH') ? '{}' : + ($realtype eq 'ARRAY') ? '[]' : + "''" ; + push @post, $name . " = " . $s->{seen}{$id}[0]; + } + else { + $out = $s->{seen}{$id}[0]; + if ($name =~ /^([\@\%])/) { + my $start = $1; + if ($out =~ /^\\$start/) { + $out = substr($out, 1); + } + else { + $out = $start . '{' . $out . '}'; + } + } + } + return $out; +# } + } + else { + # store our name + $s->{seen}{$id} = [ (($name =~ /^[@%]/) ? ('\\' . $name ) : + ($realtype eq 'CODE' and + $name =~ /^[*](.*)$/) ? ('\\&' . $1 ) : + $name ), + $val ]; + } + } + + $s->{level}++; + $ipad = $s->{xpad} x $s->{level}; + + if ($realpack) { # we have a blessed ref + $out = $s->{'bless'} . '( '; + $blesspad = $s->{apad}; + $s->{apad} .= ' ' if ($s->{indent} >= 2); + } + + if ($realtype eq 'SCALAR') { + if ($realpack) { + $out .= 'do{\\(my $o = ' . $s->_dump($$val, "\${$name}") . ')}'; + } + else { + $out .= '\\' . $s->_dump($$val, "\${$name}"); + } + } + elsif ($realtype eq 'GLOB') { + $out .= '\\' . $s->_dump($$val, "*{$name}"); + } + elsif ($realtype eq 'ARRAY') { + my($v, $pad, $mname); + my($i) = 0; + $out .= ($name =~ /^\@/) ? '(' : '['; + $pad = $s->{sep} . $s->{pad} . $s->{apad}; + ($name =~ /^\@(.*)$/) ? ($mname = "\$" . $1) : + # omit -> if $foo->[0]->{bar}, but not ${$foo->[0]}->{bar} + ($name =~ /^\\?[\%\@\*\$][^{].*[]}]$/) ? ($mname = $name) : + ($mname = $name . '->'); + $mname .= '->' if $mname =~ /^\*.+\{[A-Z]+\}$/; + for $v (@$val) { + $sname = $mname . '[' . $i . ']'; + $out .= $pad . $ipad . '#' . $i if $s->{indent} >= 3; + $out .= $pad . $ipad . $s->_dump($v, $sname); + $out .= "," if $i++ < $#$val; + } + $out .= $pad . ($s->{xpad} x ($s->{level} - 1)) if $i; + $out .= ($name =~ /^\@/) ? ')' : ']'; + } + elsif ($realtype eq 'HASH') { + my($k, $v, $pad, $lpad, $mname); + $out .= ($name =~ /^\%/) ? '(' : '{'; + $pad = $s->{sep} . $s->{pad} . $s->{apad}; + $lpad = $s->{apad}; + ($name =~ /^\%(.*)$/) ? ($mname = "\$" . $1) : + # omit -> if $foo->[0]->{bar}, but not ${$foo->[0]}->{bar} + ($name =~ /^\\?[\%\@\*\$][^{].*[]}]$/) ? ($mname = $name) : + ($mname = $name . '->'); + $mname .= '->' if $mname =~ /^\*.+\{[A-Z]+\}$/; + while (($k, $v) = each %$val) { + my $nk = $s->_dump($k, ""); + $nk = $1 if !$s->{quotekeys} and $nk =~ /^[\"\']([A-Za-z_]\w*)[\"\']$/; + $sname = $mname . '{' . $nk . '}'; + $out .= $pad . $ipad . $nk . " => "; + + # temporarily alter apad + $s->{apad} .= (" " x (length($nk) + 4)) if $s->{indent} >= 2; + $out .= $s->_dump($val->{$k}, $sname) . ","; + $s->{apad} = $lpad if $s->{indent} >= 2; + } + if (substr($out, -1) eq ',') { + chop $out; + $out .= $pad . ($s->{xpad} x ($s->{level} - 1)); + } + $out .= ($name =~ /^\%/) ? ')' : '}'; + } + elsif ($realtype eq 'CODE') { + $out .= 'sub { "DUMMY" }'; + carp "Encountered CODE ref, using dummy placeholder" if $s->{purity}; + } + else { + croak "Can\'t handle $realtype type."; + } + + if ($realpack) { # we have a blessed ref + $out .= ', \'' . $realpack . '\'' . ' )'; + $out .= '->' . $s->{toaster} . '()' if $s->{toaster} ne ''; + $s->{apad} = $blesspad; + } + $s->{level}--; + + } + else { # simple scalar + + my $ref = \$_[1]; + # first, catalog the scalar + if ($name ne '') { + ($id) = ("$ref" =~ /\(([^\(]*)\)$/); + if (exists $s->{seen}{$id}) { + if ($s->{seen}{$id}[2]) { + $out = $s->{seen}{$id}[0]; + #warn "[<$out]\n"; + return "\${$out}"; + } + } + else { + #warn "[>\\$name]\n"; + $s->{seen}{$id} = ["\\$name", $ref]; + } + } + if (ref($ref) eq 'GLOB' or "$ref" =~ /=GLOB\([^()]+\)$/) { # glob + my $name = substr($val, 1); + if ($name =~ /^[A-Za-z_][\w:]*$/) { + $name =~ s/^main::/::/; + $sname = $name; + } + else { + $sname = $s->_dump($name, ""); + $sname = '{' . $sname . '}'; + } + if ($s->{purity}) { + my $k; + local ($s->{level}) = 0; + for $k (qw(SCALAR ARRAY HASH)) { + my $gval = *$val{$k}; + next unless defined $gval; + next if $k eq "SCALAR" && ! defined $$gval; # always there + + # _dump can push into @post, so we hold our place using $postlen + my $postlen = scalar @post; + $post[$postlen] = "\*$sname = "; + local ($s->{apad}) = " " x length($post[$postlen]) if $s->{indent} >= 2; + $post[$postlen] .= $s->_dump($gval, "\*$sname\{$k\}"); + } + } + $out .= '*' . $sname; + } + elsif (!defined($val)) { + $out .= "undef"; + } + elsif ($val =~ /^-?[1-9]\d{0,8}$/) { # safe decimal number + $out .= $val; + } + else { # string + if ($s->{useqq}) { + $out .= qquote($val, $s->{useqq}); + } + else { + $val =~ s/([\\\'])/\\$1/g; + $out .= '\'' . $val . '\''; + } + } + } + if ($id) { + # if we made it this far, $id was added to seen list at current + # level, so remove it to get deep copies + if ($s->{deepcopy}) { + delete($s->{seen}{$id}); + } + elsif ($name) { + $s->{seen}{$id}[2] = 1; + } + } + return $out; +} + +# +# non-OO style of earlier version +# +sub Dumper { + return Data::Dumper->Dump([@_]); +} + +# +# same, only calls the XS version +# +sub DumperX { + return Data::Dumper->Dumpxs([@_], []); +} + +sub Dumpf { return Data::Dumper->Dump(@_) } + +sub Dumpp { print Data::Dumper->Dump(@_) } + +# +# reset the "seen" cache +# +sub Reset { + my($s) = shift; + $s->{seen} = {}; + return $s; +} + +sub Indent { + my($s, $v) = @_; + if (defined($v)) { + if ($v == 0) { + $s->{xpad} = ""; + $s->{sep} = ""; + } + else { + $s->{xpad} = " "; + $s->{sep} = "\n"; + } + $s->{indent} = $v; + return $s; + } + else { + return $s->{indent}; + } +} + +sub Pad { + my($s, $v) = @_; + defined($v) ? (($s->{pad} = $v), return $s) : $s->{pad}; +} + +sub Varname { + my($s, $v) = @_; + defined($v) ? (($s->{varname} = $v), return $s) : $s->{varname}; +} + +sub Purity { + my($s, $v) = @_; + defined($v) ? (($s->{purity} = $v), return $s) : $s->{purity}; +} + +sub Useqq { + my($s, $v) = @_; + defined($v) ? (($s->{useqq} = $v), return $s) : $s->{useqq}; +} + +sub Terse { + my($s, $v) = @_; + defined($v) ? (($s->{terse} = $v), return $s) : $s->{terse}; +} + +sub Freezer { + my($s, $v) = @_; + defined($v) ? (($s->{freezer} = $v), return $s) : $s->{freezer}; +} + +sub Toaster { + my($s, $v) = @_; + defined($v) ? (($s->{toaster} = $v), return $s) : $s->{toaster}; +} + +sub Deepcopy { + my($s, $v) = @_; + defined($v) ? (($s->{deepcopy} = $v), return $s) : $s->{deepcopy}; +} + +sub Quotekeys { + my($s, $v) = @_; + defined($v) ? (($s->{quotekeys} = $v), return $s) : $s->{quotekeys}; +} + +sub Bless { + my($s, $v) = @_; + defined($v) ? (($s->{'bless'} = $v), return $s) : $s->{'bless'}; +} + +# used by qquote below +my %esc = ( + "\a" => "\\a", + "\b" => "\\b", + "\t" => "\\t", + "\n" => "\\n", + "\f" => "\\f", + "\r" => "\\r", + "\e" => "\\e", +); + +# put a string value in double quotes +sub qquote { + local($_) = shift; + s/([\\\"\@\$])/\\$1/g; + return qq("$_") unless /[^\040-\176]/; # fast exit + + my $high = shift || ""; + s/([\a\b\t\n\f\r\e])/$esc{$1}/g; + + # no need for 3 digits in escape for these + s/([\0-\037])(?!\d)/'\\'.sprintf('%o',ord($1))/eg; + + s/([\0-\037\177])/'\\'.sprintf('%03o',ord($1))/eg; + if ($high eq "iso8859") { + s/([\200-\240])/'\\'.sprintf('%o',ord($1))/eg; + } elsif ($high eq "utf8") { +# use utf8; +# $str =~ s/([^\040-\176])/sprintf "\\x{%04x}", ord($1)/ge; + } elsif ($high eq "8bit") { + # leave it as it is + } else { + s/([\0-\037\177-\377])/'\\'.sprintf('%03o',ord($1))/eg; + } + return qq("$_"); +} + +1; +__END__ + +=head1 NAME + +Data::Dumper - stringified perl data structures, suitable for both printing and C<eval> + + +=head1 SYNOPSIS + + use Data::Dumper; + + # simple procedural interface + print Dumper($foo, $bar); + + # extended usage with names + print Data::Dumper->Dump([$foo, $bar], [qw(foo *ary)]); + + # configuration variables + { + local $Data::Dump::Purity = 1; + eval Data::Dumper->Dump([$foo, $bar], [qw(foo *ary)]); + } + + # OO usage + $d = Data::Dumper->new([$foo, $bar], [qw(foo *ary)]); + ... + print $d->Dump; + ... + $d->Purity(1)->Terse(1)->Deepcopy(1); + eval $d->Dump; + + +=head1 DESCRIPTION + +Given a list of scalars or reference variables, writes out their contents in +perl syntax. The references can also be objects. The contents of each +variable is output in a single Perl statement. Handles self-referential +structures correctly. + +The return value can be C<eval>ed to get back an identical copy of the +original reference structure. + +Any references that are the same as one of those passed in will be named +C<$VAR>I<n> (where I<n> is a numeric suffix), and other duplicate references +to substructures within C<$VAR>I<n> will be appropriately labeled using arrow +notation. You can specify names for individual values to be dumped if you +use the C<Dump()> method, or you can change the default C<$VAR> prefix to +something else. See C<$Data::Dumper::Varname> and C<$Data::Dumper::Terse> +below. + +The default output of self-referential structures can be C<eval>ed, but the +nested references to C<$VAR>I<n> will be undefined, since a recursive +structure cannot be constructed using one Perl statement. You should set the +C<Purity> flag to 1 to get additional statements that will correctly fill in +these references. + +In the extended usage form, the references to be dumped can be given +user-specified names. If a name begins with a C<*>, the output will +describe the dereferenced type of the supplied reference for hashes and +arrays, and coderefs. Output of names will be avoided where possible if +the C<Terse> flag is set. + +In many cases, methods that are used to set the internal state of the +object will return the object itself, so method calls can be conveniently +chained together. + +Several styles of output are possible, all controlled by setting +the C<Indent> flag. See L<Configuration Variables or Methods> below +for details. + + +=head2 Methods + +=over 4 + +=item I<PACKAGE>->new(I<ARRAYREF [>, I<ARRAYREF]>) + +Returns a newly created C<Data::Dumper> object. The first argument is an +anonymous array of values to be dumped. The optional second argument is an +anonymous array of names for the values. The names need not have a leading +C<$> sign, and must be comprised of alphanumeric characters. You can begin +a name with a C<*> to specify that the dereferenced type must be dumped +instead of the reference itself, for ARRAY and HASH references. + +The prefix specified by C<$Data::Dumper::Varname> will be used with a +numeric suffix if the name for a value is undefined. + +Data::Dumper will catalog all references encountered while dumping the +values. Cross-references (in the form of names of substructures in perl +syntax) will be inserted at all possible points, preserving any structural +interdependencies in the original set of values. Structure traversal is +depth-first, and proceeds in order from the first supplied value to +the last. + +=item I<$OBJ>->Dump I<or> I<PACKAGE>->Dump(I<ARRAYREF [>, I<ARRAYREF]>) + +Returns the stringified form of the values stored in the object (preserving +the order in which they were supplied to C<new>), subject to the +configuration options below. In an array context, it returns a list +of strings corresponding to the supplied values. + +The second form, for convenience, simply calls the C<new> method on its +arguments before dumping the object immediately. + +=item I<$OBJ>->Dumpxs I<or> I<PACKAGE>->Dumpxs(I<ARRAYREF [>, I<ARRAYREF]>) + +This method is available if you were able to compile and install the XSUB +extension to C<Data::Dumper>. It is exactly identical to the C<Dump> method +above, only about 4 to 5 times faster, since it is written entirely in C. + +=item I<$OBJ>->Seen(I<[HASHREF]>) + +Queries or adds to the internal table of already encountered references. +You must use C<Reset> to explicitly clear the table if needed. Such +references are not dumped; instead, their names are inserted wherever they +are encountered subsequently. This is useful especially for properly +dumping subroutine references. + +Expects a anonymous hash of name => value pairs. Same rules apply for names +as in C<new>. If no argument is supplied, will return the "seen" list of +name => value pairs, in an array context. Otherwise, returns the object +itself. + +=item I<$OBJ>->Values(I<[ARRAYREF]>) + +Queries or replaces the internal array of values that will be dumped. +When called without arguments, returns the values. Otherwise, returns the +object itself. + +=item I<$OBJ>->Names(I<[ARRAYREF]>) + +Queries or replaces the internal array of user supplied names for the values +that will be dumped. When called without arguments, returns the names. +Otherwise, returns the object itself. + +=item I<$OBJ>->Reset + +Clears the internal table of "seen" references and returns the object +itself. + +=back + +=head2 Functions + +=over 4 + +=item Dumper(I<LIST>) + +Returns the stringified form of the values in the list, subject to the +configuration options below. The values will be named C<$VAR>I<n> in the +output, where I<n> is a numeric suffix. Will return a list of strings +in an array context. + +=item DumperX(I<LIST>) + +Identical to the C<Dumper()> function above, but this calls the XSUB +implementation. Only available if you were able to compile and install +the XSUB extensions in C<Data::Dumper>. + +=back + +=head2 Configuration Variables or Methods + +Several configuration variables can be used to control the kind of output +generated when using the procedural interface. These variables are usually +C<local>ized in a block so that other parts of the code are not affected by +the change. + +These variables determine the default state of the object created by calling +the C<new> method, but cannot be used to alter the state of the object +thereafter. The equivalent method names should be used instead to query +or set the internal state of the object. + +The method forms return the object itself when called with arguments, +so that they can be chained together nicely. + +=over 4 + +=item $Data::Dumper::Indent I<or> I<$OBJ>->Indent(I<[NEWVAL]>) + +Controls the style of indentation. It can be set to 0, 1, 2 or 3. Style 0 +spews output without any newlines, indentation, or spaces between list +items. It is the most compact format possible that can still be called +valid perl. Style 1 outputs a readable form with newlines but no fancy +indentation (each level in the structure is simply indented by a fixed +amount of whitespace). Style 2 (the default) outputs a very readable form +which takes into account the length of hash keys (so the hash value lines +up). Style 3 is like style 2, but also annotates the elements of arrays +with their index (but the comment is on its own line, so array output +consumes twice the number of lines). Style 2 is the default. + +=item $Data::Dumper::Purity I<or> I<$OBJ>->Purity(I<[NEWVAL]>) + +Controls the degree to which the output can be C<eval>ed to recreate the +supplied reference structures. Setting it to 1 will output additional perl +statements that will correctly recreate nested references. The default is +0. + +=item $Data::Dumper::Pad I<or> I<$OBJ>->Pad(I<[NEWVAL]>) + +Specifies the string that will be prefixed to every line of the output. +Empty string by default. + +=item $Data::Dumper::Varname I<or> I<$OBJ>->Varname(I<[NEWVAL]>) + +Contains the prefix to use for tagging variable names in the output. The +default is "VAR". + +=item $Data::Dumper::Useqq I<or> I<$OBJ>->Useqq(I<[NEWVAL]>) + +When set, enables the use of double quotes for representing string values. +Whitespace other than space will be represented as C<[\n\t\r]>, "unsafe" +characters will be backslashed, and unprintable characters will be output as +quoted octal integers. Since setting this variable imposes a performance +penalty, the default is 0. The C<Dumpxs()> method does not honor this +flag yet. + +=item $Data::Dumper::Terse I<or> I<$OBJ>->Terse(I<[NEWVAL]>) + +When set, Data::Dumper will emit single, non-self-referential values as +atoms/terms rather than statements. This means that the C<$VAR>I<n> names +will be avoided where possible, but be advised that such output may not +always be parseable by C<eval>. + +=item $Data::Dumper::Freezer I<or> $I<OBJ>->Freezer(I<[NEWVAL]>) + +Can be set to a method name, or to an empty string to disable the feature. +Data::Dumper will invoke that method via the object before attempting to +stringify it. This method can alter the contents of the object (if, for +instance, it contains data allocated from C), and even rebless it in a +different package. The client is responsible for making sure the specified +method can be called via the object, and that the object ends up containing +only perl data types after the method has been called. Defaults to an empty +string. + +=item $Data::Dumper::Toaster I<or> $I<OBJ>->Toaster(I<[NEWVAL]>) + +Can be set to a method name, or to an empty string to disable the feature. +Data::Dumper will emit a method call for any objects that are to be dumped +using the syntax C<bless(DATA, CLASS)->METHOD()>. Note that this means that +the method specified will have to perform any modifications required on the +object (like creating new state within it, and/or reblessing it in a +different package) and then return it. The client is responsible for making +sure the method can be called via the object, and that it returns a valid +object. Defaults to an empty string. + +=item $Data::Dumper::Deepcopy I<or> $I<OBJ>->Deepcopy(I<[NEWVAL]>) + +Can be set to a boolean value to enable deep copies of structures. +Cross-referencing will then only be done when absolutely essential +(i.e., to break reference cycles). Default is 0. + +=item $Data::Dumper::Quotekeys I<or> $I<OBJ>->Quotekeys(I<[NEWVAL]>) + +Can be set to a boolean value to control whether hash keys are quoted. +A false value will avoid quoting hash keys when it looks like a simple +string. Default is 1, which will always enclose hash keys in quotes. + +=item $Data::Dumper::Bless I<or> $I<OBJ>->Bless(I<[NEWVAL]>) + +Can be set to a string that specifies an alternative to the C<bless> +builtin operator used to create objects. A function with the specified +name should exist, and should accept the same arguments as the builtin. +Default is C<bless>. + +=back + +=head2 Exports + +=over 4 + +=item Dumper + +=back + +=head1 EXAMPLES + +Run these code snippets to get a quick feel for the behavior of this +module. When you are through with these examples, you may want to +add or change the various configuration variables described above, +to see their behavior. (See the testsuite in the Data::Dumper +distribution for more examples.) + + + use Data::Dumper; + + package Foo; + sub new {bless {'a' => 1, 'b' => sub { return "foo" }}, $_[0]}; + + package Fuz; # a weird REF-REF-SCALAR object + sub new {bless \($_ = \ 'fu\'z'), $_[0]}; + + package main; + $foo = Foo->new; + $fuz = Fuz->new; + $boo = [ 1, [], "abcd", \*foo, + {1 => 'a', 023 => 'b', 0x45 => 'c'}, + \\"p\q\'r", $foo, $fuz]; + + ######## + # simple usage + ######## + + $bar = eval(Dumper($boo)); + print($@) if $@; + print Dumper($boo), Dumper($bar); # pretty print (no array indices) + + $Data::Dumper::Terse = 1; # don't output names where feasible + $Data::Dumper::Indent = 0; # turn off all pretty print + print Dumper($boo), "\n"; + + $Data::Dumper::Indent = 1; # mild pretty print + print Dumper($boo); + + $Data::Dumper::Indent = 3; # pretty print with array indices + print Dumper($boo); + + $Data::Dumper::Useqq = 1; # print strings in double quotes + print Dumper($boo); + + + ######## + # recursive structures + ######## + + @c = ('c'); + $c = \@c; + $b = {}; + $a = [1, $b, $c]; + $b->{a} = $a; + $b->{b} = $a->[1]; + $b->{c} = $a->[2]; + print Data::Dumper->Dump([$a,$b,$c], [qw(a b c)]); + + + $Data::Dumper::Purity = 1; # fill in the holes for eval + print Data::Dumper->Dump([$a, $b], [qw(*a b)]); # print as @a + print Data::Dumper->Dump([$b, $a], [qw(*b a)]); # print as %b + + + $Data::Dumper::Deepcopy = 1; # avoid cross-refs + print Data::Dumper->Dump([$b, $a], [qw(*b a)]); + + + $Data::Dumper::Purity = 0; # avoid cross-refs + print Data::Dumper->Dump([$b, $a], [qw(*b a)]); + + + ######## + # object-oriented usage + ######## + + $d = Data::Dumper->new([$a,$b], [qw(a b)]); + $d->Seen({'*c' => $c}); # stash a ref without printing it + $d->Indent(3); + print $d->Dump; + $d->Reset->Purity(0); # empty the seen cache + print join "----\n", $d->Dump; + + + ######## + # persistence + ######## + + package Foo; + sub new { bless { state => 'awake' }, shift } + sub Freeze { + my $s = shift; + print STDERR "preparing to sleep\n"; + $s->{state} = 'asleep'; + return bless $s, 'Foo::ZZZ'; + } + + package Foo::ZZZ; + sub Thaw { + my $s = shift; + print STDERR "waking up\n"; + $s->{state} = 'awake'; + return bless $s, 'Foo'; + } + + package Foo; + use Data::Dumper; + $a = Foo->new; + $b = Data::Dumper->new([$a], ['c']); + $b->Freezer('Freeze'); + $b->Toaster('Thaw'); + $c = $b->Dump; + print $c; + $d = eval $c; + print Data::Dumper->Dump([$d], ['d']); + + + ######## + # symbol substitution (useful for recreating CODE refs) + ######## + + sub foo { print "foo speaking\n" } + *other = \&foo; + $bar = [ \&other ]; + $d = Data::Dumper->new([\&other,$bar],['*other','bar']); + $d->Seen({ '*foo' => \&foo }); + print $d->Dump; + + +=head1 BUGS + +Due to limitations of Perl subroutine call semantics, you cannot pass an +array or hash. Prepend it with a C<\> to pass its reference instead. This +will be remedied in time, with the arrival of prototypes in later versions +of Perl. For now, you need to use the extended usage form, and prepend the +name with a C<*> to output it as a hash or array. + +C<Data::Dumper> cheats with CODE references. If a code reference is +encountered in the structure being processed, an anonymous subroutine that +contains the string '"DUMMY"' will be inserted in its place, and a warning +will be printed if C<Purity> is set. You can C<eval> the result, but bear +in mind that the anonymous sub that gets created is just a placeholder. +Someday, perl will have a switch to cache-on-demand the string +representation of a compiled piece of code, I hope. If you have prior +knowledge of all the code refs that your data structures are likely +to have, you can use the C<Seen> method to pre-seed the internal reference +table and make the dumped output point to them, instead. See L<EXAMPLES> +above. + +The C<Useqq> flag is not honored by C<Dumpxs()> (it always outputs +strings in single quotes). + +SCALAR objects have the weirdest looking C<bless> workaround. + + +=head1 AUTHOR + +Gurusamy Sarathy gsar@umich.edu + +Copyright (c) 1996-98 Gurusamy Sarathy. All rights reserved. +This program is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + + +=head1 VERSION + +Version 2.10 (31 Oct 1998) + +=head1 SEE ALSO + +perl(1) + +=cut diff --git a/gnu/usr.bin/perl/ext/Data/Dumper/Dumper.xs b/gnu/usr.bin/perl/ext/Data/Dumper/Dumper.xs new file mode 100644 index 00000000000..a3da110453d --- /dev/null +++ b/gnu/usr.bin/perl/ext/Data/Dumper/Dumper.xs @@ -0,0 +1,861 @@ +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +#include "patchlevel.h" + +#if PATCHLEVEL < 5 +# ifndef PL_sv_undef +# define PL_sv_undef sv_undef +# endif +# ifndef ERRSV +# define ERRSV GvSV(errgv) +# endif +# ifndef newSVpvn +# define newSVpvn newSVpv +# endif +#endif + +static I32 num_q _((char *s, STRLEN slen)); +static I32 esc_q _((char *dest, char *src, STRLEN slen)); +static SV *sv_x _((SV *sv, char *str, STRLEN len, I32 n)); +static I32 DD_dump _((SV *val, char *name, STRLEN namelen, SV *retval, + HV *seenhv, AV *postav, I32 *levelp, I32 indent, + SV *pad, SV *xpad, SV *apad, SV *sep, + SV *freezer, SV *toaster, + I32 purity, I32 deepcopy, I32 quotekeys, SV *bless)); + +/* does a string need to be protected? */ +static I32 +needs_quote(register char *s) +{ +TOP: + if (s[0] == ':') { + if (*++s) { + if (*s++ != ':') + return 1; + } + else + return 1; + } + if (isIDFIRST(*s)) { + while (*++s) + if (!isALNUM(*s)) + if (*s == ':') + goto TOP; + else + return 1; + } + else + return 1; + return 0; +} + +/* count the number of "'"s and "\"s in string */ +static I32 +num_q(register char *s, register STRLEN slen) +{ + register I32 ret = 0; + + while (slen > 0) { + if (*s == '\'' || *s == '\\') + ++ret; + ++s; + --slen; + } + return ret; +} + + +/* returns number of chars added to escape "'"s and "\"s in s */ +/* slen number of characters in s will be escaped */ +/* destination must be long enough for additional chars */ +static I32 +esc_q(register char *d, register char *s, register STRLEN slen) +{ + register I32 ret = 0; + + while (slen > 0) { + switch (*s) { + case '\'': + case '\\': + *d = '\\'; + ++d; ++ret; + default: + *d = *s; + ++d; ++s; --slen; + break; + } + } + return ret; +} + +/* append a repeated string to an SV */ +static SV * +sv_x(SV *sv, register char *str, STRLEN len, I32 n) +{ + if (sv == Nullsv) + sv = newSVpvn("", 0); + else + assert(SvTYPE(sv) >= SVt_PV); + + if (n > 0) { + SvGROW(sv, len*n + SvCUR(sv) + 1); + if (len == 1) { + char *start = SvPVX(sv) + SvCUR(sv); + SvCUR(sv) += n; + start[n] = '\0'; + while (n > 0) + start[--n] = str[0]; + } + else + while (n > 0) { + sv_catpvn(sv, str, len); + --n; + } + } + return sv; +} + +/* + * This ought to be split into smaller functions. (it is one long function since + * it exactly parallels the perl version, which was one long thing for + * efficiency raisins.) Ugggh! + */ +static I32 +DD_dump(SV *val, char *name, STRLEN namelen, SV *retval, HV *seenhv, + AV *postav, I32 *levelp, I32 indent, SV *pad, SV *xpad, + SV *apad, SV *sep, SV *freezer, SV *toaster, I32 purity, + I32 deepcopy, I32 quotekeys, SV *bless) +{ + char tmpbuf[128]; + U32 i; + char *c, *r, *realpack, id[128]; + SV **svp; + SV *sv, *ipad, *ival; + SV *blesspad = Nullsv; + AV *seenentry = Nullav; + char *iname; + STRLEN inamelen, idlen = 0; + U32 flags; + U32 realtype; + + if (!val) + return 0; + + flags = SvFLAGS(val); + realtype = SvTYPE(val); + + if (SvGMAGICAL(val)) + mg_get(val); + if (SvROK(val)) { + + if (SvOBJECT(SvRV(val)) && freezer && + SvPOK(freezer) && SvCUR(freezer)) + { + dSP; ENTER; SAVETMPS; PUSHMARK(sp); + XPUSHs(val); PUTBACK; + i = perl_call_method(SvPVX(freezer), G_EVAL|G_SCALAR); + SPAGAIN; + if (SvTRUE(ERRSV)) + warn("WARNING(Freezer method call failed): %s", + SvPVX(ERRSV)); + else if (i) + val = newSVsv(POPs); + PUTBACK; FREETMPS; LEAVE; + if (i) + (void)sv_2mortal(val); + } + + ival = SvRV(val); + flags = SvFLAGS(ival); + realtype = SvTYPE(ival); + (void) sprintf(id, "0x%lx", (unsigned long)ival); + idlen = strlen(id); + if (SvOBJECT(ival)) + realpack = HvNAME(SvSTASH(ival)); + else + realpack = Nullch; + + /* if it has a name, we need to either look it up, or keep a tab + * on it so we know when we hit it later + */ + if (namelen) { + if ((svp = hv_fetch(seenhv, id, idlen, FALSE)) + && (sv = *svp) && SvROK(sv) && (seenentry = (AV*)SvRV(sv))) + { + SV *othername; + if ((svp = av_fetch(seenentry, 0, FALSE)) + && (othername = *svp)) + { + if (purity && *levelp > 0) { + SV *postentry; + + if (realtype == SVt_PVHV) + sv_catpvn(retval, "{}", 2); + else if (realtype == SVt_PVAV) + sv_catpvn(retval, "[]", 2); + else + sv_catpvn(retval, "''", 2); + postentry = newSVpvn(name, namelen); + sv_catpvn(postentry, " = ", 3); + sv_catsv(postentry, othername); + av_push(postav, postentry); + } + else { + if (name[0] == '@' || name[0] == '%') { + if ((SvPVX(othername))[0] == '\\' && + (SvPVX(othername))[1] == name[0]) { + sv_catpvn(retval, SvPVX(othername)+1, + SvCUR(othername)-1); + } + else { + sv_catpvn(retval, name, 1); + sv_catpvn(retval, "{", 1); + sv_catsv(retval, othername); + sv_catpvn(retval, "}", 1); + } + } + else + sv_catsv(retval, othername); + } + return 1; + } + else { + warn("ref name not found for %s", id); + return 0; + } + } + else { /* store our name and continue */ + SV *namesv; + if (name[0] == '@' || name[0] == '%') { + namesv = newSVpvn("\\", 1); + sv_catpvn(namesv, name, namelen); + } + else if (realtype == SVt_PVCV && name[0] == '*') { + namesv = newSVpvn("\\", 2); + sv_catpvn(namesv, name, namelen); + (SvPVX(namesv))[1] = '&'; + } + else + namesv = newSVpvn(name, namelen); + seenentry = newAV(); + av_push(seenentry, namesv); + (void)SvREFCNT_inc(val); + av_push(seenentry, val); + (void)hv_store(seenhv, id, strlen(id), + newRV((SV*)seenentry), 0); + SvREFCNT_dec(seenentry); + } + } + + (*levelp)++; + ipad = sv_x(Nullsv, SvPVX(xpad), SvCUR(xpad), *levelp); + + if (realpack) { /* we have a blessed ref */ + STRLEN blesslen; + char *blessstr = SvPV(bless, blesslen); + sv_catpvn(retval, blessstr, blesslen); + sv_catpvn(retval, "( ", 2); + if (indent >= 2) { + blesspad = apad; + apad = newSVsv(apad); + sv_x(apad, " ", 1, blesslen+2); + } + } + + if (realtype <= SVt_PVBM) { /* scalar ref */ + SV *namesv = newSVpvn("${", 2); + sv_catpvn(namesv, name, namelen); + sv_catpvn(namesv, "}", 1); + if (realpack) { /* blessed */ + sv_catpvn(retval, "do{\\(my $o = ", 13); + DD_dump(ival, SvPVX(namesv), SvCUR(namesv), retval, seenhv, + postav, levelp, indent, pad, xpad, apad, sep, + freezer, toaster, purity, deepcopy, quotekeys, bless); + sv_catpvn(retval, ")}", 2); + } /* plain */ + else { + sv_catpvn(retval, "\\", 1); + DD_dump(ival, SvPVX(namesv), SvCUR(namesv), retval, seenhv, + postav, levelp, indent, pad, xpad, apad, sep, + freezer, toaster, purity, deepcopy, quotekeys, bless); + } + SvREFCNT_dec(namesv); + } + else if (realtype == SVt_PVGV) { /* glob ref */ + SV *namesv = newSVpvn("*{", 2); + sv_catpvn(namesv, name, namelen); + sv_catpvn(namesv, "}", 1); + sv_catpvn(retval, "\\", 1); + DD_dump(ival, SvPVX(namesv), SvCUR(namesv), retval, seenhv, + postav, levelp, indent, pad, xpad, apad, sep, + freezer, toaster, purity, deepcopy, quotekeys, bless); + SvREFCNT_dec(namesv); + } + else if (realtype == SVt_PVAV) { + SV *totpad; + I32 ix = 0; + I32 ixmax = av_len((AV *)ival); + + SV *ixsv = newSViv(0); + /* allowing for a 24 char wide array index */ + New(0, iname, namelen+28, char); + (void)strcpy(iname, name); + inamelen = namelen; + if (name[0] == '@') { + sv_catpvn(retval, "(", 1); + iname[0] = '$'; + } + else { + sv_catpvn(retval, "[", 1); + /* omit "->" in $foo{bar}->[0], but not in ${$foo}->[0] */ + /*if (namelen > 0 + && name[namelen-1] != ']' && name[namelen-1] != '}' + && (namelen < 4 || (name[1] != '{' && name[2] != '{')))*/ + if ((namelen > 0 + && name[namelen-1] != ']' && name[namelen-1] != '}') + || (namelen > 4 + && (name[1] == '{' + || (name[0] == '\\' && name[2] == '{')))) + { + iname[inamelen++] = '-'; iname[inamelen++] = '>'; + iname[inamelen] = '\0'; + } + } + if (iname[0] == '*' && iname[inamelen-1] == '}' && inamelen >= 8 && + (instr(iname+inamelen-8, "{SCALAR}") || + instr(iname+inamelen-7, "{ARRAY}") || + instr(iname+inamelen-6, "{HASH}"))) { + iname[inamelen++] = '-'; iname[inamelen++] = '>'; + } + iname[inamelen++] = '['; iname[inamelen] = '\0'; + totpad = newSVsv(sep); + sv_catsv(totpad, pad); + sv_catsv(totpad, apad); + + for (ix = 0; ix <= ixmax; ++ix) { + STRLEN ilen; + SV *elem; + svp = av_fetch((AV*)ival, ix, FALSE); + if (svp) + elem = *svp; + else + elem = &PL_sv_undef; + + ilen = inamelen; + sv_setiv(ixsv, ix); + (void) sprintf(iname+ilen, "%ld", ix); + ilen = strlen(iname); + iname[ilen++] = ']'; iname[ilen] = '\0'; + if (indent >= 3) { + sv_catsv(retval, totpad); + sv_catsv(retval, ipad); + sv_catpvn(retval, "#", 1); + sv_catsv(retval, ixsv); + } + sv_catsv(retval, totpad); + sv_catsv(retval, ipad); + DD_dump(elem, iname, ilen, retval, seenhv, postav, + levelp, indent, pad, xpad, apad, sep, + freezer, toaster, purity, deepcopy, quotekeys, bless); + if (ix < ixmax) + sv_catpvn(retval, ",", 1); + } + if (ixmax >= 0) { + SV *opad = sv_x(Nullsv, SvPVX(xpad), SvCUR(xpad), (*levelp)-1); + sv_catsv(retval, totpad); + sv_catsv(retval, opad); + SvREFCNT_dec(opad); + } + if (name[0] == '@') + sv_catpvn(retval, ")", 1); + else + sv_catpvn(retval, "]", 1); + SvREFCNT_dec(ixsv); + SvREFCNT_dec(totpad); + Safefree(iname); + } + else if (realtype == SVt_PVHV) { + SV *totpad, *newapad; + SV *iname, *sname; + HE *entry; + char *key; + I32 klen; + SV *hval; + + iname = newSVpvn(name, namelen); + if (name[0] == '%') { + sv_catpvn(retval, "(", 1); + (SvPVX(iname))[0] = '$'; + } + else { + sv_catpvn(retval, "{", 1); + /* omit "->" in $foo[0]->{bar}, but not in ${$foo}->{bar} */ + if ((namelen > 0 + && name[namelen-1] != ']' && name[namelen-1] != '}') + || (namelen > 4 + && (name[1] == '{' + || (name[0] == '\\' && name[2] == '{')))) + { + sv_catpvn(iname, "->", 2); + } + } + if (name[0] == '*' && name[namelen-1] == '}' && namelen >= 8 && + (instr(name+namelen-8, "{SCALAR}") || + instr(name+namelen-7, "{ARRAY}") || + instr(name+namelen-6, "{HASH}"))) { + sv_catpvn(iname, "->", 2); + } + sv_catpvn(iname, "{", 1); + totpad = newSVsv(sep); + sv_catsv(totpad, pad); + sv_catsv(totpad, apad); + + (void)hv_iterinit((HV*)ival); + i = 0; + while ((entry = hv_iternext((HV*)ival))) { + char *nkey; + I32 nticks = 0; + + if (i) + sv_catpvn(retval, ",", 1); + i++; + key = hv_iterkey(entry, &klen); + hval = hv_iterval((HV*)ival, entry); + + if (quotekeys || needs_quote(key)) { + nticks = num_q(key, klen); + New(0, nkey, klen+nticks+3, char); + nkey[0] = '\''; + if (nticks) + klen += esc_q(nkey+1, key, klen); + else + (void)Copy(key, nkey+1, klen, char); + nkey[++klen] = '\''; + nkey[++klen] = '\0'; + } + else { + New(0, nkey, klen, char); + (void)Copy(key, nkey, klen, char); + } + + sname = newSVsv(iname); + sv_catpvn(sname, nkey, klen); + sv_catpvn(sname, "}", 1); + + sv_catsv(retval, totpad); + sv_catsv(retval, ipad); + sv_catpvn(retval, nkey, klen); + sv_catpvn(retval, " => ", 4); + if (indent >= 2) { + char *extra; + I32 elen = 0; + newapad = newSVsv(apad); + New(0, extra, klen+4+1, char); + while (elen < (klen+4)) + extra[elen++] = ' '; + extra[elen] = '\0'; + sv_catpvn(newapad, extra, elen); + Safefree(extra); + } + else + newapad = apad; + + DD_dump(hval, SvPVX(sname), SvCUR(sname), retval, seenhv, + postav, levelp, indent, pad, xpad, newapad, sep, + freezer, toaster, purity, deepcopy, quotekeys, bless); + SvREFCNT_dec(sname); + Safefree(nkey); + if (indent >= 2) + SvREFCNT_dec(newapad); + } + if (i) { + SV *opad = sv_x(Nullsv, SvPVX(xpad), SvCUR(xpad), *levelp-1); + sv_catsv(retval, totpad); + sv_catsv(retval, opad); + SvREFCNT_dec(opad); + } + if (name[0] == '%') + sv_catpvn(retval, ")", 1); + else + sv_catpvn(retval, "}", 1); + SvREFCNT_dec(iname); + SvREFCNT_dec(totpad); + } + else if (realtype == SVt_PVCV) { + sv_catpvn(retval, "sub { \"DUMMY\" }", 15); + if (purity) + warn("Encountered CODE ref, using dummy placeholder"); + } + else { + warn("cannot handle ref type %ld", realtype); + } + + if (realpack) { /* free blessed allocs */ + if (indent >= 2) { + SvREFCNT_dec(apad); + apad = blesspad; + } + sv_catpvn(retval, ", '", 3); + sv_catpvn(retval, realpack, strlen(realpack)); + sv_catpvn(retval, "' )", 3); + if (toaster && SvPOK(toaster) && SvCUR(toaster)) { + sv_catpvn(retval, "->", 2); + sv_catsv(retval, toaster); + sv_catpvn(retval, "()", 2); + } + } + SvREFCNT_dec(ipad); + (*levelp)--; + } + else { + STRLEN i; + + if (namelen) { + (void) sprintf(id, "0x%lx", (unsigned long)val); + if ((svp = hv_fetch(seenhv, id, (idlen = strlen(id)), FALSE)) && + (sv = *svp) && SvROK(sv) && + (seenentry = (AV*)SvRV(sv))) + { + SV *othername; + if ((svp = av_fetch(seenentry, 0, FALSE)) && (othername = *svp) + && (svp = av_fetch(seenentry, 2, FALSE)) && *svp && SvIV(*svp) > 0) + { + sv_catpvn(retval, "${", 2); + sv_catsv(retval, othername); + sv_catpvn(retval, "}", 1); + return 1; + } + } + else { + SV *namesv; + namesv = newSVpvn("\\", 1); + sv_catpvn(namesv, name, namelen); + seenentry = newAV(); + av_push(seenentry, namesv); + av_push(seenentry, newRV(val)); + (void)hv_store(seenhv, id, strlen(id), newRV((SV*)seenentry), 0); + SvREFCNT_dec(seenentry); + } + } + + if (SvIOK(val)) { + STRLEN len; + i = SvIV(val); + (void) sprintf(tmpbuf, "%d", i); + len = strlen(tmpbuf); + sv_catpvn(retval, tmpbuf, len); + } + else if (realtype == SVt_PVGV) {/* GLOBs can end up with scribbly names */ + c = SvPV(val, i); + ++c; --i; /* just get the name */ + if (i >= 6 && strncmp(c, "main::", 6) == 0) { + c += 4; + i -= 4; + } + if (needs_quote(c)) { + sv_grow(retval, SvCUR(retval)+6+2*i); + r = SvPVX(retval)+SvCUR(retval); + r[0] = '*'; r[1] = '{'; r[2] = '\''; + i += esc_q(r+3, c, i); + i += 3; + r[i++] = '\''; r[i++] = '}'; + r[i] = '\0'; + } + else { + sv_grow(retval, SvCUR(retval)+i+2); + r = SvPVX(retval)+SvCUR(retval); + r[0] = '*'; strcpy(r+1, c); + i++; + } + SvCUR_set(retval, SvCUR(retval)+i); + + if (purity) { + static char *entries[] = { "{SCALAR}", "{ARRAY}", "{HASH}" }; + static STRLEN sizes[] = { 8, 7, 6 }; + SV *e; + SV *nname = newSVpvn("", 0); + SV *newapad = newSVpvn("", 0); + GV *gv = (GV*)val; + I32 j; + + for (j=0; j<3; j++) { + e = ((j == 0) ? GvSV(gv) : (j == 1) ? (SV*)GvAV(gv) : (SV*)GvHV(gv)); + if (!e) + continue; + if (j == 0 && !SvOK(e)) + continue; + + { + I32 nlevel = 0; + SV *postentry = newSVpvn(r,i); + + sv_setsv(nname, postentry); + sv_catpvn(nname, entries[j], sizes[j]); + sv_catpvn(postentry, " = ", 3); + av_push(postav, postentry); + e = newRV(e); + + SvCUR(newapad) = 0; + if (indent >= 2) + (void)sv_x(newapad, " ", 1, SvCUR(postentry)); + + DD_dump(e, SvPVX(nname), SvCUR(nname), postentry, + seenhv, postav, &nlevel, indent, pad, xpad, + newapad, sep, freezer, toaster, purity, + deepcopy, quotekeys, bless); + SvREFCNT_dec(e); + } + } + + SvREFCNT_dec(newapad); + SvREFCNT_dec(nname); + } + } + else if (val == &PL_sv_undef || !SvOK(val)) { + sv_catpvn(retval, "undef", 5); + } + else { + c = SvPV(val, i); + sv_grow(retval, SvCUR(retval)+3+2*i); + r = SvPVX(retval)+SvCUR(retval); + r[0] = '\''; + i += esc_q(r+1, c, i); + ++i; + r[i++] = '\''; + r[i] = '\0'; + SvCUR_set(retval, SvCUR(retval)+i); + } + } + + if (idlen) { + if (deepcopy) + (void)hv_delete(seenhv, id, idlen, G_DISCARD); + else if (namelen && seenentry) { + SV *mark = *av_fetch(seenentry, 2, TRUE); + sv_setiv(mark,1); + } + } + return 1; +} + + +MODULE = Data::Dumper PACKAGE = Data::Dumper PREFIX = Data_Dumper_ + +# +# This is the exact equivalent of Dump. Well, almost. The things that are +# different as of now (due to Laziness): +# * doesnt do double-quotes yet. +# + +void +Data_Dumper_Dumpxs(href, ...) + SV *href; + PROTOTYPE: $;$$ + PPCODE: + { + HV *hv; + SV *retval, *valstr; + HV *seenhv = Nullhv; + AV *postav, *todumpav, *namesav; + I32 level = 0; + I32 indent, terse, useqq, i, imax, postlen; + SV **svp; + SV *val, *name, *pad, *xpad, *apad, *sep, *tmp, *varname; + SV *freezer, *toaster, *bless; + I32 purity, deepcopy, quotekeys; + char tmpbuf[1024]; + I32 gimme = GIMME; + + if (!SvROK(href)) { /* call new to get an object first */ + SV *valarray; + SV *namearray; + + if (items == 3) { + valarray = ST(1); + namearray = ST(2); + } + else + croak("Usage: Data::Dumper::Dumpxs(PACKAGE, VAL_ARY_REF, NAME_ARY_REF)"); + + ENTER; + SAVETMPS; + + PUSHMARK(sp); + XPUSHs(href); + XPUSHs(sv_2mortal(newSVsv(valarray))); + XPUSHs(sv_2mortal(newSVsv(namearray))); + PUTBACK; + i = perl_call_method("new", G_SCALAR); + SPAGAIN; + if (i) + href = newSVsv(POPs); + + PUTBACK; + FREETMPS; + LEAVE; + if (i) + (void)sv_2mortal(href); + } + + todumpav = namesav = Nullav; + seenhv = Nullhv; + val = pad = xpad = apad = sep = tmp = varname + = freezer = toaster = bless = &PL_sv_undef; + name = sv_newmortal(); + indent = 2; + terse = useqq = purity = deepcopy = 0; + quotekeys = 1; + + retval = newSVpvn("", 0); + if (SvROK(href) + && (hv = (HV*)SvRV((SV*)href)) + && SvTYPE(hv) == SVt_PVHV) { + + if ((svp = hv_fetch(hv, "seen", 4, FALSE)) && SvROK(*svp)) + seenhv = (HV*)SvRV(*svp); + if ((svp = hv_fetch(hv, "todump", 6, FALSE)) && SvROK(*svp)) + todumpav = (AV*)SvRV(*svp); + if ((svp = hv_fetch(hv, "names", 5, FALSE)) && SvROK(*svp)) + namesav = (AV*)SvRV(*svp); + if ((svp = hv_fetch(hv, "indent", 6, FALSE))) + indent = SvIV(*svp); + if ((svp = hv_fetch(hv, "purity", 6, FALSE))) + purity = SvIV(*svp); + if ((svp = hv_fetch(hv, "terse", 5, FALSE))) + terse = SvTRUE(*svp); + if ((svp = hv_fetch(hv, "useqq", 5, FALSE))) + useqq = SvTRUE(*svp); + if ((svp = hv_fetch(hv, "pad", 3, FALSE))) + pad = *svp; + if ((svp = hv_fetch(hv, "xpad", 4, FALSE))) + xpad = *svp; + if ((svp = hv_fetch(hv, "apad", 4, FALSE))) + apad = *svp; + if ((svp = hv_fetch(hv, "sep", 3, FALSE))) + sep = *svp; + if ((svp = hv_fetch(hv, "varname", 7, FALSE))) + varname = *svp; + if ((svp = hv_fetch(hv, "freezer", 7, FALSE))) + freezer = *svp; + if ((svp = hv_fetch(hv, "toaster", 7, FALSE))) + toaster = *svp; + if ((svp = hv_fetch(hv, "deepcopy", 8, FALSE))) + deepcopy = SvTRUE(*svp); + if ((svp = hv_fetch(hv, "quotekeys", 9, FALSE))) + quotekeys = SvTRUE(*svp); + if ((svp = hv_fetch(hv, "bless", 5, FALSE))) + bless = *svp; + postav = newAV(); + + if (todumpav) + imax = av_len(todumpav); + else + imax = -1; + valstr = newSVpvn("",0); + for (i = 0; i <= imax; ++i) { + SV *newapad; + + av_clear(postav); + if ((svp = av_fetch(todumpav, i, FALSE))) + val = *svp; + else + val = &PL_sv_undef; + if ((svp = av_fetch(namesav, i, TRUE))) + sv_setsv(name, *svp); + else + SvOK_off(name); + + if (SvOK(name)) { + if ((SvPVX(name))[0] == '*') { + if (SvROK(val)) { + switch (SvTYPE(SvRV(val))) { + case SVt_PVAV: + (SvPVX(name))[0] = '@'; + break; + case SVt_PVHV: + (SvPVX(name))[0] = '%'; + break; + case SVt_PVCV: + (SvPVX(name))[0] = '*'; + break; + default: + (SvPVX(name))[0] = '$'; + break; + } + } + else + (SvPVX(name))[0] = '$'; + } + else if ((SvPVX(name))[0] != '$') + sv_insert(name, 0, 0, "$", 1); + } + else { + STRLEN nchars = 0; + sv_setpvn(name, "$", 1); + sv_catsv(name, varname); + (void) sprintf(tmpbuf, "%ld", i+1); + nchars = strlen(tmpbuf); + sv_catpvn(name, tmpbuf, nchars); + } + + if (indent >= 2) { + SV *tmpsv = sv_x(Nullsv, " ", 1, SvCUR(name)+3); + newapad = newSVsv(apad); + sv_catsv(newapad, tmpsv); + SvREFCNT_dec(tmpsv); + } + else + newapad = apad; + + DD_dump(val, SvPVX(name), SvCUR(name), valstr, seenhv, + postav, &level, indent, pad, xpad, newapad, sep, + freezer, toaster, purity, deepcopy, quotekeys, + bless); + + if (indent >= 2) + SvREFCNT_dec(newapad); + + postlen = av_len(postav); + if (postlen >= 0 || !terse) { + sv_insert(valstr, 0, 0, " = ", 3); + sv_insert(valstr, 0, 0, SvPVX(name), SvCUR(name)); + sv_catpvn(valstr, ";", 1); + } + sv_catsv(retval, pad); + sv_catsv(retval, valstr); + sv_catsv(retval, sep); + if (postlen >= 0) { + I32 i; + sv_catsv(retval, pad); + for (i = 0; i <= postlen; ++i) { + SV *elem; + svp = av_fetch(postav, i, FALSE); + if (svp && (elem = *svp)) { + sv_catsv(retval, elem); + if (i < postlen) { + sv_catpvn(retval, ";", 1); + sv_catsv(retval, sep); + sv_catsv(retval, pad); + } + } + } + sv_catpvn(retval, ";", 1); + sv_catsv(retval, sep); + } + sv_setpvn(valstr, "", 0); + if (gimme == G_ARRAY) { + XPUSHs(sv_2mortal(retval)); + if (i < imax) /* not the last time thro ? */ + retval = newSVpvn("",0); + } + } + SvREFCNT_dec(postav); + SvREFCNT_dec(valstr); + } + else + croak("Call to new() method failed to return HASH ref"); + if (gimme == G_SCALAR) + XPUSHs(sv_2mortal(retval)); + } diff --git a/gnu/usr.bin/perl/ext/Data/Dumper/Makefile.PL b/gnu/usr.bin/perl/ext/Data/Dumper/Makefile.PL new file mode 100644 index 00000000000..12930c5c398 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Data/Dumper/Makefile.PL @@ -0,0 +1,11 @@ +use ExtUtils::MakeMaker; +WriteMakefile( + NAME => "Data::Dumper", + VERSION_FROM => 'Dumper.pm', + 'dist' => { + COMPRESS => 'gzip -9f', + SUFFIX => 'gz', + DIST_DEFAULT => 'all tardist', + }, + MAN3PODS => {}, +); diff --git a/gnu/usr.bin/perl/ext/Data/Dumper/Todo b/gnu/usr.bin/perl/ext/Data/Dumper/Todo new file mode 100644 index 00000000000..7dcd40b8e3e --- /dev/null +++ b/gnu/usr.bin/perl/ext/Data/Dumper/Todo @@ -0,0 +1,34 @@ +=head1 NAME + +TODO - seeds germane, yet not germinated + +=head1 DESCRIPTION + +The following functionality will be supported in the next few releases. + +=over 4 + +=item $Data::Dumper::Maxdepth I<or> $I<OBJ>->Maxdepth(I<NEWVAL>) + +Depth beyond which we don't venture into a structure. Has no effect when +C<Data::Dumper::Purity> is set. (useful in debugger when we often don't +want to see more than enough). + +=item $Data::Dumper::Expdepth I<or> $I<OBJ>->Expdepth(I<NEWVAL>) + +Dump contents explicitly up to a certain depth and then use names for +cross-referencing identical references. (useful in debugger, in situations +where we don't care so much for cross-references). + +=item Make C<Dumpxs()> honor C<$Useqq> + +=item Fix formatting when Terse is set and Indent >= 2 + +=item Output space after '\' (ref constructor) for high enough Indent + +=item Implement redesign that allows various backends (Perl, Lisp, +some-binary-data-format, graph-description-languages, etc.) + +=item Dump traversal in breadth-first order + +=back diff --git a/gnu/usr.bin/perl/ext/DynaLoader/DynaLoader_pm.PL b/gnu/usr.bin/perl/ext/DynaLoader/DynaLoader_pm.PL new file mode 100644 index 00000000000..cf7d7085bc8 --- /dev/null +++ b/gnu/usr.bin/perl/ext/DynaLoader/DynaLoader_pm.PL @@ -0,0 +1,730 @@ + +use Config; + +sub to_string { + my ($value) = @_; + $value =~ s/\\/\\\\'/g; + $value =~ s/'/\\'/g; + return "'$value'"; +} + +unlink "DynaLoader.pm" if -f "DynaLoader.pm"; +open OUT, ">DynaLoader.pm" or die $!; +print OUT <<'EOT'; + +# Generated from DynaLoader.pm.PL (resolved %Config::Config values) + +package DynaLoader; + +# And Gandalf said: 'Many folk like to know beforehand what is to +# be set on the table; but those who have laboured to prepare the +# feast like to keep their secret; for wonder makes the words of +# praise louder.' + +# (Quote from Tolkien sugested by Anno Siegel.) +# +# See pod text at end of file for documentation. +# See also ext/DynaLoader/README in source tree for other information. +# +# Tim.Bunce@ig.co.uk, August 1994 + +$VERSION = $VERSION = "1.03"; # avoid typo warning + +require AutoLoader; +*AUTOLOAD = \&AutoLoader::AUTOLOAD; + +# The following require can't be removed during maintenance +# releases, sadly, because of the risk of buggy code that does +# require Carp; Carp::croak "..."; without brackets dying +# if Carp hasn't been loaded in earlier compile time. :-( +# We'll let those bugs get found on the development track. +require Carp if $] < 5.00450; + + +# enable debug/trace messages from DynaLoader perl code +$dl_debug = $ENV{PERL_DL_DEBUG} || 0 unless defined $dl_debug; + +# +# Flags to alter dl_load_file behaviour. Assigned bits: +# 0x01 make symbols available for linking later dl_load_file's. +# (only known to work on Solaris 2 using dlopen(RTLD_GLOBAL)) +# (ignored under VMS; effect is built-in to image linking) +# +# This is called as a class method $module->dl_load_flags. The +# definition here will be inherited and result on "default" loading +# behaviour unless a sub-class of DynaLoader defines its own version. +# + +sub dl_load_flags { 0x00 } + +# ($dl_dlext, $dlsrc) +# = @Config::Config{'dlext', 'dlsrc'}; +EOT + +print OUT " (\$dl_dlext, \$dlsrc) = (", + to_string($Config::Config{'dlext'}), ",", + to_string($Config::Config{'dlsrc'}), ")\n;" ; + +print OUT <<'EOT'; + +# Some systems need special handling to expand file specifications +# (VMS support by Charles Bailey <bailey@HMIVAX.HUMGEN.UPENN.EDU>) +# See dl_expandspec() for more details. Should be harmless but +# inefficient to define on systems that don't need it. +$do_expand = $Is_VMS = $^O eq 'VMS'; + +@dl_require_symbols = (); # names of symbols we need +@dl_resolve_using = (); # names of files to link with +@dl_library_path = (); # path to look for files +@dl_librefs = (); # things we have loaded +@dl_modules = (); # Modules we have loaded + +# This is a fix to support DLD's unfortunate desire to relink -lc +@dl_resolve_using = dl_findfile('-lc') if $dlsrc eq "dl_dld.xs"; + +# Initialise @dl_library_path with the 'standard' library path +# for this platform as determined by Configure + +# push(@dl_library_path, split(' ', $Config::Config{'libpth'}); +EOT + +print OUT "push(\@dl_library_path, split(' ', ", + to_string($Config::Config{'libpth'}), "));\n"; + +print OUT <<'EOT'; + +# Add to @dl_library_path any extra directories we can gather from +# environment variables. So far LD_LIBRARY_PATH is the only known +# variable used for this purpose. Others may be added later. +push(@dl_library_path, split(/:/, $ENV{LD_LIBRARY_PATH})) + if $ENV{LD_LIBRARY_PATH}; + + +# No prizes for guessing why we don't say 'bootstrap DynaLoader;' here. +boot_DynaLoader('DynaLoader') if defined(&boot_DynaLoader) && + !defined(&dl_load_file); + + +if ($dl_debug) { + print STDERR "DynaLoader.pm loaded (@INC, @dl_library_path)\n"; + print STDERR "DynaLoader not linked into this perl\n" + unless defined(&boot_DynaLoader); +} + +1; # End of main code + + +sub croak { require Carp; Carp::croak(@_) } + +# The bootstrap function cannot be autoloaded (without complications) +# so we define it here: + +sub bootstrap { + # use local vars to enable $module.bs script to edit values + local(@args) = @_; + local($module) = $args[0]; + local(@dirs, $file); + + unless ($module) { + require Carp; + Carp::confess("Usage: DynaLoader::bootstrap(module)"); + } + + # A common error on platforms which don't support dynamic loading. + # Since it's fatal and potentially confusing we give a detailed message. + croak("Can't load module $module, dynamic loading not available in this perl.\n". + " (You may need to build a new perl executable which either supports\n". + " dynamic loading or has the $module module statically linked into it.)\n") + unless defined(&dl_load_file); + + my @modparts = split(/::/,$module); + my $modfname = $modparts[-1]; + + # Some systems have restrictions on files names for DLL's etc. + # mod2fname returns appropriate file base name (typically truncated) + # It may also edit @modparts if required. + $modfname = &mod2fname(\@modparts) if defined &mod2fname; + + my $modpname = join('/',@modparts); + + print STDERR "DynaLoader::bootstrap for $module ", + "(auto/$modpname/$modfname.$dl_dlext)\n" if $dl_debug; + + foreach (@INC) { + chop($_ = VMS::Filespec::unixpath($_)) if $Is_VMS; + my $dir = "$_/auto/$modpname"; + next unless -d $dir; # skip over uninteresting directories + + # check for common cases to avoid autoload of dl_findfile + my $try = "$dir/$modfname.$dl_dlext"; + last if $file = ($do_expand) ? dl_expandspec($try) : (-f $try && $try); + + # no luck here, save dir for possible later dl_findfile search + push @dirs, $dir; + } + # last resort, let dl_findfile have a go in all known locations + $file = dl_findfile(map("-L$_",@dirs,@INC), $modfname) unless $file; + + croak("Can't locate loadable object for module $module in \@INC (\@INC contains: @INC)") + unless $file; # wording similar to error from 'require' + + my $bootname = "boot_$module"; + $bootname =~ s/\W/_/g; + @dl_require_symbols = ($bootname); + + # Execute optional '.bootstrap' perl script for this module. + # The .bs file can be used to configure @dl_resolve_using etc to + # match the needs of the individual module on this architecture. + my $bs = $file; + $bs =~ s/(\.\w+)?$/\.bs/; # look for .bs 'beside' the library + if (-s $bs) { # only read file if it's not empty + print STDERR "BS: $bs ($^O, $dlsrc)\n" if $dl_debug; + eval { do $bs; }; + warn "$bs: $@\n" if $@; + } + + # Many dynamic extension loading problems will appear to come from + # this section of code: XYZ failed at line 123 of DynaLoader.pm. + # Often these errors are actually occurring in the initialisation + # C code of the extension XS file. Perl reports the error as being + # in this perl code simply because this was the last perl code + # it executed. + + my $libref = dl_load_file($file, $module->dl_load_flags) or + croak("Can't load '$file' for module $module: ".dl_error()."\n"); + + push(@dl_librefs,$libref); # record loaded object + + my @unresolved = dl_undef_symbols(); + if (@unresolved) { + require Carp; + Carp::carp("Undefined symbols present after loading $file: @unresolved\n"); + } + + my $boot_symbol_ref = dl_find_symbol($libref, $bootname) or + croak("Can't find '$bootname' symbol in $file\n"); + + my $xs = dl_install_xsub("${module}::bootstrap", $boot_symbol_ref, $file); + + push(@dl_modules, $module); # record loaded module + + # See comment block above + &$xs(@args); +} + + +#sub _check_file { # private utility to handle dl_expandspec vs -f tests +# my($file) = @_; +# return $file if (!$do_expand && -f $file); # the common case +# return $file if ( $do_expand && ($file=dl_expandspec($file))); +# return undef; +#} + + +# Let autosplit and the autoloader deal with these functions: +__END__ + + +sub dl_findfile { + # Read ext/DynaLoader/DynaLoader.doc for detailed information. + # This function does not automatically consider the architecture + # or the perl library auto directories. + my (@args) = @_; + my (@dirs, $dir); # which directories to search + my (@found); # full paths to real files we have found +EOT + +print OUT ' my $dl_ext= ' . to_string($Config::Config{'dlext'}) . + "; # \$Config::Config{'dlext'} suffix for perl extensions\n"; +print OUT ' my $dl_so = ' . to_string($Config::Config{'so'}) . + "; # \$Config::Config{'so'} suffix for shared libraries\n"; + +print OUT <<'EOT'; + + print STDERR "dl_findfile(@args)\n" if $dl_debug; + + # accumulate directories but process files as they appear + arg: foreach(@args) { + # Special fast case: full filepath requires no search + if ($Is_VMS && m%[:>/\]]% && -f $_) { + push(@found,dl_expandspec(VMS::Filespec::vmsify($_))); + last arg unless wantarray; + next; + } + elsif (m:/: && -f $_ && !$do_expand) { + push(@found,$_); + last arg unless wantarray; + next; + } + + # Deal with directories first: + # Using a -L prefix is the preferred option (faster and more robust) + if (m:^-L:) { s/^-L//; push(@dirs, $_); next; } + + # Otherwise we try to try to spot directories by a heuristic + # (this is a more complicated issue than it first appears) + if (m:/: && -d $_) { push(@dirs, $_); next; } + + # VMS: we may be using native VMS directry syntax instead of + # Unix emulation, so check this as well + if ($Is_VMS && /[:>\]]/ && -d $_) { push(@dirs, $_); next; } + + # Only files should get this far... + my(@names, $name); # what filenames to look for + if (m:-l: ) { # convert -lname to appropriate library name + s/-l//; + push(@names,"lib$_.$dl_so"); + push(@names,"lib$_.a"); + } else { # Umm, a bare name. Try various alternatives: + # these should be ordered with the most likely first + push(@names,"$_.$dl_ext") unless m/\.$dl_ext$/o; + push(@names,"$_.$dl_so") unless m/\.$dl_so$/o; + push(@names,"lib$_.$dl_so") unless m:/:; + push(@names,"$_.a") if !m/\.a$/ and $dlsrc eq "dl_dld.xs"; + push(@names, $_); + } + foreach $dir (@dirs, @dl_library_path) { + next unless -d $dir; + chop($dir = VMS::Filespec::unixpath($dir)) if $Is_VMS; + foreach $name (@names) { + my($file) = "$dir/$name"; + print STDERR " checking in $dir for $name\n" if $dl_debug; + $file = ($do_expand) ? dl_expandspec($file) : (-f $file && $file); + #$file = _check_file($file); + if ($file) { + push(@found, $file); + next arg; # no need to look any further + } + } + } + } + if ($dl_debug) { + foreach(@dirs) { + print STDERR " dl_findfile ignored non-existent directory: $_\n" unless -d $_; + } + print STDERR "dl_findfile found: @found\n"; + } + return $found[0] unless wantarray; + @found; +} + + +sub dl_expandspec { + my($spec) = @_; + # Optional function invoked if DynaLoader.pm sets $do_expand. + # Most systems do not require or use this function. + # Some systems may implement it in the dl_*.xs file in which case + # this autoload version will not be called but is harmless. + + # This function is designed to deal with systems which treat some + # 'filenames' in a special way. For example VMS 'Logical Names' + # (something like unix environment variables - but different). + # This function should recognise such names and expand them into + # full file paths. + # Must return undef if $spec is invalid or file does not exist. + + my $file = $spec; # default output to input + + if ($Is_VMS) { # dl_expandspec should be defined in dl_vms.xs + require Carp; + Carp::croak("dl_expandspec: should be defined in XS file!\n"); + } else { + return undef unless -f $file; + } + print STDERR "dl_expandspec($spec) => $file\n" if $dl_debug; + $file; +} + +sub dl_find_symbol_anywhere +{ + my $sym = shift; + my $libref; + foreach $libref (@dl_librefs) { + my $symref = dl_find_symbol($libref,$sym); + return $symref if $symref; + } + return undef; +} + +=head1 NAME + +DynaLoader - Dynamically load C libraries into Perl code + +dl_error(), dl_findfile(), dl_expandspec(), dl_load_file(), dl_find_symbol(), dl_find_symbol_anywhere(), dl_undef_symbols(), dl_install_xsub(), dl_load_flags(), bootstrap() - routines used by DynaLoader modules + +=head1 SYNOPSIS + + package YourPackage; + require DynaLoader; + @ISA = qw(... DynaLoader ...); + bootstrap YourPackage; + + # optional method for 'global' loading + sub dl_load_flags { 0x01 } + + +=head1 DESCRIPTION + +This document defines a standard generic interface to the dynamic +linking mechanisms available on many platforms. Its primary purpose is +to implement automatic dynamic loading of Perl modules. + +This document serves as both a specification for anyone wishing to +implement the DynaLoader for a new platform and as a guide for +anyone wishing to use the DynaLoader directly in an application. + +The DynaLoader is designed to be a very simple high-level +interface that is sufficiently general to cover the requirements +of SunOS, HP-UX, NeXT, Linux, VMS and other platforms. + +It is also hoped that the interface will cover the needs of OS/2, NT +etc and also allow pseudo-dynamic linking (using C<ld -A> at runtime). + +It must be stressed that the DynaLoader, by itself, is practically +useless for accessing non-Perl libraries because it provides almost no +Perl-to-C 'glue'. There is, for example, no mechanism for calling a C +library function or supplying arguments. A C::DynaLib module +is available from CPAN sites which performs that function for some +common system types. + +DynaLoader Interface Summary + + @dl_library_path + @dl_resolve_using + @dl_require_symbols + $dl_debug + @dl_librefs + @dl_modules + Implemented in: + bootstrap($modulename) Perl + @filepaths = dl_findfile(@names) Perl + $flags = $modulename->dl_load_flags Perl + $symref = dl_find_symbol_anywhere($symbol) Perl + + $libref = dl_load_file($filename, $flags) C + $symref = dl_find_symbol($libref, $symbol) C + @symbols = dl_undef_symbols() C + dl_install_xsub($name, $symref [, $filename]) C + $message = dl_error C + +=over 4 + +=item @dl_library_path + +The standard/default list of directories in which dl_findfile() will +search for libraries etc. Directories are searched in order: +$dl_library_path[0], [1], ... etc + +@dl_library_path is initialised to hold the list of 'normal' directories +(F</usr/lib>, etc) determined by B<Configure> (C<$Config{'libpth'}>). This should +ensure portability across a wide range of platforms. + +@dl_library_path should also be initialised with any other directories +that can be determined from the environment at runtime (such as +LD_LIBRARY_PATH for SunOS). + +After initialisation @dl_library_path can be manipulated by an +application using push and unshift before calling dl_findfile(). +Unshift can be used to add directories to the front of the search order +either to save search time or to override libraries with the same name +in the 'normal' directories. + +The load function that dl_load_file() calls may require an absolute +pathname. The dl_findfile() function and @dl_library_path can be +used to search for and return the absolute pathname for the +library/object that you wish to load. + +=item @dl_resolve_using + +A list of additional libraries or other shared objects which can be +used to resolve any undefined symbols that might be generated by a +later call to load_file(). + +This is only required on some platforms which do not handle dependent +libraries automatically. For example the Socket Perl extension +library (F<auto/Socket/Socket.so>) contains references to many socket +functions which need to be resolved when it's loaded. Most platforms +will automatically know where to find the 'dependent' library (e.g., +F</usr/lib/libsocket.so>). A few platforms need to be told the +location of the dependent library explicitly. Use @dl_resolve_using +for this. + +Example usage: + + @dl_resolve_using = dl_findfile('-lsocket'); + +=item @dl_require_symbols + +A list of one or more symbol names that are in the library/object file +to be dynamically loaded. This is only required on some platforms. + +=item @dl_librefs + +An array of the handles returned by successful calls to dl_load_file(), +made by bootstrap, in the order in which they were loaded. +Can be used with dl_find_symbol() to look for a symbol in any of +the loaded files. + +=item @dl_modules + +An array of module (package) names that have been bootstrap'ed. + +=item dl_error() + +Syntax: + + $message = dl_error(); + +Error message text from the last failed DynaLoader function. Note +that, similar to errno in unix, a successful function call does not +reset this message. + +Implementations should detect the error as soon as it occurs in any of +the other functions and save the corresponding message for later +retrieval. This will avoid problems on some platforms (such as SunOS) +where the error message is very temporary (e.g., dlerror()). + +=item $dl_debug + +Internal debugging messages are enabled when $dl_debug is set true. +Currently setting $dl_debug only affects the Perl side of the +DynaLoader. These messages should help an application developer to +resolve any DynaLoader usage problems. + +$dl_debug is set to C<$ENV{'PERL_DL_DEBUG'}> if defined. + +For the DynaLoader developer/porter there is a similar debugging +variable added to the C code (see dlutils.c) and enabled if Perl was +built with the B<-DDEBUGGING> flag. This can also be set via the +PERL_DL_DEBUG environment variable. Set to 1 for minimal information or +higher for more. + +=item dl_findfile() + +Syntax: + + @filepaths = dl_findfile(@names) + +Determine the full paths (including file suffix) of one or more +loadable files given their generic names and optionally one or more +directories. Searches directories in @dl_library_path by default and +returns an empty list if no files were found. + +Names can be specified in a variety of platform independent forms. Any +names in the form B<-lname> are converted into F<libname.*>, where F<.*> is +an appropriate suffix for the platform. + +If a name does not already have a suitable prefix and/or suffix then +the corresponding file will be searched for by trying combinations of +prefix and suffix appropriate to the platform: "$name.o", "lib$name.*" +and "$name". + +If any directories are included in @names they are searched before +@dl_library_path. Directories may be specified as B<-Ldir>. Any other +names are treated as filenames to be searched for. + +Using arguments of the form C<-Ldir> and C<-lname> is recommended. + +Example: + + @dl_resolve_using = dl_findfile(qw(-L/usr/5lib -lposix)); + + +=item dl_expandspec() + +Syntax: + + $filepath = dl_expandspec($spec) + +Some unusual systems, such as VMS, require special filename handling in +order to deal with symbolic names for files (i.e., VMS's Logical Names). + +To support these systems a dl_expandspec() function can be implemented +either in the F<dl_*.xs> file or code can be added to the autoloadable +dl_expandspec() function in F<DynaLoader.pm>. See F<DynaLoader.pm> for +more information. + +=item dl_load_file() + +Syntax: + + $libref = dl_load_file($filename, $flags) + +Dynamically load $filename, which must be the path to a shared object +or library. An opaque 'library reference' is returned as a handle for +the loaded object. Returns undef on error. + +The $flags argument to alters dl_load_file behaviour. +Assigned bits: + + 0x01 make symbols available for linking later dl_load_file's. + (only known to work on Solaris 2 using dlopen(RTLD_GLOBAL)) + (ignored under VMS; this is a normal part of image linking) + +(On systems that provide a handle for the loaded object such as SunOS +and HPUX, $libref will be that handle. On other systems $libref will +typically be $filename or a pointer to a buffer containing $filename. +The application should not examine or alter $libref in any way.) + +This is the function that does the real work. It should use the +current values of @dl_require_symbols and @dl_resolve_using if required. + + SunOS: dlopen($filename) + HP-UX: shl_load($filename) + Linux: dld_create_reference(@dl_require_symbols); dld_link($filename) + NeXT: rld_load($filename, @dl_resolve_using) + VMS: lib$find_image_symbol($filename,$dl_require_symbols[0]) + +(The dlopen() function is also used by Solaris and some versions of +Linux, and is a common choice when providing a "wrapper" on other +mechanisms as is done in the OS/2 port.) + +=item dl_loadflags() + +Syntax: + + $flags = dl_loadflags $modulename; + +Designed to be a method call, and to be overridden by a derived class +(i.e. a class which has DynaLoader in its @ISA). The definition in +DynaLoader itself returns 0, which produces standard behavior from +dl_load_file(). + +=item dl_find_symbol() + +Syntax: + + $symref = dl_find_symbol($libref, $symbol) + +Return the address of the symbol $symbol or C<undef> if not found. If the +target system has separate functions to search for symbols of different +types then dl_find_symbol() should search for function symbols first and +then other types. + +The exact manner in which the address is returned in $symref is not +currently defined. The only initial requirement is that $symref can +be passed to, and understood by, dl_install_xsub(). + + SunOS: dlsym($libref, $symbol) + HP-UX: shl_findsym($libref, $symbol) + Linux: dld_get_func($symbol) and/or dld_get_symbol($symbol) + NeXT: rld_lookup("_$symbol") + VMS: lib$find_image_symbol($libref,$symbol) + + +=item dl_find_symbol_anywhere() + +Syntax: + + $symref = dl_find_symbol_anywhere($symbol) + +Applies dl_find_symbol() to the members of @dl_librefs and returns +the first match found. + +=item dl_undef_symbols() + +Example + + @symbols = dl_undef_symbols() + +Return a list of symbol names which remain undefined after load_file(). +Returns C<()> if not known. Don't worry if your platform does not provide +a mechanism for this. Most do not need it and hence do not provide it, +they just return an empty list. + + +=item dl_install_xsub() + +Syntax: + + dl_install_xsub($perl_name, $symref [, $filename]) + +Create a new Perl external subroutine named $perl_name using $symref as +a pointer to the function which implements the routine. This is simply +a direct call to newXSUB(). Returns a reference to the installed +function. + +The $filename parameter is used by Perl to identify the source file for +the function if required by die(), caller() or the debugger. If +$filename is not defined then "DynaLoader" will be used. + + +=item bootstrap() + +Syntax: + +bootstrap($module) + +This is the normal entry point for automatic dynamic loading in Perl. + +It performs the following actions: + +=over 8 + +=item * + +locates an auto/$module directory by searching @INC + +=item * + +uses dl_findfile() to determine the filename to load + +=item * + +sets @dl_require_symbols to C<("boot_$module")> + +=item * + +executes an F<auto/$module/$module.bs> file if it exists +(typically used to add to @dl_resolve_using any files which +are required to load the module on the current platform) + +=item * + +calls dl_load_flags() to determine how to load the file. + +=item * + +calls dl_load_file() to load the file + +=item * + +calls dl_undef_symbols() and warns if any symbols are undefined + +=item * + +calls dl_find_symbol() for "boot_$module" + +=item * + +calls dl_install_xsub() to install it as "${module}::bootstrap" + +=item * + +calls &{"${module}::bootstrap"} to bootstrap the module (actually +it uses the function reference returned by dl_install_xsub for speed) + +=back + +=back + + +=head1 AUTHOR + +Tim Bunce, 11 August 1994. + +This interface is based on the work and comments of (in no particular +order): Larry Wall, Robert Sanders, Dean Roehrich, Jeff Okamoto, Anno +Siegel, Thomas Neumann, Paul Marquess, Charles Bailey, myself and others. + +Larry Wall designed the elegant inherited bootstrap mechanism and +implemented the first Perl 5 dynamic loader using it. + +Solaris global loading added by Nick Ing-Simmons with design/coding +assistance from Tim Bunce, January 1996. + +=cut +EOT + +close OUT or die $!; + diff --git a/gnu/usr.bin/perl/ext/DynaLoader/dl_beos.xs b/gnu/usr.bin/perl/ext/DynaLoader/dl_beos.xs new file mode 100644 index 00000000000..42a27cb1f17 --- /dev/null +++ b/gnu/usr.bin/perl/ext/DynaLoader/dl_beos.xs @@ -0,0 +1,115 @@ +/* + * dl_beos.xs, by Tom Spindler + * based on dl_dlopen.xs, by Paul Marquess + * $Id:$ + */ + +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +#include <be/kernel/image.h> +#include <OS.h> +#include <stdlib.h> +#include <limits.h> + +#define dlerror() strerror(errno) + +#include "dlutils.c" /* SaveError() etc */ + +static void +dl_private_init() +{ + (void)dl_generic_private_init(); +} + +MODULE = DynaLoader PACKAGE = DynaLoader + +BOOT: + (void)dl_private_init(); + + +void * +dl_load_file(filename, flags=0) + char * filename + int flags + CODE: +{ image_id bogo; + char *path; + path = malloc(PATH_MAX); + if (*filename != '/') { + getcwd(path, PATH_MAX); + strcat(path, "/"); + strcat(path, filename); + } else { + strcpy(path, filename); + } + + DLDEBUG(1,PerlIO_printf(PerlIO_stderr(), "dl_load_file(%s,%x):\n", path, flags)); + bogo = load_add_on(path); + DLDEBUG(2,PerlIO_printf(PerlIO_stderr(), " libref=%lx\n", (unsigned long) RETVAL)); + ST(0) = sv_newmortal() ; + if (bogo < 0) { + SaveError("%s", strerror(bogo)); + PerlIO_printf(PerlIO_stderr(), "load_add_on(%s) : %d (%s)\n", path, bogo, strerror(bogo)); + } else { + RETVAL = (void *) bogo; + sv_setiv( ST(0), (IV)RETVAL); + } + free(path); +} + +void * +dl_find_symbol(libhandle, symbolname) + void * libhandle + char * symbolname + CODE: + status_t retcode; + void *adr = 0; +#ifdef DLSYM_NEEDS_UNDERSCORE + symbolname = form("_%s", symbolname); +#endif + RETVAL = NULL; + DLDEBUG(2, PerlIO_printf(PerlIO_stderr(), + "dl_find_symbol(handle=%lx, symbol=%s)\n", + (unsigned long) libhandle, symbolname)); + retcode = get_image_symbol((image_id) libhandle, symbolname, + B_SYMBOL_TYPE_TEXT, (void **) &adr); + RETVAL = adr; + DLDEBUG(2, PerlIO_printf(PerlIO_stderr(), + " symbolref = %lx\n", (unsigned long) RETVAL)); + ST(0) = sv_newmortal() ; + if (RETVAL == NULL) { + SaveError("%s", strerror(retcode)) ; + PerlIO_printf(PerlIO_stderr(), "retcode = %p (%s)\n", retcode, strerror(retcode)); + } else + sv_setiv( ST(0), (IV)RETVAL); + + +void +dl_undef_symbols() + PPCODE: + + + +# These functions should not need changing on any platform: + +void +dl_install_xsub(perl_name, symref, filename="$Package") + char * perl_name + void * symref + char * filename + CODE: + DLDEBUG(2,PerlIO_printf(PerlIO_stderr(), "dl_install_xsub(name=%s, symref=%lx)\n", + perl_name, (unsigned long) symref)); + ST(0)=sv_2mortal(newRV((SV*)newXS(perl_name, (void(*)_((CV *)))symref, filename))); + + +char * +dl_error() + CODE: + RETVAL = LastError ; + OUTPUT: + RETVAL + +# end. diff --git a/gnu/usr.bin/perl/ext/DynaLoader/dl_mpeix.xs b/gnu/usr.bin/perl/ext/DynaLoader/dl_mpeix.xs new file mode 100644 index 00000000000..4cc07ec4c3c --- /dev/null +++ b/gnu/usr.bin/perl/ext/DynaLoader/dl_mpeix.xs @@ -0,0 +1,129 @@ +/* + * Author: Mark Klein (mklein@dis.com) + * Version: 2.1, 1996/07/25 + * Version: 2.2, 1997/09/25 Mark Bixby (markb@cccd.edu) + * Version: 2.3, 1998/11/19 Mark Bixby (markb@cccd.edu) + */ + +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +#ifdef __GNUC__ +extern void HPGETPROCPLABEL( int parms, + char * procname, + int * plabel, + int * status, + char * firstfile, + int casesensitive, + int symboltype, + int * datasize, + int position, + int searchpath, + int binding); +#else +#pragma intrinsic HPGETPROCPLABEL +#endif +#include "dlutils.c" /* for SaveError() etc */ + +typedef struct { + char filename[PATH_MAX + 3]; + } t_mpe_dld, *p_mpe_dld; + +static AV *dl_resolve_using = Nullav; + +static void +dl_private_init() +{ + (void)dl_generic_private_init(); + dl_resolve_using = perl_get_av("DynaLoader::dl_resolve_using", 0x4); +} + +MODULE = DynaLoader PACKAGE = DynaLoader + +BOOT: + (void)dl_private_init(); + +void * +dl_load_file(filename, flags=0) + char * filename + int flags + PREINIT: + char buf[PATH_MAX + 3]; + p_mpe_dld obj = NULL; + int i; + CODE: + DLDEBUG(1,PerlIO_printf(PerlIO_stderr(), "dl_load_file(%s,%x):\n", filename, +flags)); + if (flags & 0x01) + warn("Can't make loaded symbols global on this platform while loading %s +",filename); + obj = (p_mpe_dld) safemalloc(sizeof(t_mpe_dld)); + memzero(obj, sizeof(t_mpe_dld)); + if (filename[0] != '/') + { + getcwd(buf,sizeof(buf)); + sprintf(obj->filename," %s/%s ",buf,filename); + } + else + sprintf(obj->filename," %s ",filename); + + DLDEBUG(2,PerlIO_printf(PerlIO_stderr()," libref=%x\n", obj)); + + ST(0) = sv_newmortal() ; + if (obj == NULL) + SaveError("%s",Strerror(errno)); + else + sv_setiv( ST(0), (IV)obj); + +void * +dl_find_symbol(libhandle, symbolname) + void * libhandle + char * symbolname + CODE: + int datalen; + p_mpe_dld obj = (p_mpe_dld) libhandle; + char symname[PATH_MAX + 3]; + void * symaddr = NULL; + int status; + DLDEBUG(2,PerlIO_printf(PerlIO_stderr(),"dl_find_symbol(handle=%x, symbol=%s)\n", + libhandle, symbolname)); + ST(0) = sv_newmortal() ; + errno = 0; + + sprintf(symname, " %s ", symbolname); + HPGETPROCPLABEL(8, symname, &symaddr, &status, obj->filename, 1, + 0, &datalen, 1, 0, 0); + + DLDEBUG(2,PerlIO_printf(PerlIO_stderr()," symbolref(PROCEDURE) = %x, status=%x\n", symaddr, status)); + + if (status != 0) { + SaveError("%s",(errno) ? Strerror(errno) : "Symbol not found") ; + } else { + sv_setiv( ST(0), (IV)symaddr); + } + +void +dl_undef_symbols() + PPCODE: + +# These functions should not need changing on any platform: + +void +dl_install_xsub(perl_name, symref, filename="$Package") + char * perl_name + void * symref + char * filename + CODE: + DLDEBUG(2,PerlIO_printf(PerlIO_stderr(),"dl_install_xsub(name=%s, symref=%x)\n", + perl_name, symref)); + ST(0)=sv_2mortal(newRV((SV*)newXS(perl_name, (void(*)())symref, filename))); + +char * +dl_error() + CODE: + RETVAL = LastError ; + OUTPUT: + RETVAL + +# end. diff --git a/gnu/usr.bin/perl/ext/Errno/ChangeLog b/gnu/usr.bin/perl/ext/Errno/ChangeLog new file mode 100644 index 00000000000..2bfa003d96a --- /dev/null +++ b/gnu/usr.bin/perl/ext/Errno/ChangeLog @@ -0,0 +1,50 @@ +Change 170 on 1998/07/05 by <gbarr@pobox.com> (Graham Barr) + + Fixed three problems reported by Hans Mulder for NeXT + + - Errno_pm.PL does not recognize #define lines because they have + whitespace before the '#'. ANSI does not allow that in portable + code; that didn't stop the author of NeXT's <errno.h>. + + - Cpp output lines look like this: #1 "errno.c" + Errno_pm.PL does not recognize that format; it wants whitespace + before the line number. + + - Cpp does a syntax check on files with names ending in ".c"; it + reports fatal errors on input lines like: "ENOSYS" [[ENOSYS]] + Workaround: use $Config{cppstdin}, like Errno 1.04 did. + +Change 160 on 1998/06/27 by <gbarr@pobox.com> (Graham Barr) + + - Added patch from Sarathy to support Win32 + - Changed use of $Config{cpp} to $Config{cpprun} as suggested by + Tom Horsley + +Change 159 on 1998/06/27 by <gbarr@pobox.com> (Graham Barr) + + - Changed to use cpp to locate required files + - Moved dummy Errno.pm file into d/ + - Added support for VMS + +Change 158 on 1998/06/27 by <gbarr@pobox.com> (Graham Barr) + + Rename errno.pl to Errno_pm.PL + +Change 146 on 1998/05/31 by <gbarr@pobox.com> (Graham Barr) + + Added ChangeLog to MANIFEST + +Change 140 on 1998/05/23 by <gbarr@pobox.com> (Graham Barr) + + Fix type in errno.pl + +Change 139 on 1998/05/23 by <gbarr@pobox.com> (Graham Barr) + + Moved code to generate Errno.pm into errno.pl + +Change 136 on 1998/05/19 by <gbarr@pobox.com> (Graham Barr) + + Changed to use cpp to locate constants + + Added t/errno.t + diff --git a/gnu/usr.bin/perl/ext/Errno/Errno_pm.PL b/gnu/usr.bin/perl/ext/Errno/Errno_pm.PL new file mode 100644 index 00000000000..c1f26fc1c7e --- /dev/null +++ b/gnu/usr.bin/perl/ext/Errno/Errno_pm.PL @@ -0,0 +1,316 @@ +use ExtUtils::MakeMaker; +use Config; +use strict; + +use vars qw($VERSION); + +$VERSION = "1.111"; + +my %err = (); + +unlink "Errno.pm" if -f "Errno.pm"; +open OUT, ">Errno.pm" or die "Cannot open Errno.pm: $!"; +select OUT; +my $file; +foreach $file (get_files()) { + process_file($file); +} +write_errno_pm(); +unlink "errno.c" if -f "errno.c"; + +sub process_file { + my($file) = @_; + + return unless defined $file and -f $file; + + local *FH; + if (($^O eq 'VMS') && ($Config{vms_cc_type} ne 'gnuc')) { + unless(open(FH," LIBRARY/EXTRACT=ERRNO/OUTPUT=SYS\$OUTPUT $file |")) { + warn "Cannot open '$file'"; + return; + } + } else { + unless(open(FH,"< $file")) { + # This file could be a temporary file created by cppstdin + # so only warn under -w, and return + warn "Cannot open '$file'" if $^W; + return; + } + } + while(<FH>) { + $err{$1} = 1 + if /^\s*#\s*define\s+(E\w+)\s+/; + } + close(FH); +} + +my $cppstdin; + +sub default_cpp { + unless (defined $cppstdin) { + use File::Spec; + $cppstdin = $Config{cppstdin}; + my $upup_cppstdin = File::Spec->catfile(File::Spec->updir, + File::Spec->updir, + "cppstdin"); + my $cppstdin_is_wrapper = + ($cppstdin eq 'cppstdin' + and -f $upup_cppstdin + and -x $upup_cppstdin); + $cppstdin = $upup_cppstdin if $cppstdin_is_wrapper; + } + return "$cppstdin $Config{cppflags} $Config{cppminus}"; +} + +sub get_files { + my %file = (); + # VMS keeps its include files in system libraries (well, except for Gcc) + if ($^O eq 'VMS') { + if ($Config{vms_cc_type} eq 'decc') { + $file{'Sys$Library:DECC$RTLDEF.TLB'} = 1; + } elsif ($Config{vms_cc_type} eq 'vaxc') { + $file{'Sys$Library:vaxcdef.tlb'} = 1; + } elsif ($Config{vms_cc_type} eq 'gcc') { + $file{'gnu_cc_include:[000000]errno.h'} = 1; + } + } elsif ($^O eq 'os390') { + # OS/390 C compiler doesn't generate #file or #line directives + $file{'/usr/include/errno.h'} = 1; + } elsif ($^O eq 'vmesa') { + # OS/390 C compiler doesn't generate #file or #line directives + $file{'../../vmesa/errno.h'} = 1; + } else { + open(CPPI,"> errno.c") or + die "Cannot open errno.c"; + + print CPPI "#include <errno.h>\n"; + + close(CPPI); + + # invoke CPP and read the output + if ($^O eq 'MSWin32') { + open(CPPO,"$Config{cpprun} $Config{cppflags} errno.c |") or + die "Cannot run '$Config{cpprun} $Config{cppflags} errno.c'"; + } else { + my $cpp = default_cpp(); + open(CPPO,"$cpp < errno.c |") or + die "Cannot exec $cpp"; + } + + my $pat; + if ($^O eq 'MSWin32' and $Config{cc} =~ /^bcc/i) { + $pat = '^/\*\s+(.+)\s+\d+\s*:\s+\*/'; + } + else { + $pat = '^#(?:line)?\s*\d+\s+"([^"]+)"'; + } + while(<CPPO>) { + if ($^O eq 'os2' or $^O eq 'MSWin32') { + if (/$pat/o) { + my $f = $1; + $f =~ s,\\\\,/,g; + $file{$f} = 1; + } + } + else { + $file{$1} = 1 if /$pat/o; + } + } + close(CPPO); + } + return keys %file; +} + +sub write_errno_pm { + my $err; + + # quick sanity check + + die "No error definitions found" unless keys %err; + + # create the CPP input + + open(CPPI,"> errno.c") or + die "Cannot open errno.c"; + + print CPPI "#include <errno.h>\n"; + + foreach $err (keys %err) { + print CPPI '"',$err,'" [[',$err,']]',"\n"; + } + + close(CPPI); + + # invoke CPP and read the output + + if ($^O eq 'VMS') { + my $cpp = "$Config{cppstdin} $Config{cppflags} $Config{cppminus}"; + $cpp =~ s/sys\$input//i; + open(CPPO,"$cpp errno.c |") or + die "Cannot exec $Config{cppstdin}"; + } elsif ($^O eq 'MSWin32') { + open(CPPO,"$Config{cpprun} $Config{cppflags} errno.c |") or + die "Cannot run '$Config{cpprun} $Config{cppflags} errno.c'"; + } else { + my $cpp = default_cpp(); + open(CPPO,"$cpp < errno.c |") + or die "Cannot exec $cpp"; + } + + %err = (); + + while(<CPPO>) { + my($name,$expr); + next unless ($name, $expr) = /"(.*?)"\s*\[\s*\[\s*(.*?)\s*\]\s*\]/; + next if $name eq $expr; + $err{$name} = eval $expr; + } + close(CPPO); + + # Write Errno.pm + + print <<"EDQ"; +# +# This file is auto-generated. ***ANY*** changes here will be lost +# + +package Errno; +use vars qw(\@EXPORT_OK \%EXPORT_TAGS \@ISA \$VERSION \%errno \$AUTOLOAD); +use Exporter (); +use Config; +use strict; + +\$Config{'myarchname'} eq "$Config{'myarchname'}" or + die "Errno architecture ($Config{'myarchname'}) does not match executable architecture (\$Config{'myarchname'})"; + +\$VERSION = "$VERSION"; +\@ISA = qw(Exporter); + +EDQ + + my $len = 0; + my @err = sort { $err{$a} <=> $err{$b} } keys %err; + map { $len = length if length > $len } @err; + + my $j = "\@EXPORT_OK = qw(" . join(" ",keys %err) . ");\n"; + $j =~ s/(.{50,70})\s/$1\n\t/g; + print $j,"\n"; + +print <<'ESQ'; +%EXPORT_TAGS = ( + POSIX => [qw( +ESQ + + my $k = join(" ", grep { exists $err{$_} } + qw(E2BIG EACCES EADDRINUSE EADDRNOTAVAIL EAFNOSUPPORT + EAGAIN EALREADY EBADF EBUSY ECHILD ECONNABORTED + ECONNREFUSED ECONNRESET EDEADLK EDESTADDRREQ EDOM EDQUOT + EEXIST EFAULT EFBIG EHOSTDOWN EHOSTUNREACH EINPROGRESS + EINTR EINVAL EIO EISCONN EISDIR ELOOP EMFILE EMLINK + EMSGSIZE ENAMETOOLONG ENETDOWN ENETRESET ENETUNREACH + ENFILE ENOBUFS ENODEV ENOENT ENOEXEC ENOLCK ENOMEM + ENOPROTOOPT ENOSPC ENOSYS ENOTBLK ENOTCONN ENOTDIR + ENOTEMPTY ENOTSOCK ENOTTY ENXIO EOPNOTSUPP EPERM + EPFNOSUPPORT EPIPE EPROCLIM EPROTONOSUPPORT EPROTOTYPE + ERANGE EREMOTE ERESTART EROFS ESHUTDOWN ESOCKTNOSUPPORT + ESPIPE ESRCH ESTALE ETIMEDOUT ETOOMANYREFS ETXTBSY + EUSERS EWOULDBLOCK EXDEV)); + + $k =~ s/(.{50,70})\s/$1\n\t/g; + print "\t",$k,"\n )]\n);\n\n"; + + foreach $err (@err) { + printf "sub %s () { %d }\n",,$err,$err{$err}; + } + + print <<'ESQ'; + +sub TIEHASH { bless [] } + +sub FETCH { + my ($self, $errname) = @_; + my $proto = prototype("Errno::$errname"); + if (defined($proto) && $proto eq "") { + no strict 'refs'; + return $! == &$errname; + } + require Carp; + Carp::confess("No errno $errname"); +} + +sub STORE { + require Carp; + Carp::confess("ERRNO hash is read only!"); +} + +*CLEAR = \&STORE; +*DELETE = \&STORE; + +sub NEXTKEY { + my($k,$v); + while(($k,$v) = each %Errno::) { + my $proto = prototype("Errno::$k"); + last if (defined($proto) && $proto eq ""); + + } + $k +} + +sub FIRSTKEY { + my $s = scalar keys %Errno::; + goto &NEXTKEY; +} + +sub EXISTS { + my ($self, $errname) = @_; + my $proto = prototype($errname); + defined($proto) && $proto eq ""; +} + +tie %!, __PACKAGE__; + +1; +__END__ + +=head1 NAME + +Errno - System errno constants + +=head1 SYNOPSIS + + use Errno qw(EINTR EIO :POSIX); + +=head1 DESCRIPTION + +C<Errno> defines and conditionally exports all the error constants +defined in your system C<errno.h> include file. It has a single export +tag, C<:POSIX>, which will export all POSIX defined error numbers. + +C<Errno> also makes C<%!> magic such that each element of C<%!> has a non-zero +value only if C<$!> is set to that value, eg + + use Errno; + + unless (open(FH, "/fangorn/spouse")) { + if ($!{ENOENT}) { + warn "Get a wife!\n"; + } else { + warn "This path is barred: $!"; + } + } + +=head1 AUTHOR + +Graham Barr <gbarr@pobox.com> + +=head1 COPYRIGHT + +Copyright (c) 1997-8 Graham Barr. All rights reserved. +This program is free software; you can redistribute it and/or modify it +under the same terms as Perl itself. + +=cut + +ESQ + +} diff --git a/gnu/usr.bin/perl/ext/Errno/Makefile.PL b/gnu/usr.bin/perl/ext/Errno/Makefile.PL new file mode 100644 index 00000000000..604d4fb8e8c --- /dev/null +++ b/gnu/usr.bin/perl/ext/Errno/Makefile.PL @@ -0,0 +1,30 @@ +use ExtUtils::MakeMaker; + +@VMS = ($^O eq 'VMS') ? (MAN3PODS => {}) : (); + +WriteMakefile( + NAME => 'Errno', + VERSION_FROM => 'Errno_pm.PL', + MAN3PODS => {}, # Pods will be built by installman. + PL_FILES => {'Errno_pm.PL'=>'Errno.pm'}, + PM => {'Errno.pm' => '$(INST_LIBDIR)/Errno.pm'}, + 'clean' => {FILES => 'Errno.pm'}, + 'dist' => { + COMPRESS => 'gzip -9f', + SUFFIX => '.gz', + DIST_DEFAULT => 'd/Errno.pm tardist', + }, + @VMS, +); + +sub MY::postamble { + my $TARG = MM->catfile('d','Errno.pm'); +qq!$TARG : Makefile + echo '#This is a dummy file so CPAN will find a VERSION' > $TARG + echo 'package Errno;' >> $TARG + echo '\$\$VERSION = "\$(VERSION)";' >>$TARG + echo '#This is to make sure require will return an error' >>$TARG + echo '0;' >>$TARG + +! +} diff --git a/gnu/usr.bin/perl/ext/Fcntl/Makefile.PL b/gnu/usr.bin/perl/ext/Fcntl/Makefile.PL index 66a6df6060d..03463737131 100644 --- a/gnu/usr.bin/perl/ext/Fcntl/Makefile.PL +++ b/gnu/usr.bin/perl/ext/Fcntl/Makefile.PL @@ -1,7 +1,7 @@ use ExtUtils::MakeMaker; WriteMakefile( NAME => 'Fcntl', - MAN3PODS => ' ', # Pods will be built by installman. + MAN3PODS => {}, # Pods will be built by installman. XSPROTOARG => '-noprototypes', # XXX remove later? VERSION_FROM => 'Fcntl.pm', ); diff --git a/gnu/usr.bin/perl/ext/GDBM_File/GDBM_File.xs b/gnu/usr.bin/perl/ext/GDBM_File/GDBM_File.xs index a423c88c705..ac1ca8c68d9 100644 --- a/gnu/usr.bin/perl/ext/GDBM_File/GDBM_File.xs +++ b/gnu/usr.bin/perl/ext/GDBM_File/GDBM_File.xs @@ -23,8 +23,7 @@ typedef datum gdatum; typedef void (*FATALFUNC)(); static int -not_here(s) -char *s; +not_here(char *s) { croak("GDBM_File::%s not implemented on this architecture", s); return -1; @@ -41,9 +40,7 @@ char *s; #endif static double -constant(name, arg) -char *name; -int arg; +constant(char *name, int arg) { errno = 0; switch (*name) { diff --git a/gnu/usr.bin/perl/ext/GDBM_File/Makefile.PL b/gnu/usr.bin/perl/ext/GDBM_File/Makefile.PL index d24461350b6..2a7256fa41b 100644 --- a/gnu/usr.bin/perl/ext/GDBM_File/Makefile.PL +++ b/gnu/usr.bin/perl/ext/GDBM_File/Makefile.PL @@ -2,7 +2,7 @@ use ExtUtils::MakeMaker; WriteMakefile( NAME => 'GDBM_File', LIBS => ["-L/usr/local/lib -lgdbm", "-ldbm"], - MAN3PODS => ' ', # Pods will be built by installman. + MAN3PODS => {}, # Pods will be built by installman. XSPROTOARG => '-noprototypes', # XXX remove later? VERSION_FROM => 'GDBM_File.pm', ); diff --git a/gnu/usr.bin/perl/ext/GDBM_File/hints/sco.pl b/gnu/usr.bin/perl/ext/GDBM_File/hints/sco.pl new file mode 100644 index 00000000000..5c74a77495f --- /dev/null +++ b/gnu/usr.bin/perl/ext/GDBM_File/hints/sco.pl @@ -0,0 +1,2 @@ +# SCO OSR5 needs to link with libc.so again to have C<fsync> defined +$self->{LIBS} = ['-lgdbm -lc']; diff --git a/gnu/usr.bin/perl/ext/IPC/SysV/ChangeLog b/gnu/usr.bin/perl/ext/IPC/SysV/ChangeLog new file mode 100644 index 00000000000..fff95bec431 --- /dev/null +++ b/gnu/usr.bin/perl/ext/IPC/SysV/ChangeLog @@ -0,0 +1,28 @@ +Fri Jul 3 15:06:40 1998 Jarkko Hietaniemi <jhi@iki.fi> + + - Integrated IPC::SysV 1.03 to Perl 5.004_69. + +Change 142 on 1998/05/31 by <gbarr@pobox.com> (Graham Barr) + + - Changed SHMLBA from a constSUB to an XS sub as on some systems it is not + a constant + - Added a missing MODULE line to SysV.xs so ftok is defined in IPC::SysV + +Change 138 on 1998/05/23 by <gbarr@pobox.com> (Graham Barr) + + Applied patch from Jarkko Hietaniemi to add constats for UNICOS + + Reduced size of XS object by changing constant sub definition + into a loop + + Updated POD to include ftok() + +Change 135 on 1998/05/18 by <gbarr@pobox.com> (Graham Barr) + + applied changes from Jarkko Hietaniemi <jhi@iki.fi> to add + new constants and ftok + + fixed to compile with >5.004_50 + + surrounded newCONSTSUB with #ifndef as perl now defines this itself + diff --git a/gnu/usr.bin/perl/ext/IPC/SysV/MANIFEST b/gnu/usr.bin/perl/ext/IPC/SysV/MANIFEST new file mode 100644 index 00000000000..4b2aa00daf1 --- /dev/null +++ b/gnu/usr.bin/perl/ext/IPC/SysV/MANIFEST @@ -0,0 +1,10 @@ +MANIFEST +Makefile.PL +Msg.pm +README +Semaphore.pm +SysV.pm +SysV.xs +t/msg.t +t/sem.t +ChangeLog diff --git a/gnu/usr.bin/perl/ext/IPC/SysV/Makefile.PL b/gnu/usr.bin/perl/ext/IPC/SysV/Makefile.PL new file mode 100644 index 00000000000..d4ea5583de6 --- /dev/null +++ b/gnu/usr.bin/perl/ext/IPC/SysV/Makefile.PL @@ -0,0 +1,37 @@ +# This -*- perl -*- script makes the Makefile +# $Id: Makefile.PL,v 1.1.1.1 1999/04/29 22:38:48 millert Exp $ + +require 5.002; +use ExtUtils::MakeMaker; + +#--- MY package + +sub MY::libscan +{ + my($self,$path) = @_; + + return '' + if($path =~ m:/(RCS|CVS|SCCS)/: || + $path =~ m:[~%]$: || + $path =~ m:\.(orig|rej)$: + ); + + $path; +} + +WriteMakefile( + VERSION_FROM => "SysV.pm", + NAME => "IPC::SysV", + MAN3PODS => {}, # Pods will be built by installman. + + 'dist' => {COMPRESS => 'gzip -9f', + SUFFIX => 'gz', + DIST_DEFAULT => 'all tardist', + }, + + 'clean' => {FILES => join(" ", + map { "$_ */$_ */*/$_" } + qw(*% *.html *.b[ac]k *.old *.orig)) + }, + 'macro' => { INSTALLDIRS => 'perl' }, +); diff --git a/gnu/usr.bin/perl/ext/IPC/SysV/Msg.pm b/gnu/usr.bin/perl/ext/IPC/SysV/Msg.pm new file mode 100644 index 00000000000..a739ca23679 --- /dev/null +++ b/gnu/usr.bin/perl/ext/IPC/SysV/Msg.pm @@ -0,0 +1,223 @@ +# IPC::Msg.pm +# +# Copyright (c) 1997 Graham Barr <gbarr@pobox.com>. All rights reserved. +# This program is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. + +package IPC::Msg; + +use IPC::SysV qw(IPC_STAT IPC_SET IPC_RMID); +use strict; +use vars qw($VERSION); +use Carp; + +$VERSION = "1.00"; + +{ + package IPC::Msg::stat; + + use Class::Struct qw(struct); + + struct 'IPC::Msg::stat' => [ + uid => '$', + gid => '$', + cuid => '$', + cgid => '$', + mode => '$', + qnum => '$', + qbytes => '$', + lspid => '$', + lrpid => '$', + stime => '$', + rtime => '$', + ctime => '$', + ]; +} + +sub new { + @_ == 3 || croak 'new IPC::Msg ( KEY , FLAGS )'; + my $class = shift; + + my $id = msgget($_[0],$_[1]); + + defined($id) + ? bless \$id, $class + : undef; +} + +sub id { + my $self = shift; + $$self; +} + +sub stat { + my $self = shift; + my $data = ""; + msgctl($$self,IPC_STAT,$data) or + return undef; + IPC::Msg::stat->new->unpack($data); +} + +sub set { + my $self = shift; + my $ds; + + if(@_ == 1) { + $ds = shift; + } + else { + croak 'Bad arg count' if @_ % 2; + my %arg = @_; + my $ds = $self->stat + or return undef; + my($key,$val); + $ds->$key($val) + while(($key,$val) = each %arg); + } + + msgctl($$self,IPC_SET,$ds->pack); +} + +sub remove { + my $self = shift; + (msgctl($$self,IPC_RMID,0), undef $$self)[0]; +} + +sub rcv { + @_ <= 5 && @_ >= 3 or croak '$msg->rcv( BUF, LEN, TYPE, FLAGS )'; + my $self = shift; + my $buf = ""; + msgrcv($$self,$buf,$_[1],$_[2] || 0, $_[3] || 0) or + return; + my $type; + ($type,$_[0]) = unpack("L a*",$buf); + $type; +} + +sub snd { + @_ <= 4 && @_ >= 3 or croak '$msg->snd( TYPE, BUF, FLAGS )'; + my $self = shift; + msgsnd($$self,pack("L a*",$_[0],$_[1]), $_[2] || 0); +} + + +1; + +__END__ + +=head1 NAME + +IPC::Msg - SysV Msg IPC object class + +=head1 SYNOPSIS + + use IPC::SysV qw(IPC_PRIVATE S_IRWXU S_IRWXG S_IRWXO); + use IPC::Msg; + + $msg = new IPC::Msg(IPC_PRIVATE, S_IRWXU | S_IRWXG | S_IRWXO); + + $msg->snd(pack("L a*",$msgtype,$msg)); + + $msg->rcv($buf,256); + + $ds = $msg->stat; + + $msg->remove; + +=head1 DESCRIPTION + +=head1 METHODS + +=over 4 + +=item new ( KEY , FLAGS ) + +Creates a new message queue associated with C<KEY>. A new queue is +created if + +=over 4 + +=item * + +C<KEY> is equal to C<IPC_PRIVATE> + +=item * + +C<KEY> does not already have a message queue +associated with it, and C<I<FLAGS> & IPC_CREAT> is true. + +=back + +On creation of a new message queue C<FLAGS> is used to set the +permissions. + +=item id + +Returns the system message queue identifier. + +=item rcv ( BUF, LEN [, TYPE [, FLAGS ]] ) + +Read a message from the queue. Returns the type of the message read. See +L<msgrcv> + +=item remove + +Remove and destroy the message queue from the system. + +=item set ( STAT ) + +=item set ( NAME => VALUE [, NAME => VALUE ...] ) + +C<set> will set the following values of the C<stat> structure associated +with the message queue. + + uid + gid + mode (oly the permission bits) + qbytes + +C<set> accepts either a stat object, as returned by the C<stat> method, +or a list of I<name>-I<value> pairs. + +=item snd ( TYPE, MSG [, FLAGS ] ) + +Place a message on the queue with the data from C<MSG> and with type C<TYPE>. +See L<msgsnd>. + +=item stat + +Returns an object of type C<IPC::Msg::stat> which is a sub-class of +C<Class::Struct>. It provides the following fields. For a description +of these fields see you system documentation. + + uid + gid + cuid + cgid + mode + qnum + qbytes + lspid + lrpid + stime + rtime + ctime + +=back + +=head1 SEE ALSO + +L<IPC::SysV> L<Class::Struct> + +=head1 AUTHOR + +Graham Barr <gbarr@pobox.com> + +=head1 COPYRIGHT + +Copyright (c) 1997 Graham Barr. All rights reserved. +This program is free software; you can redistribute it and/or modify it +under the same terms as Perl itself. + +=cut + diff --git a/gnu/usr.bin/perl/ext/IPC/SysV/README b/gnu/usr.bin/perl/ext/IPC/SysV/README new file mode 100644 index 00000000000..d412c4c712d --- /dev/null +++ b/gnu/usr.bin/perl/ext/IPC/SysV/README @@ -0,0 +1,20 @@ +Copyright (c) 1997 Graham Barr <gbarr@pobox.com>. All rights reserved. +This package is free software; you can redistribute it and/or +modify it under the same terms as Perl itself. + +The SysV-IPC contains three packages + + IPC::Semaphore + - Provides an object interface to using SysV IPC semaphores + + IPC::Msg + - Provides an object interface to using SysV IPC messages + + IPC::SysV + - Provides the constants required to use the system SysV IPC calls. + +Currently there is not object support for SysV shared memory, but +SysV::SharedMem is a project for the future. + +Share and enjoy! + diff --git a/gnu/usr.bin/perl/ext/IPC/SysV/Semaphore.pm b/gnu/usr.bin/perl/ext/IPC/SysV/Semaphore.pm new file mode 100644 index 00000000000..464eb0bc192 --- /dev/null +++ b/gnu/usr.bin/perl/ext/IPC/SysV/Semaphore.pm @@ -0,0 +1,297 @@ +# IPC::Semaphore +# +# Copyright (c) 1997 Graham Barr <gbarr@pobox.com>. All rights reserved. +# This program is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. + +package IPC::Semaphore; + +use IPC::SysV qw(GETNCNT GETZCNT GETVAL SETVAL GETPID GETALL SETALL + IPC_STAT IPC_SET IPC_RMID); +use strict; +use vars qw($VERSION); +use Carp; + +$VERSION = "1.00"; + +{ + package IPC::Semaphore::stat; + + use Class::Struct qw(struct); + + struct 'IPC::Semaphore::stat' => [ + uid => '$', + gid => '$', + cuid => '$', + cgid => '$', + mode => '$', + ctime => '$', + otime => '$', + nsems => '$', + ]; +} + +sub new { + @_ == 4 || croak 'new ' . __PACKAGE__ . '( KEY, NSEMS, FLAGS )'; + my $class = shift; + + my $id = semget($_[0],$_[1],$_[2]); + + defined($id) + ? bless \$id, $class + : undef; +} + +sub id { + my $self = shift; + $$self; +} + +sub remove { + my $self = shift; + (semctl($$self,0,IPC_RMID,0), undef $$self)[0]; +} + +sub getncnt { + @_ == 2 || croak '$sem->getncnt( SEM )'; + my $self = shift; + my $sem = shift; + my $v = semctl($$self,$sem,GETNCNT,0); + $v ? 0 + $v : undef; +} + +sub getzcnt { + @_ == 2 || croak '$sem->getzcnt( SEM )'; + my $self = shift; + my $sem = shift; + my $v = semctl($$self,$sem,GETZCNT,0); + $v ? 0 + $v : undef; +} + +sub getval { + @_ == 2 || croak '$sem->getval( SEM )'; + my $self = shift; + my $sem = shift; + my $v = semctl($$self,$sem,GETVAL,0); + $v ? 0 + $v : undef; +} + +sub getpid { + @_ == 2 || croak '$sem->getpid( SEM )'; + my $self = shift; + my $sem = shift; + my $v = semctl($$self,$sem,GETPID,0); + $v ? 0 + $v : undef; +} + +sub op { + @_ >= 4 || croak '$sem->op( OPLIST )'; + my $self = shift; + croak 'Bad arg count' if @_ % 3; + my $data = pack("s*",@_); + semop($$self,$data); +} + +sub stat { + my $self = shift; + my $data = ""; + semctl($$self,0,IPC_STAT,$data) + or return undef; + IPC::Semaphore::stat->new->unpack($data); +} + +sub set { + my $self = shift; + my $ds; + + if(@_ == 1) { + $ds = shift; + } + else { + croak 'Bad arg count' if @_ % 2; + my %arg = @_; + my $ds = $self->stat + or return undef; + my($key,$val); + $ds->$key($val) + while(($key,$val) = each %arg); + } + + my $v = semctl($$self,0,IPC_SET,$ds->pack); + $v ? 0 + $v : undef; +} + +sub getall { + my $self = shift; + my $data = ""; + semctl($$self,0,GETALL,$data) + or return (); + (unpack("s*",$data)); +} + +sub setall { + my $self = shift; + my $data = pack("s*",@_); + semctl($$self,0,SETALL,$data); +} + +sub setval { + @_ == 3 || croak '$sem->setval( SEM, VAL )'; + my $self = shift; + my $sem = shift; + my $val = shift; + semctl($$self,$sem,SETVAL,$val); +} + +1; + +__END__ + +=head1 NAME + +IPC::Semaphore - SysV Semaphore IPC object class + +=head1 SYNOPSIS + + use IPC::SysV qw(IPC_PRIVATE S_IRWXU IPC_CREAT); + use IPC::Semaphore; + + $sem = new IPC::Semaphore(IPC_PRIVATE, 10, S_IRWXU | IPC_CREAT); + + $sem->setall( (0) x 10); + + @sem = $sem->getall; + + $ncnt = $sem->getncnt; + + $zcnt = $sem->getzcnt; + + $ds = $sem->stat; + + $sem->remove; + +=head1 DESCRIPTION + +=head1 METHODS + +=over 4 + +=item new ( KEY , NSEMS , FLAGS ) + +Create a new semaphore set associated with C<KEY>. C<NSEMS> is the number +of semaphores in the set. A new set is created if + +=over 4 + +=item * + +C<KEY> is equal to C<IPC_PRIVATE> + +=item * + +C<KEY> does not already have a semaphore identifier +associated with it, and C<I<FLAGS> & IPC_CREAT> is true. + +=back + +On creation of a new semaphore set C<FLAGS> is used to set the +permissions. + +=item getall + +Returns the values of the semaphore set as an array. + +=item getncnt ( SEM ) + +Returns the number of processed waiting for the semaphore C<SEM> to +become greater than it's current value + +=item getpid ( SEM ) + +Returns the process id of the last process that performed an operation +on the semaphore C<SEM>. + +=item getval ( SEM ) + +Returns the current value of the semaphore C<SEM>. + +=item getzcnt ( SEM ) + +Returns the number of processed waiting for the semaphore C<SEM> to +become zero. + +=item id + +Returns the system identifier for the semaphore set. + +=item op ( OPLIST ) + +C<OPLIST> is a list of operations to pass to C<semop>. C<OPLIST> is +a concatenation of smaller lists, each which has three values. The +first is the semaphore number, the second is the operation and the last +is a flags value. See L<semop> for more details. For example + + $sem->op( + 0, -1, IPC_NOWAIT, + 1, 1, IPC_NOWAIT + ); + +=item remove + +Remove and destroy the semaphore set from the system. + +=item set ( STAT ) + +=item set ( NAME => VALUE [, NAME => VALUE ...] ) + +C<set> will set the following values of the C<stat> structure associated +with the semaphore set. + + uid + gid + mode (oly the permission bits) + +C<set> accepts either a stat object, as returned by the C<stat> method, +or a list of I<name>-I<value> pairs. + +=item setall ( VALUES ) + +Sets all values in the semaphore set to those given on the C<VALUES> list. +C<VALUES> must contain the correct number of values. + +=item setval ( N , VALUE ) + +Set the C<N>th value in the semaphore set to C<VALUE> + +=item stat + +Returns an object of type C<IPC::Semaphore::stat> which is a sub-class of +C<Class::Struct>. It provides the following fields. For a description +of these fields see you system documentation. + + uid + gid + cuid + cgid + mode + ctime + otime + nsems + +=back + +=head1 SEE ALSO + +L<IPC::SysV> L<Class::Struct> L<semget> L<semctl> L<semop> + +=head1 AUTHOR + +Graham Barr <gbarr@pobox.com> + +=head1 COPYRIGHT + +Copyright (c) 1997 Graham Barr. All rights reserved. +This program is free software; you can redistribute it and/or modify it +under the same terms as Perl itself. + +=cut diff --git a/gnu/usr.bin/perl/ext/IPC/SysV/SysV.pm b/gnu/usr.bin/perl/ext/IPC/SysV/SysV.pm new file mode 100644 index 00000000000..eb245937aa4 --- /dev/null +++ b/gnu/usr.bin/perl/ext/IPC/SysV/SysV.pm @@ -0,0 +1,98 @@ +# IPC::SysV.pm +# +# Copyright (c) 1997 Graham Barr <gbarr@pobox.com>. All rights reserved. +# This program is free software; you can redistribute it and/or +# modify it under the same terms as Perl itself. + +package IPC::SysV; + +use strict; +use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); +use Carp; +use Config; + +require Exporter; +@ISA = qw(Exporter); + +$VERSION = "1.03"; + +@EXPORT_OK = qw( + GETALL GETNCNT GETPID GETVAL GETZCNT + + IPC_ALLOC IPC_CREAT IPC_EXCL IPC_GETACL IPC_LOCKED IPC_M + IPC_NOERROR IPC_NOWAIT IPC_PRIVATE IPC_R IPC_RMID IPC_SET + IPC_SETACL IPC_SETLABEL IPC_STAT IPC_W IPC_WANTED + + MSG_FWAIT MSG_LOCKED MSG_MWAIT MSG_NOERROR MSG_QWAIT + MSG_R MSG_RWAIT MSG_STAT MSG_W MSG_WWAIT + + SEM_A SEM_ALLOC SEM_DEST SEM_ERR SEM_ORDER SEM_R SEM_UNDO + + SETALL SETVAL + + SHMLBA + + SHM_A SHM_CLEAR SHM_COPY SHM_DCACHE SHM_DEST SHM_ECACHE + SHM_FMAP SHM_ICACHE SHM_INIT SHM_LOCK SHM_LOCKED SHM_MAP + SHM_NOSWAP SHM_R SHM_RDONLY SHM_REMOVED SHM_RND SHM_SHARE_MMU + SHM_SHATTR SHM_SIZE SHM_UNLOCK SHM_W + + S_IRUSR S_IWUSR S_IRWXU + S_IRGRP S_IWGRP S_IRWXG + S_IROTH S_IWOTH S_IRWXO + + ftok +); + +BOOT_XS: { + # If I inherit DynaLoader then I inherit AutoLoader and I DON'T WANT TO + require DynaLoader; + + # DynaLoader calls dl_load_flags as a static method. + *dl_load_flags = DynaLoader->can('dl_load_flags'); + + do { + __PACKAGE__->can('bootstrap') || \&DynaLoader::bootstrap + }->(__PACKAGE__, $VERSION); +} + +1; + +__END__ + +=head1 NAME + +IPC::SysV - SysV IPC constants + +=head1 SYNOPSIS + + use IPC::SysV qw(IPC_STAT IPC_PRIVATE); + +=head1 DESCRIPTION + +C<IPC::SysV> defines and conditionally exports all the constants +defined in your system include files which are needed by the SysV +IPC calls. + +=item ftok( PATH, ID ) + +Return a key based on PATH and ID, which can be used as a key for +C<msgget>, C<semget> and C<shmget>. See L<ftok> + +=head1 SEE ALSO + +L<IPC::Msg>, L<IPC::Semaphore>, L<ftok> + +=head1 AUTHORS + +Graham Barr <gbarr@pobox.com> +Jarkko Hietaniemi <jhi@iki.fi> + +=head1 COPYRIGHT + +Copyright (c) 1997 Graham Barr. All rights reserved. +This program is free software; you can redistribute it and/or modify it +under the same terms as Perl itself. + +=cut + diff --git a/gnu/usr.bin/perl/ext/IPC/SysV/SysV.xs b/gnu/usr.bin/perl/ext/IPC/SysV/SysV.xs new file mode 100644 index 00000000000..ecd52702222 --- /dev/null +++ b/gnu/usr.bin/perl/ext/IPC/SysV/SysV.xs @@ -0,0 +1,443 @@ +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +#include <sys/types.h> +#ifdef __linux__ +# include <asm/page.h> +#endif +#if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM) +#ifndef HAS_SEM +# include <sys/ipc.h> +#endif +# ifdef HAS_MSG +# include <sys/msg.h> +# endif +# ifdef HAS_SHM +# if defined(PERL_SCO) || defined(PERL_ISC) +# include <sys/sysmacros.h> /* SHMLBA */ +# endif +# include <sys/shm.h> +# ifndef HAS_SHMAT_PROTOTYPE + extern Shmat_t shmat _((int, char *, int)); +# endif +# if defined(__sparc__) && (defined(__NetBSD__) || defined(__OpenBSD__)) +# undef SHMLBA /* not static: determined at boot time */ +# define SHMLBA getpagesize() +# endif +# endif +#endif + +/* Required to get 'struct pte' for SHMLBA on ULTRIX. */ +#if defined(__ultrix) || defined(__ultrix__) || defined(ultrix) +# include <machine/pte.h> +#endif + +/* Required in BSDI to get PAGE_SIZE definition for SHMLBA. + * Ugly. More beautiful solutions welcome. + * Shouting at BSDI sounds quite beautiful. */ +#ifdef __bsdi__ +# include <vm/vm_param.h> /* move upwards under HAS_SHM? */ +#endif + +#ifndef S_IRWXU +# ifdef S_IRUSR +# define S_IRWXU (S_IRUSR|S_IWUSR|S_IWUSR) +# define S_IRWXG (S_IRGRP|S_IWGRP|S_IWGRP) +# define S_IRWXO (S_IROTH|S_IWOTH|S_IWOTH) +# else +# define S_IRWXU 0700 +# define S_IRWXG 0070 +# define S_IRWXO 0007 +# endif +#endif + +MODULE=IPC::SysV PACKAGE=IPC::Msg::stat + +PROTOTYPES: ENABLE + +void +pack(obj) + SV * obj +PPCODE: +{ +#ifdef HAS_MSG + SV *sv; + struct msqid_ds ds; + AV *list = (AV*)SvRV(obj); + sv = *av_fetch(list,0,TRUE); ds.msg_perm.uid = SvIV(sv); + sv = *av_fetch(list,1,TRUE); ds.msg_perm.gid = SvIV(sv); + sv = *av_fetch(list,4,TRUE); ds.msg_perm.mode = SvIV(sv); + sv = *av_fetch(list,6,TRUE); ds.msg_qbytes = SvIV(sv); + ST(0) = sv_2mortal(newSVpv((char *)&ds,sizeof(ds))); + XSRETURN(1); +#else + croak("System V msgxxx is not implemented on this machine"); +#endif +} + +void +unpack(obj,buf) + SV * obj + SV * buf +PPCODE: +{ +#ifdef HAS_MSG + STRLEN len; + SV **sv_ptr; + struct msqid_ds *ds = (struct msqid_ds *)SvPV(buf,len); + AV *list = (AV*)SvRV(obj); + if (len != sizeof(*ds)) { + croak("Bad arg length for %s, length is %d, should be %d", + "IPC::Msg::stat", + len, sizeof(*ds)); + } + sv_ptr = av_fetch(list,0,TRUE); + sv_setiv(*sv_ptr, ds->msg_perm.uid); + sv_ptr = av_fetch(list,1,TRUE); + sv_setiv(*sv_ptr, ds->msg_perm.gid); + sv_ptr = av_fetch(list,2,TRUE); + sv_setiv(*sv_ptr, ds->msg_perm.cuid); + sv_ptr = av_fetch(list,3,TRUE); + sv_setiv(*sv_ptr, ds->msg_perm.cgid); + sv_ptr = av_fetch(list,4,TRUE); + sv_setiv(*sv_ptr, ds->msg_perm.mode); + sv_ptr = av_fetch(list,5,TRUE); + sv_setiv(*sv_ptr, ds->msg_qnum); + sv_ptr = av_fetch(list,6,TRUE); + sv_setiv(*sv_ptr, ds->msg_qbytes); + sv_ptr = av_fetch(list,7,TRUE); + sv_setiv(*sv_ptr, ds->msg_lspid); + sv_ptr = av_fetch(list,8,TRUE); + sv_setiv(*sv_ptr, ds->msg_lrpid); + sv_ptr = av_fetch(list,9,TRUE); + sv_setiv(*sv_ptr, ds->msg_stime); + sv_ptr = av_fetch(list,10,TRUE); + sv_setiv(*sv_ptr, ds->msg_rtime); + sv_ptr = av_fetch(list,11,TRUE); + sv_setiv(*sv_ptr, ds->msg_ctime); + XSRETURN(1); +#else + croak("System V msgxxx is not implemented on this machine"); +#endif +} + +MODULE=IPC::SysV PACKAGE=IPC::Semaphore::stat + +void +unpack(obj,ds) + SV * obj + SV * ds +PPCODE: +{ +#ifdef HAS_SEM + STRLEN len; + AV *list = (AV*)SvRV(obj); + struct semid_ds *data = (struct semid_ds *)SvPV(ds,len); + if(!sv_isa(obj, "IPC::Semaphore::stat")) + croak("method %s not called a %s object", + "unpack","IPC::Semaphore::stat"); + if (len != sizeof(*data)) { + croak("Bad arg length for %s, length is %d, should be %d", + "IPC::Semaphore::stat", + len, sizeof(*data)); + } + sv_setiv(*av_fetch(list,0,TRUE), data[0].sem_perm.uid); + sv_setiv(*av_fetch(list,1,TRUE), data[0].sem_perm.gid); + sv_setiv(*av_fetch(list,2,TRUE), data[0].sem_perm.cuid); + sv_setiv(*av_fetch(list,3,TRUE), data[0].sem_perm.cgid); + sv_setiv(*av_fetch(list,4,TRUE), data[0].sem_perm.mode); + sv_setiv(*av_fetch(list,5,TRUE), data[0].sem_ctime); + sv_setiv(*av_fetch(list,6,TRUE), data[0].sem_otime); + sv_setiv(*av_fetch(list,7,TRUE), data[0].sem_nsems); + XSRETURN(1); +#else + croak("System V semxxx is not implemented on this machine"); +#endif +} + +void +pack(obj) + SV * obj +PPCODE: +{ +#ifdef HAS_SEM + SV **sv_ptr; + SV *sv; + struct semid_ds ds; + AV *list = (AV*)SvRV(obj); + if(!sv_isa(obj, "IPC::Semaphore::stat")) + croak("method %s not called a %s object", + "pack","IPC::Semaphore::stat"); + if((sv_ptr = av_fetch(list,0,TRUE)) && (sv = *sv_ptr)) + ds.sem_perm.uid = SvIV(*sv_ptr); + if((sv_ptr = av_fetch(list,1,TRUE)) && (sv = *sv_ptr)) + ds.sem_perm.gid = SvIV(*sv_ptr); + if((sv_ptr = av_fetch(list,2,TRUE)) && (sv = *sv_ptr)) + ds.sem_perm.cuid = SvIV(*sv_ptr); + if((sv_ptr = av_fetch(list,3,TRUE)) && (sv = *sv_ptr)) + ds.sem_perm.cgid = SvIV(*sv_ptr); + if((sv_ptr = av_fetch(list,4,TRUE)) && (sv = *sv_ptr)) + ds.sem_perm.mode = SvIV(*sv_ptr); + if((sv_ptr = av_fetch(list,5,TRUE)) && (sv = *sv_ptr)) + ds.sem_ctime = SvIV(*sv_ptr); + if((sv_ptr = av_fetch(list,6,TRUE)) && (sv = *sv_ptr)) + ds.sem_otime = SvIV(*sv_ptr); + if((sv_ptr = av_fetch(list,7,TRUE)) && (sv = *sv_ptr)) + ds.sem_nsems = SvIV(*sv_ptr); + ST(0) = sv_2mortal(newSVpv((char *)&ds,sizeof(ds))); + XSRETURN(1); +#else + croak("System V semxxx is not implemented on this machine"); +#endif +} + +MODULE=IPC::SysV PACKAGE=IPC::SysV + +int +ftok(path, id) + char * path + int id + CODE: +#if defined(HAS_SEM) || defined(HAS_SHM) + key_t k = ftok(path, id); + ST(0) = k == (key_t) -1 ? &PL_sv_undef : sv_2mortal(newSViv(k)); +#else + DIE(no_func, "ftok"); +#endif + +int +SHMLBA() + CODE: +#ifdef SHMLBA + ST(0) = sv_2mortal(newSViv(SHMLBA)); +#else + croak("SHMLBA is not defined on this architecture"); +#endif + +BOOT: +{ + HV *stash = gv_stashpvn("IPC::SysV", 9, TRUE); + /* + * constant subs for IPC::SysV + */ + struct { char *n; I32 v; } IPC__SysV__const[] = { +#ifdef GETVAL + {"GETVAL", GETVAL}, +#endif +#ifdef GETPID + {"GETPID", GETPID}, +#endif +#ifdef GETNCNT + {"GETNCNT", GETNCNT}, +#endif +#ifdef GETZCNT + {"GETZCNT", GETZCNT}, +#endif +#ifdef GETALL + {"GETALL", GETALL}, +#endif +#ifdef IPC_ALLOC + {"IPC_ALLOC", IPC_ALLOC}, +#endif +#ifdef IPC_CREAT + {"IPC_CREAT", IPC_CREAT}, +#endif +#ifdef IPC_EXCL + {"IPC_EXCL", IPC_EXCL}, +#endif +#ifdef IPC_GETACL + {"IPC_GETACL", IPC_EXCL}, +#endif +#ifdef IPC_LOCKED + {"IPC_LOCKED", IPC_LOCKED}, +#endif +#ifdef IPC_M + {"IPC_M", IPC_M}, +#endif +#ifdef IPC_NOERROR + {"IPC_NOERROR", IPC_NOERROR}, +#endif +#ifdef IPC_NOWAIT + {"IPC_NOWAIT", IPC_NOWAIT}, +#endif +#ifdef IPC_PRIVATE + {"IPC_PRIVATE", IPC_PRIVATE}, +#endif +#ifdef IPC_R + {"IPC_R", IPC_R}, +#endif +#ifdef IPC_RMID + {"IPC_RMID", IPC_RMID}, +#endif +#ifdef IPC_SET + {"IPC_SET", IPC_SET}, +#endif +#ifdef IPC_SETACL + {"IPC_SETACL", IPC_SETACL}, +#endif +#ifdef IPC_SETLABEL + {"IPC_SETLABEL", IPC_SETLABEL}, +#endif +#ifdef IPC_STAT + {"IPC_STAT", IPC_STAT}, +#endif +#ifdef IPC_W + {"IPC_W", IPC_W}, +#endif +#ifdef IPC_WANTED + {"IPC_WANTED", IPC_WANTED}, +#endif +#ifdef MSG_NOERROR + {"MSG_NOERROR", MSG_NOERROR}, +#endif +#ifdef MSG_FWAIT + {"MSG_FWAIT", MSG_FWAIT}, +#endif +#ifdef MSG_LOCKED + {"MSG_LOCKED", MSG_LOCKED}, +#endif +#ifdef MSG_MWAIT + {"MSG_MWAIT", MSG_MWAIT}, +#endif +#ifdef MSG_WAIT + {"MSG_WAIT", MSG_WAIT}, +#endif +#ifdef MSG_R + {"MSG_R", MSG_R}, +#endif +#ifdef MSG_RWAIT + {"MSG_RWAIT", MSG_RWAIT}, +#endif +#ifdef MSG_STAT + {"MSG_STAT", MSG_STAT}, +#endif +#ifdef MSG_W + {"MSG_W", MSG_W}, +#endif +#ifdef MSG_WWAIT + {"MSG_WWAIT", MSG_WWAIT}, +#endif +#ifdef SEM_A + {"SEM_A", SEM_A}, +#endif +#ifdef SEM_ALLOC + {"SEM_ALLOC", SEM_ALLOC}, +#endif +#ifdef SEM_DEST + {"SEM_DEST", SEM_DEST}, +#endif +#ifdef SEM_ERR + {"SEM_ERR", SEM_ERR}, +#endif +#ifdef SEM_R + {"SEM_R", SEM_R}, +#endif +#ifdef SEM_ORDER + {"SEM_ORDER", SEM_ORDER}, +#endif +#ifdef SEM_UNDO + {"SEM_UNDO", SEM_UNDO}, +#endif +#ifdef SETVAL + {"SETVAL", SETVAL}, +#endif +#ifdef SETALL + {"SETALL", SETALL}, +#endif +#ifdef SHM_CLEAR + {"SHM_CLEAR", SHM_CLEAR}, +#endif +#ifdef SHM_COPY + {"SHM_COPY", SHM_COPY}, +#endif +#ifdef SHM_DCACHE + {"SHM_DCACHE", SHM_DCACHE}, +#endif +#ifdef SHM_DEST + {"SHM_DEST", SHM_DEST}, +#endif +#ifdef SHM_ECACHE + {"SHM_ECACHE", SHM_ECACHE}, +#endif +#ifdef SHM_FMAP + {"SHM_FMAP", SHM_FMAP}, +#endif +#ifdef SHM_ICACHE + {"SHM_ICACHE", SHM_ICACHE}, +#endif +#ifdef SHM_INIT + {"SHM_INIT", SHM_INIT}, +#endif +#ifdef SHM_LOCK + {"SHM_LOCK", SHM_LOCK}, +#endif +#ifdef SHM_LOCKED + {"SHM_LOCKED", SHM_LOCKED}, +#endif +#ifdef SHM_MAP + {"SHM_MAP", SHM_MAP}, +#endif +#ifdef SHM_NOSWAP + {"SHM_NOSWAP", SHM_NOSWAP}, +#endif +#ifdef SHM_RDONLY + {"SHM_RDONLY", SHM_RDONLY}, +#endif +#ifdef SHM_REMOVED + {"SHM_REMOVED", SHM_REMOVED}, +#endif +#ifdef SHM_RND + {"SHM_RND", SHM_RND}, +#endif +#ifdef SHM_SHARE_MMU + {"SHM_SHARE_MMU", SHM_SHARE_MMU}, +#endif +#ifdef SHM_SHATTR + {"SHM_SHATTR", SHM_SHATTR}, +#endif +#ifdef SHM_SIZE + {"SHM_SIZE", SHM_SIZE}, +#endif +#ifdef SHM_UNLOCK + {"SHM_UNLOCK", SHM_UNLOCK}, +#endif +#ifdef SHM_W + {"SHM_W", SHM_W}, +#endif +#ifdef S_IRUSR + {"S_IRUSR", S_IRUSR}, +#endif +#ifdef S_IWUSR + {"S_IWUSR", S_IWUSR}, +#endif +#ifdef S_IRWXU + {"S_IRWXU", S_IRWXU}, +#endif +#ifdef S_IRGRP + {"S_IRGRP", S_IRGRP}, +#endif +#ifdef S_IWGRP + {"S_IWGRP", S_IWGRP}, +#endif +#ifdef S_IRWXG + {"S_IRWXG", S_IRWXG}, +#endif +#ifdef S_IROTH + {"S_IROTH", S_IROTH}, +#endif +#ifdef S_IWOTH + {"S_IWOTH", S_IWOTH}, +#endif +#ifdef S_IRWXO + {"S_IRWXO", S_IRWXO}, +#endif + {Nullch,0}}; + char *name; + int i; + + for(i = 0 ; name = IPC__SysV__const[i].n ; i++) { + newCONSTSUB(stash,name, newSViv(IPC__SysV__const[i].v)); + } +} + diff --git a/gnu/usr.bin/perl/ext/IPC/SysV/t/msg.t b/gnu/usr.bin/perl/ext/IPC/SysV/t/msg.t new file mode 100644 index 00000000000..2a982f054a7 --- /dev/null +++ b/gnu/usr.bin/perl/ext/IPC/SysV/t/msg.t @@ -0,0 +1,41 @@ +use IPC::SysV qw(IPC_PRIVATE IPC_RMID IPC_NOWAIT IPC_STAT S_IRWXU S_IRWXG S_IRWXO); + +use IPC::Msg; +#Creating a message queue + +print "1..9\n"; + +$msq = new IPC::Msg(IPC_PRIVATE, S_IRWXU | S_IRWXG | S_IRWXO) + || die "msgget: ",$!+0," $!\n"; + +print "ok 1\n"; + +#Putting a message on the queue +$msgtype = 1; +$msg = "hello"; +$msq->snd($msgtype,$msg,0) || print "not "; +print "ok 2\n"; + +#Check if there are messages on the queue +$ds = $msq->stat() or print "not "; +print "ok 3\n"; + +print "not " unless $ds && $ds->qnum() == 1; +print "ok 4\n"; + +#Retreiving a message from the queue +$rmsgtype = 0; # Give me any type +$rmsgtype = $msq->rcv($rmsg,256,$rmsgtype,IPC_NOWAIT) || print "not "; +print "ok 5\n"; + +print "not " unless $rmsgtype == $msgtype && $rmsg eq $msg; +print "ok 6\n"; + +$ds = $msq->stat() or print "not "; +print "ok 7\n"; + +print "not " unless $ds && $ds->qnum() == 0; +print "ok 8\n"; + +$msq->remove || print "not "; +print "ok 9\n"; diff --git a/gnu/usr.bin/perl/ext/IPC/SysV/t/sem.t b/gnu/usr.bin/perl/ext/IPC/SysV/t/sem.t new file mode 100644 index 00000000000..9d6fff64f23 --- /dev/null +++ b/gnu/usr.bin/perl/ext/IPC/SysV/t/sem.t @@ -0,0 +1,51 @@ + +use IPC::SysV qw( + SETALL + IPC_PRIVATE + IPC_CREAT + IPC_RMID + IPC_NOWAIT + IPC_STAT + S_IRWXU + S_IRWXG + S_IRWXO +); +use IPC::Semaphore; + +print "1..10\n"; + +$sem = new IPC::Semaphore(IPC_PRIVATE, 10, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT) + || die "semget: ",$!+0," $!\n"; + +print "ok 1\n"; + +my $st = $sem->stat || print "not "; +print "ok 2\n"; + +$sem->setall( (0) x 10) || print "not "; +print "ok 3\n"; + +my @sem = $sem->getall; +print "not " unless join("",@sem) eq "0000000000"; +print "ok 4\n"; + +$sem[2] = 1; +$sem->setall( @sem ) || print "not "; +print "ok 5\n"; + +@sem = $sem->getall; +print "not " unless join("",@sem) eq "0010000000"; +print "ok 6\n"; + +my $ncnt = $sem->getncnt(0); +print "not " if $sem->getncnt(0) || !defined($ncnt); +print "ok 7\n"; + +$sem->op(2,-1,IPC_NOWAIT) || print "not "; +print "ok 8\n"; + +print "not " if $sem->getncnt(0); +print "ok 9\n"; + +$sem->remove || print "not "; +print "ok 10\n"; diff --git a/gnu/usr.bin/perl/ext/NDBM_File/Makefile.PL b/gnu/usr.bin/perl/ext/NDBM_File/Makefile.PL index ca4c107c0d2..6ceab55a4ae 100644 --- a/gnu/usr.bin/perl/ext/NDBM_File/Makefile.PL +++ b/gnu/usr.bin/perl/ext/NDBM_File/Makefile.PL @@ -2,7 +2,7 @@ use ExtUtils::MakeMaker; WriteMakefile( NAME => 'NDBM_File', LIBS => ["-L/usr/local/lib -lndbm", "-ldbm -lucb"], - MAN3PODS => ' ', # Pods will be built by installman. + MAN3PODS => {}, # Pods will be built by installman. XSPROTOARG => '-noprototypes', # XXX remove later? VERSION_FROM => 'NDBM_File.pm', ); diff --git a/gnu/usr.bin/perl/ext/ODBM_File/Makefile.PL b/gnu/usr.bin/perl/ext/ODBM_File/Makefile.PL index 76a5d199990..2732a32ac34 100644 --- a/gnu/usr.bin/perl/ext/ODBM_File/Makefile.PL +++ b/gnu/usr.bin/perl/ext/ODBM_File/Makefile.PL @@ -2,7 +2,7 @@ use ExtUtils::MakeMaker; WriteMakefile( NAME => 'ODBM_File', LIBS => ["-ldbm -lucb"], - MAN3PODS => ' ', # Pods will be built by installman. + MAN3PODS => {}, # Pods will be built by installman. XSPROTOARG => '-noprototypes', # XXX remove later? VERSION_FROM => 'ODBM_File.pm', ); diff --git a/gnu/usr.bin/perl/ext/ODBM_File/typemap b/gnu/usr.bin/perl/ext/ODBM_File/typemap index a6b0e5faa86..5e12e739338 100644 --- a/gnu/usr.bin/perl/ext/ODBM_File/typemap +++ b/gnu/usr.bin/perl/ext/ODBM_File/typemap @@ -14,8 +14,8 @@ FATALFUNC T_OPAQUEPTR INPUT T_DATUM - $var.dptr = SvPV($arg, na); - $var.dsize = (int)na; + $var.dptr = SvPV($arg, PL_na); + $var.dsize = (int)PL_na; T_GDATUM UNIMPLEMENTED OUTPUT diff --git a/gnu/usr.bin/perl/ext/POSIX/Makefile.PL b/gnu/usr.bin/perl/ext/POSIX/Makefile.PL index 3359d1742c3..d379fdb908e 100644 --- a/gnu/usr.bin/perl/ext/POSIX/Makefile.PL +++ b/gnu/usr.bin/perl/ext/POSIX/Makefile.PL @@ -1,8 +1,8 @@ use ExtUtils::MakeMaker; WriteMakefile( NAME => 'POSIX', - LIBS => ["-lm -lposix -lcposix"], - MAN3PODS => ' ', # Pods will be built by installman. + ($^O eq 'MSWin32' ? () : (LIBS => ["-lm -lposix -lcposix"])), + MAN3PODS => {}, # Pods will be built by installman. XSPROTOARG => '-noprototypes', # XXX remove later? VERSION_FROM => 'POSIX.pm', ); diff --git a/gnu/usr.bin/perl/ext/POSIX/hints/bsdos.pl b/gnu/usr.bin/perl/ext/POSIX/hints/bsdos.pl new file mode 100644 index 00000000000..62732ac7b9d --- /dev/null +++ b/gnu/usr.bin/perl/ext/POSIX/hints/bsdos.pl @@ -0,0 +1,3 @@ +# BSD platforms have extra fields in struct tm that need to be initialized. +# XXX A Configure test is needed. +$self->{CCFLAGS} = $Config{ccflags} . ' -DSTRUCT_TM_HASZONE' ; diff --git a/gnu/usr.bin/perl/ext/POSIX/hints/dynixptx.pl b/gnu/usr.bin/perl/ext/POSIX/hints/dynixptx.pl new file mode 100644 index 00000000000..9b636843823 --- /dev/null +++ b/gnu/usr.bin/perl/ext/POSIX/hints/dynixptx.pl @@ -0,0 +1,4 @@ +# Need to add an extra '-lc' to the end to work around a DYNIX/ptx bug +# PR#227670 - linker error on fpgetround() + +$self->{LIBS} = ['-ldb -lm -lc']; diff --git a/gnu/usr.bin/perl/ext/POSIX/hints/freebsd.pl b/gnu/usr.bin/perl/ext/POSIX/hints/freebsd.pl new file mode 100644 index 00000000000..62732ac7b9d --- /dev/null +++ b/gnu/usr.bin/perl/ext/POSIX/hints/freebsd.pl @@ -0,0 +1,3 @@ +# BSD platforms have extra fields in struct tm that need to be initialized. +# XXX A Configure test is needed. +$self->{CCFLAGS} = $Config{ccflags} . ' -DSTRUCT_TM_HASZONE' ; diff --git a/gnu/usr.bin/perl/ext/POSIX/hints/linux.pl b/gnu/usr.bin/perl/ext/POSIX/hints/linux.pl new file mode 100644 index 00000000000..f1d19814ae1 --- /dev/null +++ b/gnu/usr.bin/perl/ext/POSIX/hints/linux.pl @@ -0,0 +1,5 @@ +# libc6, aka glibc2, seems to need STRUCT_TM_HASZONE defined. +# Thanks to Bart Schuller <schuller@Lunatech.com> +# See Message-ID: <19971009002636.50729@tanglefoot> +# XXX A Configure test is needed. +$self->{CCFLAGS} = $Config{ccflags} . ' -DSTRUCT_TM_HASZONE -DHINT_SC_EXIST' ; diff --git a/gnu/usr.bin/perl/ext/POSIX/hints/mint.pl b/gnu/usr.bin/perl/ext/POSIX/hints/mint.pl new file mode 100644 index 00000000000..b975cbb2ee6 --- /dev/null +++ b/gnu/usr.bin/perl/ext/POSIX/hints/mint.pl @@ -0,0 +1,2 @@ +$self->{CCFLAGS} = $Config{ccflags} . ' -DNO_LOCALECONV_GROUPING -DNO_LOCALECONV_MON_GROUPING'; + diff --git a/gnu/usr.bin/perl/ext/POSIX/hints/netbsd.pl b/gnu/usr.bin/perl/ext/POSIX/hints/netbsd.pl new file mode 100644 index 00000000000..62732ac7b9d --- /dev/null +++ b/gnu/usr.bin/perl/ext/POSIX/hints/netbsd.pl @@ -0,0 +1,3 @@ +# BSD platforms have extra fields in struct tm that need to be initialized. +# XXX A Configure test is needed. +$self->{CCFLAGS} = $Config{ccflags} . ' -DSTRUCT_TM_HASZONE' ; diff --git a/gnu/usr.bin/perl/ext/POSIX/hints/openbsd.pl b/gnu/usr.bin/perl/ext/POSIX/hints/openbsd.pl new file mode 100644 index 00000000000..62732ac7b9d --- /dev/null +++ b/gnu/usr.bin/perl/ext/POSIX/hints/openbsd.pl @@ -0,0 +1,3 @@ +# BSD platforms have extra fields in struct tm that need to be initialized. +# XXX A Configure test is needed. +$self->{CCFLAGS} = $Config{ccflags} . ' -DSTRUCT_TM_HASZONE' ; diff --git a/gnu/usr.bin/perl/ext/POSIX/hints/sunos_4.pl b/gnu/usr.bin/perl/ext/POSIX/hints/sunos_4.pl new file mode 100644 index 00000000000..32b3558a5e8 --- /dev/null +++ b/gnu/usr.bin/perl/ext/POSIX/hints/sunos_4.pl @@ -0,0 +1,10 @@ +# SunOS 4.1.3 has two extra fields in struct tm. This works around +# the problem. Other BSD platforms may have similar problems. +# This state of affairs also persists in glibc2, found +# on linux systems running libc6. +# XXX A Configure test is needed. + +# Although <unistd.h> is inappropriate in general for SunOS, we need it +# in POSIX.xs to get the correct prototype for ttyname(). + +$self->{CCFLAGS} = $Config{ccflags} . ' -DSTRUCT_TM_HASZONE -DI_UNISTD' ; diff --git a/gnu/usr.bin/perl/ext/POSIX/typemap b/gnu/usr.bin/perl/ext/POSIX/typemap index 45e0862ff0f..63e41c77bf1 100644 --- a/gnu/usr.bin/perl/ext/POSIX/typemap +++ b/gnu/usr.bin/perl/ext/POSIX/typemap @@ -4,6 +4,7 @@ Uid_t T_NV Time_t T_NV Gid_t T_NV Off_t T_NV +Dev_t T_NV fd T_IV speed_t T_IV tcflag_t T_IV diff --git a/gnu/usr.bin/perl/ext/SDBM_File/sdbm/dba.c b/gnu/usr.bin/perl/ext/SDBM_File/sdbm/dba.c index 4f227e52456..05e70c8961c 100644 --- a/gnu/usr.bin/perl/ext/SDBM_File/sdbm/dba.c +++ b/gnu/usr.bin/perl/ext/SDBM_File/sdbm/dba.c @@ -4,6 +4,7 @@ #include <stdio.h> #include <sys/file.h> +#include "EXTERN.h" #include "sdbm.h" char *progname; diff --git a/gnu/usr.bin/perl/ext/SDBM_File/sdbm/dbd.c b/gnu/usr.bin/perl/ext/SDBM_File/sdbm/dbd.c index 697a5475977..04ab842e2d6 100644 --- a/gnu/usr.bin/perl/ext/SDBM_File/sdbm/dbd.c +++ b/gnu/usr.bin/perl/ext/SDBM_File/sdbm/dbd.c @@ -4,6 +4,7 @@ #include <stdio.h> #include <sys/file.h> +#include "EXTERN.h" #include "sdbm.h" char *progname; diff --git a/gnu/usr.bin/perl/ext/SDBM_File/sdbm/dbu.c b/gnu/usr.bin/perl/ext/SDBM_File/sdbm/dbu.c index 106262872e2..a3c0004da9f 100644 --- a/gnu/usr.bin/perl/ext/SDBM_File/sdbm/dbu.c +++ b/gnu/usr.bin/perl/ext/SDBM_File/sdbm/dbu.c @@ -1,6 +1,7 @@ #include <stdio.h> #include <sys/file.h> #ifdef SDBM +#include "EXTERN.h" #include "sdbm.h" #else #include <ndbm.h> diff --git a/gnu/usr.bin/perl/ext/SDBM_File/sdbm/hash.c b/gnu/usr.bin/perl/ext/SDBM_File/sdbm/hash.c index eb585ac1025..9b276485993 100644 --- a/gnu/usr.bin/perl/ext/SDBM_File/sdbm/hash.c +++ b/gnu/usr.bin/perl/ext/SDBM_File/sdbm/hash.c @@ -8,6 +8,7 @@ */ #include "config.h" +#include "EXTERN.h" #include "sdbm.h" /* * polynomial conversion ignoring overflows @@ -17,9 +18,7 @@ * 65587 even better. */ long -sdbm_hash(str, len) -register char *str; -register int len; +sdbm_hash(register char *str, register int len) { register unsigned long n = 0; diff --git a/gnu/usr.bin/perl/ext/SDBM_File/sdbm/util.c b/gnu/usr.bin/perl/ext/SDBM_File/sdbm/util.c index 4b03d89f09a..16bd4ac9a5c 100644 --- a/gnu/usr.bin/perl/ext/SDBM_File/sdbm/util.c +++ b/gnu/usr.bin/perl/ext/SDBM_File/sdbm/util.c @@ -6,9 +6,7 @@ #endif void -oops(s1, s2) -register char *s1; -register char *s2; +oops(register char *s1, register char *s2) { extern int errno, sys_nerr; extern char *sys_errlist[]; @@ -24,8 +22,7 @@ register char *s2; } int -okpage(pag) -char *pag; +okpage(char *pag) { register unsigned n; register off; diff --git a/gnu/usr.bin/perl/ext/Socket/Makefile.PL b/gnu/usr.bin/perl/ext/Socket/Makefile.PL index 7b9469a728e..3819143ac0e 100644 --- a/gnu/usr.bin/perl/ext/Socket/Makefile.PL +++ b/gnu/usr.bin/perl/ext/Socket/Makefile.PL @@ -2,6 +2,6 @@ use ExtUtils::MakeMaker; WriteMakefile( NAME => 'Socket', VERSION_FROM => 'Socket.pm', - MAN3PODS => ' ', # Pods will be built by installman. + MAN3PODS => {}, # Pods will be built by installman. XSPROTOARG => '-noprototypes', # XXX remove later? ); diff --git a/gnu/usr.bin/perl/ext/Thread/Makefile.PL b/gnu/usr.bin/perl/ext/Thread/Makefile.PL new file mode 100644 index 00000000000..e67fbb7e264 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/Makefile.PL @@ -0,0 +1,7 @@ +use ExtUtils::MakeMaker; +WriteMakefile( + NAME => 'Thread', + VERSION_FROM => 'Thread.pm', + MAN3PODS => {} + ); + diff --git a/gnu/usr.bin/perl/ext/Thread/Notes b/gnu/usr.bin/perl/ext/Thread/Notes new file mode 100644 index 00000000000..1505877ee9d --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/Notes @@ -0,0 +1,13 @@ +Should cvcache be per CV (keyed by thread) or per thread (keyed by CV)? + +Maybe ought to protect all SVs by a mutex for SvREFCNT_{dec,inc}, +upgrades and so on. Then use SvMUTEX instead of CvMUTEX for CVs. +On the other hand, people shouldn't expect concurrent operations +on non-lexicals to be safe anyway. + +Probably don't need to bother keeping track of CvOWNER on clones. + +Either @_ needs to be made lexical or other arrangments need to be +made so that some globs (or just *_) are per-thread. + +tokenbuf and buf probably ought to be global protected by a global lock. diff --git a/gnu/usr.bin/perl/ext/Thread/README b/gnu/usr.bin/perl/ext/Thread/README new file mode 100644 index 00000000000..a6b22fb4ae6 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/README @@ -0,0 +1,20 @@ +See the README.threads in the main perl 5.004_xx development +distribution (x >= 50) for details of how to build and use this. +If all else fails, read on. + +If your version of patch can't create a file from scratch, then you'll +need to create an empty thread.h manually first. Perl itself will need +to be built with -DUSE_THREADS yet. If you're using MIT pthreads or +another threads package that needs pthread_init() to be called, then +add -DNEED_PTHREAD_INIT. If you're using a threads library that only +follows one of the old POSIX drafts, then you'll probably need to add +-DOLD_PTHREADS_API. I haven't tested -DOLD_PTHREADS_API properly yet +and I think you may still have to tweak a couple of the mutex calls +to follow the old API. + +This extension is copyright Malcolm Beattie 1995-1997 and is freely +distributable under your choice of the GNU Public License or the +Artistic License (see the main perl distribution). + +Malcolm Beattie +mbeattie@sable.ox.ac.uk diff --git a/gnu/usr.bin/perl/ext/Thread/Thread.pm b/gnu/usr.bin/perl/ext/Thread/Thread.pm new file mode 100644 index 00000000000..c8bca0db713 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/Thread.pm @@ -0,0 +1,185 @@ +package Thread; +require Exporter; +require DynaLoader; +use vars qw($VERSION @ISA @EXPORT); + +$VERSION = "1.0"; + +@ISA = qw(Exporter DynaLoader); +@EXPORT_OK = qw(yield cond_signal cond_broadcast cond_wait async); + +=head1 NAME + +Thread - multithreading + +=head1 SYNOPSIS + + use Thread; + + my $t = new Thread \&start_sub, @start_args; + + $t->join; + + my $tid = Thread->self->tid; + + my $tlist = Thread->list; + + lock($scalar); + + use Thread 'async'; + + use Thread 'eval'; + +=head1 DESCRIPTION + +The C<Thread> module provides multithreading support for perl. + +=head1 FUNCTIONS + +=over 8 + +=item new \&start_sub + +=item new \&start_sub, LIST + +C<new> starts a new thread of execution in the referenced subroutine. The +optional list is passed as parameters to the subroutine. Execution +continues in both the subroutine and the code after the C<new> call. + +C<new Thread> returns a thread object representing the newly created +thread. + +=item lock VARIABLE + +C<lock> places a lock on a variable until the lock goes out of scope. If +the variable is locked by another thread, the C<lock> call will block until +it's available. C<lock> is recursive, so multiple calls to C<lock> are +safe--the variable will remain locked until the outermost lock on the +variable goes out of scope. + +Locks on variables only affect C<lock> calls--they do I<not> affect normal +access to a variable. (Locks on subs are different, and covered in a bit) +If you really, I<really> want locks to block access, then go ahead and tie +them to something and manage this yourself. This is done on purpose. While +managing access to variables is a good thing, perl doesn't force you out of +its living room... + +If a container object, such as a hash or array, is locked, all the elements +of that container are not locked. For example, if a thread does a C<lock +@a>, any other thread doing a C<lock($a[12])> won't block. + +You may also C<lock> a sub, using C<lock &sub>. Any calls to that sub from +another thread will block until the lock is released. This behaviour is not +equvalent to C<use attrs qw(locked)> in the sub. C<use attrs qw(locked)> +serializes access to a subroutine, but allows different threads +non-simultaneous access. C<lock &sub>, on the other hand, will not allow +I<any> other thread access for the duration of the lock. + +Finally, C<lock> will traverse up references exactly I<one> level. +C<lock(\$a)> is equivalent to C<lock($a)>, while C<lock(\\$a)> is not. + +=item async BLOCK; + +C<async> creates a thread to execute the block immediately following +it. This block is treated as an anonymous sub, and so must have a +semi-colon after the closing brace. Like C<new Thread>, C<async> returns a +thread object. + +=item Thread->self + +The C<Thread-E<gt>self> function returns a thread object that represents +the thread making the C<Thread-E<gt>self> call. + +=item Thread->list + +C<Thread-E<gt>list> returns a list of thread objects for all running and +finished but un-C<join>ed threads. + +=item cond_wait VARIABLE + +The C<cond_wait> function takes a B<locked> variable as a parameter, +unlocks the variable, and blocks until another thread does a C<cond_signal> +or C<cond_broadcast> for that same locked variable. The variable that +C<cond_wait> blocked on is relocked after the C<cond_wait> is satisfied. +If there are multiple threads C<cond_wait>ing on the same variable, all but +one will reblock waiting to reaquire the lock on the variable. (So if +you're only using C<cond_wait> for synchronization, give up the lock as +soon as possible) + +=item cond_signal VARIABLE + +The C<cond_signal> function takes a locked variable as a parameter and +unblocks one thread that's C<cond_wait>ing on that variable. If more than +one thread is blocked in a C<cond_wait> on that variable, only one (and +which one is indeterminate) will be unblocked. + +If there are no threads blocked in a C<cond_wait> on the variable, the +signal is discarded. + +=item cond_broadcast VARIABLE + +The C<cond_broadcast> function works similarly to C<cond_wait>. +C<cond_broadcast>, though, will unblock B<all> the threads that are blocked +in a C<cond_wait> on the locked variable, rather than only one. + +=back + +=head1 METHODS + +=over 8 + +=item join + +C<join> waits for a thread to end and returns any values the thread exited +with. C<join> will block until the thread has ended, though it won't block +if the thread has already terminated. + +If the thread being C<join>ed C<die>d, the error it died with will be +returned at this time. If you don't want the thread performing the C<join> +to die as well, you should either wrap the C<join> in an C<eval> or use the +C<eval> thread method instead of C<join>. + +=item eval + +The C<eval> method wraps an C<eval> around a C<join>, and so waits for a +thread to exit, passing along any values the thread might have returned. +Errors, of course, get placed into C<$@>. + +=item tid + +The C<tid> method returns the tid of a thread. The tid is a monotonically +increasing integer assigned when a thread is created. The main thread of a +program will have a tid of zero, while subsequent threads will have tids +assigned starting with one. + +=head1 LIMITATIONS + +The sequence number used to assign tids is a simple integer, and no +checking is done to make sure the tid isn't currently in use. If a program +creates more than 2^32 - 1 threads in a single run, threads may be assigned +duplicate tids. This limitation may be lifted in a future version of Perl. + +=head1 SEE ALSO + +L<attrs>, L<Thread::Queue>, L<Thread::Semaphore>, L<Thread::Specific>. + +=cut + +# +# Methods +# + +# +# Exported functions +# +sub async (&) { + return new Thread $_[0]; +} + +sub eval { + return eval { shift->join; }; +} + +bootstrap Thread; + +1; diff --git a/gnu/usr.bin/perl/ext/Thread/Thread.xs b/gnu/usr.bin/perl/ext/Thread/Thread.xs new file mode 100644 index 00000000000..2337e8c3881 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/Thread.xs @@ -0,0 +1,662 @@ +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +/* Magic signature for Thread's mg_private is "Th" */ +#define Thread_MAGIC_SIGNATURE 0x5468 + +#ifdef __cplusplus +#ifdef I_UNISTD +#include <unistd.h> +#endif +#endif +#include <fcntl.h> + +static int sig_pipe[2]; + +#ifndef THREAD_RET_TYPE +#define THREAD_RET_TYPE void * +#define THREAD_RET_CAST(x) ((THREAD_RET_TYPE) x) +#endif + +static void +remove_thread(struct perl_thread *t) +{ +#ifdef USE_THREADS + DEBUG_S(WITH_THR(PerlIO_printf(PerlIO_stderr(), + "%p: remove_thread %p\n", thr, t))); + MUTEX_LOCK(&PL_threads_mutex); + MUTEX_DESTROY(&t->mutex); + PL_nthreads--; + t->prev->next = t->next; + t->next->prev = t->prev; + COND_BROADCAST(&PL_nthreads_cond); + MUTEX_UNLOCK(&PL_threads_mutex); +#endif +} + +static THREAD_RET_TYPE +threadstart(void *arg) +{ +#ifdef USE_THREADS +#ifdef FAKE_THREADS + Thread savethread = thr; + LOGOP myop; + dSP; + I32 oldscope = PL_scopestack_ix; + I32 retval; + AV *av; + int i; + + DEBUG_S(PerlIO_printf(PerlIO_stderr(), "new thread %p starting at %s\n", + thr, SvPEEK(TOPs))); + thr = (Thread) arg; + savemark = TOPMARK; + thr->prev = thr->prev_run = savethread; + thr->next = savethread->next; + thr->next_run = savethread->next_run; + savethread->next = savethread->next_run = thr; + thr->wait_queue = 0; + thr->private = 0; + + /* Now duplicate most of perl_call_sv but with a few twists */ + PL_op = (OP*)&myop; + Zero(PL_op, 1, LOGOP); + myop.op_flags = OPf_STACKED; + myop.op_next = Nullop; + myop.op_flags |= OPf_KNOW; + myop.op_flags |= OPf_WANT_LIST; + PL_op = pp_entersub(ARGS); + DEBUG_S(if (!PL_op) + PerlIO_printf(PerlIO_stderr(), "thread starts at Nullop\n")); + /* + * When this thread is next scheduled, we start in the right + * place. When the thread runs off the end of the sub, perl.c + * handles things, using savemark to figure out how much of the + * stack is the return value for any join. + */ + thr = savethread; /* back to the old thread */ + return 0; +#else + Thread thr = (Thread) arg; + LOGOP myop; + djSP; + I32 oldmark = TOPMARK; + I32 oldscope = PL_scopestack_ix; + I32 retval; + SV *sv; + AV *av = newAV(); + int i, ret; + dJMPENV; + DEBUG_S(PerlIO_printf(PerlIO_stderr(), "new thread %p waiting to start\n", + thr)); + + /* Don't call *anything* requiring dTHR until after SET_THR() */ + /* + * Wait until our creator releases us. If we didn't do this, then + * it would be potentially possible for out thread to carry on and + * do stuff before our creator fills in our "self" field. For example, + * if we went and created another thread which tried to JOIN with us, + * then we'd be in a mess. + */ + MUTEX_LOCK(&thr->mutex); + MUTEX_UNLOCK(&thr->mutex); + + /* + * It's safe to wait until now to set the thread-specific pointer + * from our pthread_t structure to our struct perl_thread, since + * we're the only thread who can get at it anyway. + */ + SET_THR(thr); + + /* Only now can we use SvPEEK (which calls sv_newmortal which does dTHR) */ + DEBUG_S(PerlIO_printf(PerlIO_stderr(), "new thread %p starting at %s\n", + thr, SvPEEK(TOPs))); + + sv = POPs; + PUTBACK; + ENTER; + SAVETMPS; + perl_call_sv(sv, G_ARRAY|G_EVAL); + SPAGAIN; + retval = SP - (PL_stack_base + oldmark); + SP = PL_stack_base + oldmark + 1; + if (SvCUR(thr->errsv)) { + STRLEN n_a; + MUTEX_LOCK(&thr->mutex); + thr->flags |= THRf_DID_DIE; + MUTEX_UNLOCK(&thr->mutex); + av_store(av, 0, &PL_sv_no); + av_store(av, 1, newSVsv(thr->errsv)); + DEBUG_S(PerlIO_printf(PerlIO_stderr(), "%p died: %s\n", + thr, SvPV(thr->errsv, n_a))); + } else { + DEBUG_S(STMT_START { + for (i = 1; i <= retval; i++) { + PerlIO_printf(PerlIO_stderr(), "%p return[%d] = %s\n", + thr, i, SvPEEK(SP[i - 1])); + } + } STMT_END); + av_store(av, 0, &PL_sv_yes); + for (i = 1; i <= retval; i++, SP++) + sv_setsv(*av_fetch(av, i, TRUE), SvREFCNT_inc(*SP)); + } + FREETMPS; + LEAVE; + + finishoff: +#if 0 + /* removed for debug */ + SvREFCNT_dec(PL_curstack); +#endif + SvREFCNT_dec(thr->cvcache); + SvREFCNT_dec(thr->threadsv); + SvREFCNT_dec(thr->specific); + SvREFCNT_dec(thr->errsv); + SvREFCNT_dec(thr->errhv); + + /*Safefree(cxstack);*/ + while (PL_curstackinfo->si_next) + PL_curstackinfo = PL_curstackinfo->si_next; + while (PL_curstackinfo) { + PERL_SI *p = PL_curstackinfo->si_prev; + SvREFCNT_dec(PL_curstackinfo->si_stack); + Safefree(PL_curstackinfo->si_cxstack); + Safefree(PL_curstackinfo); + PL_curstackinfo = p; + } + Safefree(PL_markstack); + Safefree(PL_scopestack); + Safefree(PL_savestack); + Safefree(PL_retstack); + Safefree(PL_tmps_stack); + Safefree(PL_ofs); + + SvREFCNT_dec(PL_rs); + SvREFCNT_dec(PL_nrs); + SvREFCNT_dec(PL_statname); + Safefree(PL_screamfirst); + Safefree(PL_screamnext); + Safefree(PL_reg_start_tmp); + SvREFCNT_dec(PL_lastscream); + SvREFCNT_dec(PL_defoutgv); + + MUTEX_LOCK(&thr->mutex); + DEBUG_S(PerlIO_printf(PerlIO_stderr(), + "%p: threadstart finishing: state is %u\n", + thr, ThrSTATE(thr))); + switch (ThrSTATE(thr)) { + case THRf_R_JOINABLE: + ThrSETSTATE(thr, THRf_ZOMBIE); + MUTEX_UNLOCK(&thr->mutex); + DEBUG_S(PerlIO_printf(PerlIO_stderr(), + "%p: R_JOINABLE thread finished\n", thr)); + break; + case THRf_R_JOINED: + ThrSETSTATE(thr, THRf_DEAD); + MUTEX_UNLOCK(&thr->mutex); + remove_thread(thr); + DEBUG_S(PerlIO_printf(PerlIO_stderr(), + "%p: R_JOINED thread finished\n", thr)); + break; + case THRf_R_DETACHED: + ThrSETSTATE(thr, THRf_DEAD); + MUTEX_UNLOCK(&thr->mutex); + SvREFCNT_dec(av); + DEBUG_S(PerlIO_printf(PerlIO_stderr(), + "%p: DETACHED thread finished\n", thr)); + remove_thread(thr); /* This might trigger main thread to finish */ + break; + default: + MUTEX_UNLOCK(&thr->mutex); + croak("panic: illegal state %u at end of threadstart", ThrSTATE(thr)); + /* NOTREACHED */ + } + return THREAD_RET_CAST(av); /* Available for anyone to join with */ + /* us unless we're detached, in which */ + /* case noone sees the value anyway. */ +#endif +#else + return THREAD_RET_CAST(NULL); +#endif +} + +static SV * +newthread (SV *startsv, AV *initargs, char *classname) +{ +#ifdef USE_THREADS + dSP; + Thread savethread; + int i; + SV *sv; + int err; +#ifndef THREAD_CREATE + static pthread_attr_t attr; + static int attr_inited = 0; + sigset_t fullmask, oldmask; +#endif + + savethread = thr; + thr = new_struct_thread(thr); + /* temporarily pretend to be the child thread in case the + * XPUSHs() below want to grow the child's stack. This is + * safe, since the other thread is not yet created, and we + * are the only ones who know about it */ + SET_THR(thr); + SPAGAIN; + DEBUG_S(PerlIO_printf(PerlIO_stderr(), + "%p: newthread (%p), tid is %u, preparing stack\n", + savethread, thr, thr->tid)); + /* The following pushes the arg list and startsv onto the *new* stack */ + PUSHMARK(SP); + /* Could easily speed up the following greatly */ + for (i = 0; i <= AvFILL(initargs); i++) + XPUSHs(SvREFCNT_inc(*av_fetch(initargs, i, FALSE))); + XPUSHs(SvREFCNT_inc(startsv)); + PUTBACK; + + /* On your marks... */ + SET_THR(savethread); + MUTEX_LOCK(&thr->mutex); + +#ifdef THREAD_CREATE + err = THREAD_CREATE(thr, threadstart); +#else + /* Get set... */ + sigfillset(&fullmask); + if (sigprocmask(SIG_SETMASK, &fullmask, &oldmask) == -1) + croak("panic: sigprocmask"); + err = 0; + if (!attr_inited) { + attr_inited = 1; +#ifdef OLD_PTHREADS_API + err = pthread_attr_create(&attr); +#else + err = pthread_attr_init(&attr); +#endif +#ifdef OLD_PTHREADS_API +#ifdef VMS +/* This is available with the old pthreads API, but only with */ +/* DecThreads (VMS and Digital Unix) */ + if (err == 0) + err = pthread_attr_setdetach_np(&attr, ATTR_JOINABLE); +#endif +#else + if (err == 0) + err = pthread_attr_setdetachstate(&attr, ATTR_JOINABLE); +#endif + } + if (err == 0) +#ifdef OLD_PTHREADS_API + err = pthread_create(&thr->self, attr, threadstart, (void*) thr); +#else + err = pthread_create(&thr->self, &attr, threadstart, (void*) thr); +#endif +#endif + if (err) { + MUTEX_UNLOCK(&thr->mutex); + DEBUG_S(PerlIO_printf(PerlIO_stderr(), + "%p: create of %p failed %d\n", + savethread, thr, err)); + /* Thread creation failed--clean up */ + SvREFCNT_dec(thr->cvcache); + remove_thread(thr); + MUTEX_DESTROY(&thr->mutex); + for (i = 0; i <= AvFILL(initargs); i++) + SvREFCNT_dec(*av_fetch(initargs, i, FALSE)); + SvREFCNT_dec(startsv); + return NULL; + } + +#ifdef THREAD_POST_CREATE + THREAD_POST_CREATE(thr); +#else + if (sigprocmask(SIG_SETMASK, &oldmask, 0)) + croak("panic: sigprocmask"); +#endif + + sv = newSViv(thr->tid); + sv_magic(sv, thr->oursv, '~', 0, 0); + SvMAGIC(sv)->mg_private = Thread_MAGIC_SIGNATURE; + sv = sv_bless(newRV_noinc(sv), gv_stashpv(classname, TRUE)); + + /* Go */ + MUTEX_UNLOCK(&thr->mutex); + + return sv; +#else + croak("No threads in this perl"); + return &PL_sv_undef; +#endif +} + +static Signal_t handle_thread_signal _((int sig)); + +static Signal_t +handle_thread_signal(int sig) +{ + unsigned char c = (unsigned char) sig; + /* + * We're not really allowed to call fprintf in a signal handler + * so don't be surprised if this isn't robust while debugging + * with -DL. + */ + DEBUG_S(PerlIO_printf(PerlIO_stderr(), + "handle_thread_signal: got signal %d\n", sig);); + write(sig_pipe[1], &c, 1); +} + +MODULE = Thread PACKAGE = Thread +PROTOTYPES: DISABLE + +void +new(classname, startsv, ...) + char * classname + SV * startsv + AV * av = av_make(items - 2, &ST(2)); + PPCODE: + XPUSHs(sv_2mortal(newthread(startsv, av, classname))); + +void +join(t) + Thread t + AV * av = NO_INIT + int i = NO_INIT + PPCODE: +#ifdef USE_THREADS + DEBUG_S(PerlIO_printf(PerlIO_stderr(), "%p: joining %p (state %u)\n", + thr, t, ThrSTATE(t));); + MUTEX_LOCK(&t->mutex); + switch (ThrSTATE(t)) { + case THRf_R_JOINABLE: + case THRf_R_JOINED: + ThrSETSTATE(t, THRf_R_JOINED); + MUTEX_UNLOCK(&t->mutex); + break; + case THRf_ZOMBIE: + ThrSETSTATE(t, THRf_DEAD); + MUTEX_UNLOCK(&t->mutex); + remove_thread(t); + break; + default: + MUTEX_UNLOCK(&t->mutex); + croak("can't join with thread"); + /* NOTREACHED */ + } + JOIN(t, &av); + + if (SvTRUE(*av_fetch(av, 0, FALSE))) { + /* Could easily speed up the following if necessary */ + for (i = 1; i <= AvFILL(av); i++) + XPUSHs(sv_2mortal(*av_fetch(av, i, FALSE))); + } else { + STRLEN n_a; + char *mess = SvPV(*av_fetch(av, 1, FALSE), n_a); + DEBUG_S(PerlIO_printf(PerlIO_stderr(), + "%p: join propagating die message: %s\n", + thr, mess)); + croak(mess); + } +#endif + +void +detach(t) + Thread t + CODE: +#ifdef USE_THREADS + DEBUG_S(PerlIO_printf(PerlIO_stderr(), "%p: detaching %p (state %u)\n", + thr, t, ThrSTATE(t));); + MUTEX_LOCK(&t->mutex); + switch (ThrSTATE(t)) { + case THRf_R_JOINABLE: + ThrSETSTATE(t, THRf_R_DETACHED); + /* fall through */ + case THRf_R_DETACHED: + DETACH(t); + MUTEX_UNLOCK(&t->mutex); + break; + case THRf_ZOMBIE: + ThrSETSTATE(t, THRf_DEAD); + DETACH(t); + MUTEX_UNLOCK(&t->mutex); + remove_thread(t); + break; + default: + MUTEX_UNLOCK(&t->mutex); + croak("can't detach thread"); + /* NOTREACHED */ + } +#endif + +void +equal(t1, t2) + Thread t1 + Thread t2 + PPCODE: + PUSHs((t1 == t2) ? &PL_sv_yes : &PL_sv_no); + +void +flags(t) + Thread t + PPCODE: +#ifdef USE_THREADS + PUSHs(sv_2mortal(newSViv(t->flags))); +#endif + +void +self(classname) + char * classname + PREINIT: + SV *sv; + PPCODE: +#ifdef USE_THREADS + sv = newSViv(thr->tid); + sv_magic(sv, thr->oursv, '~', 0, 0); + SvMAGIC(sv)->mg_private = Thread_MAGIC_SIGNATURE; + PUSHs(sv_2mortal(sv_bless(newRV_noinc(sv), + gv_stashpv(classname, TRUE)))); +#endif + +U32 +tid(t) + Thread t + CODE: +#ifdef USE_THREADS + MUTEX_LOCK(&t->mutex); + RETVAL = t->tid; + MUTEX_UNLOCK(&t->mutex); +#else + RETVAL = 0; +#endif + OUTPUT: + RETVAL + +void +DESTROY(t) + SV * t + PPCODE: + PUSHs(&PL_sv_yes); + +void +yield() + CODE: +{ +#ifdef USE_THREADS + YIELD; +#endif +} + +void +cond_wait(sv) + SV * sv + MAGIC * mg = NO_INIT +CODE: +#ifdef USE_THREADS + if (SvROK(sv)) + sv = SvRV(sv); + + mg = condpair_magic(sv); + DEBUG_S(PerlIO_printf(PerlIO_stderr(), "%p: cond_wait %p\n", thr, sv)); + MUTEX_LOCK(MgMUTEXP(mg)); + if (MgOWNER(mg) != thr) { + MUTEX_UNLOCK(MgMUTEXP(mg)); + croak("cond_wait for lock that we don't own\n"); + } + MgOWNER(mg) = 0; + COND_SIGNAL(MgOWNERCONDP(mg)); + COND_WAIT(MgCONDP(mg), MgMUTEXP(mg)); + while (MgOWNER(mg)) + COND_WAIT(MgOWNERCONDP(mg), MgMUTEXP(mg)); + MgOWNER(mg) = thr; + MUTEX_UNLOCK(MgMUTEXP(mg)); +#endif + +void +cond_signal(sv) + SV * sv + MAGIC * mg = NO_INIT +CODE: +#ifdef USE_THREADS + if (SvROK(sv)) + sv = SvRV(sv); + + mg = condpair_magic(sv); + DEBUG_S(PerlIO_printf(PerlIO_stderr(), "%p: cond_signal %p\n",thr,sv)); + MUTEX_LOCK(MgMUTEXP(mg)); + if (MgOWNER(mg) != thr) { + MUTEX_UNLOCK(MgMUTEXP(mg)); + croak("cond_signal for lock that we don't own\n"); + } + COND_SIGNAL(MgCONDP(mg)); + MUTEX_UNLOCK(MgMUTEXP(mg)); +#endif + +void +cond_broadcast(sv) + SV * sv + MAGIC * mg = NO_INIT +CODE: +#ifdef USE_THREADS + if (SvROK(sv)) + sv = SvRV(sv); + + mg = condpair_magic(sv); + DEBUG_S(PerlIO_printf(PerlIO_stderr(), "%p: cond_broadcast %p\n", + thr, sv)); + MUTEX_LOCK(MgMUTEXP(mg)); + if (MgOWNER(mg) != thr) { + MUTEX_UNLOCK(MgMUTEXP(mg)); + croak("cond_broadcast for lock that we don't own\n"); + } + COND_BROADCAST(MgCONDP(mg)); + MUTEX_UNLOCK(MgMUTEXP(mg)); +#endif + +void +list(classname) + char * classname + PREINIT: + Thread t; + AV * av; + SV ** svp; + int n = 0; + PPCODE: +#ifdef USE_THREADS + av = newAV(); + /* + * Iterate until we have enough dynamic storage for all threads. + * We mustn't do any allocation while holding threads_mutex though. + */ + MUTEX_LOCK(&PL_threads_mutex); + do { + n = PL_nthreads; + MUTEX_UNLOCK(&PL_threads_mutex); + if (AvFILL(av) < n - 1) { + int i = AvFILL(av); + for (i = AvFILL(av); i < n - 1; i++) { + SV *sv = newSViv(0); /* fill in tid later */ + sv_magic(sv, 0, '~', 0, 0); /* fill in other magic later */ + av_push(av, sv_bless(newRV_noinc(sv), + gv_stashpv(classname, TRUE))); + + } + } + MUTEX_LOCK(&PL_threads_mutex); + } while (n < PL_nthreads); + n = PL_nthreads; /* Get the final correct value */ + + /* + * At this point, there's enough room to fill in av. + * Note that we are holding threads_mutex so the list + * won't change out from under us but all the remaining + * processing is "fast" (no blocking, malloc etc.) + */ + t = thr; + svp = AvARRAY(av); + do { + SV *sv = (SV*)SvRV(*svp); + sv_setiv(sv, t->tid); + SvMAGIC(sv)->mg_obj = SvREFCNT_inc(t->oursv); + SvMAGIC(sv)->mg_flags |= MGf_REFCOUNTED; + SvMAGIC(sv)->mg_private = Thread_MAGIC_SIGNATURE; + t = t->next; + svp++; + } while (t != thr); + /* */ + MUTEX_UNLOCK(&PL_threads_mutex); + /* Truncate any unneeded slots in av */ + av_fill(av, n - 1); + /* Finally, push all the new objects onto the stack and drop av */ + EXTEND(SP, n); + for (svp = AvARRAY(av); n > 0; n--, svp++) + PUSHs(*svp); + (void)sv_2mortal((SV*)av); +#endif + + +MODULE = Thread PACKAGE = Thread::Signal + +void +kill_sighandler_thread() + PPCODE: + write(sig_pipe[1], "\0", 1); + PUSHs(&PL_sv_yes); + +void +init_thread_signals() + PPCODE: + PL_sighandlerp = handle_thread_signal; + if (pipe(sig_pipe) == -1) + XSRETURN_UNDEF; + PUSHs(&PL_sv_yes); + +void +await_signal() + PREINIT: + unsigned char c; + SSize_t ret; + CODE: + do { + ret = read(sig_pipe[0], &c, 1); + } while (ret == -1 && errno == EINTR); + if (ret == -1) + croak("panic: await_signal"); + ST(0) = sv_newmortal(); + if (ret) + sv_setsv(ST(0), c ? psig_ptr[c] : &PL_sv_no); + DEBUG_S(PerlIO_printf(PerlIO_stderr(), + "await_signal returning %s\n", SvPEEK(ST(0)));); + +MODULE = Thread PACKAGE = Thread::Specific + +void +data(classname = "Thread::Specific") + char * classname + PPCODE: +#ifdef USE_THREADS + if (AvFILL(thr->specific) == -1) { + GV *gv = gv_fetchpv("Thread::Specific::FIELDS", TRUE, SVt_PVHV); + av_store(thr->specific, 0, newRV((SV*)GvHV(gv))); + } + XPUSHs(sv_bless(newRV((SV*)thr->specific),gv_stashpv(classname,TRUE))); +#endif diff --git a/gnu/usr.bin/perl/ext/Thread/Thread/Queue.pm b/gnu/usr.bin/perl/ext/Thread/Thread/Queue.pm new file mode 100644 index 00000000000..6d5f82be344 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/Thread/Queue.pm @@ -0,0 +1,99 @@ +package Thread::Queue; +use Thread qw(cond_wait cond_broadcast); + +=head1 NAME + +Thread::Queue - thread-safe queues + +=head1 SYNOPSIS + + use Thread::Queue; + my $q = new Thread::Queue; + $q->enqueue("foo", "bar"); + my $foo = $q->dequeue; # The "bar" is still in the queue. + my $foo = $q->dequeue_nb; # returns "bar", or undef if the queue was + # empty + my $left = $q->pending; # returns the number of items still in the queue + +=head1 DESCRIPTION + +A queue, as implemented by C<Thread::Queue> is a thread-safe data structure +much like a list. Any number of threads can safely add elements to the end +of the list, or remove elements from the head of the list. (Queues don't +permit adding or removing elements from the middle of the list) + +=head1 FUNCTIONS AND METHODS + +=over 8 + +=item new + +The C<new> function creates a new empty queue. + +=item enqueue LIST + +The C<enqueue> method adds a list of scalars on to the end of the queue. +The queue will grow as needed to accomodate the list. + +=item dequeue + +The C<dequeue> method removes a scalar from the head of the queue and +returns it. If the queue is currently empty, C<dequeue> will block the +thread until another thread C<enqueue>s a scalar. + +=item dequeue_nb + +The C<dequeue_nb> method, like the C<dequeue> method, removes a scalar from +the head of the queue and returns it. Unlike C<dequeue>, though, +C<dequeue_nb> won't block if the queue is empty, instead returning +C<undef>. + +=item pending + +The C<pending> method returns the number of items still in the queue. (If +there can be multiple readers on the queue it's best to lock the queue +before checking to make sure that it stays in a consistent state) + +=back + +=head1 SEE ALSO + +L<Thread> + +=cut + +sub new { + my $class = shift; + return bless [@_], $class; +} + +sub dequeue { + use attrs qw(locked method); + my $q = shift; + cond_wait $q until @$q; + return shift @$q; +} + +sub dequeue_nb { + use attrs qw(locked method); + my $q = shift; + if (@$q) { + return shift @$q; + } else { + return undef; + } +} + +sub enqueue { + use attrs qw(locked method); + my $q = shift; + push(@$q, @_) and cond_broadcast $q; +} + +sub pending { + use attrs qw(locked method); + my $q = shift; + return scalar(@$q); +} + +1; diff --git a/gnu/usr.bin/perl/ext/Thread/Thread/Semaphore.pm b/gnu/usr.bin/perl/ext/Thread/Thread/Semaphore.pm new file mode 100644 index 00000000000..915808cbed7 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/Thread/Semaphore.pm @@ -0,0 +1,87 @@ +package Thread::Semaphore; +use Thread qw(cond_wait cond_broadcast); + +=head1 NAME + +Thread::Semaphore - thread-safe semaphores + +=head1 SYNOPSIS + + use Thread::Semaphore; + my $s = new Thread::Semaphore; + $s->up; # Also known as the semaphore V -operation. + # The guarded section is here + $s->down; # Also known as the semaphore P -operation. + + # The default semaphore value is 1. + my $s = new Thread::Semaphore($initial_value); + $s->up($up_value); + $s->down($up_value); + +=head1 DESCRIPTION + +Semaphores provide a mechanism to regulate access to resources. Semaphores, +unlike locks, aren't tied to particular scalars, and so may be used to +control access to anything you care to use them for. + +Semaphores don't limit their values to zero or one, so they can be used to +control access to some resource that may have more than one of. (For +example, filehandles) Increment and decrement amounts aren't fixed at one +either, so threads can reserve or return multiple resources at once. + +=head1 FUNCTIONS AND METHODS + +=over 8 + +=item new + +=item new NUMBER + +C<new> creates a new semaphore, and initializes its count to the passed +number. If no number is passed, the semaphore's count is set to one. + +=item down + +=item down NUMBER + +The C<down> method decreases the semaphore's count by the specified number, +or one if no number has been specified. If the semaphore's count would drop +below zero, this method will block until such time that the semaphore's +count is equal to or larger than the amount you're C<down>ing the +semaphore's count by. + +=item up + +=item up NUMBER + +The C<up> method increases the semaphore's count by the number specified, +or one if no number's been specified. This will unblock any thread blocked +trying to C<down> the semaphore if the C<up> raises the semaphore count +above what the C<down>s are trying to decrement it by. + +=back + +=cut + +sub new { + my $class = shift; + my $val = @_ ? shift : 1; + bless \$val, $class; +} + +sub down { + use attrs qw(locked method); + my $s = shift; + my $inc = @_ ? shift : 1; + cond_wait $s until $$s >= $inc; + $$s -= $inc; +} + +sub up { + use attrs qw(locked method); + my $s = shift; + my $inc = @_ ? shift : 1; + ($$s += $inc) > 0 and cond_broadcast $s; +} + +1; diff --git a/gnu/usr.bin/perl/ext/Thread/Thread/Signal.pm b/gnu/usr.bin/perl/ext/Thread/Thread/Signal.pm new file mode 100644 index 00000000000..f5f03db8a82 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/Thread/Signal.pm @@ -0,0 +1,50 @@ +package Thread::Signal; +use Thread qw(async); + +=head1 NAME + +Thread::Signal - Start a thread which runs signal handlers reliably + +=head1 SYNOPSIS + + use Thread::Signal; + + $SIG{HUP} = \&some_handler; + +=head1 DESCRIPTION + +The C<Thread::Signal> module starts up a special signal handler thread. +All signals to the process are delivered to it and it runs the +associated C<$SIG{FOO}> handlers for them. Without this module, +signals arriving at inopportune moments (such as when perl's internals +are in the middle of updating critical structures) cause the perl +code of the handler to be run unsafely which can cause memory corruption +or worse. + +=head1 BUGS + +This module changes the semantics of signal handling slightly in that +the signal handler is run separately from the main thread (and in +parallel with it). This means that tricks such as calling C<die> from +a signal handler behave differently (and, in particular, can't be +used to exit directly from a system call). + +=cut + +if (!init_thread_signals()) { + require Carp; + Carp::croak("init_thread_signals failed: $!"); +} + +async { + my $sig; + while ($sig = await_signal()) { + &$sig(); + } +}; + +END { + kill_sighandler_thread(); +} + +1; diff --git a/gnu/usr.bin/perl/ext/Thread/Thread/Specific.pm b/gnu/usr.bin/perl/ext/Thread/Thread/Specific.pm new file mode 100644 index 00000000000..9c8a66a9e6a --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/Thread/Specific.pm @@ -0,0 +1,29 @@ +package Thread::Specific; + +=head1 NAME + +Thread::Specific - thread-specific keys + +=head1 SYNOPSIS + + use Thread::Specific; + my $k = key_create Thread::Specific; + +=head1 DESCRIPTION + +C<key_create> returns a unique thread-specific key. + +=cut + +sub import { + use attrs qw(locked method); + require fields; + fields->import(@_); +} + +sub key_create { + use attrs qw(locked method); + return ++$FIELDS{__MAX__}; +} + +1; diff --git a/gnu/usr.bin/perl/ext/Thread/create.t b/gnu/usr.bin/perl/ext/Thread/create.t new file mode 100644 index 00000000000..df8fc77ba34 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/create.t @@ -0,0 +1,26 @@ +use Thread 'async'; +use Config; +use Tie::Hash; + +sub start_here { + my $i; + print "In start_here with args: @_\n"; + for ($i = 1; $i <= 5; $i++) { + print "start_here: $i\n"; + sleep 1; + } +} + +async { + tie my(%h), 'Tie::StdHash'; + %h = %Config; + print "running on $h{archname}\n"; +}; + +print "Starting new thread now\n"; +$t = new Thread \&start_here, qw(foo bar baz); +print "Started thread $t\n"; +for ($count = 1; $count <= 5; $count++) { + print "main: $count\n"; + sleep 1; +} diff --git a/gnu/usr.bin/perl/ext/Thread/die.t b/gnu/usr.bin/perl/ext/Thread/die.t new file mode 100644 index 00000000000..623940579ff --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/die.t @@ -0,0 +1,16 @@ +use Thread 'async'; + +$t = async { + print "here\n"; + die "success"; + print "shouldn't get here\n"; +}; + +sleep 1; +print "joining...\n"; +eval { @r = $t->join; }; +if ($@) { + print "thread died with message: $@"; +} else { + print "thread failed to die successfully\n"; +} diff --git a/gnu/usr.bin/perl/ext/Thread/die2.t b/gnu/usr.bin/perl/ext/Thread/die2.t new file mode 100644 index 00000000000..f6b695520f9 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/die2.t @@ -0,0 +1,16 @@ +use Thread 'async'; + +$t = async { + sleep 1; + print "here\n"; + die "success if preceded by 'thread died...'"; + print "shouldn't get here\n"; +}; + +print "joining...\n"; +@r = eval { $t->join; }; +if ($@) { + print "thread died with message: $@"; +} else { + print "thread failed to die successfully\n"; +} diff --git a/gnu/usr.bin/perl/ext/Thread/io.t b/gnu/usr.bin/perl/ext/Thread/io.t new file mode 100644 index 00000000000..6012008ef57 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/io.t @@ -0,0 +1,39 @@ +use Thread; + +sub counter { +$count = 10; +while ($count--) { + sleep 1; + print "ping $count\n"; +} +} + +sub reader { + my $line; + while ($line = <STDIN>) { + print "reader: $line"; + } + print "End of input in reader\n"; + return 0; +} + +print <<'EOT'; +This test starts up a thread to read and echo whatever is typed on +the keyboard/stdin, line by line, while the main thread counts down +to zero. The test stays running until both the main thread has +finished counting down and the I/O thread has seen end-of-file on +the terminal/stdin. +EOT + +$r = new Thread \&counter; + +&reader; + +__END__ + + +$count = 10; +while ($count--) { + sleep 1; + print "ping $count\n"; +} diff --git a/gnu/usr.bin/perl/ext/Thread/join.t b/gnu/usr.bin/perl/ext/Thread/join.t new file mode 100644 index 00000000000..cba2c1cf567 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/join.t @@ -0,0 +1,11 @@ +use Thread; +sub foo { + print "In foo with args: @_\n"; + return (7, 8, 9); +} + +print "Starting thread\n"; +$t = new Thread \&foo, qw(foo bar baz); +print "Joining with $t\n"; +@results = $t->join(); +print "Joining returned ", scalar(@results), " values: @results\n"; diff --git a/gnu/usr.bin/perl/ext/Thread/join2.t b/gnu/usr.bin/perl/ext/Thread/join2.t new file mode 100644 index 00000000000..99b43a54dc5 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/join2.t @@ -0,0 +1,12 @@ +use Thread; +sub foo { + print "In foo with args: @_\n"; + return (7, 8, 9); +} + +print "Starting thread\n"; +$t = new Thread \&foo, qw(foo bar baz); +sleep 2; +print "Joining with $t\n"; +@results = $t->join(); +print "Joining returned @results\n"; diff --git a/gnu/usr.bin/perl/ext/Thread/list.t b/gnu/usr.bin/perl/ext/Thread/list.t new file mode 100644 index 00000000000..f13f4b266a4 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/list.t @@ -0,0 +1,30 @@ +use Thread qw(async); +use Thread::Semaphore; + +my $sem = Thread::Semaphore->new(0); + +$nthreads = 4; + +for (my $i = 0; $i < $nthreads; $i++) { + async { + my $tid = Thread->self->tid; + print "thread $tid started...\n"; + $sem->down; + print "thread $tid finishing\n"; + }; +} + +print "main: started $nthreads threads\n"; +sleep 2; + +my @list = Thread->list; +printf "main: Thread->list returned %d threads\n", scalar(@list); + +foreach my $t (@list) { + print "inspecting thread $t...\n"; + print "...deref is $$t\n"; + print "...flags = ", $t->flags, "\n"; + print "...tid = ", $t->tid, "\n"; +} +print "main thread telling workers to finish off...\n"; +$sem->up($nthreads); diff --git a/gnu/usr.bin/perl/ext/Thread/lock.t b/gnu/usr.bin/perl/ext/Thread/lock.t new file mode 100644 index 00000000000..fefb1298797 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/lock.t @@ -0,0 +1,27 @@ +use Thread; + +$level = 0; + +sub worker +{ + my $num = shift; + my $i; + print "thread $num starting\n"; + for ($i = 1; $i <= 20; $i++) { + print "thread $num iteration $i\n"; + select(undef, undef, undef, rand(10)/100); + { + lock($lock); + warn "thread $num saw non-zero level = $level\n" if $level; + $level++; + print "thread $num has lock\n"; + select(undef, undef, undef, rand(10)/100); + $level--; + } + print "thread $num released lock\n"; + } +} + +for ($t = 1; $t <= 5; $t++) { + new Thread \&worker, $t; +} diff --git a/gnu/usr.bin/perl/ext/Thread/queue.t b/gnu/usr.bin/perl/ext/Thread/queue.t new file mode 100644 index 00000000000..4672ba6ee74 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/queue.t @@ -0,0 +1,36 @@ +use Thread; +use Thread::Queue; + +$q = new Thread::Queue; + +sub reader { + my $tid = Thread->self->tid; + my $i = 0; + while (1) { + $i++; + print "reader (tid $tid): waiting for element $i...\n"; + my $el = $q->dequeue; + print "reader (tid $tid): dequeued element $i: value $el\n"; + select(undef, undef, undef, rand(2)); + if ($el == -1) { + # end marker + print "reader (tid $tid) returning\n"; + return; + } + } +} + +my $nthreads = 3; + +for (my $i = 0; $i < $nthreads; $i++) { + Thread->new(\&reader, $i); +} + +for (my $i = 1; $i <= 10; $i++) { + my $el = int(rand(100)); + select(undef, undef, undef, rand(2)); + print "writer: enqueuing value $el\n"; + $q->enqueue($el); +} + +$q->enqueue((-1) x $nthreads); # one end marker for each thread diff --git a/gnu/usr.bin/perl/ext/Thread/specific.t b/gnu/usr.bin/perl/ext/Thread/specific.t new file mode 100644 index 00000000000..da130b1d64c --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/specific.t @@ -0,0 +1,17 @@ +use Thread; + +use Thread::Specific qw(foo); + +sub count { + my $tid = Thread->self->tid; + my Thread::Specific $tsd = Thread::Specific::data; + for (my $i = 0; $i < 5; $i++) { + $tsd->{foo} = $i; + print "thread $tid count: $tsd->{foo}\n"; + select(undef, undef, undef, rand(2)); + } +}; + +for(my $t = 0; $t < 5; $t++) { + new Thread \&count; +} diff --git a/gnu/usr.bin/perl/ext/Thread/sync.t b/gnu/usr.bin/perl/ext/Thread/sync.t new file mode 100644 index 00000000000..9c2e5897da5 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/sync.t @@ -0,0 +1,61 @@ +use Thread; + +$level = 0; + +sub single_file { + use attrs 'locked'; + my $arg = shift; + $level++; + print "Level $level for $arg\n"; + print "(something is wrong)\n" if $level < 0 || $level > 1; + sleep 1; + $level--; + print "Back to level $level\n"; +} + +sub start_bar { + my $i; + print "start bar\n"; + for $i (1..3) { + print "bar $i\n"; + single_file("bar $i"); + sleep 1 if rand > 0.5; + } + print "end bar\n"; + return 1; +} + +sub start_foo { + my $i; + print "start foo\n"; + for $i (1..3) { + print "foo $i\n"; + single_file("foo $i"); + sleep 1 if rand > 0.5; + } + print "end foo\n"; + return 1; +} + +sub start_baz { + my $i; + print "start baz\n"; + for $i (1..3) { + print "baz $i\n"; + single_file("baz $i"); + sleep 1 if rand > 0.5; + } + print "end baz\n"; + return 1; +} + +$| = 1; +srand($$^$^T); + +$foo = new Thread \&start_foo; +$bar = new Thread \&start_bar; +$baz = new Thread \&start_baz; +$foo->join(); +$bar->join(); +$baz->join(); +print "main: threads finished, exiting\n"; diff --git a/gnu/usr.bin/perl/ext/Thread/sync2.t b/gnu/usr.bin/perl/ext/Thread/sync2.t new file mode 100644 index 00000000000..0901da46a0a --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/sync2.t @@ -0,0 +1,69 @@ +use Thread; + +$global = undef; + +sub single_file { + use attrs 'locked'; + my $who = shift; + my $i; + + print "Uh oh: $who entered while locked by $global\n" if $global; + $global = $who; + print "["; + for ($i = 0; $i < int(10 * rand); $i++) { + print $who; + select(undef, undef, undef, 0.1); + } + print "]"; + $global = undef; +} + +sub start_a { + my ($i, $j); + for ($j = 0; $j < 10; $j++) { + single_file("A"); + for ($i = 0; $i < int(10 * rand); $i++) { + print "a"; + select(undef, undef, undef, 0.1); + } + } +} + +sub start_b { + my ($i, $j); + for ($j = 0; $j < 10; $j++) { + single_file("B"); + for ($i = 0; $i < int(10 * rand); $i++) { + print "b"; + select(undef, undef, undef, 0.1); + } + } +} + +sub start_c { + my ($i, $j); + for ($j = 0; $j < 10; $j++) { + single_file("C"); + for ($i = 0; $i < int(10 * rand); $i++) { + print "c"; + select(undef, undef, undef, 0.1); + } + } +} + +$| = 1; +srand($$^$^T); + +print <<'EOT'; +Each pair of square brackets [...] should contain a repeated sequence of +a unique upper case letter. Lower case letters may appear randomly both +in and out of the brackets. +EOT +$foo = new Thread \&start_a; +$bar = new Thread \&start_b; +$baz = new Thread \&start_c; +print "\nmain: joining...\n"; +#$foo->join; +#$bar->join; +#$baz->join; +print "\ndone\n"; diff --git a/gnu/usr.bin/perl/ext/Thread/typemap b/gnu/usr.bin/perl/ext/Thread/typemap new file mode 100644 index 00000000000..21eb6c32409 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/typemap @@ -0,0 +1,24 @@ +Thread T_XSCPTR + +INPUT +T_XSCPTR + STMT_START { + MAGIC *mg; + SV *sv = ($arg); + + if (!sv_isobject(sv)) + croak(\"$var is not an object\"); + sv = (SV*)SvRV(sv); + if (!SvRMAGICAL(sv) || !(mg = mg_find(sv, '~')) + || mg->mg_private != ${ntype}_MAGIC_SIGNATURE) + croak(\"XSUB ${func_name}: $var is a forged ${ntype} object\"); + $var = ($type) SvPVX(mg->mg_obj); + DEBUG_S(PerlIO_printf(PerlIO_stderr(), + \"XSUB ${func_name}: %p\\n\", $var);) + } STMT_END +T_IVREF + if (SvROK($arg)) + $var = ($type) SvIV((SV*)SvRV($arg)); + else + croak(\"$var is not a reference\") + diff --git a/gnu/usr.bin/perl/ext/Thread/unsync.t b/gnu/usr.bin/perl/ext/Thread/unsync.t new file mode 100644 index 00000000000..f0a51efe1f7 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/unsync.t @@ -0,0 +1,37 @@ +use Thread; + +$| = 1; + +if (@ARGV) { + srand($ARGV[0]); +} else { + my $seed = $$ ^ $^T; + print "Randomising to $seed\n"; + srand($seed); +} + +sub whoami { + my ($depth, $a, $b, $c) = @_; + my $i; + print "whoami ($depth): $a $b $c\n"; + sleep 1; + whoami($depth - 1, $a, $b, $c) if $depth > 0; +} + +sub start_foo { + my $r = 3 + int(10 * rand); + print "start_foo: r is $r\n"; + whoami($r, "start_foo", "foo1", "foo2"); + print "start_foo: finished\n"; +} + +sub start_bar { + my $r = 3 + int(10 * rand); + print "start_bar: r is $r\n"; + whoami($r, "start_bar", "bar1", "bar2"); + print "start_bar: finished\n"; +} + +$foo = new Thread \&start_foo; +$bar = new Thread \&start_bar; +print "main: exiting\n"; diff --git a/gnu/usr.bin/perl/ext/Thread/unsync2.t b/gnu/usr.bin/perl/ext/Thread/unsync2.t new file mode 100644 index 00000000000..fb955ac31e1 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/unsync2.t @@ -0,0 +1,36 @@ +use Thread; + +$| = 1; + +srand($$^$^T); + +sub printargs { + my $thread = shift; + my $arg; + my $i; + while ($arg = shift) { + my $delay = int(rand(500)); + $i++; + print "$thread arg $i is $arg\n"; + 1 while $delay--; + } +} + +sub start_thread { + my $thread = shift; + my $count = 10; + while ($count--) { + my(@args) = ($thread) x int(rand(10)); + print "$thread $count calling printargs @args\n"; + printargs($thread, @args); + } +} + +new Thread (\&start_thread, "A"); +new Thread (\&start_thread, "B"); +#new Thread (\&start_thread, "C"); +#new Thread (\&start_thread, "D"); +#new Thread (\&start_thread, "E"); +#new Thread (\&start_thread, "F"); + +print "main: exiting\n"; diff --git a/gnu/usr.bin/perl/ext/Thread/unsync3.t b/gnu/usr.bin/perl/ext/Thread/unsync3.t new file mode 100644 index 00000000000..e03e9c8af10 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/unsync3.t @@ -0,0 +1,50 @@ +use Thread; + +$| = 1; + +srand($$^$^T); + +sub whoami { + my $thread = shift; + print $thread; +} + +sub uppercase { + my $count = 100; + while ($count--) { + my $i = int(rand(1000)); + 1 while $i--; + print "A"; + $i = int(rand(1000)); + 1 while $i--; + whoami("B"); + } +} + +sub lowercase { + my $count = 100; + while ($count--) { + my $i = int(rand(1000)); + 1 while $i--; + print "x"; + $i = int(rand(1000)); + 1 while $i--; + whoami("y"); + } +} + +sub numbers { + my $count = 100; + while ($count--) { + my $i = int(rand(1000)); + 1 while $i--; + print 1; + $i = int(rand(1000)); + 1 while $i--; + whoami(2); + } +} + +new Thread \&numbers; +new Thread \&uppercase; +new Thread \&lowercase; diff --git a/gnu/usr.bin/perl/ext/Thread/unsync4.t b/gnu/usr.bin/perl/ext/Thread/unsync4.t new file mode 100644 index 00000000000..494ad2be920 --- /dev/null +++ b/gnu/usr.bin/perl/ext/Thread/unsync4.t @@ -0,0 +1,38 @@ +use Thread; + +$| = 1; + +srand($$^$^T); + +sub printargs { + my(@copyargs) = @_; + my $thread = shift @copyargs; + my $arg; + my $i; + while ($arg = shift @copyargs) { + my $delay = int(rand(500)); + $i++; + print "$thread arg $i is $arg\n"; + 1 while $delay--; + } +} + +sub start_thread { + my(@threadargs) = @_; + my $thread = $threadargs[0]; + my $count = 10; + while ($count--) { + my(@args) = ($thread) x int(rand(10)); + print "$thread $count calling printargs @args\n"; + printargs($thread, @args); + } +} + +new Thread (\&start_thread, "A"); +new Thread (\&start_thread, "B"); +new Thread (\&start_thread, "C"); +new Thread (\&start_thread, "D"); +new Thread (\&start_thread, "E"); +new Thread (\&start_thread, "F"); + +print "main: exiting\n"; diff --git a/gnu/usr.bin/perl/ext/attrs/Makefile.PL b/gnu/usr.bin/perl/ext/attrs/Makefile.PL new file mode 100644 index 00000000000..86ed3f39355 --- /dev/null +++ b/gnu/usr.bin/perl/ext/attrs/Makefile.PL @@ -0,0 +1,7 @@ +use ExtUtils::MakeMaker; +WriteMakefile( + NAME => 'attrs', + VERSION_FROM => 'attrs.pm', + MAN3PODS => {}, # Pods will be built by installman. + XSPROTOARG => '-noprototypes' +); diff --git a/gnu/usr.bin/perl/ext/attrs/attrs.pm b/gnu/usr.bin/perl/ext/attrs/attrs.pm new file mode 100644 index 00000000000..fe2bf356e4f --- /dev/null +++ b/gnu/usr.bin/perl/ext/attrs/attrs.pm @@ -0,0 +1,55 @@ +package attrs; +require DynaLoader; +use vars '@ISA'; +@ISA = 'DynaLoader'; + +use vars qw($VERSION); +$VERSION = "1.0"; + +=head1 NAME + +attrs - set/get attributes of a subroutine + +=head1 SYNOPSIS + + sub foo { + use attrs qw(locked method); + ... + } + + @a = attrs::get(\&foo); + +=head1 DESCRIPTION + +This module lets you set and get attributes for subroutines. +Setting attributes takes place at compile time; trying to set +invalid attribute names causes a compile-time error. Calling +C<attr::get> on a subroutine reference or name returns its list +of attribute names. Notice that C<attr::get> is not exported. +Valid attributes are as follows. + +=over + +=item method + +Indicates that the invoking subroutine is a method. + +=item locked + +Setting this attribute is only meaningful when the subroutine or +method is to be called by multiple threads. When set on a method +subroutine (i.e. one marked with the B<method> attribute above), +perl ensures that any invocation of it implicitly locks its first +argument before execution. When set on a non-method subroutine, +perl ensures that a lock is taken on the subroutine itself before +execution. The semantics of the lock are exactly those of one +explicitly taken with the C<lock> operator immediately after the +subroutine is entered. + +=back + +=cut + +bootstrap attrs $VERSION; + +1; diff --git a/gnu/usr.bin/perl/ext/attrs/attrs.xs b/gnu/usr.bin/perl/ext/attrs/attrs.xs new file mode 100644 index 00000000000..7f7970d207d --- /dev/null +++ b/gnu/usr.bin/perl/ext/attrs/attrs.xs @@ -0,0 +1,61 @@ +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +static cv_flags_t +get_flag(char *attr) +{ + if (strnEQ(attr, "method", 6)) + return CVf_METHOD; + else if (strnEQ(attr, "locked", 6)) + return CVf_LOCKED; + else + return 0; +} + +MODULE = attrs PACKAGE = attrs + +void +import(Class, ...) +char * Class + ALIAS: + unimport = 1 + PREINIT: + int i; + CV *cv; + PPCODE: + if (!PL_compcv || !(cv = CvOUTSIDE(PL_compcv))) + croak("can't set attributes outside a subroutine scope"); + for (i = 1; i < items; i++) { + STRLEN n_a; + char *attr = SvPV(ST(i), n_a); + cv_flags_t flag = get_flag(attr); + if (!flag) + croak("invalid attribute name %s", attr); + if (ix) + CvFLAGS(cv) &= ~flag; + else + CvFLAGS(cv) |= flag; + } + +void +get(sub) +SV * sub + PPCODE: + if (SvROK(sub)) { + sub = SvRV(sub); + if (SvTYPE(sub) != SVt_PVCV) + sub = Nullsv; + } + else { + STRLEN n_a; + char *name = SvPV(sub, n_a); + sub = (SV*)perl_get_cv(name, FALSE); + } + if (!sub) + croak("invalid subroutine reference or name"); + if (CvFLAGS(sub) & CVf_METHOD) + XPUSHs(sv_2mortal(newSVpv("method", 0))); + if (CvFLAGS(sub) & CVf_LOCKED) + XPUSHs(sv_2mortal(newSVpv("locked", 0))); + diff --git a/gnu/usr.bin/perl/ext/re/Makefile.PL b/gnu/usr.bin/perl/ext/re/Makefile.PL new file mode 100644 index 00000000000..040b085f4fa --- /dev/null +++ b/gnu/usr.bin/perl/ext/re/Makefile.PL @@ -0,0 +1,41 @@ +use ExtUtils::MakeMaker; +WriteMakefile( + NAME => 're', + VERSION_FROM => 're.pm', + MAN3PODS => {}, # Pods will be built by installman. + XSPROTOARG => '-noprototypes', + OBJECT => 're_exec$(OBJ_EXT) re_comp$(OBJ_EXT) re$(OBJ_EXT)', + DEFINE => '-DPERL_EXT_RE_BUILD', + clean => { FILES => '*$(OBJ_EXT) *.c ../../lib/re.pm' }, +); + +sub MY::postamble { + if ($^O eq 'VMS') { + return <<'VMS_EOF'; +re_comp.c : [--]regcomp.c + - $(RM_F) $(MMS$TARGET_NAME) + $(CP) [--]regcomp.c $(MMS$TARGET_NAME) + +re_comp$(OBJ_EXT) : re_comp.c + +re_exec.c : [--]regexec.c + - $(RM_F) $(MMS$TARGET_NAME) + $(CP) [--]regexec.c $(MMS$TARGET_NAME) + +re_exec$(OBJ_EXT) : re_exec.c + + +VMS_EOF + } else { + return <<'EOF'; +re_comp.c: ../../regcomp.c + -$(RM_F) $@ + $(CP) ../../regcomp.c $@ + +re_exec.c: ../../regexec.c + -$(RM_F) $@ + $(CP) ../../regexec.c $@ + +EOF + } +} diff --git a/gnu/usr.bin/perl/ext/re/hints/mpeix.pl b/gnu/usr.bin/perl/ext/re/hints/mpeix.pl new file mode 100644 index 00000000000..d1fbb91f8fd --- /dev/null +++ b/gnu/usr.bin/perl/ext/re/hints/mpeix.pl @@ -0,0 +1,3 @@ +# Fall back to -O optimization to avoid known gcc 2.8.0 -O2 problems on MPE/iX. +# Mark Bixby <markb@cccd.edu> +$self->{OPTIMIZE} = '-O'; diff --git a/gnu/usr.bin/perl/ext/re/re.pm b/gnu/usr.bin/perl/ext/re/re.pm new file mode 100644 index 00000000000..83e7dbafe56 --- /dev/null +++ b/gnu/usr.bin/perl/ext/re/re.pm @@ -0,0 +1,131 @@ +package re; + +$VERSION = 0.02; + +=head1 NAME + +re - Perl pragma to alter regular expression behaviour + +=head1 SYNOPSIS + + use re 'taint'; + ($x) = ($^X =~ /^(.*)$/s); # $x is tainted here + + $pat = '(?{ $foo = 1 })'; + use re 'eval'; + /foo${pat}bar/; # won't fail (when not under -T switch) + + { + no re 'taint'; # the default + ($x) = ($^X =~ /^(.*)$/s); # $x is not tainted here + + no re 'eval'; # the default + /foo${pat}bar/; # disallowed (with or without -T switch) + } + + use re 'debug'; # NOT lexically scoped (as others are) + /^(.*)$/s; # output debugging info during + # compile and run time + + use re 'debugcolor'; # same as 'debug', but with colored output + ... + +(We use $^X in these examples because it's tainted by default.) + +=head1 DESCRIPTION + +When C<use re 'taint'> is in effect, and a tainted string is the target +of a regex, the regex memories (or values returned by the m// operator +in list context) are tainted. This feature is useful when regex operations +on tainted data aren't meant to extract safe substrings, but to perform +other transformations. + +When C<use re 'eval'> is in effect, a regex is allowed to contain +C<(?{ ... })> zero-width assertions even if the regex contains +variable interpolation. This is normally disallowed, since it is a +potential security risk. Note that this pragma is ignored when the regular +expression is obtained from tainted data, i.e. evaluation is always +disallowed with tainted regular expressions. See L<perlre/(?{ code })>. + +For the purpose of this pragma, interpolation of precompiled regular +expressions (i.e., the result of C<qr//>) is I<not> considered variable +interpolation. Thus: + + /foo${pat}bar/ + +I<is> allowed if $pat is a precompiled regular expression, even +if $pat contains C<(?{ ... })> assertions. + +When C<use re 'debug'> is in effect, perl emits debugging messages when +compiling and using regular expressions. The output is the same as that +obtained by running a C<-DDEBUGGING>-enabled perl interpreter with the +B<-Dr> switch. It may be quite voluminous depending on the complexity +of the match. Using C<debugcolor> instead of C<debug> enables a +form of output that can be used to get a colorful display on terminals +that understand termcap color sequences. Set C<$ENV{PERL_RE_TC}> to a +comma-separated list of C<termcap> properties to use for highlighting +strings on/off, pre-point part on/off. +See L<perldebug/"Debugging regular expressions"> for additional info. + +The directive C<use re 'debug'> is I<not lexically scoped>, as the +other directives are. It has both compile-time and run-time effects. + +See L<perlmodlib/Pragmatic Modules>. + +=cut + +my %bitmask = ( +taint => 0x00100000, +eval => 0x00200000, +); + +sub setcolor { + eval { # Ignore errors + require Term::Cap; + + my $terminal = Tgetent Term::Cap ({OSPEED => 9600}); # Avoid warning. + my $props = $ENV{PERL_RE_TC} || 'md,me,so,se'; # can use us/ue later + my @props = split /,/, $props; + + + $ENV{TERMCAP_COLORS} = join "\t", map {$terminal->Tputs($_,1)} @props; + }; + + not defined $ENV{TERMCAP_COLORS} or ($ENV{TERMCAP_COLORS} =~ tr/\t/\t/) >= 4 + or not defined $ENV{PERL_RE_TC} + or die "Not enough fields in \$ENV{PERL_RE_TC}=`$ENV{PERL_RE_TC}'"; +} + +sub bits { + my $on = shift; + my $bits = 0; + unless(@_) { + require Carp; + Carp::carp("Useless use of \"re\" pragma"); + } + foreach my $s (@_){ + if ($s eq 'debug' or $s eq 'debugcolor') { + setcolor() if $s eq 'debugcolor'; + require DynaLoader; + @ISA = ('DynaLoader'); + bootstrap re; + install() if $on; + uninstall() unless $on; + next; + } + $bits |= $bitmask{$s} || 0; + } + $bits; +} + +sub import { + shift; + $^H |= bits(1,@_); +} + +sub unimport { + shift; + $^H &= ~ bits(0,@_); +} + +1; diff --git a/gnu/usr.bin/perl/ext/re/re.xs b/gnu/usr.bin/perl/ext/re/re.xs new file mode 100644 index 00000000000..7230d626dc2 --- /dev/null +++ b/gnu/usr.bin/perl/ext/re/re.xs @@ -0,0 +1,46 @@ +/* We need access to debugger hooks */ +#ifndef DEBUGGING +# define DEBUGGING +#endif + +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +extern regexp* my_regcomp _((char* exp, char* xend, PMOP* pm)); +extern I32 my_regexec _((regexp* prog, char* stringarg, char* strend, + char* strbeg, I32 minend, SV* screamer, + void* data, U32 flags)); + +static int oldfl; + +#define R_DB 512 + +static void +deinstall(void) +{ + dTHR; + PL_regexecp = ®exec_flags; + PL_regcompp = &pregcomp; + if (!oldfl) + PL_debug &= ~R_DB; +} + +static void +install(void) +{ + dTHR; + PL_colorset = 0; /* Allow reinspection of ENV. */ + PL_regexecp = &my_regexec; + PL_regcompp = &my_regcomp; + oldfl = PL_debug & R_DB; + PL_debug |= R_DB; +} + +MODULE = re PACKAGE = re + +void +install() + +void +deinstall() |