Printing out the code of an anonymous subroutine

前端 未结 4 1905
挽巷
挽巷 2021-02-05 12:56

I\'m currently working in a very complex Perl architecture, and I want to create some debugging tools. Since a lot of the behavior involves anonymous subroutines, I\'d like to a

4条回答
  •  迷失自我
    2021-02-05 13:29

    The core module B::Deparse provides this functionality.

    use B::Deparse ();
    
    my $deparse = B::Deparse->new;
    
    my $code = sub {print "hello, world!"};
    
    print 'sub ', $deparse->coderef2text($code), "\n";
    

    which prints:

    sub {
        print 'hello, world!';
    }
    

    When using B::Deparse it is important to remember that what it returns is a decompiled version of the compiled tree of op-codes, not the original source text. This means that constants, arithmetic expressions, and other constructs may be folded and rewritten by the optimizer.

    The other part of the puzzle is dealing with closed over lexical variables. If the subroutines you are working with access any external lexicals, they will not be present in the output of deparse, and will cause recompilation to fail. You can solve this with the closed_over and set_closed_over functions from the PadWalker module.

    use PadWalker qw/closed_over set_closed_over/;
    
    my $closure = do {
        my $counter = 0;
        sub {$counter++}
    };
    
    print $closure->(), ' ' for 1..3; # 0 1 2
    print "\n";
    
    my $pad = closed_over $closure; # hash of lexicals
    
                     # create dummy lexicals for compilation
    my $copy = eval 'my ('.join(','=> keys %$pad).');'. 
                    'sub '.$deparse->coderef2text($closure);
    
    set_closed_over $copy, $pad;  # replace dummy lexicals with real ones
    
    print $copy->(), ' ' for 1..3; # 3 4 5
    

    Finally, if you want to find out where the subroutine's real source code is, you can use the core B module:

    use B ();
    my $meta = B::svref_2object($closure);
    
    print "$closure at ".$meta->FILE.' line '.$meta->GV->LINE."\n";
    

    which prints something like:

    CODE(0x28dcffc) at filename.pl line 21
    

提交回复
热议问题