File Coverage

lib/Java/JVM/Classfile.pm
Criterion Covered Total %
statement 457 561 81.4
branch 67 106 63.2
condition 2 6 33.3
subroutine 85 95 89.4
pod 1 24 4.1
total 612 792 77.2


line stmt bran cond sub pod time code
1              
2             # ConstantPoolEntry
3             package Java::JVM::Classfile::ConstantPoolEntry;
4 7     7   161823 use Class::Struct;
  7         15925  
  7         45  
5 7     7   12398 use overload '""' => \&as_text;
  7         14493  
  7         64  
6             struct(type => '$', #'
7             values => '@');
8             sub as_text {
9 0     0   0 my $self = shift;
10 0         0 return $self->type . '(' . join(", ", @{$self->values}) . ')';
  0         0  
11             }
12             sub value {
13 272     272   1545 my $self = shift;
14 272         9025 return $self->values->[0];
15             }
16              
17             # Method
18             package Java::JVM::Classfile::Method;
19 7     7   1080 use Class::Struct;
  7         21  
  7         37  
20 7     7   984 use overload '""' => \&as_text;
  7         115  
  7         63  
21             struct(access_flags => '@',
22             name => '$',
23             descriptor => '$',
24             attributes => '$'); #'
25             sub as_text {
26 0     0   0 my $self = shift;
27 0         0 my $result = "";
28 0         0 $result .= $self->name . " ";
29 0         0 $result .= $self->descriptor . " ";
30 0         0 $result .= "[" . join(", ", @{$self->access_flags}) . "] ";
  0         0  
31 0         0 $result .="= " . join(", ", @{$self->attributes}) . "] ";
  0         0  
32 0         0 return $result;
33             }
34              
35             # Field
36             package Java::JVM::Classfile::Field;
37 7     7   1186 use Class::Struct;
  7         17  
  7         34  
38 7     7   665 use overload '""' => \&as_text;
  7         14  
  7         46  
39             struct(access_flags => '@',
40             name => '$',
41             descriptor => '$',
42             attributes => '$'); #'
43             sub as_text {
44 0     0   0 my $self = shift;
45 0         0 my $result = "";
46 0         0 $result .= $self->name . " ";
47 0         0 $result .= $self->descriptor . " ";
48 0         0 $result .= "[" . join(", ", @{$self->access_flags}) . "] ";
  0         0  
49 0         0 $result .="= " . join(", ", @{$self->attributes}) . "] ";
  0         0  
50 0         0 return $result;
51             }
52              
53              
54             # Attribute
55             package Java::JVM::Classfile::Attribute;
56 7     7   1303 use Class::Struct;
  7         12  
  7         64  
57 7     7   782 use overload '""' => \&as_text;
  7         23  
  7         56  
58             struct(name => '$',
59             value => '$');
60             sub as_text {
61 0     0   0 my $self = shift;
62 0         0 my $name = $self->name;
63 0         0 return $name . ' (' . $self->value . ')';
64             }
65              
66             # Attribute::Code
67             package Java::JVM::Classfile::Attribute::Code;
68 7     7   840 use Class::Struct;
  7         10  
  7         37  
69 7     7   651 use overload '""' => \&as_text;
  7         13  
  7         39  
70             struct(max_stack => '$',
71             max_locals => '$',
72             code => '$',
73             exception_table => '$',
74             attributes => '$');
75             sub as_text {
76 0     0   0 my $self = shift;
77 0         0 my $return;
78 0         0 $return .= "stack(" . $self->max_stack . ")";
79 0         0 $return .= ", locals(" . $self->max_locals . ")";
80             }
81              
82             # Struct
83             package Java::JVM::Classfile::Struct;
84 7     7   1057 use Class::Struct;
  7         16  
  7         26  
85 7     7   611 use overload '""' => \&as_text;
  7         22  
  7         42  
86             struct(magic => '$',
87             version => '$',
88             constant_pool => '$',
89             access_flags => '@',
90             class => '$',
91             superclass => '$',
92             interfaces => '$',
93             fields => '$',
94             methods => '$',
95             attributes => '$',
96             ); #'
97             sub as_text {
98 0     0   0 my $self = shift;
99 0         0 my $result;
100 0         0 $result .= "Magic: " . $self->magic . "\n";
101 0         0 $result .= "Version: " . $self->version . "\n";
102 0         0 $result .= "Class: " . $self->class . "\n";
103 0         0 $result .= "Superclass: " . $self->superclass . "\n";
104 0         0 $result .= "Constant pool:\n" . join(", ", @{$self->constant_pool}) . "\n";
  0         0  
105 0         0 $result .= "Access flags: " . join(", ", @{$self->access_flags}) . "\n";
  0         0  
106 0         0 $result .= "Interfaces: " . join(", ", @{$self->interfaces} ) . "\n";
  0         0  
107 0         0 $result .= "Fields:\n" . join(",\n", @{$self->fields}) . "\n";
  0         0  
108 0         0 $result .= "Methods:\n" . join(",\n", @{$self->methods}) . "\n";
  0         0  
109 0         0 $result .= "Attributes:\n" . join(", ", @{$self->attributes}) . "\n";
  0         0  
110 0         0 return $result;
111             }
112              
113             # Instruction
114             package Java::JVM::Classfile::Instruction;
115 7     7   2673 use Class::Struct;
  7         19  
  7         27  
116             struct(label => '$',
117             op => '$', # '
118             args => '@');
119 7     7   847 use overload '""' => \&as_text;
  7         15  
  7         34  
120             sub as_text {
121 181     181   376 my $self = shift;
122 181         3513 my $label = $self->label;
123 181         4650 my $op = $self->op;
124 181         952 my @args = @{$self->args};
  181         3412  
125              
126 181         1008 my $output;
127 181 50       641 $output .= 'L' . $label . ':' if defined $label;
128 181         204 $output .= "\t";
129 181         343 $output .= $op;
130 181         181 $output .= "\t";
131 181         280 $output .= join(", ", @args);
132 181         3789 return $output;
133             }
134              
135             # LineNumber
136             package Java::JVM::Classfile::LineNumber;
137 7     7   1266 use Class::Struct;
  7         10  
  7         32  
138             struct(offset => '$',
139             line => '$');
140              
141             package Java::JVM::Classfile::LocalVariable;
142 7     7   660 use Class::Struct;
  7         54  
  7         24  
143             struct(start_pc => '$', length => '$', name => '$', descriptor => '$', index => '$');
144              
145             package Java::JVM::Classfile::LocalVariableType;
146 7     7   869 use Class::Struct;
  7         11  
  7         113  
147             struct(start_pc => '$', length => '$', name => '$', signature => '$', index => '$');
148              
149             package Java::JVM::Classfile::Exception;
150 7     7   849 use Class::Struct;
  7         10  
  7         32  
151             struct(start_pc => '$', end_pc => '$', handler_pc => '$', catch_type => '$');
152              
153              
154             # Classfile
155             package Java::JVM::Classfile;
156              
157 7     7   688 use strict;
  7         11  
  7         276  
158 7     7   38 use vars qw($VERSION);
  7         13  
  7         372  
159 7     7   6584 use IO::File;
  7         78264  
  7         967  
160 7     7   54 use Carp qw(croak);
  7         16  
  7         288  
161              
162 7     7   37 use constant Utf8 => 1;
  7         14  
  7         605  
163 7     7   36 use constant Integer => 3;
  7         36  
  7         317  
164 7     7   43 use constant Float => 4;
  7         9  
  7         267  
165 7     7   32 use constant Long => 5;
  7         16  
  7         298  
166 7     7   33 use constant Double => 6;
  7         13  
  7         273  
167 7     7   33 use constant Class => 7;
  7         11  
  7         288  
168 7     7   56 use constant Fieldref => 9;
  7         12  
  7         369  
169 7     7   32 use constant String => 8;
  7         24  
  7         266  
170 7     7   29 use constant Methodref => 10;
  7         112  
  7         546  
171 7     7   39 use constant InterfaceMethodref => 11;
  7         12  
  7         274  
172 7     7   31 use constant NameAndType => 12;
  7         12  
  7         325  
173              
174 7     7   32 use constant ACC_PUBLIC => 0x0001;
  7         13  
  7         257  
175 7     7   30 use constant ACC_PRIVATE => 0x0002;
  7         100  
  7         277  
176 7     7   31 use constant ACC_PROTECTED => 0x0004;
  7         17  
  7         264  
177 7     7   30 use constant ACC_STATIC => 0x0008;
  7         11  
  7         268  
178              
179 7     7   32 use constant ACC_FINAL => 0x0010;
  7         8  
  7         275  
180 7     7   30 use constant ACC_SYNCHRONIZED => 0x0020;
  7         9  
  7         408  
181 7     7   37 use constant ACC_VOLATILE => 0x0040;
  7         8  
  7         303  
182 7     7   31 use constant ACC_TRANSIENT => 0x0080;
  7         10  
  7         249  
183              
184 7     7   30 use constant ACC_NATIVE => 0x0100;
  7         11  
  7         246  
185 7     7   30 use constant ACC_INTERFACE => 0x0200;
  7         11  
  7         366  
186 7     7   32 use constant ACC_ABSTRACT => 0x0400;
  7         13  
  7         9305  
187 7     7   41 use constant ACC_STRICT => 0x0800;
  7         19  
  7         343  
188              
189             # Applies to classes compiled by new compilers only
190 7     7   30 use constant ACC_SUPER => 0x0020;
  7         10  
  7         502  
191 7     7   38 use constant MAX_ACC_FLAG => ACC_ABSTRACT;
  7         10  
  7         1231  
192             my @CLASSACCESS;
193             $CLASSACCESS[0] = "public";
194             $CLASSACCESS[3] = "final";
195             $CLASSACCESS[5] = "super";
196             $CLASSACCESS[8] = "interface";
197             $CLASSACCESS[9] = "abstract";
198              
199             my @METHODACCESS;
200             $METHODACCESS[0] = "public";
201             $METHODACCESS[1] = "private";
202             $METHODACCESS[2] = "protected";
203             $METHODACCESS[3] = "static";
204             $METHODACCESS[4] = "final";
205             $METHODACCESS[5] = "synchronized";
206             $METHODACCESS[7] = "native";
207             $METHODACCESS[9] = "abstract";
208             $METHODACCESS[10] = "strict";
209              
210             my @ACCESS = (
211             "public", "private", "protected", "static", "final", "synchronized",
212             "volatile", "transient", "native", "interface", "abstract");
213              
214             $VERSION = '0.20';
215              
216 7     7   34 use constant T_BOOLEAN => 4;
  7         10  
  7         295  
217 7     7   32 use constant T_CHAR => 5;
  7         15  
  7         279  
218 7     7   30 use constant T_FLOAT => 6;
  7         13  
  7         245  
219 7     7   37 use constant T_DOUBLE => 7;
  7         10  
  7         281  
220 7     7   30 use constant T_BYTE => 8;
  7         13  
  7         520  
221 7     7   48 use constant T_SHORT => 9;
  7         13  
  7         328  
222 7     7   34 use constant T_INT => 10;
  7         9  
  7         319  
223 7     7   32 use constant T_LONG => 11;
  7         13  
  7         274  
224 7     7   34 use constant T_VOID => 12;
  7         8  
  7         334  
225 7     7   40 use constant T_ARRAY => 13;
  7         13  
  7         268  
226 7     7   32 use constant T_OBJECT => 14;
  7         11  
  7         296  
227 7     7   35 use constant T_REFERENCE => 14;
  7         10  
  7         389  
228 7     7   56 use constant T_UNKNOWN => 15;
  7         12  
  7         317  
229 7     7   38 use constant T_ADDRESS => 16;
  7         19  
  7         327  
230              
231             # Import all the constants
232 7     7   5599 use Java::JVM::Classfile::Ops qw(%ops);
  7         115  
  7         35332  
233              
234             sub new {
235 6     6 1 4293 my $proto = shift;
236 6         23 my $filename = shift;
237 6   33     50 my $class = ref($proto) || $proto;
238 6         15 my $self = {};
239 6         20 $self->{FILENAME} = $filename;
240              
241 6         20 bless($self, $class);
242 6         33 return $self->_parse;
243              
244             # return $self;
245             }
246              
247             sub _parse {
248 6     6   18 my $self = shift;
249 6 50       87 $self->{FH} = IO::File->new($self->{FILENAME}) or croak("Couldn't read class " . $self->{FILENAME} . "!");
250              
251 6         734 my $magic = $self->check_magic;
252 6         37 my $version = $self->read_version;
253 6         44 my $constant_pool = $self->read_constant_pool;
254 6         37 my($access_flags, $class, $superclass) = $self->read_class_info($constant_pool);
255 6         27 my $interfaces = $self->read_interfaces($constant_pool);
256 6         32 my $fields = $self->read_fields($constant_pool);
257 6         40 my $methods = $self->read_methods($constant_pool);
258 6         21 my $attributes = $self->read_attributes($constant_pool);
259              
260 6         187 my $struct = Java::JVM::Classfile::Struct->new(
261             magic => $magic,
262             version => $version,
263             constant_pool => $constant_pool,
264             access_flags => $access_flags,
265             class => $class,
266             superclass => $superclass,
267             interfaces => $interfaces,
268             fields => $fields,
269             methods => $methods,
270             attributes => $attributes,
271             );
272              
273             # print $struct;
274              
275 6 50       904 die "Junk at end of file!\n" unless $self->{FH}->eof;
276 6         189 $self->{FH}->close;
277 6         268 return $struct;
278             }
279              
280             sub check_magic {
281 6     6 0 17 my $self = shift;
282 6         39 my $magic = $self->read_u4;
283 6 50       45 die "Not Java class file!\n" unless ($magic eq 0xCAFEBABE);
284 6         20 return $magic;
285             }
286              
287             sub read_version {
288 6     6 0 12 my $self = shift;
289 6         31 my $minor = $self->read_u2;
290 6         25 my $major = $self->read_u2;
291 6         33 return "$major.$minor";
292             }
293              
294             sub read_constant_pool {
295 6     6 0 13 my $self = shift;
296 6         49 my $count = $self->read_u2;
297              
298 6         13 my @constant_pool;
299              
300             # print "Constant pool entries: $count \n";
301 6         30 for(my $index=1; $index<$count; $index++) {
302             # print "constant pool $index: ";
303 227         6430 my $type = $self->read_u1;
304 227 100       940 if ($type == Methodref) {
    100          
    50          
    100          
    100          
    100          
    50          
    0          
    0          
    0          
    0          
305 25         58 my $class_index = $self->read_u2;
306 25         59 my $name_and_type_index = $self->read_u2;
307 25         737 $constant_pool[$index] = Java::JVM::Classfile::ConstantPoolEntry->new(type =>
308             'methodref', values => [$class_index, $name_and_type_index]);
309             # print "methodref $class_index, $name_and_type_index\n";
310             } elsif ($type == Fieldref) {
311 7         31 my $class_index = $self->read_u2;
312 7         24 my $name_and_type_index = $self->read_u2;
313 7         228 $constant_pool[$index] = Java::JVM::Classfile::ConstantPoolEntry->new(type =>
314             'fieldref', values => [$class_index, $name_and_type_index]);
315             # print "fieldref $class_index, $name_and_type_index\n";
316             } elsif ($type == InterfaceMethodref) {
317 0         0 my $class_index = $self->read_u2;
318 0         0 my $name_and_type_index = $self->read_u2;
319 0         0 $constant_pool[$index] = Java::JVM::Classfile::ConstantPoolEntry->new(type =>
320             'interfacemethodref', values => [$class_index, $name_and_type_index]);
321             # print "interfacemethodref $class_index, $name_and_type_index\n";
322             } elsif ($type == Class) {
323 29         128 my $name_index = $self->read_u2;
324 29         684 $constant_pool[$index] = Java::JVM::Classfile::ConstantPoolEntry->new(type =>
325             'class', values => [$name_index]);
326             # print "class $name_index\n";
327             } elsif ($type == Utf8) {
328 132         254 my $length = $self->read_u2;
329 132         150 my $string;
330 132         348 $string .= chr($self->read_u1) foreach (1..$length);
331 132         3267 $constant_pool[$index] = Java::JVM::Classfile::ConstantPoolEntry->new(type =>
332             'utf8', values => [$string]);
333             # print "String: $string\n";
334             } elsif ($type == NameAndType) {
335 29         62 my $name_index = $self->read_u2;
336 29         82 my $descriptor_index = $self->read_u2;
337 29         666 $constant_pool[$index] = Java::JVM::Classfile::ConstantPoolEntry->new(type =>
338             'nameandtype', values => [$name_index, $descriptor_index]);
339             # print "nameandtype: $name_index $descriptor_index\n";
340             } elsif ($type == String) {
341 5         16 my $string_index = $self->read_u2;
342 5         129 $constant_pool[$index] = Java::JVM::Classfile::ConstantPoolEntry->new(type =>
343             'string', values => [$string_index]);
344             # print "String: $string_index\n";
345             } elsif ($type == Integer) {
346 0         0 my $bytes = $self->read_u4;
347 0         0 $constant_pool[$index] = Java::JVM::Classfile::ConstantPoolEntry->new(type =>
348             'integer', values => [$bytes]);
349             # print "Integer: \n";
350             } elsif ($type == Float) {
351 0         0 my $bytes = $self->read_u4;
352 0         0 my $float = $self->float_value($bytes);
353 0         0 $constant_pool[$index] = Java::JVM::Classfile::ConstantPoolEntry->new(type =>
354             'float', values => [$float]);
355             # print "Float: \n";
356              
357             # JVM Specs: All 8-byte constants take up two entries in the constant_pool
358             # table of the class file. If a CONSTANT_Long_info or CONSTANT_Double_info
359             # structure is the item in the constant_pool table at index n, then the next
360             # usable item in the pool is located at index n+2. The constant_pool index
361             # n+1 must be valid but is considered unusable. (In retrospect, making 8-byte
362             # constants take two constant pool entries was a poor choice.)
363              
364             } elsif ($type == Long) {
365 0         0 my $high_bytes = $self->read_u4;
366 0         0 my $low_bytes = $self->read_u4;
367 0         0 my $long = $self->long_value($high_bytes, $low_bytes);
368 0         0 $constant_pool[$index] = Java::JVM::Classfile::ConstantPoolEntry->new(type =>
369             'long', values => [$long]);
370 0         0 $constant_pool[++$index] = 0;
371             # print "Long: $long\n";
372             } elsif ($type == Double) {
373 0         0 my $high_bytes = $self->read_u4;
374 0         0 my $low_bytes = $self->read_u4;
375 0         0 my $double = $self->double_value($high_bytes, $low_bytes);
376 0         0 $constant_pool[$index] = Java::JVM::Classfile::ConstantPoolEntry->new(type =>
377             'double', values => [$double]);
378 0         0 $constant_pool[++$index] = 0;
379             # print "Double: $high_bytes, $low_bytes\n";
380             } else {
381 0         0 die "unknown constant type $type in pool!\n";
382             }
383             }
384              
385 6         163 return \@constant_pool;
386             }
387              
388             # JVM long format is ((long) high_bytes << 32) + low_bytes
389             sub long_value {
390              
391 0     0 0 0 my $self = shift;
392 0         0 my ($high_bytes, $low_bytes) = @_;
393 0         0 return ($high_bytes << 32) + $low_bytes;
394             }
395              
396             # JVM floats are in IEEE 754 floating-point single format
397             sub float_value {
398              
399 0     0 0 0 my $self = shift;
400 0         0 my ($bits) = @_;
401              
402 0 0       0 my $s = (($bits >> 31) == 0) ? 1 : -1;
403 0         0 my $e = (($bits >> 23) & 0xff);
404 0 0       0 my $m = ($e == 0) ? ($bits & 0x7fffff) << 1 : ($bits & 0x7fffff) | 0x800000;
405              
406 0         0 return $s*$m*(2**($e-150));
407             }
408              
409             # JVM doubles are in IEEE 754 floating-point double format
410             sub double_value {
411              
412 0     0 0 0 my $self = shift;
413 0         0 my ($high_bytes, $low_bytes) = @_;
414 0         0 return 3.14;
415             }
416              
417             sub read_class_info {
418 6     6 0 16 my($self, $constant_pool) = @_;
419              
420              
421 6         47 my @flags;
422 6         50 my $access_flags = $self->read_u2;
423              
424 6 50       39 if(($access_flags & ACC_INTERFACE) != 0) {
425 0         0 $access_flags |= ACC_ABSTRACT;
426             }
427              
428 6 50 33     38 if((($access_flags & ACC_ABSTRACT) != 0) &&
429             (($access_flags & ACC_FINAL) != 0 )) {
430 0         0 die("Class can't be both final and abstract");
431             }
432              
433             # print "Access flags: $access_flags = ";
434 6         53 my $bits = reverse unpack("B*", pack ("c*" ,$access_flags));
435             # print "($bits) is ";
436 6         23 foreach my $index (0..length($bits)) {
437             # print $CLASSACCESS[$index] if substr($bits, $index, 1);
438 54 100       142 push @flags, $CLASSACCESS[$index] if substr($bits, $index, 1);
439             }
440             # print "\n";
441 6         32 my $myclass_name = $self->read_class_name($constant_pool, 'Class');
442              
443 6         55 my $superclass_name = $self->read_class_name($constant_pool, 'Superclass');
444              
445 6         58 return \@flags, $myclass_name, $superclass_name;
446             # print "Class is $class_name_index, super $superclass_name_index\n";
447             }
448              
449             sub read_interfaces {
450 6     6 0 17 my ($self, $constant_pool) = @_;
451              
452 6         15 my $interfaces_count = $self->read_u2;
453 6         13 my @interfaces;
454 6         22 for my $ii (0 .. $interfaces_count-1) {
455 1         13 push @interfaces, $self->read_class_name($constant_pool, 'Interface');
456             }
457              
458 6         67 return \@interfaces;
459             }
460              
461             sub read_class_name {
462 13     13 0 28 my ($self, $constant_pool, $diagnostics) = @_;
463 13         77 return get_class_name($self->read_u2, $constant_pool, $diagnostics);
464             }
465              
466             sub get_class_name {
467 14     14 0 32 my ($index, $constant_pool, $diagnostics) = @_;
468 14         28 my $class = $constant_pool->[$index];
469 14 50       362 die "$diagnostics name index doesn't point to class!" unless $class->type eq 'class';
470 14         149 my $class_name = $constant_pool->[$class->value];
471 14 50       357 die "$diagnostics name class doesn't point to string!" unless $class_name->type eq 'utf8';
472 14         126 return $class_name->value;
473             }
474              
475             sub read_fields {
476 6     6 0 13 my ($self, $constant_pool) = @_;
477              
478 6         23 my $fields_count = $self->read_u2;
479 6         33 my @fields;
480 6         28 for my $fi (0 .. $fields_count-1) {
481 2         108 my @access_flags = $self->read_access_flags;
482              
483 2         17 my $name = $self->read_name($constant_pool);
484 2         20 my $descriptor = $self->read_descriptor($constant_pool);
485 2         19 my $attributes = $self->read_attributes($constant_pool);
486              
487 2         57 push @fields, Java::JVM::Classfile::Field->new(
488             name => $name,
489             access_flags => \@access_flags,
490             descriptor => $descriptor,
491             attributes => $attributes
492             );
493             }
494            
495 6         56 return \@fields;
496             }
497              
498             sub read_constant_string {
499 44     44 0 73 my ($self, $constant_pool, $diagnostics) = @_;
500 44         84 my $name_index = $self->read_u2; #name_index
501 44         67 my $name_struct = $constant_pool->[$name_index];
502 44 50       962 die "$diagnostics: index doesn't point to string" unless $name_struct->type eq 'utf8';
503 44         335 return $name_struct->value;
504             }
505              
506 22     22 0 63 sub read_name { read_constant_string(@_, 'name');}
507              
508 22     22 0 49 sub read_descriptor { read_constant_string(@_, 'descriptor');}
509              
510 0     0 0 0 sub read_signature { read_constant_string(@_, 'signature');}
511              
512             sub read_access_flags {
513 17     17 0 24 my $self = shift;
514 17         51 my $access_flags = $self->read_u2;
515 17         27 my @access_flags;
516              
517 17         68 my $bits = reverse unpack("B*", pack ("c*" ,$access_flags));
518 17         38 foreach my $index (0..length($bits)) {
519 153 100       431 push @access_flags, $METHODACCESS[$index] if substr($bits, $index, 1);
520             }
521 17         65 return @access_flags;
522             }
523              
524             sub read_methods {
525 6     6 0 22 my($self, $constant_pool) = @_;
526              
527 6         17 my @methods;
528              
529 6         18 my $method_count = $self->read_u2;
530             # print "Methods: $method_count\n";
531              
532 6         21 foreach my $index (0..$method_count-1) {
533             # $methods[$_] = $self->read_u2;
534              
535 15         635 my @access_flags = $self->read_access_flags;
536 15         55 my $name = $self->read_name($constant_pool);
537 15         162 my $descriptor = $self->read_descriptor($constant_pool);
538 15         131 my $attributes = $self->read_attributes($constant_pool);
539              
540 15         357 push @methods, Java::JVM::Classfile::Method->new(
541             name => $name,
542             access_flags => \@access_flags,
543             descriptor => $descriptor,
544             attributes => $attributes,
545             );
546             }
547              
548 6         267 return \@methods;
549             }
550              
551             sub read_attributes {
552 38     38 0 60 my($self, $constant_pool) = @_;
553              
554 38         92 my $attributes_count = $self->read_u2;
555             # print "count: $attributes_count\n";
556 38         55 my @attributes;
557 38         98 foreach (0..$attributes_count-1) {
558 38         163 my $attribute_name_index = $self->read_u2;
559             # print "index: $attribute_name_index\n";
560 38         65 my $attribute_name = $constant_pool->[$attribute_name_index];
561             # print "= $attribute_name\n";
562 38 50       795 die "attribute_name_index doesn't point to string" unless $attribute_name->type eq 'utf8';
563 38         304 $attribute_name = $attribute_name->value;
564 38         263 my $attribute_length = $self->read_u4;
565 38         46 my $info;
566 38 100       172 if ($attribute_name eq 'Code') {
    100          
    100          
    50          
    50          
    0          
567 15         55 my $max_stack = $self->read_u2;
568 15         43 my $max_locals = $self->read_u2;
569 15         66 my $code = $self->read_code($constant_pool);
570 15         44 my $exception_table_length = $self->read_u2;
571 15         23 my @exception_table;
572 15         59 for (1 .. $exception_table_length) {
573 4         117 my $start_pc = $self->read_u2;
574 4         9 my $end_pc = $self->read_u2;
575 4         10 my $handler_pc = $self->read_u2;
576 4         19 my $catch_type_index = $self->read_u2;
577 4 100       14 my $catch_type = $catch_type_index ? get_class_name($catch_type_index, $constant_pool, 'Exception') : "*";
578 4         93 push @exception_table, Java::JVM::Classfile::Exception->new(
579             start_pc=>$start_pc, end_pc=>$end_pc, handler_pc=>$handler_pc, catch_type=>$catch_type)
580             }
581            
582 15         164 my $atts = $self->read_attributes($constant_pool);
583              
584 15         352 $info = Java::JVM::Classfile::Attribute::Code->new(
585             max_stack => $max_stack,
586             max_locals => $max_locals,
587             code => $code,
588             exception_table => \@exception_table,
589             attributes => $atts,
590             );
591             } elsif ($attribute_name eq 'SourceFile') {
592 6 50       27 die "length not 2" if $attribute_length != 2;
593 6         16 my $sourcefile_index = $self->read_u2;
594 6         14 my $sourcefile = $constant_pool->[$sourcefile_index];
595 6 50       123 die "sourcefile_index doesn't point to string" unless $sourcefile->type eq 'utf8';
596 6         65 $info = $sourcefile->value;
597             } elsif ($attribute_name eq 'LineNumberTable') {
598 15         34 my $line_number_table_length = $self->read_u2;
599 15         106 my @lines;
600 15         38 foreach (0..$line_number_table_length-1) {
601 50         761 my $start_pc = $self->read_u2;
602 50         98 my $line_number = $self->read_u2;
603 50         1139 push @lines, Java::JVM::Classfile::LineNumber->new(offset => $start_pc, line => $line_number);
604             }
605 15         408 $info = \@lines;
606             } elsif ($attribute_name eq 'LocalVariableTypeTable') {
607 0         0 my $local_variable_table_length = $self->read_u2;
608 0         0 my @local_variables;
609 0         0 for (1 .. $local_variable_table_length) {
610 0         0 my $start_pc = $self->read_u2;
611 0         0 my $length = $self->read_u2; #TODO validate
612 0         0 my $name = $self->read_name($constant_pool);
613 0         0 my $signature = $self->read_signature($constant_pool);
614 0         0 my $index = $self->read_u2;
615 0         0 push @local_variables, Java::JVM::Classfile::LocalVariableType->new(
616             start_pc=>$start_pc, 'length'=>$length, name=>$name, signature=>$signature, 'index'=>$index
617             );
618             }
619 0         0 $info = \@local_variables;
620             } elsif ($attribute_name eq 'LocalVariableTable') {
621 2         5 my $local_variable_table_length = $self->read_u2;
622 2         3 my @local_variables;
623 2         7 for (1 .. $local_variable_table_length) {
624 5         137 my $start_pc = $self->read_u2;
625 5         18 my $length = $self->read_u2; #TODO validate
626 5         13 my $name = $self->read_name($constant_pool);
627 5         36 my $descriptor = $self->read_descriptor($constant_pool);
628 5         34 my $index = $self->read_u2;
629 5         109 push @local_variables, Java::JVM::Classfile::LocalVariable->new(
630             start_pc=>$start_pc, 'length'=>$length, name=>$name, descriptor=>$descriptor, 'index'=>$index
631             );
632             }
633 2         95 $info = \@local_variables;
634             } elsif ($attribute_name eq 'Signature') {
635 0         0 $info = $self->read_signature($constant_pool);
636             } else {
637 0         0 warn "unknown attribute $attribute_name!\n";
638             # Fake it for now
639 0         0 $info = "";
640 0         0 $info .= chr($self->read_u1) foreach (0..$attribute_length-1);
641             }
642             # print "info: $info<--\n" if $attribute_name ne 'Code';
643 38         1673 push @attributes, Java::JVM::Classfile::Attribute->new(name => $attribute_name, value => $info);
644             }
645 38         989 return \@attributes;
646             }
647              
648              
649             sub read_code {
650 15     15 0 31 my($self, $constant_pool) = @_;
651              
652 15         32 my $code_length = $self->read_u4;
653 15         25 my $offset = 0;
654 15         20 my $is_wide = 0;
655 15         25 my $index = 0;
656              
657 15         22 my @instructions;
658             my @fixups;
659 0         0 my %offsets;
660 0         0 my %offset;
661              
662 15         57 while($offset < $code_length) {
663 168         181 my $origoffset = $offset;
664 168         316 my $u1 = $self->read_u1;
665 168         208 $offset += 1;
666 168         337 my $op = $ops{$u1};
667 168         352 my $opname = $op->{name};
668 168         242 my $type = $op->{type};
669 168         329 my @operands;
670             # print "# $opname ($type)\n";
671              
672 168 100       653 if ($type eq 'noargs') {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
673             } elsif ($type eq 'byte') {
674 3         9 my $u1 = $self->read_u1;
675 3         5 $offset += 1;
676 3         7 push @operands, $u1;
677             } elsif ($type eq 'bytevar') {
678 3         8 my $u1 = $self->read_u1;
679 3         5 $offset += 1;
680 3         6 push @operands, $u1;
681             } elsif ($type eq 'byteindex') {
682 5         14 my $u1 = $self->read_u1;
683 5         8 $offset += 1;
684 5         20 push @operands, $self->get_index($u1, $constant_pool);
685             } elsif ($type eq 'twobytes') {
686 4         10 my $u1 = $self->read_u1;
687 4 50       11 $u1 = $u1 - 256 if $u1 > 128;
688 4         5 $offset += 1;
689 4         8 push @operands, $u1;
690 4         8 $u1 = $self->read_u1;
691 4 50       19 $u1 = $u1 - 256 if $u1 > 128;
692 4         6 $offset += 1;
693 4         6 push @operands, $u1;
694             } elsif ($type eq 'int') {
695 1         3 my $u2 = $self->read_u2;
696 1         2 $offset += 2;
697 1         2 push @operands, $u2;
698             } elsif ($type eq 'intindex') {
699 50         110 my $u2 = $self->read_u2;
700 50         66 $offset += 2;
701 50         129 push @operands, $self->get_index($u2, $constant_pool);
702             } elsif ($type eq 'intbranch') {
703 13         31 my $u2 = $self->read_u2;
704 13 100       36 $u2 = $u2 - 65536 if $u2 > 31268;
705 13         21 $offset += 2;
706 13         20 push @operands, $u2;
707 13         24 push @fixups, $index;
708             } else {
709 0         0 die "unknown type $type, uh-oh!";
710             }
711              
712 168         4013 my $i = Java::JVM::Classfile::Instruction->new(op => $opname, args => \@operands, label => 'L'.$origoffset);
713 168         5216 push @instructions, $i;
714             # print "$i\n";
715             # print "# $offset $opname " . join(", ", @operands) . "\n";
716              
717 168         3107 $offsets{$origoffset} = $index;
718 168         315 $offset{$index} = $origoffset;
719 168         499 $index++;
720             }
721              
722             # Fix up pointers
723 15         38 my %is_target;
724 15         50 foreach my $fixup (@fixups) {
725 13         68 my $i = $instructions[$fixup];
726 13         294 my $offset = $i->args->[0] + $offset{$fixup};
727 13         87 my $target = $instructions[$offsets{$offset}];
728             # print "! Fixing up $i ($offset) -> $target\n";
729 13         266 $instructions[$fixup] = Java::JVM::Classfile::Instruction->new(
730             op => $i->op, args => ['L'.$offset], label => $i->label);
731 13         504 $i = $instructions[$fixup];
732 13         87 $is_target{$target}++;
733             }
734              
735 15         122 foreach my $i (@instructions) {
736 168 100       1052 $i->label(undef) unless $is_target{$i};
737             }
738              
739 15         180 return \@instructions;
740             }
741              
742             sub get_index {
743 55     55 0 286 my($self, $index, $constant_pool) = @_;
744              
745 55         78 my $constant = $constant_pool->[$index];
746 55         1744 my $type = $constant->type;
747 55         322 my @operands;
748              
749             # print "# $index = $constant\n";
750 55 100       178 if ($type eq 'methodref') {
    50          
    100          
    100          
    50          
    0          
    0          
751 36         1498 push @operands, $constant_pool->[$constant_pool->[$constant->values->[0]]->values->[0]]->value;
752 36         1070 push @operands, $constant_pool->[$constant_pool->[$constant->values->[1]]->values->[0]]->value;
753 36         1062 push @operands, $constant_pool->[$constant_pool->[$constant->values->[1]]->values->[1]]->value;
754             } elsif ($type eq 'interfacemethodref') {
755 0         0 push @operands, $constant_pool->[$constant_pool->[$constant->values->[0]]->values->[0]]->value;
756 0         0 push @operands, $constant_pool->[$constant_pool->[$constant->values->[1]]->values->[0]]->value;
757 0         0 push @operands, $constant_pool->[$constant_pool->[$constant->values->[1]]->values->[1]]->value;
758             } elsif ($type eq 'fieldref') {
759 10         301 push @operands, $constant_pool->[$constant_pool->[$constant->values->[0]]->values->[0]]->value;
760 10         258 push @operands, $constant_pool->[$constant_pool->[$constant->values->[1]]->values->[0]]->value;
761 10         257 push @operands, $constant_pool->[$constant_pool->[$constant->values->[1]]->values->[1]]->value;
762             } elsif ($type eq 'class') {
763 4         25 push @operands, $constant_pool->[$constant->value]->value;
764             } elsif ($type eq 'string') {
765 5         15 push @operands, $constant_pool->[$constant->value]->value;
766             } elsif ($type eq 'float') {
767 0         0 push @operands, $constant->value;
768             } elsif ($type eq 'integer') {
769 0         0 push @operands, $constant->value;
770             } else {
771 0         0 die "unknown index type $type!\n";
772             }
773              
774 55         479 return @operands;
775             }
776              
777             sub read_u4 {
778 59     59 0 79 my $self = shift;
779 59         98 my $fh = $self->{FH};
780 59         176 local $/ = \1;
781 59         365 my $int = unpack('C', <$fh>);
782 59         79 $int *= 256;
783 59         107 $int += unpack('C', <$fh>);
784 59         80 $int *= 256;
785 59         98 $int += unpack('C', <$fh>);
786 59         71 $int *= 256;
787 59         99 $int += unpack('C', <$fh>);
788 59         178 return $int;
789             }
790              
791             sub read_u2 {
792 743     743 0 854 my $self = shift;
793 743         1155 my $fh = $self->{FH};
794 743         1873 local $/ = \1;
795 743         1560 my $int = unpack('C', <$fh>);
796 743         990 $int *= 256;
797 743         1141 $int += unpack('C', <$fh>);
798 743         2474 return $int;
799             }
800              
801             sub read_u1 {
802 1947     1947 0 2141 my $self = shift;
803 1947         2353 my $fh = $self->{FH};
804 1947         4559 local $/ = \1;
805 1947         3784 my $int = unpack('C', <$fh>);
806 1947         7126 return $int;
807             }
808              
809              
810             1;
811              
812             __END__