~ubuntu-branches/ubuntu/edgy/libmodule-info-perl/edgy

« back to all changes in this revision

Viewing changes to lib/B/Utils.pm

  • Committer: Bazaar Package Importer
  • Author(s): Jay Bonci
  • Date: 2003-10-06 10:51:04 UTC
  • Revision ID: james.westby@ubuntu.com-20031006105104-1b67d55zyyay6jvo
Tags: upstream-0.24
ImportĀ upstreamĀ versionĀ 0.24

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package B::Utils;
 
2
 
 
3
use 5.006;
 
4
use strict;
 
5
use warnings;
 
6
our @EXPORT_OK = qw(all_starts all_roots anon_subs
 
7
                    walkoptree_simple walkoptree_filtered
 
8
                    walkallops_simple walkallops_filtered
 
9
                    carp croak
 
10
                    opgrep
 
11
                   );
 
12
sub import {
 
13
  my $pack = shift;
 
14
  my @exports = @_;
 
15
  my $caller = caller;
 
16
  my %EOK = map {$_ => 1} @EXPORT_OK;
 
17
  for (@exports) {
 
18
    unless ($EOK{$_}) {
 
19
      require Carp;
 
20
      Carp::croak(qq{"$_" is not exported by the $pack module});
 
21
    }
 
22
    no strict 'refs';
 
23
    *{"$caller\::$_"} = \&{"$pack\::$_"};
 
24
  }
 
25
}
 
26
 
 
27
our $VERSION = '0.04_01'; # 0.04 with some Schwern patches
 
28
 
 
29
use B qw(main_start main_root walksymtable class OPf_KIDS);
 
30
 
 
31
my (%starts, %roots, @anon_subs);
 
32
 
 
33
our @bad_stashes = qw(B Carp DB Exporter warnings Cwd Config CORE blib strict DynaLoader vars XSLoader AutoLoader base);
 
34
 
 
35
{ my $_subsdone=0;
 
36
sub _init { # To ensure runtimeness.
 
37
    return if $_subsdone;
 
38
    %starts = ( '__MAIN__' =>  main_start() );
 
39
    %roots  = ( '__MAIN__' =>  main_root()  );
 
40
    walksymtable(\%main::, 
 
41
                '_push_starts', 
 
42
                sub { 
 
43
                    return if scalar grep {$_[0] eq $_."::"} @bad_stashes;   
 
44
                    1;
 
45
                }, # Do not eat our own children!
 
46
                '');
 
47
    push @anon_subs, { root => $_->ROOT, start => $_->START} 
 
48
        for grep { class($_) eq "CV" } B::main_cv->PADLIST->ARRAY->ARRAY;
 
49
    $_subsdone=1;
 
50
}
 
51
}
 
52
 
 
53
=head1 NAME
 
54
 
 
55
B::Utils - Helper functions for op tree manipulation
 
56
 
 
57
=head1 SYNOPSIS
 
58
 
 
59
  use B::Utils;
 
60
 
 
61
=head1 DESCRIPTION
 
62
 
 
63
These functions make it easier to manipulate the op tree.
 
64
 
 
65
=head1 FUNCTIONS
 
66
 
 
67
=over 3
 
68
 
 
69
=item C<all_starts>
 
70
 
 
71
=item C<all_roots>
 
72
 
 
73
Returns a hash of all of the starting ops or root ops of optrees, keyed
 
74
to subroutine name; the optree for main program is simply keyed to C<__MAIN__>.
 
75
 
 
76
B<Note>: Certain "dangerous" stashes are not scanned for subroutines: 
 
77
the list of such stashes can be found in C<@B::Utils::bad_stashes>. Feel
 
78
free to examine and/or modify this to suit your needs. The intention is
 
79
that a simple program which uses no modules other than C<B> and
 
80
C<B::Utils> would show no addition symbols.
 
81
 
 
82
This does B<not> return the details of ops in anonymous subroutines
 
83
compiled at compile time. For instance, given 
 
84
 
 
85
    $a = sub { ... };
 
86
 
 
87
the subroutine will not appear in the hash. This is just as well, since
 
88
they're anonymous... If you want to get at them, use...
 
89
 
 
90
=item C<anon_subs()>
 
91
 
 
92
This returns an array of hash references. Each element has the keys
 
93
"start" and "root". These are the starting and root ops of all of
 
94
the anonymous subroutines in the program.
 
95
 
 
96
=cut
 
97
 
 
98
sub all_starts { _init(); return %starts; }
 
99
sub all_roots  { _init(); return %roots; }
 
100
sub anon_subs { _init(); return @anon_subs }
 
101
 
 
102
sub B::GV::_push_starts {
 
103
    my $name = $_[0]->STASH->NAME."::".$_[0]->SAFENAME;
 
104
    return unless ${$_[0]->CV};
 
105
    my $cv = $_[0]->CV;
 
106
 
 
107
    if ($cv->PADLIST->can("ARRAY") and $cv->PADLIST->ARRAY and $cv->PADLIST->ARRAY->can("ARRAY")) {
 
108
        push @anon_subs, { root => $_->ROOT, start => $_->START} 
 
109
            for grep { class($_) eq "CV" } $cv->PADLIST->ARRAY->ARRAY;
 
110
    }
 
111
    return unless ${$cv->START} and ${$cv->ROOT};
 
112
    $starts{$name} = $cv->START;
 
113
    $roots{$name} = $cv->ROOT;
 
114
};
 
115
 
 
116
sub B::SPECIAL::_push_starts{}
 
117
 
 
118
=item C<< $op->oldname >>
 
119
 
 
120
Returns the name of the op, even if it is currently optimized to null.
 
121
This helps you understand the stucture of the op tree.
 
122
 
 
123
=cut
 
124
 
 
125
sub B::OP::oldname {
 
126
    return substr(B::ppname($_[0]->targ),3) if $_[0]->name eq "null" and $_[0]->targ;
 
127
    return $_[0]->name;
 
128
}
 
129
 
 
130
=item C<< $op->kids >>
 
131
 
 
132
Returns an array of all this op's non-null children, in order.
 
133
 
 
134
=cut
 
135
 
 
136
sub B::OP::kids {
 
137
    my $op = shift;
 
138
    my @rv = ();
 
139
 
 
140
    foreach my $type (qw(first last other)) {
 
141
        my $kid = $op->$type();
 
142
        next if !$kid || class($kid) eq 'NULL';
 
143
        if( $kid->name eq 'null' ) {
 
144
            push @rv, $kid->kids;
 
145
        }
 
146
        else {
 
147
            push @rv, $kid;
 
148
        }
 
149
    }
 
150
 
 
151
    my @more_rv = ();
 
152
    foreach my $more_op (@rv) {
 
153
        my $next_op = $more_op;
 
154
        while( $next_op->can("sibling") ) {
 
155
            $next_op = $next_op->sibling;
 
156
            last if !$next_op || class($next_op) eq 'NULL';
 
157
            if( $next_op->name eq 'null' ) {
 
158
                push @more_rv, $next_op->kids;
 
159
            }
 
160
            else {
 
161
                push @more_rv, $next_op;
 
162
            }
 
163
        }
 
164
    }
 
165
 
 
166
    return @rv, @more_rv;
 
167
}
 
168
 
 
169
 
 
170
=item C<< $op->first >>
 
171
 
 
172
=item C<< $op->last >>
 
173
 
 
174
=item C<< $op->other >>
 
175
 
 
176
Normally if you call first, last or other on anything which is not an
 
177
UNOP, BINOP or LOGOP respectivly it will die.  This leads to lots of
 
178
code like:
 
179
 
 
180
    $op->first if $op->can('first');
 
181
 
 
182
B::Utils provides every op with first, last and other methods which
 
183
will simply return nothing if it isn't relevent.
 
184
 
 
185
=cut
 
186
 
 
187
foreach my $type (qw(first last other)) {
 
188
    no strict 'refs';
 
189
    *{'B::OP::'.$type} = sub {
 
190
        my($op) = shift;
 
191
        if( $op->can("SUPER::$type") ) {
 
192
            return $op->$type();
 
193
        }
 
194
        else {
 
195
            return;
 
196
        }
 
197
    }
 
198
}
 
199
 
 
200
 
 
201
=item C<< $op->parent >>
 
202
 
 
203
Returns the parent node in the op tree, if possible. Currently "possible" means
 
204
"if the tree has already been optimized"; that is, if we're during a C<CHECK>
 
205
block. (and hence, if we have valid C<next> pointers.)
 
206
 
 
207
In the future, it may be possible to search for the parent before we have the
 
208
C<next> pointers in place, but it'll take me a while to figure out how to do
 
209
that.
 
210
 
 
211
=cut
 
212
 
 
213
# This is probably the most efficient algorithm for finding the parent given the
 
214
# next node in execution order and the children of an op. You'll be glad to hear
 
215
# that it doesn't do a full search of the tree from the root, but it searches
 
216
# ever-higher subtrees using a breathtaking double recursion. It works on the
 
217
# principle that the C<next> pointer will always point to an op further northeast
 
218
# on the tree, and hence will be heading upwards toward the parent.
 
219
 
 
220
sub B::OP::parent {
 
221
    my $target = shift;
 
222
    die "I'm not sure how to do this yet. I'm sure there is a way. If you know, please email me."
 
223
        if (!$target->seq);
 
224
    my (%deadend, $search);
 
225
    $search = sub {
 
226
        my $node = shift || return undef;
 
227
 
 
228
        # Go up a level if we've got stuck, and search (for the same
 
229
        # $target) from a higher vantage point.
 
230
        return $search->($node->parent) if exists $deadend{$node};
 
231
 
 
232
        # Test the immediate children
 
233
        return $node if scalar grep {$_ == $target} $node->kids;
 
234
 
 
235
        # Recurse
 
236
        my $x;
 
237
        defined($x = $search->($_)) and return $x for $node->kids;
 
238
 
 
239
        # Not in this subtree.
 
240
        $deadend{$node}++;
 
241
        return undef;
 
242
   };
 
243
   my $result;
 
244
   my $start = $target;
 
245
   $result = $search->($start) and return $result while $start = $start->next;
 
246
   return $search->($start);
 
247
}
 
248
 
 
249
=item C<< $op->previous >>
 
250
 
 
251
Like C<< $op->next >>, but not quite.
 
252
 
 
253
=cut
 
254
 
 
255
sub B::OP::previous {
 
256
    my $target = shift;
 
257
    my $start = $target;
 
258
    my (%deadend, $search);
 
259
    $search = sub {
 
260
        my $node = shift || die;
 
261
        return $search->(find_parent($node)) if exists $deadend{$node};
 
262
        return $node if $node->{next}==$target;
 
263
        # Recurse
 
264
        my $x;
 
265
        ($_->next == $target and return $_) for $node->kids;
 
266
        defined($x = $search->($_)) and return $x for $node->{kids};
 
267
 
 
268
        # Not in this subtree.
 
269
        $deadend{$node}++;
 
270
        return undef;
 
271
   };
 
272
   my $result;
 
273
   $result = $search->($start) and return $result
 
274
        while $start = $start->next;
 
275
}
 
276
 
 
277
=item walkoptree_simple($op, \&callback, [$data])
 
278
 
 
279
The C<B> module provides various functions to walk the op tree, but
 
280
they're all rather difficult to use, requiring you to inject methods
 
281
into the C<B::OP> class. This is a very simple op tree walker with
 
282
more expected semantics.
 
283
 
 
284
The &callback is called at each op with the op itself passed in as the
 
285
first argument and any additional $data as the second.
 
286
 
 
287
All the C<walk> functions set C<$B::Utils::file> and C<$B::Utils::line>
 
288
to the appropriate values of file and line number in the program
 
289
being examined.  Since only COPs contain this information it may be
 
290
unavailable in the first few callback calls.
 
291
 
 
292
=cut
 
293
 
 
294
our ($file, $line);
 
295
 
 
296
# Make sure we reset $file and $line between runs.
 
297
sub walkoptree_simple {
 
298
    ($file, $line) = ('__none__', 0);
 
299
 
 
300
    _walkoptree_simple(@_);
 
301
}
 
302
 
 
303
sub _walkoptree_simple {
 
304
    my ($op, $callback, $data) = @_;
 
305
    ($file, $line) = ($op->file, $op->line) if $op->isa("B::COP");
 
306
    $callback->($op,$data);
 
307
    if ($$op && ($op->flags & OPf_KIDS)) {
 
308
        my $kid;
 
309
        for ($kid = $op->first; $$kid; $kid = $kid->sibling) {
 
310
            _walkoptree_simple($kid, $callback, $data);
 
311
        }
 
312
    }
 
313
}
 
314
 
 
315
=item walkoptree_filtered($op, \&filter, \&callback, [$data])
 
316
 
 
317
This is much the same as C<walkoptree_simple>, but will only call the
 
318
callback if the C<filter> returns true. The C<filter> is passed the 
 
319
op in question as a parameter; the C<opgrep> function is fantastic 
 
320
for building your own filters.
 
321
 
 
322
=cut
 
323
 
 
324
sub walkoptree_filtered {
 
325
    ($file, $line) = ('__none__', 0);
 
326
    
 
327
    _walkoptree_filtered(@_);
 
328
}
 
329
 
 
330
sub _walkoptree_filtered {
 
331
    my ($op, $filter, $callback, $data) = @_;
 
332
    ($file, $line) = ($op->file, $op->line) if $op->isa("B::COP");
 
333
    $callback->($op,$data) if $filter->($op);
 
334
    if ($$op && ($op->flags & OPf_KIDS)) {
 
335
        my $kid;
 
336
        for ($kid = $op->first; $$kid; $kid = $kid->sibling) {
 
337
            _walkoptree_filtered($kid, $filter, $callback, $data);
 
338
        }
 
339
    }
 
340
}
 
341
 
 
342
=item walkallops_simple(\&callback, [$data])
 
343
 
 
344
This combines C<walkoptree_simple> with C<all_roots> and C<anon_subs>
 
345
to examine every op in the program. C<$B::Utils::sub> is set to the
 
346
subroutine name if you're in a subroutine, C<__MAIN__> if you're in
 
347
the main program and C<__ANON__> if you're in an anonymous subroutine.
 
348
 
 
349
=cut
 
350
 
 
351
our $sub;
 
352
 
 
353
sub walkallops_simple {
 
354
    my ($callback, $data) = @_;
 
355
    _init();
 
356
    for $sub (keys %roots) {
 
357
        walkoptree_simple($roots{$sub}, $callback, $data);
 
358
    }
 
359
    $sub = "__ANON__";
 
360
    for (@anon_subs) {
 
361
        walkoptree_simple($_->{root}, $callback, $data);
 
362
    }
 
363
}
 
364
 
 
365
=item walkallops_filtered(\&filter, \&callback, [$data])
 
366
 
 
367
Same as above, but filtered.
 
368
 
 
369
=cut
 
370
 
 
371
sub walkallops_filtered {
 
372
    my ($filter, $callback, $data) = @_;
 
373
    _init();
 
374
    for $sub (keys %roots) {
 
375
        walkoptree_filtered($roots{$sub}, $filter, $callback, $data);
 
376
    }
 
377
    $sub = "__ANON__";
 
378
    for (@anon_subs) {
 
379
        walkoptree_filtered($_->{root}, $filter, $callback, $data);
 
380
    }
 
381
}
 
382
 
 
383
=item carp(@args) 
 
384
 
 
385
=item croak(@args) 
 
386
 
 
387
Warn and die, respectively, from the perspective of the position of the op in
 
388
the program. Sounds complicated, but it's exactly the kind of error reporting
 
389
you expect when you're grovelling through an op tree.
 
390
 
 
391
=cut
 
392
 
 
393
sub _preparewarn {
 
394
    my $args = join '', @_;
 
395
    $args = "Something's wrong " unless $args;
 
396
    $args .= " at $file line $line.\n" unless substr($args, length($args) -1) eq "\n";
 
397
}
 
398
 
 
399
sub carp  (@) { CORE::warn(_preparewarn(@_)) }
 
400
sub croak (@) { CORE::die(_preparewarn(@_)) }
 
401
 
 
402
=item opgrep(\%conditions, @ops)
 
403
 
 
404
Returns the ops which meet the given conditions. The conditions should be
 
405
specified like this:
 
406
 
 
407
    @barewords = opgrep(
 
408
                        { name => "const", private => OPpCONST_BARE },
 
409
                        @ops
 
410
                       );
 
411
 
 
412
You can specify alternation by giving an arrayref of values:
 
413
 
 
414
    @svs = opgrep ( { name => ["padsv", "gvsv"] }, @ops)
 
415
 
 
416
And you can specify inversion by making the first element of the arrayref
 
417
a "!". (Hint: if you want to say "anything", say "not nothing": C<["!"]>)
 
418
 
 
419
You may also specify the conditions to be matched in nearby ops.
 
420
 
 
421
    walkallops_filtered(
 
422
        sub { opgrep( {name => "exec", 
 
423
                       next => {
 
424
                                 name    => "nextstate",
 
425
                                 sibling => { name => [qw(! exit warn die)] }
 
426
                               }
 
427
                      }, @_)},
 
428
        sub { 
 
429
              carp("Statement unlikely to be reached"); 
 
430
              carp("\t(Maybe you meant system() when you said exec()?)\n");
 
431
        }
 
432
    )
 
433
 
 
434
Get that?
 
435
 
 
436
Here are the things that can be tested:
 
437
 
 
438
        name targ type seq flags private pmflags pmpermflags
 
439
        first other last sibling next pmreplroot pmreplstart pmnext
 
440
 
 
441
=cut
 
442
 
 
443
sub opgrep {
 
444
    my ($cref, @ops) = @_;
 
445
    my %conds = %$cref;
 
446
    my @rv = ();
 
447
 
 
448
    OPLOOP: for my $o (grep defined, @ops) {
 
449
        # First, let's skim off ops of the wrong type.
 
450
        for my $type (qw(first other last pmreplroot pmreplstart pmnext pmflags pmpermflags)) {
 
451
            next OPLOOP if exists $conds{$type} and !$o->can($type);
 
452
        }
 
453
 
 
454
        for my $test (qw(name targ type seq flags private pmflags pmpermflags)) {
 
455
            next unless exists $conds{$test};
 
456
            next OPLOOP unless $o->can($test);
 
457
 
 
458
            my @conds = ref $conds{$test} ? @{$conds{$test}} : $conds{$test};
 
459
 
 
460
            if ($conds[0] eq "!") {
 
461
                my @conds = @{$conds{$test}}; shift @conds;
 
462
                next OPLOOP if grep {$o->$test eq $_} @conds;
 
463
            } else {
 
464
                next OPLOOP unless grep {$o->$test eq $_} @conds;
 
465
            }
 
466
        }
 
467
 
 
468
        for my $neighbour (qw(first other last sibling next pmreplroot pmreplstart pmnext)) {
 
469
            next unless exists $conds{$neighbour};
 
470
            # We know it can, because we tested that above
 
471
            # Recurse, recurse!
 
472
            next OPLOOP unless opgrep($conds{$neighbour}, $o->$neighbour);
 
473
        }
 
474
 
 
475
        push @rv, $o;
 
476
    }
 
477
    return @rv;
 
478
}
 
479
 
 
480
1;
 
481
 
 
482
=back
 
483
 
 
484
=head2 EXPORT
 
485
 
 
486
None by default.
 
487
 
 
488
=head1 AUTHOR
 
489
 
 
490
Simon Cozens, C<simon@cpan.org>
 
491
 
 
492
=head1 TODO
 
493
 
 
494
I need to add more Fun Things, and possibly clean up some parts where
 
495
the (previous/parent) algorithm has catastrophic cases, but it's more
 
496
important to get this out right now than get it right.
 
497
 
 
498
=head1 SEE ALSO
 
499
 
 
500
L<B>, L<B::Generate>.
 
501
 
 
502
=cut