File Coverage

lib/B/DeparseTree/Fragment.pm
Criterion Covered Total %
statement 18 218 8.2
branch 0 98 0.0
condition 0 30 0.0
subroutine 6 20 30.0
pod 0 14 0.0
total 24 380 6.3


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