File Coverage

blib/lib/Python/Bytecode/SAX.pm
Criterion Covered Total %
statement 15 238 6.3
branch 0 72 0.0
condition 0 9 0.0
subroutine 5 29 17.2
pod 0 12 0.0
total 20 360 5.5


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Python::Bytecode::SAX - Process Python bytecode, generating SAX events.
4              
5             =head1 SYNOPSIS
6              
7             use Python::Bytecode::SAX;
8             use XML::SAX::Writer;
9             my $handler = XML::SAX::Writer->new( Output => 'foo.xml' );
10             my $parser = Python::Bytecode::SAX->new( Handler => $handler, SAX => 2 );
11             $parser->parse_file('foo.pyc');
12              
13             Or
14              
15             use Python::Bytecode::SAX;
16             use XML::Handler::YAWriter;
17             my $handler = XML::Handler::YAWriter->new(
18             AsFile => 'foo.xml',
19             Pretty => {
20             CompactAttrIndent => 1,
21             PrettyWhiteIndent => 1,
22             PrettyWhiteNewline => 1,
23             CatchEmptyElement => 1
24             }
25             );
26             my $parser = Python::Bytecode::SAX->new( Handler => $handler, SAX => 1 );
27             $parser->parse_file('foo.pyc');
28              
29             =head1 DESCRIPTION
30              
31             This module reads and decodes a Python bytecode file, generating SAX1 or SAX2
32             events (SAX1 is the default) for what it finds.
33              
34             Until more documentation is written, you can examine the two XML files generated
35             in the F directory by C to get a feel for the overal structure and
36             the element and attribute names.
37              
38             =head1 HISTORY
39              
40             Based on the L module by Simon Cozens Esimon@cpan.orgE,
41             which is based on the F file in the Python Standard Library.
42              
43             =head1 SEE ALSO
44              
45             L by Simon Cozens Esimon@cpan.orgE.
46              
47             =head1 AUTHOR
48              
49             Gregor N. Purdy Egregor@focusresearch.comE
50              
51             =head1 COPYRIGHT
52              
53             Copyright (C) 2003 Gregor N. Purdy. All rights reserved.
54              
55             =head1 LICENSE
56              
57             This program is free software. Its use is subject to the same license as Perl.
58              
59             =cut
60              
61             package Python::Bytecode::SAX;
62 2     2   2265 use 5.6.0;
  2         7  
  2         162  
63              
64             our $VERSION = '0.1';
65              
66 2     2   12 use strict;
  2         5  
  2         75  
67 2     2   2746 use Data::Dumper;
  2         24285  
  2         225  
68 2     2   17 use Carp;
  2         4  
  2         278  
69              
70 0     0     use overload '""' => sub { my $obj = shift;
71 0           "{name}.", file ".$obj->{filename}." line ".$obj->{lineno}." at ".sprintf('0x%x>',0+$obj);
72 2     2   11 }, "0+" => sub { $_[0] }, fallback => 1;
  2     0   4  
  2         21  
  0            
73              
74             my $no_main_yet;
75              
76              
77             #
78             # new()
79             #
80              
81             sub new
82             {
83 0     0 0   my $class = shift;
84 0           my %opts = @_;
85              
86 0           return bless { %opts }, $class;
87             }
88              
89              
90             #
91             # _start_doc()
92             #
93              
94             sub _start_doc
95             {
96 0     0     my $self = shift;
97              
98 0           $self->{Handler}->start_document();
99             }
100              
101              
102             #
103             # _end_doc()
104             #
105              
106             sub _end_doc
107             {
108 0     0     my $self = shift;
109              
110 0           $self->_flush;
111 0           $self->{Handler}->end_document();
112             }
113              
114              
115             #
116             # _start_elem()
117             #
118              
119             sub _start_elem
120             {
121 0     0     my $self = shift;
122 0           my $name = shift;
123              
124 0           $self->_flush;
125 0           $self->{_elem} = $name;
126 0           $self->{_attrs} = { };
127             }
128              
129              
130             #
131             # _attr()
132             #
133              
134             sub _attr
135             {
136 0     0     my $self = shift;
137 0           my $attr = shift;
138 0           my $value = shift;
139              
140 0           $self->{_attrs}{$attr} = $value;
141             }
142              
143              
144             #
145             # _end_elem()
146             #
147              
148             sub _end_elem
149             {
150 0     0     my $self = shift;
151 0           my $name = shift;
152              
153 0           $self->_flush;
154            
155 0   0       my $SAX = $self->{SAX} || 1;
156              
157 0 0         if ($SAX == 1) {
158 0           $self->{Handler}->end_element({ Name => $name });
159             }
160             else {
161 0           $self->{Handler}->end_element({ Name => $name });
162             }
163             }
164              
165              
166             #
167             # _chars()
168             #
169              
170             sub _chars
171             {
172 0     0     my $self = shift;
173 0           my $data = shift;
174              
175 0           $self->_flush;
176              
177 0   0       my $SAX = $self->{SAX} || 1;
178              
179 0 0         if ($SAX == 1) {
180 0           $self->{Handler}->characters({ Data => $data });
181             }
182             else {
183 0           $self->{Handler}->characters({ Data => $data });
184             }
185             }
186              
187              
188             #
189             # _flush
190             #
191              
192             sub _flush
193             {
194 0     0     my $self = shift;
195              
196 0 0         return unless $self->{_elem};
197              
198 0   0       my $SAX = $self->{SAX} || 1;
199              
200 0 0         if ($SAX == 1) {
201 0           $self->{Handler}->start_element({ Name => $self->{_elem}, Attributes => $self->{_attrs} });
202             }
203             else {
204 0           my $attrs = { };
205 0           foreach my $attr (keys %{$self->{_attrs}}) {
  0            
206 0           $attrs->{$attr}{Name} = $attr;
207 0           $attrs->{$attr}{Value} = $self->{_attrs}{$attr};
208             }
209 0           $self->{Handler}->start_element({ Name => $self->{_elem}, Attributes => $attrs });
210             }
211              
212 0           $self->{_elem} = undef;
213 0           $self->{_attrs} = undef;
214             }
215              
216              
217             #
218             # parse_string()
219             #
220             # Open a filehandle for reading the string.
221             #
222              
223             sub parse_string
224             {
225 0     0 0   my $self = shift;
226 0           my $string = shift;
227              
228 0           my $fh;
229 0 0         open($fh, "<", \$string) or die;
230 0           $self->{fh} = $fh;
231              
232 0           return $self->_parse;
233             }
234              
235              
236             #
237             # parse_file()
238             #
239             # Open a filehandle for reading the file.
240             #
241              
242             sub parse_file
243             {
244 0     0 0   my $self = shift;
245 0           my $file = shift;
246              
247 0           my $fh;
248 0 0         open($fh, "<", $file) or die;
249 0           $self->{fh} = $fh;
250              
251 0           return $self->_parse;
252             }
253              
254              
255             #
256             # _parse()
257             #
258              
259             sub _parse
260             {
261 0     0     my $self = shift;
262              
263 0           my $magic = $self->r_long();
264 0           my $magic2 = $self->r_long(); # Second magic number
265 0           my $version = $Python::Bytecode::versions{$magic};
266              
267 0           my $data = _get_data_by_magic($magic);
268 0           $self->_init($data);
269              
270 0           $self->_start_doc();
271              
272 0           $self->_start_elem('file');
273 0           $self->_attr(magic => $magic);
274 0           $self->_attr(magic2 => $magic2);
275 0           $self->_attr(version => $version);
276              
277 0           $self->r_object(emit => 1);
278              
279 0           $self->_end_elem('file');
280              
281 0           $self->_end_doc();
282              
283 0           return $self;
284             }
285              
286              
287             #
288             # _get_data_by_magic()
289             #
290              
291             sub _get_data_by_magic
292             {
293 0     0     require Python::Bytecode::v21;
294 0           require Python::Bytecode::v22;
295 0           my $magic = shift;
296 0 0         unless (exists $Python::Bytecode::data{$magic}) {
297 0           require Carp;
298 0           Carp::croak("Unrecognised magic number $magic; Only know Python versions "
299 0           . join ", ", map { "$_ ($Python::Bytecode::versions{$_})" } keys %Python::Bytecode::versions
300             );
301             }
302 0           return $Python::Bytecode::data{$magic};
303             }
304              
305              
306             #
307             # r_byte()
308             #
309              
310             sub r_byte
311             {
312 0     0 0   my $self = shift;
313 0           return ord getc $self->{fh};
314             }
315              
316              
317             #
318             # r_long()
319             #
320              
321             sub r_long
322             {
323 0     0 0   my $self = shift;
324 0           my $x = $self->r_byte;
325 0           $x |= $self->r_byte << 8;
326 0           $x |= $self->r_byte << 16;
327 0           $x |= $self->r_byte << 24;
328 0           return $x;
329             }
330              
331              
332             #
333             # r_short()
334             #
335              
336             sub r_short
337             {
338 0     0 0   my $self = shift;
339 0           my $x = $self->r_byte;
340 0           $x |= $self->r_byte << 8;
341 0           $x |= -($x & 0x8000);
342 0           return $x;
343             }
344              
345              
346             #
347             # r_string()
348             #
349              
350             sub r_string
351             {
352 0     0 0   my $self = shift;
353 0           my $length = $self->r_long;
354 0           my $buf;
355 0 0         if ( exists $self->{stuff}) {
356 0           $buf = join "", splice ( @{$self->{stuff}},0,$length,() );
  0            
357             } else {
358 0           read $self->{fh}, $buf, $length;
359             }
360 0           return $buf;
361             }
362              
363              
364             #
365             # r_object()
366             #
367              
368             sub r_object
369             {
370 0     0 0   my $self = shift;
371 0           my %options = @_;
372              
373 0 0         confess "Don't know whether or not to emit!" unless exists $options{emit};
374              
375 0           my $type = chr $self->r_byte();
376              
377 0 0         if ($type eq "c") {
    0          
    0          
    0          
    0          
378 0           my $code = $self->r_code(emit => $options{emit}, $options{index});
379 0           return $code;
380             }
381             elsif ($type eq 's') {
382 0 0         if ($options{emit}) {
383 0           $self->_start_elem('literal');
384 0           $self->_attr(type => 'String');
385 0           $self->_attr(value => $self->r_string);
386 0 0         $self->_attr(index => $options{index}) if defined $options{index};
387 0           $self->_end_elem('literal');
388             }
389             else {
390 0           return $self->r_string();
391             }
392             }
393             elsif ($type eq 'i') {
394 0 0         if ($options{emit}) {
395 0           $self->_start_elem('literal');
396 0           $self->_attr(type => 'Long');
397 0           $self->_attr(value => $self->r_long);
398 0 0         $self->_attr(index => $options{index}) if defined $options{index};
399 0           $self->_end_elem('literal');
400             }
401             else {
402 0           return $self->r_long();
403             }
404             }
405             elsif ($type eq 'N') {
406 0 0         if ($options{emit}) {
407 0           $self->_start_elem('literal');
408 0           $self->_attr(type => 'None');
409 0 0         $self->_attr(index => $options{index}) if defined $options{index};
410 0           $self->_end_elem('literal');
411             }
412             else {
413 0           return undef;
414             }
415             }
416             elsif ($type eq "(") {
417 0           $self->r_tuple(name => $options{name}, index => $options{index});
418             }
419             else {
420 0           die "Oops! I didn't implement ".ord $type;
421             }
422             }
423              
424              
425             #
426             # r_tuple()
427             #
428              
429             sub r_tuple
430             {
431 0     0 0   my $self = shift;
432 0           my %options = @_;
433              
434 0           my $n = $self->r_long;
435            
436 0 0         $options{name} = 'tuple' unless $options{name};
437              
438 0           $self->_start_elem($options{name});
439 0 0         $self->_attr(index => $options{index}) if defined $options{index};
440 0           $self->_attr('count' => $n);
441            
442 0           for my $i (0..$n - 1) {
443 0           $self->r_object(index => $i, emit => 1);
444             }
445              
446 0           $self->_end_elem($options{name});
447             }
448              
449              
450             #
451             # r_code()
452             #
453              
454             sub r_code {
455 0     0 0   my $self = shift;
456              
457 0           $self->_start_elem('outer_code');
458 0           $self->_attr('argcount' => $self->r_short);
459 0           $self->_attr('nlocals' => $self->r_short);
460 0           $self->_attr('stacksize' => $self->r_short);
461 0           $self->_attr('flags' => $self->r_short);
462              
463 0           $self->_start_elem('code');
464 0           $self->disassemble($self->r_object(emit => 0));
465 0           $self->_end_elem('code');
466              
467 0           $self->r_object(name => 'constants', emit => 1);
468 0           $self->r_object(name => 'names', emit => 1);
469 0           $self->r_object(name => 'varnames', emit => 1);
470 0           $self->r_object(name => 'freevars', emit => 1);
471 0           $self->r_object(name => 'cellvars', emit => 1);
472              
473 0           $self->_start_elem('filename');
474 0           $self->r_object(emit => 1);
475 0           $self->_end_elem('filename');
476              
477 0           $self->_start_elem('name');
478 0           $self->r_object(emit => 1);
479 0           $self->_end_elem('name');
480              
481 0           $self->_start_elem('lineno');
482 0           $self->_chars($self->r_short);
483 0           $self->_end_elem('lineno');
484              
485 0           $self->_start_elem('lnotab'); # Maps address to line #?
486 0           $self->r_object(emit => 1);
487 0           $self->_end_elem('lnotab');
488              
489 0           $self->_end_elem('outer_code');
490             }
491              
492              
493             ###############################################################################
494             ###############################################################################
495             ##
496             ## DATA TABLES
497             ##
498             ###############################################################################
499             ###############################################################################
500              
501              
502             $Parrot::Bytecode::DATA = <
503              
504             # This'll amuse you. It's actually lifted directly from dis.py :)
505             # Instruction opcodes for compiled code
506              
507             def_op('STOP_CODE', 0)
508             def_op('POP_TOP', 1)
509             def_op('ROT_TWO', 2)
510             def_op('ROT_THREE', 3)
511             def_op('DUP_TOP', 4)
512             def_op('ROT_FOUR', 5)
513              
514             def_op('UNARY_POSITIVE', 10)
515             def_op('UNARY_NEGATIVE', 11)
516             def_op('UNARY_NOT', 12)
517             def_op('UNARY_CONVERT', 13)
518              
519             def_op('UNARY_INVERT', 15)
520              
521             def_op('BINARY_POWER', 19)
522              
523             def_op('BINARY_MULTIPLY', 20)
524             def_op('BINARY_DIVIDE', 21)
525             def_op('BINARY_MODULO', 22)
526             def_op('BINARY_ADD', 23)
527             def_op('BINARY_SUBTRACT', 24)
528             def_op('BINARY_SUBSCR', 25)
529              
530             def_op('SLICE+0', 30)
531             def_op('SLICE+1', 31)
532             def_op('SLICE+2', 32)
533             def_op('SLICE+3', 33)
534              
535             def_op('STORE_SLICE+0', 40)
536             def_op('STORE_SLICE+1', 41)
537             def_op('STORE_SLICE+2', 42)
538             def_op('STORE_SLICE+3', 43)
539              
540             def_op('DELETE_SLICE+0', 50)
541             def_op('DELETE_SLICE+1', 51)
542             def_op('DELETE_SLICE+2', 52)
543             def_op('DELETE_SLICE+3', 53)
544              
545             def_op('INPLACE_ADD', 55)
546             def_op('INPLACE_SUBTRACT', 56)
547             def_op('INPLACE_MULTIPLY', 57)
548             def_op('INPLACE_DIVIDE', 58)
549             def_op('INPLACE_MODULO', 59)
550             def_op('STORE_SUBSCR', 60)
551             def_op('DELETE_SUBSCR', 61)
552              
553             def_op('BINARY_LSHIFT', 62)
554             def_op('BINARY_RSHIFT', 63)
555             def_op('BINARY_AND', 64)
556             def_op('BINARY_XOR', 65)
557             def_op('BINARY_OR', 66)
558             def_op('INPLACE_POWER', 67)
559              
560             def_op('PRINT_EXPR', 70)
561             def_op('PRINT_ITEM', 71)
562             def_op('PRINT_NEWLINE', 72)
563             def_op('PRINT_ITEM_TO', 73)
564             def_op('PRINT_NEWLINE_TO', 74)
565             def_op('INPLACE_LSHIFT', 75)
566             def_op('INPLACE_RSHIFT', 76)
567             def_op('INPLACE_AND', 77)
568             def_op('INPLACE_XOR', 78)
569             def_op('INPLACE_OR', 79)
570             def_op('BREAK_LOOP', 80)
571              
572             def_op('LOAD_LOCALS', 82)
573             def_op('RETURN_VALUE', 83)
574             def_op('IMPORT_STAR', 84)
575             def_op('EXEC_STMT', 85)
576              
577             def_op('POP_BLOCK', 87)
578             def_op('END_FINALLY', 88)
579             def_op('BUILD_CLASS', 89)
580              
581             HAVE_ARGUMENT = 90 # Opcodes from here have an argument:
582              
583             name_op('STORE_NAME', 90) # Index in name list
584             name_op('DELETE_NAME', 91) # ""
585             def_op('UNPACK_SEQUENCE', 92) # Number of tuple items
586              
587             name_op('STORE_ATTR', 95) # Index in name list
588             name_op('DELETE_ATTR', 96) # ""
589             name_op('STORE_GLOBAL', 97) # ""
590             name_op('DELETE_GLOBAL', 98) # ""
591             def_op('DUP_TOPX', 99) # number of items to duplicate
592             def_op('LOAD_CONST', 100) # Index in const list
593             hasconst.append(100)
594             name_op('LOAD_NAME', 101) # Index in name list
595             def_op('BUILD_TUPLE', 102) # Number of tuple items
596             def_op('BUILD_LIST', 103) # Number of list items
597             def_op('BUILD_MAP', 104) # Always zero for now
598             name_op('LOAD_ATTR', 105) # Index in name list
599             def_op('COMPARE_OP', 106) # Comparison operator
600             hascompare.append(106)
601             name_op('IMPORT_NAME', 107) # Index in name list
602             name_op('IMPORT_FROM', 108) # Index in name list
603              
604             jrel_op('JUMP_FORWARD', 110) # Number of bytes to skip
605             jrel_op('JUMP_IF_FALSE', 111) # ""
606             jrel_op('JUMP_IF_TRUE', 112) # ""
607             jabs_op('JUMP_ABSOLUTE', 113) # Target byte offset from beginning of code
608             jrel_op('FOR_LOOP', 114) # Number of bytes to skip
609              
610             name_op('LOAD_GLOBAL', 116) # Index in name list
611              
612             jrel_op('SETUP_LOOP', 120) # Distance to target address
613             jrel_op('SETUP_EXCEPT', 121) # ""
614             jrel_op('SETUP_FINALLY', 122) # ""
615              
616             def_op('LOAD_FAST', 124) # Local variable number
617             haslocal.append(124)
618             def_op('STORE_FAST', 125) # Local variable number
619             haslocal.append(125)
620             def_op('DELETE_FAST', 126) # Local variable number
621             haslocal.append(126)
622              
623             def_op('SET_LINENO', 127) # Current line number
624             SET_LINENO = 127
625              
626             def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3)
627             def_op('CALL_FUNCTION', 131) # #args + (#kwargs << 8)
628             def_op('MAKE_FUNCTION', 132) # Number of args with default values
629             def_op('BUILD_SLICE', 133) # Number of items
630              
631             def_op('CALL_FUNCTION_VAR', 140) # #args + (#kwargs << 8)
632             def_op('CALL_FUNCTION_KW', 141) # #args + (#kwargs << 8)
633             def_op('CALL_FUNCTION_VAR_KW', 142) # #args + (#kwargs << 8)
634              
635             def_op('EXTENDED_ARG', 143)
636             EXTENDED_ARG = 143
637              
638             EOF
639              
640              
641             #
642             # _init()
643             #
644              
645             # Set up op code data structures
646              
647             sub _init
648             {
649 0     0     my $self = shift;
650 0           my $data = shift;
651 0           my @opnames;
652             my %c; # Natty constants.
653 0           my %has;
654 0           for (split /\n/, $data) { # This ought to come predigested, but I am lazy
655 0 0 0       next if /^#/ or not /\S/;
656 0 0         if (/^def_op\('([^']+)', (\d+)\)/) { $opnames[$2]=$1; }
  0 0          
    0          
    0          
657 0           elsif (/^(jrel|jabs|name)_op\('([^']+)', (\d+)\)/) { $opnames[$3]=$2; $has{$1}{$3}++ }
  0            
658 0           elsif (/(\w+)\s*=\s*(\d+)/) { $c{$1}=$2; }
659 0           elsif (/^has(\w+)\.append\((\d+)\)/) { $has{$1}{$2}++ }
660             }
661 0           $self->{opnames} = \@opnames;
662 0           $self->{has} = \%has;
663 0           $self->{c} = \%c;
664             }
665              
666              
667             #
668             # disassemble()
669             #
670              
671             my @cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', 'is not', 'exception match', 'BAD');
672              
673             sub disassemble
674             {
675 0     0 0   my $self = shift;
676 0           my $code = shift;
677              
678 0 0         confess "NO CODE!" unless $code;
679              
680 0           my @code = map { ord } split //, $code;
  0            
681              
682 0           my $offset = 0;
683 0           my $extarg = 0;
684 0           my @dis;
685              
686 0           while (@code) {
687 0           my $c = shift @code;
688              
689 0           my $opname = $self->opname($c);
690              
691 0           $self->_start_elem('op');
692 0           $self->_attr('offset' => $offset);
693 0           $self->_attr('name' => $opname);
694 0           $self->_attr('code' => $c);
695            
696 0           $offset++;
697 0           my $arg;
698              
699 0 0         if ($c>=$self->{c}{HAVE_ARGUMENT}) {
700 0           $arg = shift @code;
701 0           $arg += (256 * shift (@code)) + $extarg;
702 0           $extarg = 0;
703 0 0         $extarg = $arg * 65535 if ($c == $self->{c}{EXTENDED_ARG});
704 0           $offset+=2;
705              
706 0           $self->_attr('value' => $arg);
707              
708 0 0         if ($self->{has}{const}{$c}) {
    0          
    0          
    0          
    0          
    0          
709 0           $self->_attr('value-type' => 'const-index');
710             }
711             elsif ($self->{has}{"local"}{$c}) {
712 0           $self->_attr('value-type' => 'local-index');
713             }
714             elsif ($self->{has}{name}{$c}) {
715 0           $self->_attr('value-type' => 'name-index');
716             }
717             elsif ($self->{has}{compare}{$c}) {
718 0           $self->_attr('value-type' => 'compare-op');
719 0           $self->_attr('value-compare' => $cmp_op[$arg]);
720             }
721             elsif ($self->{has}{jrel}{$c}) {
722 0           $self->_attr('value-type' => 'jump-offset');
723 0           $self->_attr('value-jump' => 'rel');
724 0           $self->_attr('value-target' => $offset + $arg );
725             }
726             elsif ($self->{has}{jabs}{$c}) {
727 0           $self->_attr('value-type' => 'jump-target');
728 0           $self->_attr('value-jump' => 'rel');
729 0           $self->_attr('value-target' => $arg );
730             }
731             else {
732 0           $self->_attr('value-type' => 'literal');
733             }
734             }
735              
736 0           $self->_end_elem('op');
737             }
738             }
739              
740              
741             #
742             # opname()
743             #
744              
745             sub opname {
746 0     0 0   $_[0]->{opnames}[$_[1]];
747             }
748              
749              
750             1;
751