File Coverage

lib/B/DeparseTree/Fragment.pm
Criterion Covered Total %
statement 15 215 6.9
branch 0 98 0.0
condition 0 30 0.0
subroutine 5 19 26.3
pod 0 14 0.0
total 20 376 5.3


line stmt bran cond sub pod time code
1             package B::DeparseTree::Fragment;
2              
3 2     2   13083 use strict; use warnings;
  2     2   4  
  2         49  
  2         8  
  2         5  
  2         55  
4 2     2   9 use Data::Printer;
  2         4  
  2         16  
5 2     2   94 use vars qw(@ISA @EXPORT);
  2         4  
  2         3906  
6             @ISA = ('Exporter');
7             @EXPORT = qw(deparse_offset
8             dump
9             extract_node_info
10             get_addr_info
11             get_parent_addr_info get_parent_op
12             get_prev_addr_info
13             get_prev_info
14             trim_line_pair
15             underline_parent
16             );
17              
18             sub deparse_offset
19             {
20 0     0 0   my ($funcname, $address) = @_;
21              
22 0           my $deparse = B::DeparseTree->new();
23 0 0         if ($funcname eq "DB::DB") {
24 0           $deparse->main2info;
25             } else {
26 0           $deparse->coderef2info(\&$funcname);
27             }
28 0           get_addr_info($deparse, $address);
29             }
30              
31             sub get_addr($$)
32             {
33 0     0 0   my ($deparse, $addr) = @_;
34 0 0         return undef unless $addr;
35 0           return $deparse->{optree}{$addr};
36             }
37              
38             sub get_addr_info($$)
39             {
40 0     0 0   my ($deparse, $addr) = @_;
41 0           my $op_info = get_addr($deparse, $addr);
42 0           return $op_info;
43             }
44              
45             sub get_parent_op($)
46             {
47 0     0 0   my ($op_info) = @_;
48 0 0         return undef unless $op_info;
49 0           my $deparse = $op_info->{deparse};
50              
51             # FIXME:
52 0           return $deparse->{ops}{$op_info->{addr}}{parent};
53             }
54              
55             sub get_parent_addr_info($)
56             {
57 0     0 0   my ($op_info) = @_;
58 0           my $deparse = $op_info->{deparse};
59             # FIXME
60             # my $parent_op = get_parent_op($op_info);
61 0           my $parent_addr = $op_info->{parent};
62 0 0         return undef unless $parent_addr;
63 0           return $deparse->{optree}{$parent_addr};
64             }
65              
66             sub get_prev_info($);
67             sub get_prev_info($)
68             {
69 0     0 0   my ($op_info) = @_;
70 0 0         return undef unless $op_info;
71             return $op_info->{prev_expr}
72 0           }
73              
74             sub get_prev_addr_info($);
75             sub get_prev_addr_info($)
76             {
77 0     0 0   my ($op_info) = @_;
78 0 0         return undef unless $op_info;
79 0 0         if (!exists $op_info->{prev_expr}) {
80 0           my $parent_info = get_parent_addr_info($op_info);
81 0 0         if ($parent_info) {
82 0           return get_prev_addr_info($parent_info);
83             } else {
84 0           return undef;
85             }
86             }
87             return $op_info->{prev_expr}
88 0           }
89              
90             sub underline_parent($$$) {
91 0     0 0   my ($child_text, $parent_text, $char) = @_;
92 0           my $start_pos = index($parent_text, $child_text);
93 0           return (' ' x $start_pos) . ($char x length($child_text));
94              
95             }
96             # Return either 2 or 3 strings in an array reference.
97             # There are various cases to consider.
98             # 1. Child and parent texts are no more than a single line:
99             # return and the underline, two entries. For example:
100             # my ($a, $b) = (5, 6);
101             # -----
102             # 2. The parent spans more than a line but the child is
103             # on that line. Return an array of the first line of the parent
104             # with elision and the child underline, two entries. Example
105             # if the child is $a in:
106             # if ($a) {
107             # $b
108             # }
109             # return:
110             # if ($a) {...
111             # --
112             # -----
113             # 3. The parent spans more than a line and the child is
114             # not that line. Return an array of the first line of the parent
115             # with elision, then the line containing the child and the child underline,
116             # three entries. Example:
117             # if the child is $b in:
118             # if ($a) {
119             # $b;
120             # $c;
121             # }
122             # return:
123             # if ($a) {...
124             # $b;
125             # --
126              
127             # 4. The parent spans more than a line and the child is
128             # not that line and also spans more than a single line.
129             # Do the same as 3. but add eplises to the underline.
130             # Example:
131             # if the child is "\$b;\n \$c" in:
132             # if ($a) {
133             # $b;
134             # $c;
135             # }
136             # return:
137             # if ($a) {...
138             # $b;
139             # ---...
140             # 5. Like 4, but the child is on the first line. A cross between
141             # 3 and 4. No elipses for the first line is needed, just one on the
142             # underline
143             #
144             sub trim_line_pair($$$$) {
145 0     0 0   my ($parent_text, $child_text, $parent_underline, $start_pos) = @_;
146             # If the parent text is longer than a line, use just the line.
147             # The underline indicator adds an elipsis to show it is elided.
148 0           my @parent_lines = split(/\n/, $parent_text);
149 0           my $i = 0;
150 0 0         if (scalar(@parent_lines) > 1) {
151 0           for ($i=0; $start_pos > length($parent_lines[$i]); $i++) {
152 0           my $l = length($parent_lines[$i]);
153 0           $start_pos -= ($l+1);
154 0           $parent_underline = substr($parent_underline, $l+1);
155             }
156             }
157 0           my @result = ();
158 0 0         if ($i > 0) {
159 0           push @result, $parent_lines[0] . '...';
160             }
161 0           my $stripped_parent = $parent_lines[$i];
162 0           my @child_lines = split(/\n/, $child_text);
163 0 0         if (scalar(@child_lines) > 1) {
164 0           $parent_underline = substr($parent_underline, 0, length($child_lines[0])+1) . '...';
165             }
166              
167 0           push @result, $stripped_parent, $parent_underline;
168 0           return \@result;
169             }
170              
171             # FIXME: this is a mess
172              
173             # return an ARRAY ref to strings that can be joined with "\n" to
174             # give a position in the text. Here are some examples
175             # after joining
176             #
177             # my ($x, $y) = (1, 2)
178             # ------
179             # my ($x, $y) = (1, 2)
180             # ~~~~~
181             # my ($x, $y) = (1, 2)
182             # -
183             # (1, 2)
184             #
185             # if ($x) { ...
186             # my ($x, $y = (1, 2)
187             # -------------------
188             #
189             # if ($x) { ...
190             # my ($x, $y = (1, 2)...
191             # -------------------
192              
193             # When we can, we text in the context of the surrounding
194             # text which is obtained by going up levels of the tree
195             # form the instruction. We might have to go a few levels
196             # up the tree before we find a text that spans more than
197             # a single line. In the fourth case where we don't
198             # have an underline but simply have "(1, 2)" that means
199             # were unable to get the parent text.
200              
201             # We hope that in the normal case, using the place holders in the
202             # format specifiers, we can know for sure where a child fits in as
203             # that child's node is stored in the parent as some "texts"
204             # entry. However this isn't always possible right now. So in the
205             # second example where "~" was used instead of "-", to
206             # indicate that the result was obtained the result by string matching
207             # rather than by exact node matching inside the parent.
208             # We can also use the "|" instead for instructions that really
209             # don't have an equivalent concept in the source code, so we've
210             # artificially tagged a location that is reasonable. "pushmark"
211             # and "padrange" instructions would be in this category.
212             #
213             # In the last two examples, we show how we do elision. The ...
214             # in the parent text means that we have only given the first line
215             # of the parent text along with the line that the child fits in.
216             # if there is an elision in the child text it means that that
217             # spans more than one line.
218              
219             sub extract_node_info($)
220             {
221 0     0 0   my ($info) = @_;
222              
223 0           my $child_text = $info->{text};
224 0           my $parent_text = undef;
225 0           my $candidate_pair = undef;
226 0           my $marked_position = undef;
227              
228             # Some opcodes like pushmark , padrange, and null,
229             # don't have an well-defined correspondence to a string in the
230             # source code, so we have made a somewhat arbitrary association fitting
231             # into the parent string. Examples of such artificial associations are
232             # the function name part of call, or an open brace of a scope.
233             # You can tell these nodes because they have a "position" field.
234 0 0         if (exists $info->{position}) {
235 0           my $found_pos = $info->{position};
236 0           $marked_position = $found_pos;
237 0           $parent_text = $child_text;
238 0           $child_text = substr($parent_text,
239             $found_pos->[0], $found_pos->[1]);
240 0           my $parent_underline = ' ' x $found_pos->[0];
241 0           $parent_underline .= '|' x $found_pos->[1];
242 0           $candidate_pair = trim_line_pair($parent_text, $child_text,
243             $parent_underline,
244             $found_pos->[0]);
245              
246             }
247              
248 0 0         my $parent = $info->{parent} ? $info->{parent} : undef;
249 0 0         unless ($parent) {
250 0 0         return $candidate_pair ? $candidate_pair : [$child_text];
251             }
252              
253 0           my $child_addr = $info->{addr};
254 0           my $deparsed = $info->{deparse};
255 0           my $parent_info = $deparsed->{optree}{$parent};
256              
257 0 0         unless ($parent_info) {
258 0 0         return $candidate_pair ? $candidate_pair : [$child_text];
259             }
260              
261 0 0         my $separator = exists $parent_info->{sep} ? $parent_info->{sep} : '';
262 0 0         my @texts = exists $parent_info->{texts} ? @{$parent_info->{texts}} : ($parent_info->{text});
  0            
263 0           my $parent_line = '';
264 0           my $text_len = $#texts;
265 0           my $result = '';
266              
267 0 0 0       if (!exists $parent_info->{fmt}
      0        
268             and scalar(@texts) == 1
269 0           and eval{$texts[0]->isa("B::DeparseTree::TreeNode")}) {
270 0           $parent_info = $texts[0];
271             }
272 0 0 0       if (exists $parent_info->{fmt} || exists $parent_info->{position}) {
273             # If the child text is the same as the parent's, go up the parent
274             # chain until we find something different.
275 0   0       while ($parent_info->{text} eq $child_text
      0        
276             && $parent_info->{parent}
277             && $deparsed->{optree}{$parent_info->{parent}}
278             ) {
279 0 0         last if ! exists $deparsed->{optree}{$parent_info->{parent}};
280 0           $parent_info = $deparsed->{optree}{$parent_info->{parent}};
281             }
282 0           my $fmt = $parent_info->{fmt};
283 0           my $indexes = $parent_info->{indexes};
284 0           my $args = $parent_info->{texts};
285 0           my ($str, $found_pos) = $deparsed->template_engine($fmt, $indexes, $args,
286             $child_addr);
287              
288             # Keep gathering parent text until we have at least one full line.
289 0   0       while (index($str, "\n") == -1 && $parent_info->{parent}) {
290 0           $child_addr = $parent_info->{addr};
291 0 0         last if ! exists $deparsed->{optree}{$parent_info->{parent}};
292 0           $parent_info = $deparsed->{optree}{$parent_info->{parent}};
293 0           $fmt = $parent_info->{fmt};
294 0           $indexes = $parent_info->{indexes};
295 0           $args = $parent_info->{texts};
296 0           my ($next_str, $next_found_pos) = $deparsed->template_engine($fmt,
297             $indexes, $args,
298             $child_addr);
299 0 0         last unless $next_found_pos;
300 0           my $nl_pos = index($next_str, "\n");
301 0   0       while ($nl_pos >= 0 and $nl_pos < $next_found_pos->[0]) {
302 0           $next_str = substr($next_str, $nl_pos+1);
303 0           $next_found_pos->[0] -= ($nl_pos+1);
304 0           $nl_pos = index($next_str, "\n");
305             }
306 0           $str = $next_str;
307 0 0         if ($found_pos) {
308 0           $found_pos->[0] += $next_found_pos->[0];
309             } else {
310 0           $found_pos = $next_found_pos;
311             }
312             }
313              
314 0 0         if (defined($found_pos)) {
315 0           my $parent_underline;
316 0 0         if ($marked_position) {
317 0           $parent_underline = ' ' x ($found_pos->[0] + $marked_position->[0]);
318 0           $parent_underline .= '|' x $marked_position->[1];
319             } else {
320 0           $parent_underline = ' ' x $found_pos->[0];
321 0           $parent_underline .= '-' x $found_pos->[1];
322             }
323 0           return trim_line_pair($str, $child_text, $parent_underline, $found_pos->[0]);
324             }
325 0           $result = $str;
326             } else {
327 0           for (my $i=0; $i <= $text_len; $i++) {
328 0           my $text = $texts[$i];
329 0 0         $result .= $separator if $result;
330              
331 0 0         if (ref($text)) {
332 0 0 0       if (ref($text) eq 'ARRAY' and (scalar(@$text) == 2)) {
    0          
333 0 0         if ($text->[1] == $child_addr) {
334 0           $child_text = $text->[0];
335 0           my $parent_underline = ' ' x length($result);
336 0           $result .= $text->[0];
337 0           $parent_underline .= '-' x length($text->[0]);
338 0 0         if ($i < $text_len) {
339 0           $result .= $separator;
340 0           my @remain_texts = @texts[$i+1..$#texts];
341 0           my $tail = $deparsed->combine2str($separator, \@remain_texts);
342 0           $result .= $tail;
343             }
344 0           return trim_line_pair($result, $child_text, $parent_underline, 0);
345             } else {
346 0           $result .= $text->[0];
347             }
348             } elsif ($text->{addr} == $child_addr) {
349 0           my $parent_underline = ' ' x length($result);
350 0           $result .= $text->{text};
351 0           $parent_underline .= '-' x length($text->{text});
352 0 0         if ($i < $text_len) {
353 0           $result .= $separator;
354 0           my @remain_texts = @texts[$i+1..$#texts];
355 0           my $tail = $deparsed->combine2str($separator, \@remain_texts);
356 0           $result .= $tail;
357             }
358 0           return trim_line_pair($result, $child_text, $parent_underline, 0);
359             } else {
360 0           $result .= $text->{text};
361             }
362             } else {
363 0           $result .= $text;
364             }
365             }
366             }
367             # Can't find by node address info, so just try to find the string
368             # inside of the parent.
369 0           $parent_text = $parent_info->{text};
370 0           my $start_index = index($parent_text, $child_text);
371 0 0         if ($start_index >= 0) {
372 0 0         if (index($parent_text, $child_text, $start_index+1) < 0) {
373             # It is in there *uniquely*!
374 0           my $parent_underline = underline_parent($child_text, $parent_text, '~');
375 0           return trim_line_pair($parent_text, $child_text, $parent_underline, $start_index);
376             }
377             }
378             }
379              
380             # Dump out full information of a node in relation to its
381             # parent
382             sub dump($) {
383 0     0 0   my ($deparse_tree) = @_;
384 0           my @addrs = sort keys %{$deparse_tree->{optree}};
  0            
385 0           for (my $i=0; $i < $#addrs; $i++) {
386 0           printf("%d: %s\n", $i, ('=' x 50));
387 0           my $info = get_addr_info($deparse_tree, $addrs[$i]);
388 0 0         if ($info) {
389 0           printf "0x%0x\n", $addrs[$i];
390 0           p $info ;
391             }
392 0 0         if ($info->{parent}) {
393 0           my $parent = get_parent_addr_info($info);
394 0 0         if ($parent) {
395 0           p $parent ;
396 0           my $texts = extract_node_info($info);
397 0 0         if ($texts) {
398 0           print join("\n", @$texts), "\n";
399             }
400             }
401             }
402 0           printf("%d: %s\n", $i, ('=' x 50));
403             }
404             }
405              
406             # Dump out essention information of a node in relation to its
407             # parent
408             sub dump_relations($) {
409 0     0 0   my ($deparse_tree) = @_;
410 0           my @addrs = sort keys %{$deparse_tree->{optree}};
  0            
411 0           for (my $i=0; $i < $#addrs; $i++) {
412 0           my $info = get_addr_info($deparse_tree, $addrs[$i]);
413 0 0 0       next unless $info && $info->{parent};
414 0           my $parent = get_parent_addr_info($info);
415 0 0         next unless $parent;
416 0           printf("%d: %s\n", $i, ('=' x 50));
417 0           print "Child info:\n";
418 0           printf "\taddr: 0x%0x, parent: 0x%0x\n", $addrs[$i], $parent->{addr};
419 0 0         printf "\top: %s\n", $info->{op}->can('name') ? $info->{op}->name : $info->{op} ;
420 0           printf "\ttext: %s\n\n", $info->{text};
421             # p $parent ;
422 0           my $texts = extract_node_info($info);
423 0 0         if ($texts) {
424 0           print join("\n", @$texts), "\n";
425             }
426 0           printf("%d: %s\n", $i, ('=' x 50));
427             }
428             }
429              
430             sub dump_tree($$);
431              
432             # Dump out the entire texts in tree format
433             sub dump_tree($$) {
434 0     0 0   my ($deparse_tree, $info) = @_;
435 0 0 0       if (ref($info) and (ref($info->{texts}) eq 'ARRAY')) {
436 0           foreach my $child_info (@{$info->{texts}}) {
  0            
437 0 0         if (ref($child_info)) {
438 0 0         if (ref($child_info) eq 'ARRAY') {
    0          
439 0           p $child_info;
440             } elsif (ref($child_info) eq 'B::DeparseTree::TreeNode') {
441 0           dump_tree($deparse_tree, $child_info)
442             } else {
443 0           printf "Unknown child_info type %s\n", ref($child_info);
444 0           p $child_info;
445             }
446             }
447             }
448 0           print '-' x 50, "\n";
449             }
450 0           p $info ;
451 0           print '=' x 50, "\n";
452             }
453              
454             unless (caller) {
455             sub bug() {
456 0     0 0   my ($a, $b) = @_;
457 0 0         ($a, $b) = ($b, $a) if ($a > $b);
458             # -((1, 2) x 2);
459             # no strict;
460             # for ( $i=0; $i;) {};
461             # my ($a, $b, $c);
462             # CORE::exec($foo $bar);
463             # exec $foo $bar;
464             # exec $foo $bar;
465             }
466              
467             my $child_text = '$foo $bar';
468             my $result = 'exec $foo $bar';
469             my $parent_underline = " ---------";
470             my $start_pos = 0;
471             my $lines = trim_line_pair($result, $child_text, $parent_underline,
472             $start_pos);
473             print join("\n", @$lines), "\n";
474              
475             my $deparse = B::DeparseTree->new();
476 2     2   16 use B;
  2         4  
  2         231  
477             $deparse->pessimise(B::main_root, B::main_start);
478             # my @addrs = sort keys %{$deparse->{ops}}, "\n";
479             # use Data::Printer;
480             # p @addrs;
481              
482             # my @info_addrs = sort keys %{$deparse->{optree}}, "\n";
483             # print '-' x 40, "\n";
484             # p @info_addrs;
485              
486             # $deparse->init();
487             # my $svref = B::svref_2object(\&bug);
488             # my $x = $deparse->deparse_sub($svref, $addrs[9]);
489             # my $x = $deparse->deparse_sub($svref);
490             # dump_tree($deparse, $x);
491              
492             # # my @info_addrs = sort keys %{$deparse->{optree}}, "\n";
493             # # print '-' x 40, "\n";
494             # # p @info_addrs;
495              
496             # #my $info = get_addr_info($deparse, $addrs[10]);
497             # # p $info;
498             # exit 0;
499              
500             $deparse->coderef2info(\&bug);
501             # $deparse->coderef2info(\&get_addr_info);
502             my @addrs = sort keys %{$deparse->{optree}}, "\n";
503             B::DeparseTree::Fragment::dump($deparse);
504              
505             # my ($parent_text, $pu);
506             # $parent_text = "now is the time";
507             # $child_text = 'is';
508             # $start_pos = index($parent_text, $child_text);
509             # $pu = underline_parent($child_text, $parent_text, '-');
510             # print join("\n", @{trim_line_pair($parent_text, $child_text,
511             # $pu, $start_pos)}), "\n";
512             # $parent_text = "if (\$a) {\n\$b\n}";
513             # $child_text = '$b';
514             # $start_pos = index($parent_text, $child_text);
515             # $pu = underline_parent($child_text, $parent_text, '-');
516             # print join("\n", @{trim_line_pair($parent_text, $child_text,
517             # $pu, $start_pos)}), "\n";
518              
519             # $parent_text = "if (\$a) {\n \$b;\n \$c}";
520             # $child_text = '$b';
521             # $start_pos = index($parent_text, $child_text);
522             # $pu = underline_parent($child_text, $parent_text, '-');
523             # print join("\n", @{trim_line_pair($parent_text, $child_text,
524             # $pu, $start_pos)}), "\n";
525             # $parent_text = "if (\$a) {\n \$b;\n \$c}";
526             # $child_text = "\$b;\n \$c";
527             # $start_pos = index($parent_text, $child_text);
528             # $pu = underline_parent($child_text, $parent_text, '-');
529             # print join("\n", @{trim_line_pair($parent_text, $child_text,
530             # $pu, $start_pos)}), "\n";
531             }
532              
533             1;