tphyahoo has asked for the wisdom of the Perl Monks concerning the following question:

Hi, I inherited 17 pages of perl code that, in my opinion, overuses global vars and has an overly complicated calling structure. The code works, but I'd like to refactor by reducing the number of global vars, and maybe simplify the calling structure as well.

Curently I'm going through a printout with pencil and marking up

Is there a way to do any of the above in an automated way, with tools (komodo?) or public domain .pl programs?

Would be nice to run something like

printGlobalVars.pl myscript.pl and it outputs file scoped "my" variables. (use strict and use warnings are on.)

Better yet would be if it indicated which functions used which global vars. With that and a calling structure, the task of refactoring is made significantly easier.

Thanks in advance!

Replies are listed 'Best First'.
Re: grepping out global vars?
by BrowserUk (Patriarch) on Jan 17, 2005 at 14:24 UTC

    Maybe the simple solution is to make a copy the script and in the copy.

    1. Comment out or delete any "use vars ..." lines.
    2. edit out any "our" keywords.
    3. Syntax check with "perl -wc copy.pl"

    That ought to produce output somethng like:

    P:\test>perl -wc junk.pl Global symbol "$c" requires explicit package name at junk.pl line 8. Global symbol "$rv" requires explicit package name at junk.pl line 9. Global symbol "$c" requires explicit package name at junk.pl line 10. Global symbol "$c" requires explicit package name at junk.pl line 11. Global symbol "$c" requires explicit package name at junk.pl line 12. Global symbol "$c" requires explicit package name at junk.pl line 12. Global symbol "$c" requires explicit package name at junk.pl line 17. Global symbol "$rv" requires explicit package name at junk.pl line 22. Global symbol "$c" requires explicit package name at junk.pl line 22. junk.pl had compilation errors.

    Is that the sort of information you are after?

    There is also a module Devel::Something (Xref?) that produces a cross reference listing, but cpan appears to n down right now.


    Examine what is said, not who speaks.
    Silence betokens consent.
    Love the truth but pardon error.

      It's B::Xref and it's pretty cool.

      X:\perl>perl -MO=Xref c:\perl\lib\strict.pm File c:\perl\lib\strict.pm Subroutine (definitions) Package Internals &HvREHASH s0 &SvREADONLY s0 &SvREFCNT s0 &hash_seed s0 &hv_clear_placeholders s0 &rehash_seed s0 Package PerlIO &get_layers s0 Package Regexp &DESTROY s0 Package UNIVERSAL &VERSION s0 &can s0 &isa s0 Package Win32 &BuildNumber s0 &CopyFile s0 &DomainName s0 &FormatMessage s0 &FsType s0 &GetCwd s0 &GetFullPathName s0 &GetLastError s0 &GetLongPathName s0 &GetNextAvailDrive s0 &GetOSVersion s0 &GetShortPathName s0 &GetTickCount s0 &IsWin95 s0 &IsWinNT s0 &LoginName s0 &NodeName s0 &SetChildShowWindow s0 &SetCwd s0 &SetLastError s0 &Sleep s0 &Spawn s0 Subroutine (main) Package (lexical) $default_bits i25 %bitmask i5 Package strict $VERSION 3 &bits &25 c:\perl\lib\strict.pm syntax OK
        It's B::Xref and it's pretty cool.

        There is also a Devel::Xref which I eventually pursuaded CPAN to allow me to download.

        However, the documentation is less than clear (to me) on how to use it. I can get some output, but you have to embed it in the script, and the output is somewhat confusing.

        Now you've pointed out B::Xref, and demonstrated how easy it is to use, I am way impressed. It is really cool. And fast!

        I just tried it out on Date::Manip. Very interesting, if slightly scary reading.

        Thanks for posting, I can see me using that a lot.


        Examine what is said, not who speaks.
        Silence betokens consent.
        Love the truth but pardon error.
      That is exactly what I've done, in the past. In fact, since such code is usually (in my experience) written without strict and without use vars (let alone our), simply turning on strict gives you the full scoop on global vars.
Re: grepping out global vars?
by ambrus (Abbot) on Jan 17, 2005 at 14:38 UTC

    Run your program in the perl debugger. After it, you can print the values of assigned global variables with the |V debugger command. Note that this will print only those variables, that were used, so run the program first and break it after you think it's set all the global variables it uses, and invoke |V only after that. Also, you'll see lots of magic variables and other such junk in the output, which you should ignore.

    Then, if you also want to know where those variables are accessed or changed, see Debugging with tied variables, or just grep for their names in the source code.

    (Running with strict and seeing the errors is also a good idea, and is probably simpler, as someone else has recommended.)

Re: grepping out global vars?
by dragonchild (Archbishop) on Jan 17, 2005 at 14:22 UTC
    What you're asking for (static analysis of Perl) is impossible due to the dynamic nature of the language. The way you're doing it is the exact right way. And, in fact, it's what I do whenever I inherit Perl code. Reading code is hard and it takes time. But, it the only way to know what's going on.

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

Re: grepping out global vars?
by chromatic (Archbishop) on Jan 18, 2005 at 07:19 UTC

    It's actually really easy to write a B:: module to do this. I spent a few minutes on this:

    package B::FindGlobals; use B::Utils 'walkallops_filtered'; sub compile { return sub { walkallops_filtered( \&gv_only, \&report_global ); }; } sub gv_only { my $op = shift; return unless $op->name() =~ /^gv/; return unless $B::Utils::file eq $0; return 1; } sub report_global { my $op = shift; printf "%s: % 4d %s\n", $B::Utils::file, $B::Utils::line, $op->gv()->NAME(); } 1;

    Install B::Utils, then run it on the file you want to check with perl -MO=FindGlobals filename.pl. You'll have to stuff this module somewhere in @INC under a B directory.

      Question: Do you have to put the FindGlobals package in the B:: namespace for B::Utils to do its work?

      Being right, does not endow the right to be rude; politeness costs nothing.
      Being unknowing, is not the same as being stupid.
      Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
      Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

        Not for B::Utils; that should work anywhere. You do have to put B::FindGlobals in an appropriate location for O to do its work though. That's the magic -MO=FindGlobals notation.

Re: grepping out global vars?
by tphyahoo (Vicar) on Jan 18, 2005 at 13:46 UTC